claude-harness 0.1.0: global CLAUDE.md, hooks, install/new-project scripts, docs

This commit is contained in:
andrey 2026-09-27 15:44:38 +00:00
commit 34a23260b2
15 changed files with 507 additions and 0 deletions

7
CHANGELOG.md Normal file
View File

@ -0,0 +1,7 @@
# Changelog
## 0.1.0 — 2026-09-27
- Первая версия после пилота на obsidian-voice-note (10 задач, 125 тестов, PR #2).
- Хуки: guard-main (main + detached HEAD, Bash-запись, исключение для openspec/), require-plan (.bounded).
- Глобальный CLAUDE.md: маршрутизация superpowers/OpenSpec/Gitea/codegraph, контроллер SDD на sonnet, чеклист закрытия из 6 пунктов, раздел «Decisions made without discussion».
- new-project.sh и шаблон проекта.

71
README.md Normal file
View File

@ -0,0 +1,71 @@
# claude-harness
Обвязка для разработки с Claude Code: superpowers как процесс, Gitea как память между сессиями, OpenSpec как библиотека спек, codegraph как карта кода, три блокирующих хука как жёсткие правила. Проверена на реальной фиче 27.09.2026 (см. `docs/PILOT-2026-09-27.md`).
## Состав
| Слой | Инструмент | Что помнит | Откуда |
|---|---|---|---|
| Процесс внутри фичи | superpowers ≥ 6.4.1 | ledger плана, рулинги | плагин Claude Code |
| Задачи между сессиями | Gitea issues + PR (`tea`) | что делать, блокеры, human gate на merge | ваш сервер |
| Текущее поведение системы | OpenSpec | требования, дельты, история решений | `npm i -g @fission-ai/openspec` |
| Карта кода | codegraph (MCP) | связи символов для сабагентов | `brew install`/`npm i -g` |
| Контекст по модулям | вложенные CLAUDE.md, ARCHITECTURE.md | границы, конвенции | этот пакет |
| Жёсткие правила | `guard-main.sh`, `require-plan.sh` (PreToolUse) | то, что нельзя доверять промпту | этот пакет |
Что **не** входит и почему: beads (дублирует Gitea), template-bridge (устарел, дублирует ledger), caveman по умолчанию (сжимает отчёты и рулинги; остаётся доступен по `/caveman`).
## Файлы
```
global/CLAUDE.md глобальные правила процесса → ~/.claude/CLAUDE.md
hooks/guard-main.sh запрет изменений на main/detached HEAD (Edit/Write/Bash); исключение — коммиты только в openspec/
hooks/require-plan.sh правки в src/ только при наличии плана или файла .bounded
hooks/settings.hooks.json блок PreToolUse для ~/.claude/settings.json
caveman/config.json defaultMode: off → ~/.config/caveman/config.json
project-template/ CLAUDE.md, ARCHITECTURE.md, gitignore для нового проекта
examples/ реальный CLAUDE.md проекта-пилота
install.sh установка глобальной части, идемпотентно; --check проверяет окружение
new-project.sh каркас нового проекта: git, CLAUDE.md, openspec init, codegraph init, remote
docs/HARNESS.md как это работает: слои, цикл фичи, SDD, дисциплина закрытия
docs/PILOT-2026-09-27.md результаты пилота и находки
```
## Установка
```bash
# 1. Плагины Claude Code (внутри сессии claude)
/plugin install superpowers@claude-plugins-official
/plugin uninstall beads@beads-marketplace # если стоит
/plugin uninstall template-bridge@template-bridge-marketplace
# 2. CLI
brew install tea && tea login add --name home --url https://<gitea> --token <token> # scopes: read:user, write:repository, write:issue
npm i -g @fission-ai/openspec
# codegraph: https://github.com/colbymchenry/codegraph
# 3. Глобальная часть
bash install.sh --check
bash install.sh
# 4. Проверка хука: в любом репозитории на main запустить claude и попросить создать файл.
# Ожидается BLOCKED и предложение создать worktree.
```
## Новый проект
```bash
bash new-project.sh ~/projects/my-app ssh://git@<gitea>:2222/<owner>/my-app.git
```
Заполните `{{...}}` в `CLAUDE.md`, положите фикстуры, запустите `claude` и опишите задачу своими словами. Дальше по `docs/HARNESS.md`.
## Цикл одной фичи, коротко
фраза → агент заводит issue → brainstorming (вопросы, подходы, дизайн) → спека в `openspec/changes/<id>/` + раздел «Decisions made without discussion» → ваше утверждение → план с кодом → ваше утверждение → SDD (контроллер на sonnet, implementer-ы haiku/sonnet, ревью каждой задачи, финальное ревью на opus) → PR в Gitea с рулингами → **вы делаете merge** → «закрывай»: archive, close issue, удаление ветки/worktree/workspace, pull main.
Ручных шагов три: два утверждения и merge. Читать обязательно: спеку один раз, раздел решений каждый раз, когда он пополняется.
## Обновление
`git pull && bash install.sh`. Бэкапы `CLAUDE.md` и `settings.json` создаются автоматически. Раз в два месяца сверяйтесь с `RELEASE-NOTES.md` superpowers: семантика skills между версиями меняется.

1
caveman/config.json Normal file
View File

@ -0,0 +1 @@
{ "defaultMode": "off" }

68
docs/HARNESS.md Normal file
View File

@ -0,0 +1,68 @@
# Как работает обвязка
## Идея
Каждый инструмент отвечает за один вид памяти, и слои не пересекаются:
- **Gitea** помнит *что делать* между сессиями и неделями. Issue = фича, PR = точка человеческого контроля.
- **OpenSpec** помнит *как система должна себя вести*. `openspec/specs/` — текущее состояние, `openspec/changes/<id>/` — дельта текущей фичи. После merge дельта архивируется и вливается в specs.
- **Superpowers** ведёт *процесс внутри одной фичи*: brainstorming → writing-plans → subagent-driven-development → ревью → finishing-a-development-branch. Прогресс внутри плана живёт в ledger (`.superpowers/sdd/<plan>/progress.md`), который переживает компакцию и обрыв сессии, но удаляется после завершения.
- **codegraph** — карта кода для сабагентов: `codegraph_explore` вместо grep/Read по файлам.
- **Хуки** — то, что нельзя доверять промпту. Superpowers *просит*, хуки *запрещают*.
Клей между слоями — `global/CLAUDE.md`. Superpowers сам заявляет, что инструкции пользователя выше skills, поэтому вся маршрутизация живёт там.
## Цикл фичи
| # | Шаг | Кто | Артефакт |
|---|---|---|---|
| 1 | Формулируете цель одной фразой; агент заводит issue | вы + агент | issue #N |
| 2 | `brainstorming`: классификация spike / bounded / architectural, вопросы по одному, подходы, дизайн по частям | агент + вы | — |
| 3 | Спека в `openspec/changes/<id>/` (proposal, specs/*, design) + раздел «Decisions made without discussion»; **ваше утверждение** | агент, вы | change |
| 4 | `using-git-worktrees`: ветка `feat/N-slug` | агент | worktree |
| 5 | `writing-plans`: план с Global Constraints, Review Focus, полным кодом задач; Task 1 — тестовая инфраструктура; **ваше утверждение** | агент, вы | `docs/superpowers/plans/*.md` |
| 6 | SDD: контроллер (sonnet) → implementer на задачу (haiku/sonnet) → reviewer (sonnet) → fix loop до 5 раундов → ledger | агент автономно | коммиты, ledger |
| 7 | Финальное ревью всей ветки (opus), одна волна фиксов | агент | — |
| 8 | `finishing-a-development-branch`: PR в Gitea с «Rulings I made» | агент | PR |
| 9 | Ревью PR в Gitea; комментарии → `receiving-code-review` → правки в ветке; **вы делаете merge** | вы ↔ агент | merge |
| 10 | «Закрывай»: чеклист из 6 пунктов (см. CLAUDE.md) | агент | archive, closed issue, чистый main |
Шаги 6–8 идут без остановок. Агент останавливается только на необратимой операции, вопросе безопасности, побочном эффекте вне worktree или плане, который невозможно исполнить.
**Bounded-задача** (флаг, фикс) идёт короче: дизайн в чате, файл `.bounded` в корне worktree (пропускает хук require-plan), TDD, коммит, PR. Без OpenSpec change и без плана.
## Subagent-driven development
Три роли, один цикл на задачу:
- **Контроллер** видит план, спеку, ledger и короткие статусы. Режет план на брифы (`task-brief`), запускает сабагентов, выносит рулинги, ведёт ledger. Кода не пишет. Запускается фоновым сабагентом на sonnet.
- **Implementer** — свежий сабагент на задачу. Видит только свой бриф и интерфейсы соседних задач. TDD, коммит, отчёт в файл, статус DONE / DONE_WITH_CONCERNS / NEEDS_CONTEXT / BLOCKED.
- **Reviewer** — свежий сабагент, получает бриф, отчёт и diff файлом (`review-package`). Два вердикта: соответствие спеке и качество.
Fix loop: раунды 1–3 продолжают того же implementer, 4–5 — свежий на более сильной модели. После пятого контроллер адьюдицирует сам и записывает рулинг. Minor-замечания в цикл не входят, откладываются до финального ревью.
Шаблоны сабагентов входят в superpowers: `implementer-prompt.md`, `task-reviewer-prompt.md`, `re-review-prompt.md`, `code-reviewer.md`. Это роли процесса, а не доменные специалисты; доменная экспертиза приходит через бриф с кодом, проектный CLAUDE.md и доменные skills.
## Решения без обсуждения
Единственное место, где видно, что агент решил за вас. Правило в CLAUDE.md: любое такое решение — на этапе спеки, плана или исполнения — пишется в `openspec/changes/<id>/design.md`, раздел `## Decisions made without discussion`, с датой, обоснованием и ценой ошибки. Ledger удаляется, чат теряется, design.md архивируется со спекой.
Читать: спеку один раз при утверждении, раздел решений каждый раз, когда он пополняется (после спеки, после плана, в финальном отчёте SDD). Каждое решение — вопрос «согласен ли я»: согласен — ничего не делать; не согласен — сказать агенту, он поправит; непонятно — спросить.
## Хуки
| Правило | Файл | Механизм |
|---|---|---|
| Никаких изменений на main / detached HEAD | `guard-main.sh`, PreToolUse на Edit/Write/MultiEdit/Bash | проверяет ветку; для Bash блокирует команды, похожие на запись (`>`, `tee`, `sed -i`, `mv`, `rm`, `git commit/push`…), пропускает `git worktree add`; коммит/push в main разрешены только если изменены исключительно файлы в `openspec/` (архивация) |
| Правки в `src/` только при плане или `.bounded` | `require-plan.sh`, PreToolUse на Edit/Write/MultiEdit | ищет план в `docs/superpowers/plans/` для текущей ветки или файл `.bounded` |
| Тесты зелёные перед коммитом | git pre-commit в проекте (не входит в пакет) | обычный git-хук с командой тестов |
Exit 2 блокирует вызов и показывает агенту stderr, поэтому агент сам делает то, что просит сообщение. Оба обхода, замеченные на пилоте (запись через `echo >` вместо Write; push через detached HEAD), закрыты.
## Дисциплина между сессиями
Новая сессия начинается с `tea issues ls` и `openspec list`, а не с памяти о прошлой. Незавершённый план возобновляется из ledger в worktree. Связка разваливается не на исполнении, а на закрытии — поэтому «закрывай» выполняет весь чеклист без остановок.
## Стоимость
Bootstrap superpowers (~3 КБ) вставляется на каждом старте и после компакции. SDD стоит свежий контекст на задачу и на ревью; подробный план с готовым кодом позволяет отдать большинство задач haiku. Для планов до 5 задач дешевле `executing-plans` (Native): один контекст, одно ревью в конце.

43
docs/PILOT-2026-09-27.md Normal file
View File

@ -0,0 +1,43 @@
# Пилот: obsidian-voice-note, core-recording-lifecycle
Первая подсистема плагина голосовых заметок для Obsidian (жизненный цикл записи и очередь отправки) прошла цикл целиком за один день: фраза → issue #1 → brainstorming → спека → план на 10 задач → SDD → PR #2 → merge → archive → close.
| Показатель | Значение |
|---|---|
| Brainstorming → утверждённая спека | ~35 мин, 5 вопросов, 3 подхода, дизайн в 4 частях |
| План | 2528 строк, 10 задач с полным кодом, Review Focus 5 пунктов, spec coverage |
| Исполнение SDD | 1 ч 35 мин, без вопросов к человеку |
| Ревью с первого раза | 8 из 10; 2 раунда фиксов (один косметический: подпись коммита) |
| Финальное ревью (opus) | 0 critical, 1 important, 3 minor, одна волна фиксов, 7/7 addressed |
| Тесты | 125/125, golden сошёлся с первого прогона; `src/core` 670 строк |
| Модели исполнителей | haiku 7 задач, sonnet 3; ревьюеры sonnet, финал opus |
| Решения без обсуждения | 36 в design.md (28 спека/план, 8 исполнение) |
| Обрыв контроллера | случайная остановка перед Task 1; возобновился из ledger без потерь |
## Что подтвердилось
- Автотриггер brainstorming без вызова по имени; skills OpenSpec его не перехватили.
- Спека легла в `openspec/changes/`, issue завёл агент, worktree создал сам после блокировки хуком.
- Раздел «Decisions made without discussion» ведётся на всех этапах, включая контроллер SDD; агент сам убрал из него пункт, который предложил человек.
- Контроллер следовал спеке как источнику истины против плана (рулинг F-1) — правильно.
- Подробный план с кодом → большинство задач на haiku без брака.
## Находки и что изменено
1. **Контроллер «на среднем уровне» запустился на Opus.** Сессия поняла фразу как «уровнем ниже». → CLAUDE.md: контроллер — сабагент на sonnet, явно.
2. **codegraph в worktree возвращал пусто** даже после `codegraph init`; implementer-ы читали файлы напрямую. → CLAUDE.md: при пустом ответе отметить в ledger и не тратить раунды. Причина (индекс привязан к корню репо) — открытый вопрос.
3. **Push в main через detached HEAD.** При архивации агент сделал `git switch --detach origin/main` и запушил; `git branch --show-current` пуст, хук пропустил. → guard-main: detached HEAD защищён, коммит/push в main только с файлами `openspec/`; архивация объявлена единственным исключением.
4. Хвосты закрытия (ветка, worktree, pull main) оставались человеку. → чеклист закрытия из 6 пунктов в CLAUDE.md.
## Мелкие наблюдения
- Первый тест хука: агент обошёл блокировку Write через `echo >`. Bash добавлен в matcher.
- Вставленный текст без своей строки вызывает вопрос-подтверждение; добавлять слово перед вставкой.
- haiku дважды подписал коммит своим именем вместо трейлера из плана.
- Версии зависимостей не зафиксированы (TypeScript 7, vitest 5, esbuild 0.28).
## Отложено до следующей фичи
- T1: Node-глобалы (`process`, `Buffer`) проходят проверку типов в `src/core`; страж ловит только импорты. Bounded-фикс до обёртки.
- Зафиксировать версии зависимостей. Bounded-фикс.
- Провайдер распознавания (AITUNNEL, OpenAI-compatible) — issue #2. Обёртка Obsidian — issue #3.

View File

@ -0,0 +1,28 @@
# Obsidian Voice Notes — плагин голосовых заметок
## Что это
Плагин Obsidian: запись голоса с телефона или десктопа → отправка файлом во внешний сервис распознавания → текст в заметку. Mobile-first: основной сценарий — iPhone, десктоп получает то же.
## Стек и ограничения
- TypeScript, esbuild (стандартный шаблон obsidian-sample-plugin), vitest для тестов.
- `isDesktopOnly: false` в manifest.json. Никакого Node API (`fs`, `child_process`, `path`), только Web API и API Obsidian (`requestUrl`, `app.vault`).
- Запись: `MediaRecorder`. Форматы на входе различаются: iOS даёт audio/mp4, Android — audio/webm, десктоп — webm/ogg. Провайдер должен принимать все три или конвертировать.
- Отправка файлом, без стриминга.
## Архитектура
- `src/core/` — чистый TypeScript без импортов из `obsidian`: машина состояний записи (created → saved → sent → transcribed → inserted, ветки на потерю сети и обрыв записи), очередь отправки, провайдеры распознавания, формирование markdown-заметки. Покрывается vitest полностью.
- `src/obsidian/` — тонкая обёртка: команды, настройки, UI, работа с vault. Юнит-тестов нет, проверяется руками в тестовом vault.
- Провайдер — интерфейс с реализациями: `openai-compatible` (AITUNNEL по умолчанию; Groq, OpenAI, whisper на VPS — тот же код, другой URL) и при необходимости `yandex`. Переключение на следующего провайдера по 403/таймауту.
- Ключи API — в настройках плагина (`data.json`), в git не попадают.
## Команды
- `npm run dev` — сборка в watch-режиме в тестовый vault.
- `npm test` — vitest, вся папка `tests/`. Зелёным считается весь набор.
- `npm run build` — продакшн-сборка `main.js`.
## Тесты
- Golden-тесты: `tests/fixtures/<name>.m4a` + `tests/fixtures/<name>.txt` (эталонный транскрипт). Провайдер в тестах мокается; golden-файлы проверяют формирование заметки и обработку ответа, а не сам сервис.
- Тестовый vault: `<путь укажите>` — синхронизируется между Mac и телефоном.
## Первая подсистема
Жизненный цикл записи и очередь (core), затем провайдер, затем обёртка. Тестовая инфраструктура — Task 1 первого плана.

34
global/CLAUDE.md Normal file
View File

@ -0,0 +1,34 @@
# Процесс разработки
## Роли инструментов
- Superpowers ведёт процесс внутри одной фичи: brainstorming → writing-plans → исполнение → ревью → finishing-a-development-branch.
- Gitea (CLI `tea`) хранит задачи между сессиями. Одна фича = одна issue = одна ветка = один PR. Issue заводит агент (`tea issues create`) из формулировки цели, до brainstorming.
- OpenSpec хранит требования. `openspec/specs/` — текущее поведение системы, `openspec/changes/<id>/` — дельта текущей фичи. Спека — единственный источник истины при рулингах.
- Ledger superpowers (`.superpowers/sdd/`) — прогресс только внутри текущего плана. Задачи плана в Gitea не дублировать.
## Маршрутизация
- Bounded-задача (флаг, фикс, мелкая правка): без OpenSpec change и без плана. Дизайн в чате, затем создать файл `.bounded` в корне worktree (разрешает правки в src/ без плана), TDD, коммит, PR.
- Architectural-задача: brainstorming пишет спеку не в `docs/superpowers/specs/`, а в `openspec/changes/<id>/`. В поле **Spec** плана указывается этот путь.
- Перед architectural brainstorming прочитать релевантные файлы в `openspec/specs/` и ARCHITECTURE.md, если есть.
- Карта кода — codegraph (MCP). Перед изменением implementer вызывает `codegraph_explore` по своей области вместо grep/Read по файлам. Это инструмент внутри процесса, а не замена skill-check. В новом проекте: `codegraph init` один раз. Если `codegraph_explore` возвращает пусто (известная проблема в worktree под `.claude/worktrees/`), один раз сообщить об этом в ledger и дальше использовать Read, не тратя раунды на переиндексацию.
## Исполнение
- Работа только в worktree. Ветка называется по issue: `feat/<issue-id>-<slug>`.
- Режим по умолчанию: subagent-driven-development. Контроллер SDD запускается как фоновый сабагент **на модели sonnet** (не на модели сессии) — сессия только ждёт его отчёт. Для планов до 5 задач — executing-plans (Native).
- Модели исполнителей выбираются по SDD Model Selection: задача с готовым кодом в плане — haiku, интеграция — sonnet, ревью всей ветки — opus. Модель в dispatch указывать всегда.
## Завершение
- finishing-a-development-branch создаёт PR в Gitea (`tea pulls create`) и ссылается на issue. Комментарии к PR обрабатываются через receiving-code-review: агент читает их из Gitea, правит в той же ветке, отвечает в PR. Merge — только по явной команде человека.
- После merge по команде «закрывай» выполняется весь чеклист закрытия, без остановок между пунктами:
1. `openspec archive <id>`, коммит и push архива прямо в main (единственное разрешённое исключение из правила «в main только через PR»: хук пропускает коммиты, затрагивающие только `openspec/`).
2. `tea issues close <id>` с комментарием-ссылкой на PR.
3. Удалить SDD workspace (`.superpowers/sdd/<plan>/`).
4. Удалить ветку фичи на сервере и локально (`git push origin --delete`, `git branch -D`), удалить worktree (`git worktree remove`). Остальные worktree не трогать.
5. Обновить основной checkout: если рабочее дерево там чистое — `git pull --ff-only`; если есть незакоммиченные изменения — не трогать, а перечислить их в итоговом сообщении.
6. Итоговое сообщение: что сделано по пунктам 1–5, что осталось человеку.
Любой другой push в main — только через PR.
- Список "Rulings I made" из финального сообщения копируется в описание PR.
- Любое решение, принятое агентом без обсуждения с человеком — на этапе спеки, плана или исполнения — дописывается в `openspec/changes/<id>/design.md` в раздел `## Decisions made without discussion`: дата, решение, обоснование, что стоит ошибка. Ledger SDD удаляется, чат теряется; design.md архивируется вместе со спекой и остаётся в истории.
## Стиль
- Отчёты сабагентов, рулинги и ledger не сжимать. Caveman включается только по явной команде.

44
hooks/guard-main.sh Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env bash
# PreToolUse hook: block file changes on main/master and on detached HEAD.
# Covers Edit/Write/MultiEdit directly and Bash commands that look like writes.
# Single exception: commits/pushes to main that touch only openspec/ (archive step).
set -euo pipefail
input=$(cat)
eval "$(printf '%s' "$input" | python3 -c '
import sys, json, shlex
d = json.load(sys.stdin); ti = d.get("tool_input", {}) or {}
print("cwd=" + shlex.quote(d.get("cwd", "")))
print("tool=" + shlex.quote(d.get("tool_name", "")))
print("cmd=" + shlex.quote(ti.get("command", "") or ""))' 2>/dev/null || echo 'cwd=; tool=; cmd=')"
[ -n "$cwd" ] || exit 0
git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
branch=$(git -C "$cwd" branch --show-current 2>/dev/null || true)
case "$branch" in main|master|"") ;; *) exit 0 ;; esac
label=${branch:-"detached HEAD"}
only_openspec() { # $1 = git diff range/args; true if every changed path is under openspec/
local files
files=$(git -C "$cwd" diff --name-only $1 2>/dev/null || true)
[ -n "$files" ] && ! printf '%s\n' "$files" | grep -vq '^openspec/'
}
if [ "$tool" = "Bash" ]; then
case "$cmd" in
*"git worktree"*|*"git checkout -b"*|*"git switch -c"*|*"openspec archive"*) exit 0 ;;
esac
# archive exception: commit of staged openspec-only changes, push of openspec-only commits
if printf '%s' "$cmd" | grep -Eq '\bgit\s+commit\b'; then
only_openspec "--cached" && exit 0
fi
if printf '%s' "$cmd" | grep -Eq '\bgit\s+push\b'; then
only_openspec "origin/main...HEAD" && exit 0
fi
if printf '%s' "$cmd" | grep -Eq '(^|[^<])>{1,2}[^&]|\btee\b|\bsed\s+-i|\bmv\b|\bcp\b|\brm\b|\btouch\b|\bmkdir\b|\bgit\s+(add|commit|push|apply|stash\s+pop)\b|\bnpm\s+(i|install|init)\b|\bpip\s+install\b'; then
echo "BLOCKED: shell write on $label ($(printf '%s' "$cmd" | head -c 80)). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main directly." >&2
exit 2
fi
exit 0
fi
echo "BLOCKED: you are on $label. Create an isolated worktree first (superpowers:using-git-worktrees), never edit main directly." >&2
exit 2

24
hooks/require-plan.sh Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
# PreToolUse hook: edits under src/ require a plan for this branch or a .bounded marker.
set -euo pipefail
input=$(cat)
read -r cwd fpath < <(printf '%s' "$input" | python3 -c '
import sys,json
d=json.load(sys.stdin); ti=d.get("tool_input",{}) or {}
print(d.get("cwd",""), ti.get("file_path") or ti.get("path") or "")' 2>/dev/null || echo " ")
[ -n "$cwd" ] && [ -n "$fpath" ] || exit 0
root=$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null || true)
[ -n "$root" ] || exit 0
case "$fpath" in
"$root"/src/*|src/*) ;;
*) exit 0 ;;
esac
[ -f "$root/.bounded" ] && exit 0
branch=$(git -C "$cwd" branch --show-current 2>/dev/null || true)
if ls "$root"/docs/superpowers/plans/*.md >/dev/null 2>&1; then
if [ -z "$branch" ] || grep -lq -- "$branch" "$root"/docs/superpowers/plans/*.md 2>/dev/null; then exit 0; fi
# any plan exists but none names this branch: still allow if only one plan (single-feature repo)
[ "$(ls "$root"/docs/superpowers/plans/*.md | wc -l)" -eq 1 ] && exit 0
fi
echo "BLOCKED: editing $fpath without a plan. Run superpowers:brainstorming; for a bounded change create '$root/.bounded' first." >&2
exit 2

18
hooks/settings.hooks.json Normal file
View File

@ -0,0 +1,18 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write|MultiEdit|Bash",
"hooks": [
{ "type": "command", "command": "\"$HOME/.claude/hooks/guard-main.sh\"" }
]
},
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{ "type": "command", "command": "\"$HOME/.claude/hooks/require-plan.sh\"" }
]
}
]
}
}

74
install.sh Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env bash
# claude-harness: установка глобальной части (CLAUDE.md, хуки, конфиг caveman, слияние settings.json).
# Идемпотентно. Запуск: bash install.sh | проверка окружения: bash install.sh --check
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
CC="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
check() {
local ok=0
echo "== Проверка окружения"
for bin in claude git python3 node npm tea openspec codegraph; do
if command -v "$bin" >/dev/null 2>&1; then echo " ok $bin ($(command -v "$bin"))"; else echo " MISS $bin"; ok=1; fi
done
if [ -f "$CC/plugins/installed_plugins.json" ]; then
for p in superpowers caveman; do
if grep -q "\"$p@" "$CC/plugins/installed_plugins.json"; then echo " ok plugin $p"; else echo " MISS plugin $p (/plugin install ...)"; fi
done
for p in beads template-bridge; do
grep -q "\"$p@" "$CC/plugins/installed_plugins.json" && echo " WARN plugin $p установлен — конфликтует с обвязкой, удалить" || true
done
else
echo " WARN не найден $CC/plugins/installed_plugins.json"
fi
if [ -f "$CC/settings.json" ] && grep -q 'codegraph prompt-hook' "$CC/settings.json"; then
echo " WARN codegraph prompt-hook в settings.json — рекомендуется отключить на время пилота"
fi
return $ok
}
if [ "${1:-}" = "--check" ]; then check; exit $?; fi
mkdir -p "$CC/hooks" "$HOME/.config/caveman"
stamp=$(date +%Y%m%d-%H%M%S)
# 1. Глобальный CLAUDE.md
if [ -f "$CC/CLAUDE.md" ] && ! cmp -s "$HERE/global/CLAUDE.md" "$CC/CLAUDE.md"; then
cp "$CC/CLAUDE.md" "$CC/CLAUDE.md.bak.$stamp"; echo "backup: CLAUDE.md.bak.$stamp"
fi
cp "$HERE/global/CLAUDE.md" "$CC/CLAUDE.md"
# 2. Хуки
cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$CC/hooks/"
chmod +x "$CC/hooks/"*.sh
# 3. caveman off by default
cp "$HERE/caveman/config.json" "$HOME/.config/caveman/config.json"
# 4. settings.json: слить блок hooks, вычистить хвосты beads/template-bridge
SETTINGS="$CC/settings.json"
[ -f "$SETTINGS" ] || echo '{}' > "$SETTINGS"
cp "$SETTINGS" "$SETTINGS.bak.$stamp"
python3 - "$SETTINGS" "$HERE/hooks/settings.hooks.json" <<'PY'
import json, sys
sp, hp = sys.argv[1], sys.argv[2]
s = json.load(open(sp)); h = json.load(open(hp))
hooks = s.setdefault("hooks", {})
pre = hooks.setdefault("PreToolUse", [])
def ours(entry):
return any(("guard-main.sh" in x.get("command","") or "require-plan.sh" in x.get("command","")) for x in entry.get("hooks",[]))
pre[:] = [e for e in pre if not ours(e)]
pre.extend(h["hooks"]["PreToolUse"])
for ev in ("SessionStart","PreCompact"):
if ev in hooks:
for e in hooks[ev]:
e["hooks"] = [x for x in e.get("hooks",[]) if "bd prime" not in x.get("command","") and "WORKFLOW REMINDER" not in x.get("command","")]
hooks[ev] = [e for e in hooks[ev] if e.get("hooks")]
if not hooks[ev]: del hooks[ev]
json.dump(s, open(sp,"w"), ensure_ascii=False, indent=2)
print("settings.json: hooks merged")
PY
echo "done: $CC/CLAUDE.md, $CC/hooks/{guard-main,require-plan}.sh, ~/.config/caveman/config.json"
echo "Плагины Claude Code ставятся вручную: /plugin install superpowers@claude-plugins-official"
check || true

40
new-project.sh Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# claude-harness: каркас нового проекта под обвязку.
# Запуск: bash new-project.sh <папка> [ssh-url репозитория в Gitea]
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
DIR="${1:?usage: new-project.sh <dir> [git-remote-url]}"
REMOTE="${2:-}"
NAME="$(basename "$DIR")"
mkdir -p "$DIR" && cd "$DIR"
[ -d .git ] || git init -b main
sed "s/{{PROJECT_NAME}}/$NAME/" "$HERE/project-template/CLAUDE.md" > CLAUDE.md
cp "$HERE/project-template/ARCHITECTURE.md" ARCHITECTURE.md
cp "$HERE/project-template/gitignore" .gitignore
mkdir -p tests/fixtures docs/superpowers/plans
if command -v openspec >/dev/null 2>&1; then
[ -d openspec ] || openspec init --tools claude 2>/dev/null || openspec init
else
echo "WARN: openspec не установлен (npm i -g @fission-ai/openspec)"
fi
if command -v codegraph >/dev/null 2>&1; then
[ -d .codegraph ] || codegraph init >/dev/null 2>&1 || echo "WARN: codegraph init не удался"
fi
git add -A
git commit -qm "chore: bootstrap project skeleton (claude-harness)" || true
if [ -n "$REMOTE" ]; then
git remote get-url origin >/dev/null 2>&1 || git remote add origin "$REMOTE"
git push -u origin main
fi
cat <<MSG
Проект $NAME готов. Дальше:
1. Заполните {{...}} в CLAUDE.md и ARCHITECTURE.md.
2. Положите первые фикстуры в tests/fixtures/.
3. cd $DIR && claude
«Хочу сделать ... Описание и ограничения в CLAUDE.md. Давай начнём.»
MSG

View File

@ -0,0 +1,15 @@
# Архитектура
{{Карта модулей на одну страницу: что где лежит, границы, потоки данных. Читается агентом перед architectural brainstorming. Обновляется вместе со спеками.}}
## Модули
| Модуль | Ответственность | Зависит от |
|---|---|---|
| | | |
## Границы
- Что не должно импортировать что.
## Открытые вопросы
-

View File

@ -0,0 +1,31 @@
# {{PROJECT_NAME}}
## Что это
{{ОДНИМ АБЗАЦЕМ: что делает система, для кого, главный сценарий.}}
## Стек и ограничения
- {{Язык, сборка, тесты — например: Python 3.12, uv, pytest.}}
- {{Жёсткие ограничения среды исполнения — без чего код не должен собираться.}}
## Архитектура
- `src/core/` (или {{ваш путь}}) — чистая логика без зависимостей от фреймворка/платформы. Покрывается тестами полностью.
- `src/{{adapter}}/` — тонкая обёртка над платформой. Тестируется руками или интеграционно.
- {{Ключевые интерфейсы/порты между ними, если есть.}}
## Команды
- `{{npm test / pytest}}` — весь набор тестов. Зелёным считается только весь набор.
- `{{npm run build / ...}}` — сборка.
- `{{npm run dev / ...}}` — локальный запуск.
## Тесты
- Golden-тесты: `tests/fixtures/<name>.*` + эталон `<name>.expected.*`. Внешние сервисы в тестах мокаются.
- {{Где лежит тестовое окружение (vault, БД, песочница).}}
## Первая подсистема
{{Что делается первым и почему. Тестовая инфраструктура — Task 1 первого плана.}}
## OpenSpec и superpowers
- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
- Когда brainstorming классифицировал задачу как architectural, структуру change создать через `openspec new change <id>` (или `/opsx:propose <id>`), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers.
- `/opsx:apply` не использовать: исполнение идёт через writing-plans и SDD/executing-plans.
- `openspec archive <id>` — после merge PR, как часть чеклиста закрытия.

View File

@ -0,0 +1,9 @@
node_modules/
main.js
*.js.map
data.json
.superpowers/
.bounded
tests/fixtures/*.m4a
tests/fixtures/*.webm
.DS_Store