# Spec Delta: dsh-install ## Purpose Слой установки DSH-обвязки: откуда берутся superpowers-скиллы и роли, что делает `dsh/install.sh --skills/--roles`, какие пути попадают в установленные инструкции. В Claude Code источником служит кэш плагина в `~/.claude`; в DSH источник задаётся установщиком явно (аргумент, переменная или пиннинг в `~/.dsh`), а `~/.claude` остаётся только legacy-fallback'ом с предупреждением. Контракт нужен, чтобы свежая установка на машине без Claude Code проходила полный цикл #48 и чтобы путь `~/.claude` не возвращался в runtime незаметно. ## ADDED Requirements ### Requirement: Источник superpowers задаётся при установке `dsh/install.sh --skills` SHALL разрешать источник superpowers-скиллов в порядке приоритета и MUST NOT исполнять `git clone` и любые сетевые операции. - Порядок: `--superpowers-from ` → `$DSH_SUPERPOWERS_SOURCE` → `$DSH_HOME/superpowers/skills` (пиннинг; допускается и корень `$DSH_HOME/superpowers` с подкаталогом `skills/`) → legacy-кэш Claude Code → legacy `~/.claude/skills`. - Для явного источника (`--superpowers-from`, `$DSH_SUPERPOWERS_SOURCE`) допускаются обе формы: корень superpowers (есть `skills/using-superpowers/SKILL.md`) или сам каталог `skills`. - Явно заданный, но невалидный источник MUST приводить к `FAIL` и коду возврата 1: тихий откат на legacy запрещён. - Копирование 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=` и флаг `--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: Пиннинг в ~/.dsh - **WHEN** существует `$DSH_HOME/superpowers/skills/using-superpowers/SKILL.md` и флаг/переменная не заданы - **THEN** источником служит этот каталог, `--check` печатает `ok: superpowers source ()` ### Requirement: Отсутствие источника не бывает тихим Если источник superpowers не найден, `--skills` MUST различать первичную и повторную установку и MUST NOT завершаться успехом молча. - Если `$DSH_HOME/skills/using-superpowers/SKILL.md` уже существует — `WARN` «источник не найден, копии сохранены» и код 0 (идемпотентный повтор). - Если копий нет — `FAIL` с готовой командой получения источника (`git clone --depth 1 https://github.com/obra/superpowers ~/.dsh/superpowers`) и код возврата 1. - Локальные скиллы обвязки (`dsh/skills/*`: `role`, `sdd`) MUST ставиться в обоих случаях: они не зависят от superpowers. #### Scenario: Первая установка без источника - **WHEN** `HOME` пуст, `$DSH_HOME/skills` не существует, источника нет - **THEN** `--skills` печатает `FAIL`, код 1, но `$DSH_HOME/skills/role/SKILL.md` и `$DSH_HOME/skills/sdd/SKILL.md` на месте #### Scenario: Повторная установка без источника - **WHEN** `$DSH_HOME/skills/using-superpowers/SKILL.md` уже есть, источника нет - **THEN** `--skills` печатает `WARN`, код 0, установленные скиллы не удалены ### Requirement: ~/.claude — только legacy-fallback с предупреждением superpowers-скиллы и роли MUST иметь рабочий путь `~/.dsh`; пути `~/.claude` SHALL оставаться fallback'ом для машин, где `--skills --roles` ещё не перезапускали, и MUST сопровождаться предупреждением. - Установка: legacy-кэш Claude Code и `~/.claude/skills` допустимы как источник, но `--skills` печатает `WARN: legacy superpowers source` с подсказкой перейти на пиннинг/флаг. - `--check`: legacy-источник — `WARN`, явный/пиннинг — `ok`, отсутствие — `WARN` с подсказкой, невалидный явный источник — `FAIL` и код 1 (пользователь задал его сам, подсказка «нет источника» здесь неверна). - Runtime: bootstrap и резолвер ролей сохраняют порядок `~/.dsh` → `~/.claude`, но при выборе legacy-пути один раз за процесс пишут в лог предупреждение. - Удаление fallback'ов — отдельное изменение; в этом 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: })` вызывается после установки - **THEN** выбранный путь — `$HOME/.dsh/skills/using-superpowers/SKILL.md`, текст содержит маркер ``, предупреждений о legacy нет