21 KiB
dsh-install Specification
Purpose
Слой установки DSH-обвязки: откуда берутся superpowers-скиллы и роли, что
делает dsh/install.sh --skills/--roles, какие пути попадают в установленные
инструкции. В Claude Code источником служит кэш плагина в ~/.claude; в DSH
источник задаётся установщиком явно (аргумент, переменная или пиннинг в
~/.dsh), а ~/.claude остаётся только legacy-fallback'ом с предупреждением.
Контракт нужен, чтобы свежая установка на машине без Claude Code проходила
полный цикл #48 и чтобы путь ~/.claude не возвращался в runtime незаметно.
Requirements
Requirement: Источник superpowers задаётся при установке
dsh/install.sh --skills SHALL разрешать источник superpowers-скиллов в
порядке приоритета. Сеть нужна только ветке пина; явный источник
синхронизируется локально.
- Порядок:
--superpowers-from <dir>→$DSH_SUPERPOWERS_SOURCE→ клон$DSH_HOME/superpowersна$DSH_SUPERPOWERS_REF→ legacy-кэш Claude Code → legacy~/.claude/skills. - Явный источник (
--superpowers-from,$DSH_SUPERPOWERS_SOURCE) MUST NOT требовать сети и MUST NOT проверять пин. Допускаются две формы: корень checkout (естьskills/using-superpowers/SKILL.md) или каталогskillsтакого корня. Синк явного источника MUST идти в staging вне каталога клона ($DSH_HOME/.skills.synced), потому что$DSH_HOME/superpowersзарезервирован под клон пина. - Каталог скиллов, который не является ни checkout-корнем, ни его
skills/, MUST бытьFAIL(код 1) и в--skills, и в--check: вердикты двух команд MUST совпадать. Тихий откат на legacy запрещён. - Клон-пин: отсутствующий или пустой каталог MUST клонироваться
git clone --depth 1 --branch <ref> <repo> <dir>; неудачный клон MUST удалять недоклонированный каталог (полусостояние не остаётся). - Существующий клон MUST обновляться fetch'ем от
$DSH_SUPERPOWERS_REPO(не от origin клона) с force-рефспеком тега (+refs/tags/<ref>:refs/tags/<ref>) и checkoutFETCH_HEAD: смена репозитория и переехавший тег MUST подхватываться следующим прогоном. Офлайн клон MAY переиспользоваться, только если HEAD уже ровно на пине, и тогда MUST печататьсяWARN; иначе —FAILс командой клона. - Непустой каталог на месте клона без рабочего
.gitMUST бытьFAIL(код 1) без изменения каталога. - Копирование MUST быть заменой каталога назначения (
rm -rf+cp -R), а не симлинком:~/.dsh/skillsне должен зависеть от каталога-источника.
Scenario: Явный источник без ~/.claude
- WHEN
HOME— пустой temp-каталог без~/.claude, а--superpowers-fromуказывает на каталог superpowers - THEN
--skillsсинхронизирует и копирует скиллы в$DSH_HOME/skills, печатает источник и завершается кодом 0, не обращаясь в сеть
Scenario: Переменная окружения вместо флага
- WHEN задан
DSH_SUPERPOWERS_SOURCE=<dir>и флаг--superpowers-fromне передан - THEN источник берётся из переменной; поведение совпадает с флагом
Scenario: Флаг важнее переменной
- WHEN заданы и
--superpowers-from, иDSH_SUPERPOWERS_SOURCE - THEN используется каталог из флага
Scenario: Невалидный явный источник
- WHEN
--superpowers-fromуказывает на каталог безusing-superpowers/SKILL.md - THEN
--skillsпечатаетFAILс ожидаемой структурой и завершается кодом 1, ничего не копируя из legacy-источников
Scenario: Голый каталог скиллов
- WHEN явный источник — каталог
my-skillsсusing-superpowers/SKILL.md, но без checkout-корня - THEN и
--skills, и--checkпечатаютFAIL: explicit superpowers source must be a checkout root or its skills/ directoryи завершаются кодом 1
Scenario: Пиннинг в ~/.dsh
- WHEN существует клон
$DSH_HOME/superpowersсskills/using-superpowers/SKILL.mdи флаг/переменная не заданы - THEN источником служит этот клон, а
--checkофлайн печатаетok: superpowers source (<path>)и ref из.sync-meta.json
Scenario: Обновление клона от DSH_SUPERPOWERS_REPO
- WHEN клон уже есть, а
DSH_SUPERPOWERS_REPOсменился или тег переехал - THEN прогон фетчит от
DSH_SUPERPOWERS_REPOс force-рефспеком, делает checkoutFETCH_HEADи устанавливает текст новой версии тега
Scenario: Офлайн-переиспользование клона
- WHEN клон существует, HEAD ровно на пине, сеть недоступна
- THEN
--skillsпечатаетWARN: offline; reusing the existing cloneи завершается кодом 0
Scenario: Каталог без .git на месте клона
- WHEN
$DSH_HOME/superpowersнепустой и не является git-клоном - THEN
--skillsзавершаетсяFAIL(код 1), каталог не изменяется
Requirement: Отсутствие источника не бывает тихим
Если источник superpowers недоступен, --skills MUST завершаться FAIL, кроме
случая, когда существует legacy-источник.
- Офлайн, без явного источника, без клона на пине и без legacy MUST печататься
FAIL: no superpowers sourceс готовой командой клона (git clone --depth 1 --branch <ref> <repo> <DSH_HOME>/superpowers) и код возврата 1. Тихий успех запрещён даже при уже установленных копиях. - Уже установленные копии в
$DSH_HOME/skillsMUST NOT удаляться или портиться при такомFAIL. - Если legacy-источник существует — скиллы копируются сырым текстом с
WARN: legacy superpowers sourceи кодом 0 (путь живёт до #66). - Локальные скиллы обвязки (
dsh/skills/*:role,sdd) MUST ставиться в любом исходе: они не зависят от superpowers.
Scenario: Первая установка без источника
- WHEN
HOMEпуст,$DSH_HOME/skillsне существует, клон получить не удалось, legacy нет - THEN
--skillsпечатаетFAIL: no superpowers sourceи команду клона, код 1, но$DSH_HOME/skills/role/SKILL.mdи$DSH_HOME/skills/sdd/SKILL.mdна месте
Scenario: Повторная установка без источника
- WHEN
$DSH_HOME/skills/using-superpowers/SKILL.mdуже есть, а клон получить не удалось - THEN
--skillsпечатаетFAIL: no superpowers source, код 1, установленный файл не изменён (раньше здесь былWARNи код 0)
Requirement: ~/.claude — только legacy-fallback с предупреждением
superpowers-скиллы и роли MUST иметь рабочий путь ~/.dsh; пути ~/.claude
SHALL оставаться fallback'ом для машин, где --skills --roles ещё не
перезапускали, и MUST сопровождаться предупреждением.
- Установка: legacy-кэш Claude Code и
~/.claude/skillsMAY быть источником только после того, как клон пина получить не удалось; копирование в этом случае — сырое (без sync и verify), сWARN: legacy superpowers sourceи подсказкой перейти на пин/явный источник. --check: legacy-источник —WARN, явный/пин —ok, отсутствие —WARNс подсказкой клона, невалидный явный источник —FAILи код 1 (пользователь задал его сам, подсказка «нет источника» здесь неверна).- Runtime: bootstrap и резолвер ролей сохраняют порядок
~/.dsh→~/.claude, но при выборе legacy-пути один раз за процесс пишут в лог предупреждение. - Удаление fallback'ов — отдельное изменение (#66); в этом change они не удаляются.
Scenario: Legacy-кэш как источник установки
- WHEN в
HOMEесть только кэш Claude Code с superpowers,$DSH_HOMEпуст - THEN
--skillsкопирует скиллы и печатаетWARN: legacy superpowers source
Scenario: Runtime-предупреждение
- WHEN bootstrap резолвит
using-superpowersиз~/.claude/... - THEN в лог один раз за процесс попадает предупреждение с предложением
выполнить
dsh/install.sh --skills
Scenario: ~/.dsh приоритетнее
- WHEN существуют и
~/.dsh/skills/using-superpowers/SKILL.md, и legacy-копия - THEN используется
~/.dsh, предупреждения нет
Requirement: Установленные инструкции DSH не ссылаются на ~/.claude
dsh/install.sh --global MUST писать $DSH_HOME/AGENTS.md так, чтобы пути
ролей указывали на DSH-слой, а формат инструкций не выдавался за runtime
Claude Code.
- Строки
~/.claude/rolesв установленной копии MUST заменяться на~/.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-скиллы — в~/.dsh/skills(--skills). - Повторный
--globalобязан делать бэкап прежнегоAGENTS.md(как сейчас).
Scenario: Пути ролей после установки
- WHEN выполнен
--globalво временныйDSH_HOME - THEN в
AGENTS.mdесть~/.dsh/roles/dev.mdи нет ни одного пути~/.claude
Scenario: global/CLAUDE.md не тронут
- WHEN выполнен
--global - THEN
global/CLAUDE.mdв репозитории побайтно не изменился
Requirement: Чистый HOME без ~/.claude проходит установку
Установка, bootstrap и skill role MUST работать на машине, где Claude Code
никогда не устанавливался.
--global --skills --rolesс явным источником superpowers в пустомHOMEMUST создавать$DSH_HOME/{AGENTS.md,skills/using-superpowers/SKILL.md,skills/role/SKILL.md,skills/sdd/SKILL.md,roles/dev.md}.- bootstrap MUST резолвить скилл из
$DSH_HOME/skillsприhomeэтого HOME и MUST NOT обращаться к~/.claude. - Резолвер ролей MUST находить
$DSH_HOME/rolesприhomeэтого HOME.
Scenario: Установка в чистый HOME
- WHEN
HOME— пустой temp-каталог,DSH_HOME=$HOME/.dsh,--superpowers-fromуказывает на каталог superpowers - THEN
--global --skills --rolesзавершается кодом 0, все перечисленные файлы на месте,--checkзелёный по skills и roles
Scenario: Bootstrap в чистом HOME
- WHEN
prepareBootstrap({ home: <temp HOME> })вызывается после установки - THEN выбранный путь —
$HOME/.dsh/skills/using-superpowers/SKILL.md, текст содержит маркер<!-- DSH-HARNESS-BOOTSTRAP:... -->, предупреждений о legacy нет
Requirement: Sync по пину
dsh/install.sh --skills без явного источника MUST получить superpowers из
клона $DSH_HOME/superpowers на пине $DSH_SUPERPOWERS_REF (default
v7.0.1) из $DSH_SUPERPOWERS_REPO (default
https://github.com/obra/superpowers) и MUST прогнать sync
(dsh/sync/sync-from-upstream.mjs) и verify (dsh/sync/verify-skills.mjs) до
копирования в $DSH_HOME/skills.
- Копирование MUST брать только synced-дерево; сырой текст апстрима при пине
MUST NOT попадать в
$DSH_HOME/skills. Адаптации:superpowers:-префиксы сняты, ссылки ведут наusing-superpowers/references/dsh-tools.mdизdsh/bundle/, чужие harness-референсы не копируются. - Sync MUST завершаться
FAIL(код 1) на несовпадении якоря transform-а (ноль или больше одного совпадения), на изменении набора скиллов и на отсутствииskills/в checkout. При любом падении назначение (--out) MUST остаться прежним, а staging-каталог MUST быть удалён. - Verify MUST проверять YAML frontmatter (
nameиdescriptionнепусты, без дублей ключей), отсутствие остаточныхsuperpowers:-префиксов в телахSKILL.mdи резолв всех относительных ссылок внутри synced-дерева. Дерево, прошедшее verify с нулём скиллов (verified 0 skills), MUST трактоваться какFAIL: пустое дерево не устанавливается. - В
<out>/.sync-meta.jsonMUST записыватьсяref(значение--ref) иversion(версия из.claude-plugin/plugin.jsonапстрима);--checkMUST печатать ref и version из этого файла, не обращаясь в сеть. - Повторный
--skillsMUST быть идемпотентным: каталоги*.tmp-*не остаются, установленные копии заменяются целиком (rm -rf+cp -R), а не дополняются.
Scenario: Свежий HOME, установка по пину
- WHEN
HOMEпуст, флаг иDSH_SUPERPOWERS_SOURCEне заданы, аDSH_SUPERPOWERS_REPOуказывает на репозиторий с тегомv7.0.1 - THEN
--skillsклонирует пин, печатаетsynced N filesиverified 15 skills, копирует адаптированные скиллы; вusing-superpowers/SKILL.mdестьreferences/dsh-tools.mdи нетsuperpowers:, аreferences/dsh-tools.mdсовпадает сdsh/bundle/dsh-tools.md
Scenario: Сломанный якорь апстрима
- WHEN checkout на пине не содержит якоря одного из transform-ов
- THEN sync печатает
FAILс именем transform-а и завершается кодом 1, прежнее--outи$DSH_HOME/skillsне изменены
Scenario: Изменился набор скиллов
- WHEN в checkout на пине появился или исчез каталог скилла относительно ожидаемого списка
- THEN sync печатает
added upstream:/removed upstream:и завершается кодом 1; установка ничего не копирует
Scenario: Verify нашёл висячую ссылку
- WHEN synced-дерево содержит ссылку на несуществующий файл (markdown или inline-код)
- THEN verify печатает
<skill>/SKILL.md: dangling relative reference: …и завершается кодом 1; в$DSH_HOME/skillsничего не копируется
Scenario: Нулевое дерево
- WHEN verify завершился кодом 0, но сообщил
verified 0 skills - THEN установка завершается
FAIL, ни один скилл не скопирован
Scenario: Повторный прогон
- WHEN
--skillsзапущен второй раз на том же пине - THEN установленные скиллы заменены, в
$DSH_HOME/superpowersнет каталогов*.tmp-*, а.sync-meta.json.refравен пину
Requirement: Самопроверка контракта бандла
Бандл dsh-harness MUST при активации проверять контракт DSH, на который
опирается, и MUST предоставлять команду dsh-harness-check для повторного
прогона той же проверки.
- Проверка MUST покрывать
ctx.tools.guard,ctx.onдляagent/pre-step,ctx.inject(регистрация команд) иworkingDirectory.get. Каждая отсутствующая служба MUST логироваться строкой[dsh-harness] self-check: <feature> not-ok: <detail>. - Отсутствие службы MUST отключать только свою фичу: без
ctx.tools.guardguard-обвязка пропускается, bootstrap и/roleпродолжают работать при своих службах. Активация бандла MUST NOT падать целиком из-за несовпадения контракта. - Команда
dsh-harness-checkMUST возвращать текстовый отчёт с одной строкойok/not-okна каждую проверяемую фичу. - Ключи конфига
selfCheckиcheckCommand(defaulttrue) MUST позволять выключить активационную проверку и команду соответственно. - Терминальный
--checkMUST оставаться офлайн-проверкой файлов и окружения: SDK внутриapp.asarему недоступен, контракт проверяется только изнутри DSH.
Scenario: Сломанный контракт при активации
- WHEN бандл активируется в
ctxбезtools.guard - THEN в лог попадает
self-check: guards not-ok: tools.guard, guard-wiring пропускается, bootstrap и команды продолжают работать, бандл не падает
Scenario: Проверка по требованию
- WHEN пользователь вызывает
/dsh-harness-check - THEN отчёт перечисляет каждую фичу со статусом
okилиnot-ok