feat(dsh): DSH-owned role paths in installed instructions (#49)

This commit is contained in:
Andrey Epifancev 2026-10-24 21:11:31 +04:00
parent 05178869fb
commit 60d68464a9
3 changed files with 41 additions and 6 deletions

View File

@ -266,11 +266,20 @@ install_global() {
echo "The dsh-harness bundle enforces guard-main/require-plan and provides the /role command."
echo "DSH adapter: the model loads roles with skill role; /role is the user-facing command."
echo "DSH adapter: there is no SessionStart role hint; infer the role from the first message."
echo "DSH adapter: role files are ~/.dsh/roles, then ~/.claude/roles, then $ROOT/roles."
echo "DSH adapter: bootstrap prefers ~/.dsh/skills; the ~/.claude cache is a fallback."
echo "DSH adapter: role files are ~/.dsh/roles (installed by --roles); legacy Claude Code role locations are a migration fallback."
echo "DSH adapter: superpowers skills are ~/.dsh/skills (installed by --skills from --superpowers-from, DSH_SUPERPOWERS_SOURCE, or ~/.dsh/superpowers); legacy Claude Code locations are a migration fallback."
echo "DSH adapter: a project CLAUDE.md/AGENTS.md is the instruction-file format of this harness; it does not imply a Claude Code runtime."
echo "DSH adapter: codegraph MCP tools are named mcp__codegraph__codegraph_explore."
echo
cat "$ROOT/global/CLAUDE.md"
# The shared global/CLAUDE.md is also read by Claude Code and must not
# change; only the installed DSH copy points at the DSH role layer.
python3 - "$ROOT/global/CLAUDE.md" <<'PY'
import sys
from pathlib import Path
text = Path(sys.argv[1]).read_text(encoding="utf-8")
sys.stdout.write(text.replace("~/.claude/roles", "~/.dsh/roles"))
PY
} > "$target"
echo "written: $target"
}

View File

@ -279,3 +279,27 @@ test('--check fails on an invalid explicit source', () => {
cleanup(home)
}
})
test('--global installs DSH role paths and explains the instruction format', () => {
const home = tempDir()
try {
const globalSource = join(here, '..', '..', 'global', 'CLAUDE.md')
const before = readFileSync(globalSource, 'utf8')
const run = runInstaller(home, ['--global'])
assert.equal(run.status, 0, run.stdout + run.stderr)
const agents = readFileSync(join(home, '.dsh', 'AGENTS.md'), 'utf8')
assert.match(agents, /~\/\.dsh\/roles\/dev\.md/)
assert.doesNotMatch(agents, /~\/\.claude/)
assert.match(agents, /superpowers skills are ~\/\.dsh\/skills/)
assert.match(agents, /instruction-file format of this harness/)
assert.match(agents, /does not imply a Claude Code runtime/)
assert.equal(readFileSync(globalSource, 'utf8'), before, 'global/CLAUDE.md must stay untouched')
const second = runInstaller(home, ['--global'])
assert.equal(second.status, 0, second.stdout + second.stderr)
assert.match(second.stdout, /backup: /)
} finally {
cleanup(home)
}
})

View File

@ -109,8 +109,9 @@ SHALL оставаться fallback'ом для машин, где `--skills --r
Claude Code.
- Строки `~/.claude/roles` в установленной копии MUST заменяться на
`~/.dsh/roles`; `global/CLAUDE.md` в репозитории MUST NOT меняться (его
читает Claude Code).
`~/.dsh/roles`, а adapter-шапка MUST NOT называть пути `~/.claude`: в
установленном файле не остаётся ни одного такого пути. `global/CLAUDE.md` в
репозитории MUST NOT меняться (его читает Claude Code).
- Шапка установленного файла MUST говорить, что проектный `CLAUDE.md` /
`AGENTS.md` — это формат файлов инструкций DSH, а не запущенный Claude Code,
и что роли лежат в `~/.dsh/roles` (`--roles`), а superpowers-скиллы — в
@ -119,7 +120,8 @@ Claude Code.
#### Scenario: Пути ролей после установки
- **WHEN** выполнен `--global` во временный `DSH_HOME`
- **THEN** в `AGENTS.md` есть `~/.dsh/roles/dev.md` и нет `~/.claude/roles`
- **THEN** в `AGENTS.md` есть `~/.dsh/roles/dev.md` и нет ни одного пути
`~/.claude`
#### Scenario: global/CLAUDE.md не тронут
- **WHEN** выполнен `--global`