Merge pull request 'DSH: install.sh --roles дописывает DSH-заметку в dev.md (#53)' (#54) from feat/53-dsh-role-notes into main

This commit is contained in:
aep 2026-10-24 17:14:12 +03:00
commit 67ff56190c
3 changed files with 67 additions and 0 deletions

View File

@ -224,6 +224,12 @@ bash dsh/install.sh --global --skills --roles
# mcp__codegraph__codegraph_explore.
```
`--roles` also appends the DSH SDD note to `~/.dsh/roles/dev.md` (the bullet that
points `dev` at the `sdd` skill). The note lives only in the installed copy: the
shared `roles/dev.md` stays Claude Code-compatible, so a Claude session never
sees DSH-only instructions. The insertion is idempotent — every `--roles` run
recopies the role from the repository and re-inserts exactly one note.
Live checks:
1. New session: ask "what skills do you have?" — superpowers skills and the

View File

@ -141,6 +141,11 @@ check() {
else
echo "WARN: roles not found; /role will fail until --roles is run" >&2
fi
if [ -f "$DSH_HOME/roles/dev.md" ] && grep -q 'В DSH-сессии исполнение плана' "$DSH_HOME/roles/dev.md"; then
echo "ok: role dsh notes"
else
echo "WARN: role has no DSH SDD notes; run --roles" >&2
fi
echo "DSH_HOME=$DSH_HOME"
return "$failed"
}
@ -224,6 +229,45 @@ install_roles() {
echo "installed $count roles into $DSH_HOME/roles"
}
# The DSH SDD note belongs to the installed role, not to the shared roles/ layer:
# in Claude Code the note does not apply, so it is written here, after the copy,
# and is idempotent (the source file is overwritten on every --roles run).
install_role_dsh_notes() {
local role="$DSH_HOME/roles/dev.md"
[ -f "$role" ] || return 0
python3 - "$role" <<'PY'
import sys
from pathlib import Path
path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
marker = "В DSH-сессии исполнение плана"
if marker in text:
print("ok: role dsh notes already present")
raise SystemExit(0)
note = (
"- В DSH-сессии исполнение плана идёт по skill `sdd`, а не "
"`subagent-driven-development`: асинхронный `subagent` (бриф и отчёт файлами, "
"ожидание завершением хода и уведомлением, ledger "
"`.superpowers/sdd/<plan>/progress.md` с `## Findings`, fix-loop до 5 раундов). "
"Режим контроллера — по числу параллельных планов: один план — главная сессия, "
"несколько — фоновый контроллер (нужен `maxDepth: 2` из "
"`dsh/subagent-models.yml`). В Claude Code строка не действует: там исходный "
"`subagent-driven-development`."
)
anchor = "- Режим по умолчанию: subagent-driven-development."
lines = text.splitlines(keepends=True)
for i, line in enumerate(lines):
if line.startswith(anchor):
lines.insert(i + 1, note + "\n")
break
else:
lines.append((("" if text.endswith("\n") else "\n") + note + "\n"))
path.write_text("".join(lines), encoding="utf-8")
print("ok: role dsh notes inserted")
PY
}
print_bundle_step() {
cat <<EOF
@ -251,6 +295,7 @@ if [ "$DO_SKILLS" = 1 ]; then
fi
if [ "$DO_ROLES" = 1 ]; then
install_roles
install_role_dsh_notes
fi
if [ "$DO_GLOBAL" = 1 ] || [ "$DO_SKILLS" = 1 ] || [ "$DO_ROLES" = 1 ]; then
print_bundle_step

View File

@ -52,11 +52,27 @@ test('install.sh copies skills and roles instead of linking into ~/.claude', ()
assert.ok(existsSync(join(dshHome, 'roles', `${role}.md`)), `missing ${role}.md`)
}
const devRole = join(dshHome, 'roles', 'dev.md')
const devText = readFileSync(devRole, 'utf8')
assert.match(devText, /В DSH-сессии исполнение плана идёт по skill `sdd`/)
assert.match(devText, /`maxDepth: 2`/)
assert.match(first.stdout, /role dsh notes/)
assert.equal(countNotes(devText), 1, 'note must be inserted once')
const second = run()
assert.equal(second.status, 0, second.stderr)
assert.equal(lstatSync(skill).isSymbolicLink(), false)
assert.ok(existsSync(roleSkill))
assert.match(second.stdout, /role dsh notes/)
assert.equal(countNotes(readFileSync(devRole, 'utf8')), 1, 'note must not duplicate')
const check = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' })
assert.match(check.stdout, /ok: role dsh notes/)
} finally {
cleanup(home)
}
})
function countNotes(text) {
return (text.match(/В DSH-сессии исполнение плана/g) ?? []).length
}