172 lines
13 KiB
Markdown
172 lines
13 KiB
Markdown
# Design: установка DSH без зависимости от ~/.claude
|
||
|
||
Issue: #49. Спека: `openspec/changes/dsh-install-source/specs/dsh-install/spec.md`.
|
||
Исходные факты: `dsh/PORT.md` §3/§5/§7, issue #37/#38 (runtime-отвязка уже
|
||
сделана), ответ владельца 24.10.2026 («нормально так» — без вендоринга).
|
||
|
||
## 1. Что уже сделано и чего не хватает
|
||
|
||
| Слой | Источник в runtime | Зависимость от `~/.claude` |
|
||
|---|---|---|
|
||
| bootstrap `using-superpowers` | `~/.dsh/skills` → `~/.agents/skills` → кэш CC → `~/.claude/skills` | только fallback |
|
||
| роли `/role`, skill `role` | `~/.dsh/roles` → `~/.claude/roles` → repo `roles/` | только fallback |
|
||
| установка `--skills` | кэш плагина CC | **жёсткая** |
|
||
| установка `--roles` | repo `roles/` | нет |
|
||
| установленные инструкции | `global/CLAUDE.md` + adapter-шапка | **текст путей ролей** |
|
||
|
||
То есть #37 снял зависимость в том, что исполняется, и оставил её в том, что
|
||
устанавливается. Первая же команда `--skills` на чистой машине не находит
|
||
источник, печатает `WARN` и оставляет bootstrap без скилла — при этом установка
|
||
формально успешна.
|
||
|
||
## 2. Решения
|
||
|
||
### D-1 (решение владельца). Без вендоринга superpowers
|
||
|
||
Копию апстрима в репозиторий не кладём. Обоснование: superpowers живёт своей
|
||
версией (в кэше уже 6.4.1 и 7.0.0), репозиторий обвязки не должен превращаться
|
||
в зеркало; обновление вендорной копии — ручная синхронизация, которая стареет
|
||
молча. Цена: install-time источник нужно получить один раз (клон), это лишний
|
||
шаг в quick start.
|
||
|
||
### D-2. Три уровня источника, один формат
|
||
|
||
Порядок: `--superpowers-from` → `DSH_SUPERPOWERS_SOURCE` →
|
||
`$DSH_HOME/superpowers/skills` → legacy-кэш CC → legacy `~/.claude/skills`.
|
||
|
||
- Флаг и переменная принимают **оба** каталога: корень superpowers (с `skills/`)
|
||
и сам `skills/`. Клон `git clone … ~/.dsh/superpowers` даёт первый вид, кэш
|
||
CC — второй; пользователь не должен помнить, какой именно.
|
||
- `$DSH_HOME/superpowers` — рекомендуемое место пиннинга: один клон на машину,
|
||
дальше `--skills` воспроизводим без флага и без `~/.claude`.
|
||
- Явно заданный невалидный источник — `FAIL`, а не fallback: иначе опечатка в
|
||
пути тихо даёт установку из legacy-кэша, и на чистой машине это снова
|
||
«установка прошла, bootstrap пуст».
|
||
|
||
### D-3. `FAIL` только когда ставить нечего
|
||
|
||
`--skills` без источника:
|
||
- `$DSH_HOME/skills/using-superpowers/SKILL.md` есть → `WARN` + код 0
|
||
(идемпотентный повтор, старые копии не трогаем);
|
||
- нет → `FAIL` + код 1 + готовая команда клона.
|
||
|
||
Локальные скиллы (`role`, `sdd`) ставятся в обоих случаях: они из репозитория и
|
||
от superpowers не зависят. Из-за этого `--skills` может вернуть 1, уже записав
|
||
часть файлов — это сказано в `FAIL`-сообщении.
|
||
|
||
### D-4. Legacy-пути остаются, но говорят о себе
|
||
|
||
`~/.claude` поддерживается как fallback: машины, где `--skills --roles` ещё не
|
||
перезапускали, не должны сломаться на этом change. Предупреждения:
|
||
|
||
- `--skills` из legacy-источника → `WARN: legacy superpowers source`;
|
||
- `--check` → `WARN` для legacy и для «источника нет»;
|
||
- bootstrap/`resolveRolesDir` при выборе `~/.claude` → одно предупреждение за
|
||
процесс (module-level `Set`), не на каждый `agent/pre-step`.
|
||
|
||
Удаление fallback'ов — отдельная задача: сначала все машины должны переехать на
|
||
`~/.dsh` (и это видно по `WARN`), потом код можно чистить. Цена: путь
|
||
`~/.claude` остаётся в bundle, и code review видит его ещё один цикл.
|
||
|
||
### D-5. Инструкции переписываются при установке, не в источнике
|
||
|
||
`global/CLAUDE.md` — общий с Claude Code, его правит только CC-контур. Поэтому
|
||
`install_global` пишет `AGENTS.md` в два шага: adapter-шапка (как сейчас, плюс
|
||
явная строка про формат инструкций) и тело `global/CLAUDE.md`, прогнанное через
|
||
замену `~/.claude/roles` → `~/.dsh/roles`.
|
||
|
||
Замена точечная и проверяемая тестом: после `--global` в `AGENTS.md` есть
|
||
`~/.dsh/roles/dev.md` и нет `~/.claude/roles`; сам `global/CLAUDE.md` не меняется
|
||
(тест сравнивает содержимое до/после). Цена: расхождение между источником и
|
||
установленной копией теперь intentional, и его надо помнить при правках CC-текста.
|
||
|
||
### D-6. `--check` не падает без источника
|
||
|
||
`--check` — диагностика, а не установка: отсутствие superpowers-источника
|
||
остаётся `WARN`, код возврата меняют только жёсткие `FAIL` (python3, git, node,
|
||
хуки, бандл). Так же уже сделано для openspec (#42, R-4). Исключение —
|
||
невалидный **явный** источник (`--superpowers-from`/`DSH_SUPERPOWERS_SOURCE`):
|
||
это ошибка конфигурации пользователя, `--check` даёт `FAIL`, иначе опечатка
|
||
выглядит как «источника нет».
|
||
|
||
## 3. Формулировки, которые попадут в установленный AGENTS.md
|
||
|
||
- `DSH adapter: role files are ~/.dsh/roles (installed by --roles); legacy ~/.claude/roles is a migration fallback.`
|
||
- `DSH adapter: superpowers skills are ~/.dsh/skills (installed by --skills from --superpowers-from, DSH_SUPERPOWERS_SOURCE, or ~/.dsh/superpowers); the ~/.claude cache is a legacy fallback.`
|
||
- `DSH adapter: a project CLAUDE.md/AGENTS.md is the instruction-file format of this harness; it does not imply a Claude Code runtime.`
|
||
- Тело `global/CLAUDE.md` — с заменой `~/.claude/roles` → `~/.dsh/roles`; строка
|
||
про `role-hint` остаётся, adapter-шапка говорит, что хука в DSH нет и роль
|
||
выводится из первого сообщения.
|
||
|
||
## 4. Решения, принятые без обсуждения
|
||
|
||
- **2026-10-24. Клон для пиннинга — `git clone --depth 1 https://github.com/obra/superpowers ~/.dsh/superpowers`.**
|
||
Это апстрим superpowers (public repo). `install.sh` его не запускает: сеть в
|
||
установке запрещена (D-1/спека). Цена ошибки: неверный URL — баг
|
||
документации, ловится первой же установкой на чистой машине; лечится одной
|
||
строкой в README/PORT.
|
||
- **2026-10-24. `--superpowers-from` принимает и корень, и `skills/`.**
|
||
Обоснование: два реальных источника дают разные формы (клон и кэш CC).
|
||
Цена: две ветки разбора и два теста; ошибка разбора видна сразу по `FAIL`.
|
||
- **2026-10-24. Предупреждение о legacy в runtime — один раз за процесс.**
|
||
`agent/pre-step` вызывается на каждом шаге; предупреждение на каждый шаг
|
||
утопило бы лог. Цена: после перезапуска процесса предупреждение повторится —
|
||
это ожидаемо; диагностика — `--check`.
|
||
- **2026-10-24. `--check` проверяет у пиннинга только наличие `using-superpowers`.**
|
||
Полная инвентаризация набора — работа `--skills` (он копирует всё, что есть).
|
||
Цена: неполный клон пройдёт `--check` и даст частичный набор скиллов; признак
|
||
— отсутствие ожидаемого имени в каталоге скиллов сессии.
|
||
- **2026-10-24. `tasks.md` OpenSpec не заполняется.**
|
||
Задачи живут в плане superpowers, как требует `CLAUDE.md` репозитория. Цена:
|
||
`openspec status` показывает незаполненный `tasks.md`; `archive` это не
|
||
блокирует (уже так в `dsh-openspec`).
|
||
|
||
## 5. Риски
|
||
|
||
- **`set -euo pipefail` против «частичного успеха».** `install_skills` при
|
||
`FAIL` возвращает 1; в `--skills` он вызывается через `|| status=1`, иначе
|
||
`install_local_skills` не выполнится. Тест на «первую установку без
|
||
источника» проверяет оба файла и код возврата.
|
||
- **Legacy-fallback маскирует неправильную настройку.** Лечится `WARN` в
|
||
установке, `--check` и runtime-предупреждением; полное удаление — D-4.
|
||
- **Замена `~/.claude/roles` → `~/.dsh/roles` в тексте.** Заменяем только этот
|
||
префикс, а не `~/.claude` целиком: в тексте есть упоминания хуков/CC, которые
|
||
DSH-версии не касаются. Тест проверяет и наличие нового пути, и отсутствие
|
||
старого.
|
||
- **Проверка «чистый HOME» — юнит-уровень.** Реальный fresh-session smoke в DSH
|
||
делает владелец: сессия должна подняться без `~/.claude` и показать
|
||
superpowers-скиллы. В PR это отмечено как единственный ручной шаг приёмки.
|
||
- **Апстрим superpowers может переехать.** Явный источник проверяется по
|
||
`using-superpowers/SKILL.md`; если апстрим переименует каталог, `FAIL` скажет,
|
||
что искать.
|
||
|
||
## 6. Приёмка
|
||
|
||
- `openspec validate dsh-install-source --strict` — change валиден.
|
||
- `bash dsh/tests/run.sh` — зелёный, включая новые тесты источника, `AGENTS.md`
|
||
и legacy-предупреждений.
|
||
- `bash tests/run.sh` — регрессий в CC-контуре нет.
|
||
- Temp `HOME` без `~/.claude`: `--global --skills --roles` с
|
||
`--superpowers-from`; `--check`; `prepareBootstrap({home})` — маркер на месте.
|
||
- Живой fresh-session smoke без `~/.claude` — за владельцем (в PR отмечено).
|
||
|
||
### Приёмка: прогон 24.10.2026
|
||
|
||
- `openspec validate dsh-install-source --strict` → `Change 'dsh-install-source' is valid`.
|
||
- `bash dsh/tests/run.sh` → 61/61 pass (до изменения — 57).
|
||
- Temp `HOME` без `~/.claude`: копия реальных superpowers 7.0.0 в
|
||
`<tmp>/superpowers`, `--global --skills --roles --superpowers-from` → 15
|
||
superpowers-скиллов, 2 локальных (`role`, `sdd`), 4 роли; `$HOME/.claude` не
|
||
создан; `prepareBootstrap({ home })` выбрал
|
||
`~/.dsh/skills/using-superpowers/SKILL.md`, маркер
|
||
`<!-- DSH-HARNESS-BOOTSTRAP:412d797495fc8765 -->`; `--check` — `ok: dsh skills`,
|
||
`ok: role skill`, `ok: roles`, `WARN: no superpowers source` (источник был
|
||
временным каталогом, копии на месте).
|
||
- Машина владельца: `--check` — `WARN: legacy superpowers source
|
||
(~/.claude/plugins/cache/.../7.0.0/skills)`, `ok: dsh skills`, `ok: roles`. В
|
||
установленном `~/.dsh/AGENTS.md` ещё три пути `~/.claude/roles` — нужен
|
||
повторный `--global` (ручной шаг, отмечен в PR).
|
||
- Fresh-session smoke без `~/.claude` — за владельцем: юнит-уровень
|
||
(`prepareBootstrap` с temp HOME) пройден, реальную сессию DSH поднимает
|
||
владелец.
|