diff --git a/install.sh b/install.sh index bddd032..ba1bc01 100755 --- a/install.sh +++ b/install.sh @@ -24,6 +24,12 @@ check() { if [ -f "$CC/settings.json" ] && grep -q 'codegraph prompt-hook' "$CC/settings.json"; then echo " WARN codegraph prompt-hook в settings.json — рекомендуется отключить на время пилота" fi + echo "== Установленная часть обвязки ($CC)" + for f in CLAUDE.md hooks/guard-main.sh hooks/require-plan.sh hooks/harness-check.sh hooks/role-hint.sh \ + roles/dev.md roles/admin.md roles/analyst.md roles/qa.md skills/role/SKILL.md agents/briefing.md \ + harness/adopt-project.sh harness/new-project.sh harness/link-roles.sh skills/harness-doctor/SKILL.md; do + [ -f "$CC/$f" ] && echo " ok $f" || { echo " MISS $f (bash install.sh)"; ok=1; } + done return $ok } @@ -39,15 +45,21 @@ fi cp "$HERE/global/CLAUDE.md" "$CC/CLAUDE.md" # 2. Хуки -cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$HERE/hooks/harness-check.sh" "$CC/hooks/" +cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$HERE/hooks/harness-check.sh" "$HERE/hooks/role-hint.sh" "$CC/hooks/" # 2a. Скрипты бутстрапа проекта — агент запускает их из сессии mkdir -p "$CC/harness" -cp -R "$HERE/adopt-project.sh" "$HERE/new-project.sh" "$HERE/project-template" "$CC/harness/" +rm -rf "$CC/harness/project-template" +cp -R "$HERE/adopt-project.sh" "$HERE/new-project.sh" "$HERE/link-roles.sh" "$HERE/project-template" "$CC/harness/" chmod +x "$CC/harness/"*.sh # 2b. Skill диагностики mkdir -p "$CC/skills" rm -rf "$CC/skills/harness-doctor" && cp -R "$HERE/skills/harness-doctor" "$CC/skills/harness-doctor" chmod +x "$CC/skills/harness-doctor/scripts/"*.sh +# 2c. Роли, skill /role, агент briefing +mkdir -p "$CC/roles" "$CC/skills/role" "$CC/agents" +cp "$HERE/roles/"*.md "$CC/roles/" +cp "$HERE/skills/role/SKILL.md" "$CC/skills/role/SKILL.md" +cp "$HERE/agents/briefing.md" "$CC/agents/briefing.md" chmod +x "$CC/hooks/"*.sh # 3. caveman off by default @@ -68,7 +80,8 @@ def ours(entry): pre[:] = [e for e in pre if not ours(e)] pre.extend(h["hooks"]["PreToolUse"]) ss = hooks.setdefault("SessionStart", []) -ss[:] = [e for e in ss if not any("harness-check.sh" in x.get("command","") for x in e.get("hooks",[]))] +ours_ss = ("harness-check.sh", "role-hint.sh") +ss[:] = [e for e in ss if not any(any(n in x.get("command","") for n in ours_ss) for x in e.get("hooks",[]))] ss.extend(h["hooks"]["SessionStart"]) for ev in ("SessionStart","PreCompact"): if ev in hooks: @@ -80,6 +93,6 @@ json.dump(s, open(sp,"w"), ensure_ascii=False, indent=2) print("settings.json: hooks merged") PY -echo "done: $CC/CLAUDE.md, $CC/skills/harness-doctor, $CC/hooks/{guard-main,require-plan,harness-check}.sh, $CC/harness/, ~/.config/caveman/config.json" +echo "done: $CC/CLAUDE.md, $CC/skills/harness-doctor, $CC/hooks/{guard-main,require-plan,harness-check,role-hint}.sh, $CC/roles/, $CC/skills/role, $CC/agents/briefing.md, $CC/harness/, ~/.config/caveman/config.json" echo "Плагины Claude Code ставятся вручную: /plugin install superpowers@claude-plugins-official" check || true diff --git a/tests/test-install.sh b/tests/test-install.sh new file mode 100644 index 0000000..993ca4b --- /dev/null +++ b/tests/test-install.sh @@ -0,0 +1,24 @@ +# install.sh: новые файлы ставятся, повторный запуск не дублирует хуки, --check их видит. +export HOME="$TMP/home"; export CLAUDE_CONFIG_DIR="$TMP/home/.claude"; mkdir -p "$CLAUDE_CONFIG_DIR" +echo '{"hooks":{"SessionStart":[{"matcher":"startup","hooks":[{"type":"command","command":"\"$HOME/.claude/hooks/harness-check.sh\""}]}]}}' > "$CLAUDE_CONFIG_DIR/settings.json" +bash "$ROOT/install.sh" >/dev/null 2>&1; bash "$ROOT/install.sh" >/dev/null 2>&1 +CC="$CLAUDE_CONFIG_DIR" +for r in dev admin analyst qa; do assert_file "roles/$r.md установлен" "$CC/roles/$r.md"; done +assert_file "skills/role/SKILL.md установлен" "$CC/skills/role/SKILL.md" +assert_file "agents/briefing.md установлен" "$CC/agents/briefing.md" +assert_file "hooks/role-hint.sh установлен" "$CC/hooks/role-hint.sh" +assert_eq "hooks/role-hint.sh исполняемый" "$([ -x "$CC/hooks/role-hint.sh" ] && echo x)" "x" +assert_file "harness/link-roles.sh установлен" "$CC/harness/link-roles.sh" +assert_file "harness/project-template/workspace" "$CC/harness/project-template/workspace/CLAUDE.md" +assert_file "harness/project-template/role-repo" "$CC/harness/project-template/role-repo/ROLE.md" +cnt=$(python3 -c "import json,sys; s=json.load(open(sys.argv[1])); print(sum(1 for e in s['hooks']['SessionStart'] for h in e.get('hooks',[]) if 'role-hint.sh' in h['command']))" "$CC/settings.json") +assert_eq "role-hint.sh в settings.json один раз после двух установок" "$cnt" "1" +cnt=$(python3 -c "import json,sys; s=json.load(open(sys.argv[1])); print(sum(1 for e in s['hooks']['SessionStart'] for h in e.get('hooks',[]) if 'harness-check.sh' in h['command']))" "$CC/settings.json") +assert_eq "harness-check.sh в settings.json один раз" "$cnt" "1" +out=$(bash "$ROOT/install.sh" --check 2>&1) +assert_grep "--check: roles" 'roles/' <(printf '%s' "$out") +assert_grep "--check: skill role" 'skills/role' <(printf '%s' "$out") +assert_grep "--check: agent briefing" 'agents/briefing' <(printf '%s' "$out") +rm "$CC/agents/briefing.md" +out=$(bash "$ROOT/install.sh" --check 2>&1) +assert_grep "--check: MISS briefing" 'MISS +.*briefing' <(printf '%s' "$out")