13 KiB
Install pulls superpowers from git — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: dsh/install.sh --skills сам клонирует и обновляет superpowers из git, оставляя явные источники и офлайн-кэш рабочими.
Architecture: В install.sh появляется git-шаг: клон $DSH_HOME/superpowers из $DSH_SUPERPOWERS_REPO (по умолчанию GitHub) или fetch --depth 1 + reset --hard FETCH_HEAD для существующего клона. Резолвер для --check отдельный и офлайновый. Тесты используют локальный file://-репозиторий.
Tech Stack: bash (set -euo pipefail), git CLI, Node.js ESM (node:test), OpenSpec.
Spec: openspec/changes/dsh-superpowers-git/specs/dsh-install/spec.md (дизайн — design.md).
Global Constraints
- Порядок источников:
--superpowers-from→$DSH_SUPERPOWERS_SOURCE→ git ($DSH_HOME/superpowers) → legacy-кэш CC →~/.claude/skills. - Явный валидный источник MUST NOT запускать git и не создавать
$DSH_HOME/superpowers. - Репозиторий:
$DSH_SUPERPOWERS_REPO, defaulthttps://github.com/obra/superpowers; глубина 1; обновлениеfetch --depth 1 origin+reset --hard FETCH_HEAD. - Фактический вывод
--skillsдля git:(source: <dir>, git <short-sha>). --checkMUST NOT клонировать/обновлять; показываетok: superpowers source (<dir>, git <sha>).- Неудачный первый клон MUST NOT оставлять каталог:
rm -rf $DSH_HOME/superpowers. - Ручная копия
$DSH_HOME/superpowersбез.git—pinned, без обновления, сWARN. - Тесты не выходят в сеть:
installerEnvзадаёт недоступныйDSH_SUPERPOWERS_REPO, git-тесты —file://-фикстуру. - Маркировка:
T<n>.<k>(ревью задачи),C/I/M(ревью ветки),R-n/O-n(рулинги/наблюдения).
Review Focus
- Клон падает на середине: каталог не остаётся, следующий запуск клонирует заново.
- Офлайн при существующем клоне:
WARN, копии из кэша, код 0 (не FAIL). - Ручная копия без
.git: используется,WARNоб отсутствии обновления, копии на месте. - Явный источник при пустом
DSH_HOME: git не вызывается,$DSH_HOME/superpowersне появляется. --checkна пустом HOME с недоступным репозиторием:WARN, код 0, клон не создан.- Путь
$DSH_HOMEс пробелами: клон в кавычках, sha/копирование работают.
Task 1: git-источник в --skills
Files:
- Modify:
dsh/install.sh(CLONE_HINT,git_superpowers_source,superpowers_source,install_skills,usage) - Modify:
dsh/tests/install.test.mjs
Interfaces:
-
Produces:
git_superpowers_source() -> rc 0и stdout<skills-dir>\tgit:<sha>;rc 1без источника (печатаетWARN/FAILпричину);superpowers_source()— прежний контракт<dir>\t<explicit|pinned|legacy|git:<sha>>;install_skillsпечатаетcopied N superpowers skills into <dest> (source: <dir>, git <sha>)для git и(source: <dir>)иначе.
-
Consumes:
normalize_superpowers_dir,$DSH_HOME,$DSH_SUPERPOWERS_REPO. -
Step 1: Тестовый хелпер и падающие тесты
В dsh/tests/install.test.mjs импортировать mkdirSync из node:fs, git из ./helpers.mjs, pathToFileURL из node:url; добавить:
function makeSuperpowersRepo(home, name = 'superpowers-src') {
const repo = join(home, name)
mkdirSync(repo, { recursive: true })
git(repo, ['init', '-b', 'main'])
git(repo, ['config', 'user.email', 'test@example.invalid'])
git(repo, ['config', 'user.name', 'Test'])
makeSuperpowers(repo)
git(repo, ['add', '.'])
git(repo, ['commit', '-m', 'init'])
return repo
}
installerEnv дополнительно: delete env.DSH_SUPERPOWERS_REPO и
env.DSH_SUPERPOWERS_REPO = join(home, 'no-superpowers-repo') (недоступный путь — сеть в тестах не нужна). Git-тесты переопределяют его через file://-URL.
Тесты:
test('--skills clones the git source on first run', ...)
// home; repo = makeSuperpowersRepo(home); env repo = pathToFileURL(repo).href;
// run --skills; status 0; <dshHome>/superpowers/.git существует;
// stdout /copied 2 superpowers skills/ и new RegExp(`git ${git(repo,['rev-parse','--short','HEAD'])}`);
// skills/using-superpowers на месте
test('--skills updates an existing clone to the new commit', ...)
// после первого прогона: write(join(repo,'skills','new-skill','SKILL.md'), ...);
// git add/commit; повторить --skills; status 0;
// <dshHome>/skills/new-skill/SKILL.md существует; stdout содержит новый short-sha
test('an offline update keeps the cached clone', ...)
// после клона: env repo = join(home,'gone'); run --skills; status 0;
// stdout/stderr /WARN: cannot update superpowers/; /copied 2 superpowers skills/
test('a failed first clone leaves no checkout', ...)
// пустой home (repo недоступен); run --skills; status 1;
// <dshHome>/superpowers НЕ существует; FAIL упоминает no-superpowers-repo и --superpowers-from
test('an explicit source never touches git', ...)
// --skills --superpowers-from <fixture>; status 0; <dshHome>/superpowers НЕ существует
test('a manual non-git checkout is used but not updated', ...)
// write <dshHome>/superpowers/skills/using-superpowers/SKILL.md; repo недоступен;
// run --skills; status 0; stderr /not a git checkout/; /copied 1 superpowers skills/
- Step 2: Прогнать, увидеть падение
Run: node --test dsh/tests/install.test.mjs
Expected: новые тесты падают (нет клона/обновления/сообщений).
-
Step 3: Реализовать в
dsh/install.sh -
SUPERPOWERS_REPO="${DSH_SUPERPOWERS_REPO:-https://github.com/obra/superpowers}"рядом сCLONE_HINT;CLONE_HINTпереписать наgit clone --depth 1 "$SUPERPOWERS_REPO" "$DSH_HOME/superpowers". -
git_superpowers_sourceпо описанию из Interfaces: git-клон → fetch+reset →normalize_superpowers_dir; на неудачном клонеrm -rf "$DSH_HOME/superpowers"иreturn 1; на неудачном fetch —WARNи работа из кэша. -
superpowers_source: после явных источников вызватьgit_superpowers_source; её успех вернуть как есть,rc 1— продолжить на legacy-кэш и~/.claude/skills. -
install_skills: разобрать kindgit:<sha>→ в итоговой строке(source: $source, git $sha);FAIL-сообщение — репозиторий и--superpowers-fromвместо ручного клона. -
usage: пункт 3 — git-клон$SUPERPOWERS_REPO→$DSH_HOME/superpowers, обновляется при каждом--skills. -
Step 4: Прогнать тесты
Run: node --test dsh/tests/install.test.mjs
Expected: PASS всех тестов файла.
- Step 5: Commit
git add dsh/install.sh dsh/tests/install.test.mjs
git commit -m "feat(dsh): install pulls superpowers from git (#67)"
Task 2: офлайновый --check с git-версией
Files:
- Modify:
dsh/install.sh(static_superpowers_source, блокcheck) - Modify:
dsh/tests/install.test.mjs
Interfaces:
-
Produces:
static_superpowers_source()— тот же формат, но без сети: явные источники → существующий git-клон (git:<sha>) → ручная копия (pinned) → legacy;--checkпечатаетok: superpowers source (<dir>, git <sha>),WARNдляpinned/legacy/отсутствия,FAILдля невалидного явного. -
Step 1: Падающие тесты
test('--check reports the git sha and does not clone', ...)
// сначала --skills с file://-репозиторием (клон создан);
// затем env repo = недоступный путь; run --check;
// stdout /ok: superpowers source \(.*superpowers\/skills, git [0-9a-f]+\)/; status 0
test('--check never clones on an empty HOME', ...)
// empty home, repo недоступен; run --check; status 0;
// <dshHome>/superpowers НЕ существует; stderr /WARN: no superpowers source/
test('--check warns about a manual non-git checkout', ...)
// write <dshHome>/superpowers/skills/using-superpowers/SKILL.md;
// run --check; stdout/stderr /not a git checkout/
Существующий тест --check reports the pinned source заменить этим кейсом (ручная копия больше не ok), сценарий спеки #49 обновлён в change.
- Step 2: Прогнать, увидеть падение
Run: node --test dsh/tests/install.test.mjs
Expected: FAIL на новых и переименованном тестах.
-
Step 3: Реализовать
-
Вынести
static_superpowers_source(без вызова git-команд, меняющих состояние;rev-parse --short HEAD— чтение). -
check()переключить сsuperpowers_sourceнаstatic_superpowers_source; разобрать kind:git:<sha>→ok: superpowers source ($src, git $sha),pinned→WARN: ... is not a git checkout; --skills will not update it,legacy→WARN, нет —WARNс$SUPERPOWERS_REPOи--superpowers-from. -
--checkне должен создавать$DSH_HOME/superpowersи не вызыватьgit clone/fetch. -
Step 4: Прогнать тесты
Run: bash dsh/tests/run.sh
Expected: PASS всего набора.
- Step 5: Commit
git add dsh/install.sh dsh/tests/install.test.mjs
git commit -m "feat(dsh): offline --check reports the git source (#67)"
Task 3: документация, приёмка, PR
Files:
-
Modify:
dsh/README.md(Quick start, Known limits, config) -
Modify:
dsh/PORT.md(§5 install, §7 risks, §8 phases) -
Modify:
openspec/changes/dsh-superpowers-git/design.md(раздел приёмки) -
Step 1: README/PORT
Quick start: убрать ручной git clone, оставить bash dsh/install.sh --global --skills --roles; описать DSH_SUPERPOWERS_REPO, офлайн-кэш и --superpowers-from. Known limits: свежий апстрим тянется при каждом --skills; ручная копия без .git не обновляется. PORT §5: git-источник и sha; §7: сеть в установке и откат на --superpowers-from; §8: #67.
- Step 2: Полный прогон и приёмка
bash dsh/tests/run.sh
bash tests/run.sh
openspec validate dsh-superpowers-git --strict
Живой прогон на машине владельца (после merge): bash dsh/install.sh --skills
→ клон GitHub, --check → git <sha>, повторный --skills → обновление.
В design.md записать ### Приёмка: прогон <дата> с результатами.
- Step 3: Commit, push, PR
git add -A
git commit -m "docs(dsh): git source для superpowers (#67)"
git push -u origin feat/67-dsh-superpowers-git
tea pulls create --head feat/67-dsh-superpowers-git --base main \
--title "DSH: install сам тянет свежий superpowers с git (#67)" \
--description-file <подготовленный файл>
- Step 4: Ревью ветки и итог
Свежий ревьюер (deepseek-v4-pro) по review-package; Critical/Important → один fix pass по TDD; Minor → ledger и PR; итоговая таблица маркеров.
Acceptance
- Спека
dsh-install(delta) покрыта тестами: клон, обновление, офлайн-кэш, неудачный клон, явный источник без сети, ручная копия,--checkбез сети и с sha. bash dsh/tests/run.sh,bash tests/run.sh,openspec validate --strict— зелёные.- Тесты не обращаются к GitHub: единственный источник —
file://-фикстура или недоступный путь. - После merge:
--skillsна машине владельца тянет GitHub и убираетWARN: legacy superpowers source.