2026-10-25 01:13:10 +04:00

21 KiB
Raw Permalink Blame History

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>) и checkout FETCH_HEAD: смена репозитория и переехавший тег MUST подхватываться следующим прогоном. Офлайн клон MAY переиспользоваться, только если HEAD уже ровно на пине, и тогда MUST печататься WARN; иначе — FAIL с командой клона.
  • Непустой каталог на месте клона без рабочего .git MUST быть 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-рефспеком, делает checkout FETCH_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/skills MUST 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/skills MAY быть источником только после того, как клон пина получить не удалось; копирование в этом случае — сырое (без 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 в пустом HOME MUST создавать $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.json MUST записываться ref (значение --ref) и version (версия из .claude-plugin/plugin.json апстрима); --check MUST печатать ref и version из этого файла, не обращаясь в сеть.
  • Повторный --skills MUST быть идемпотентным: каталоги *.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.guard guard-обвязка пропускается, bootstrap и /role продолжают работать при своих службах. Активация бандла MUST NOT падать целиком из-за несовпадения контракта.
  • Команда dsh-harness-check MUST возвращать текстовый отчёт с одной строкой ok/not-ok на каждую проверяемую фичу.
  • Ключи конфига selfCheck и checkCommand (default true) MUST позволять выключить активационную проверку и команду соответственно.
  • Терминальный --check MUST оставаться офлайн-проверкой файлов и окружения: 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