From 60d68464a9cc6d132c8f8b35246247af033073a3 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 21:11:31 +0400 Subject: [PATCH] feat(dsh): DSH-owned role paths in installed instructions (#49) --- dsh/install.sh | 15 +++++++++--- dsh/tests/install.test.mjs | 24 +++++++++++++++++++ .../specs/dsh-install/spec.md | 8 ++++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/dsh/install.sh b/dsh/install.sh index 1208114..050157e 100755 --- a/dsh/install.sh +++ b/dsh/install.sh @@ -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" } diff --git a/dsh/tests/install.test.mjs b/dsh/tests/install.test.mjs index 29ea9e8..ea06e5b 100644 --- a/dsh/tests/install.test.mjs +++ b/dsh/tests/install.test.mjs @@ -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) + } +}) diff --git a/openspec/changes/dsh-install-source/specs/dsh-install/spec.md b/openspec/changes/dsh-install-source/specs/dsh-install/spec.md index 57ad9a8..522efad 100644 --- a/openspec/changes/dsh-install-source/specs/dsh-install/spec.md +++ b/openspec/changes/dsh-install-source/specs/dsh-install/spec.md @@ -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`