claude-harness/adopt-project.sh
andrey.epifantsev 00b496d6da feat: worktree в <repo>/.claude/worktrees/ через EnterWorktree, вход без подтверждения (#25, 0.5.1)
С Claude Code 2.1.206 вход в worktree вне .claude/worktrees/ спрашивает
подтверждение при любых allow-правилах. EnterWorktree с именем создаёт
worktree в <repo>/.claude/worktrees/<slug> на ветке worktree-<slug> от
origin/main и входит без вопроса; ветка переименовывается в
feat/<id>-<slug>.

- roles/dev.md: «Исполнение» — EnterWorktree с именем; п. 1 закрытия —
  archive-<id>; п. 4 — ExitWorktree (keep) снимает блокировку сессии,
  затем git worktree remove и branch -D.
- .claude/worktrees/ в .gitignore: adopt-project.sh, project-template,
  этот репозиторий; doctor.sh — WARN, если не игнорируется.
- Спека hook-guards: сценарии «worktree внутри репозитория» для
  guard-main и require-plan; тесты закрепляют уже верное поведение.
- HARNESS.md (шаг 4, п. 4, подтверждения), SKILL.md, CHANGELOG 0.5.1,
  CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:20:35 +04:00

81 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# claude-harness: подключение обвязки к СУЩЕСТВУЮЩЕМУ репозиторию.
# Запуск из терминала (не из Claude Code): bash adopt-project.sh [папка] [--no-merge] [--push]
# Делает всё на ветке chore/harness-init и по умолчанию вливает её в main fast-forward.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
# Режим workspace (каталог не git, рядом репозитории направлений): bash adopt-project.sh --workspace [папка]
if [ "${1:-}" = "--workspace" ]; then
cd "${2:-.}"; NAME="$(basename "$(pwd)")"
if [ -f CLAUDE.md ]; then echo "CLAUDE.md: есть, не трогаю"
else sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/CLAUDE.md" > CLAUDE.md; echo "CLAUDE.md: создан из шаблона workspace — заполни {{...}} и таблицу репозиториев"; fi
mkdir -p .claude/roles
if [ -e .claude/roles/dev.md ]; then echo ".claude/roles/dev.md: есть, не трогаю"
else sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/roles/dev.md" > .claude/roles/dev.md; echo ".claude/roles/dev.md: заготовка"; fi
bash "$HERE/link-roles.sh" "$(pwd)"
cat <<MSG
Workspace $NAME подключён. Дальше:
1. Заполни таблицу репозиториев в CLAUDE.md и перезапусти: bash $HERE/link-roles.sh $(pwd)
2. Если обвязка ставилась с --no-permissions: разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
"Bash(git push origin --delete:*)"
3. Сессии: claude -n "dev …" | "admin …" | "qa …" | "analyst …" из этого каталога.
MSG
exit 0
fi
DIR="."; MERGE=1; PUSH=0
for a in "$@"; do case "$a" in --no-merge) MERGE=0;; --push) PUSH=1;; *) DIR="$a";; esac; done
cd "$DIR"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "ERR: $DIR — не git-репозиторий"; exit 1; }
[ -z "$(git status --porcelain)" ] || { echo "ERR: рабочее дерево не чистое — закоммить или спрячь изменения"; exit 1; }
NAME="$(basename "$(git rev-parse --show-toplevel)")"
BASE="$(git branch --show-current)"
[ -n "$BASE" ] || { echo "ERR: detached HEAD"; exit 1; }
BR=chore/harness-init
git switch -c "$BR" >/dev/null
# CLAUDE.md: свой — дописать секцию харнеса; нет — из шаблона
if [ -f CLAUDE.md ]; then
grep -q '^## OpenSpec и superpowers' CLAUDE.md || { printf '\n' >> CLAUDE.md; cat "$HERE/project-template/CLAUDE.harness-section.md" >> CLAUDE.md; echo "CLAUDE.md: добавлена секция «OpenSpec и superpowers»"; }
else
sed "s/{{PROJECT_NAME}}/$NAME/" "$HERE/project-template/CLAUDE.md" > CLAUDE.md
echo "CLAUDE.md: создан из шаблона — заполни {{...}} (или попроси агента описать проект по коду на отдельной ветке)"
fi
[ -f ARCHITECTURE.md ] || { cp "$HERE/project-template/ARCHITECTURE.md" ARCHITECTURE.md; echo "ARCHITECTURE.md: заготовка"; }
# .gitignore: только строки обвязки
touch .gitignore
for line in '.superpowers/' '.bounded' '.claude/worktrees/' '.DS_Store'; do
grep -qxF "$line" .gitignore || echo "$line" >> .gitignore
done
mkdir -p docs/superpowers/plans && touch docs/superpowers/plans/.gitkeep
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 не удался"
else echo "WARN: codegraph не установлен"; fi
git add -A
if git diff --cached --quiet; then
echo "Нечего менять: обвязка уже подключена"; git switch "$BASE" >/dev/null; git branch -d "$BR" >/dev/null; exit 0
fi
git commit -qm "chore: claude harness bootstrap"
if [ "$MERGE" = 1 ]; then
git switch "$BASE" >/dev/null && git merge --ff-only "$BR" >/dev/null && git branch -d "$BR" >/dev/null
echo "Влито в $BASE fast-forward."
[ "$PUSH" = 1 ] && git push origin "$BASE"
else
echo "Ветка $BR оставлена, в $BASE не влита."
[ "$PUSH" = 1 ] && git push -u origin "$BR"
fi
cat <<MSG
Проект $NAME подключён к обвязке. Дальше:
1. Если обвязка ставилась с --no-permissions: разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
"Bash(git push origin --delete:*)"
2. cd $(pwd) && claude
«Опиши проект в CLAUDE.md по коду: разделы «Что это», «Стек», «Архитектура», «Команды». Bounded-задача.»
MSG