From 044222d566751e4d7b82e01c6688cbb620b92444 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 21:24:39 +0400 Subject: [PATCH] =?UTF-8?q?chore(openspec):=20=D0=B0=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D0=B2=20change=20dsh-install-source=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.openspec.yaml | 0 .../2026-10-24-dsh-install-source}/design.md | 0 .../proposal.md | 0 .../specs/dsh-install/spec.md | 0 openspec/specs/dsh-install/spec.md | 149 ++++++++++++++++++ 5 files changed, 149 insertions(+) rename openspec/changes/{dsh-install-source => archive/2026-10-24-dsh-install-source}/.openspec.yaml (100%) rename openspec/changes/{dsh-install-source => archive/2026-10-24-dsh-install-source}/design.md (100%) rename openspec/changes/{dsh-install-source => archive/2026-10-24-dsh-install-source}/proposal.md (100%) rename openspec/changes/{dsh-install-source => archive/2026-10-24-dsh-install-source}/specs/dsh-install/spec.md (100%) create mode 100644 openspec/specs/dsh-install/spec.md diff --git a/openspec/changes/dsh-install-source/.openspec.yaml b/openspec/changes/archive/2026-10-24-dsh-install-source/.openspec.yaml similarity index 100% rename from openspec/changes/dsh-install-source/.openspec.yaml rename to openspec/changes/archive/2026-10-24-dsh-install-source/.openspec.yaml diff --git a/openspec/changes/dsh-install-source/design.md b/openspec/changes/archive/2026-10-24-dsh-install-source/design.md similarity index 100% rename from openspec/changes/dsh-install-source/design.md rename to openspec/changes/archive/2026-10-24-dsh-install-source/design.md diff --git a/openspec/changes/dsh-install-source/proposal.md b/openspec/changes/archive/2026-10-24-dsh-install-source/proposal.md similarity index 100% rename from openspec/changes/dsh-install-source/proposal.md rename to openspec/changes/archive/2026-10-24-dsh-install-source/proposal.md diff --git a/openspec/changes/dsh-install-source/specs/dsh-install/spec.md b/openspec/changes/archive/2026-10-24-dsh-install-source/specs/dsh-install/spec.md similarity index 100% rename from openspec/changes/dsh-install-source/specs/dsh-install/spec.md rename to openspec/changes/archive/2026-10-24-dsh-install-source/specs/dsh-install/spec.md diff --git a/openspec/specs/dsh-install/spec.md b/openspec/specs/dsh-install/spec.md new file mode 100644 index 0000000..d27e6b5 --- /dev/null +++ b/openspec/specs/dsh-install/spec.md @@ -0,0 +1,149 @@ +# 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-скиллов в +порядке приоритета и 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 нет -- 2.47.2