claude-harness/adopt-project.sh
andrey.epifantsev 897fa0f3a7 feat: install.sh ставит allow-правила цикла фичи, --no-permissions для отказа (#24, 0.5.0)
Пересмотр решения владельца 2: allow-правила (git worktree, git branch
-m/-D, push фичевых и chore-веток, push --delete, tea issues/pulls
create/list/edit/comments) ставит install.sh в ~/.claude/settings.json,
а не владелец руками. Список — global/permissions.json; merge и push в
main в нём нет. Правила только дописываются, чужие не трогаются; копия
списка — ~/.claude/harness/permissions.json для doctor.sh.

- install.sh: шаг 5 — слияние permissions.allow; --no-permissions;
  неизвестный аргумент — ошибка; --check показывает permissions N/M.
- doctor.sh, «Машина»: ok / WARN со списком недостающих / MISS.
- adopt/new-project: строка push --delete — для --no-permissions.
- harness-doctor: агент чинит install.sh --no-permissions, права ставит
  владелец (иначе обход отказа классификатора auto mode).
- EnterWorktree не входит: вход в worktree вне .claude/worktrees/
  спрашивает подтверждение при любых правилах (#25).
- README, HARNESS.md, roles/dev.md, CLAUDE.md, ретро (пометка о
  пересмотре решения 2), CHANGELOG 0.5.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:01:57 +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' '.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