great-cto 3.33.0 → 3.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 70 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.35.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -55,31 +55,30 @@
|
|
|
55
55
|
"hooks": [
|
|
56
56
|
{
|
|
57
57
|
"type": "command",
|
|
58
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')};
|
|
59
|
-
"timeout": 5
|
|
60
|
-
"statusMessage": "Refreshing orchestrator contract..."
|
|
58
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; [ -n \"$PLUGIN_DIR\" ] && node \"${PLUGIN_DIR}/scripts/lib/state-gitignore.mjs\" 2>/dev/null; true",
|
|
59
|
+
"timeout": 5
|
|
61
60
|
},
|
|
62
61
|
{
|
|
63
62
|
"type": "command",
|
|
64
|
-
"command": "export PATH=\"/opt/homebrew/bin:$HOME/.local/bin:/usr/local/bin:$PATH\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; if [ -z \"$PLUGIN_DIR\" ]; then echo 'GREAT_CTO: plugin dir not found \u2014 run /update'; else mkdir -p ~/.great_cto .great_cto; node \"${PLUGIN_DIR}/scripts/lib/sync-managed.mjs\" --plugin-dir \"${PLUGIN_DIR}\" 2>/dev/null || true; cp \"${PLUGIN_DIR}/skills/great_cto/ARCHETYPES.md\" .great_cto/ARCHETYPES.md 2>/dev/null || true; cp \"${PLUGIN_DIR}/skills/great_cto/SKILL.md\" .great_cto/SKILL.md 2>/dev/null || true; { printf \"export PATH=/opt/homebrew/bin:$HOME/.local/bin:/usr/local/bin:$PATH\\nexport ARCHETYPES_MD=.great_cto/ARCHETYPES.md\\n\"; } > .great_cto/env.sh; fi; if [ ! -d ~/.great_cto/catalog ]; then git clone --depth=1 https://github.com/davila7/claude-code-templates.git ~/.great_cto/catalog 2>/dev/null & fi; if [ ! -d ~/.great_cto/anthropic-skills ]; then git clone --depth=1 https://github.com/anthropics/skills.git ~/.great_cto/anthropic-skills 2>/dev/null & elif [ $(($(date +%s) - $(stat -f %m ~/.great_cto/anthropic-skills/.git 2>/dev/null || stat -c %Y ~/.great_cto/anthropic-skills/.git 2>/dev/null || echo 0))) -gt 604800 ]; then (cd ~/.great_cto/anthropic-skills && git pull --quiet 2>/dev/null) & fi; if [ ! -d ~/.great_cto/personal-skills ]; then git clone --depth=1 https://github.com/avelikiy/ai-agent-skills.git ~/.great_cto/personal-skills 2>/dev/null & elif [ $(($(date +%s) - $(stat -f %m ~/.great_cto/personal-skills/.git 2>/dev/null || stat -c %Y ~/.great_cto/personal-skills/.git 2>/dev/null || echo 0))) -gt 86400 ]; then (cd ~/.great_cto/personal-skills && git pull --quiet 2>/dev/null) & fi; if [ ! -f ~/.great_cto/skills-registry.json ] || [ $(($(date +%s) - $(stat -f %m ~/.great_cto/skills-registry.json 2>/dev/null || stat -c %Y ~/.great_cto/skills-registry.json 2>/dev/null || echo 0))) -gt 86400 ]; then [ -x \"${PLUGIN_DIR}/scripts/skill-discover.sh\" ] && bash \"${PLUGIN_DIR}/scripts/skill-discover.sh\" >/dev/null 2>&1 & fi; [ -x \"${PLUGIN_DIR}/scripts/hooks/welcome.sh\" ] && bash \"${PLUGIN_DIR}/scripts/hooks/welcome.sh\" 2>/dev/null || true; bash \"${PLUGIN_DIR}/scripts/apply-model-override.sh\" 2>/dev/null || true; bd prime 2>/dev/null || true; cat \"${PLUGIN_DIR}/scripts/hooks/operating-rules.md\" 2>/dev/null || true; echo '=== PREFERENCES ==='; node \"${PLUGIN_DIR}/scripts/hooks/read-global-memory.mjs\" 2>&1 || true; echo '=== PROJECT ==='; cat .great_cto/PROJECT.md 2>/dev/null || echo 'GREAT_CTO: No PROJECT.md \u2014 run /start'; PHASE=$(grep '^phase:' .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}' || echo implementation); echo '=== PHASE ==='; echo \"$PHASE\"; { [ \"$PHASE\" != review ] && [ \"$PHASE\" != release ]; } && { echo '=== BRAIN ==='; head -30 .great_cto/brain.md 2>/dev/null; } || true; [ \"$PHASE\" = implementation ] && { echo '=== CODEBASE ==='; head -20 .great_cto/CODEBASE.md 2>/dev/null; } || true; echo '=== LOCAL ==='; cat .great_cto/local.md 2>/dev/null || true; [ \"$PHASE\" != planning ] && { echo '=== HANDOFF ==='; cat .great_cto/HANDOFF.md 2>/dev/null || echo 'No handoff state \u2014 fresh session'; } || true; [ \"$PHASE\" = review ] && { echo '=== LATEST QA ==='; ls docs/qa-reports/QA-*.md 2>/dev/null | sort -V | tail -1 | xargs head -20 2>/dev/null; echo '=== LATEST CSO ==='; ls docs/security/CSO-*.md 2>/dev/null | sort -V | tail -1 | xargs head -20 2>/dev/null; } || true; [ \"$PHASE\" = release ] && { echo '=== PERF BASELINE ==='; tail -10 .great_cto/perf-baseline.log 2>/dev/null; } || true; echo '=== STATUS ==='; OPEN_GATES=$(bd list --label gate --status open 2>/dev/null | wc -l | tr -d ' '); OPEN_TASKS=$(bd list --status open 2>/dev/null | wc -l | tr -d ' '); BRANCH=$(git branch --show-current 2>/dev/null || echo '?'); LAST_AGENT=$(ls -t .great_cto/verdicts/*.log 2>/dev/null | head -1 | xargs tail -1 2>/dev/null | awk '{print $2}' || echo 'none'); printf 'branch=%s | gates=%s | tasks=%s | last_agent=%s\\n' \"$BRANCH\" \"${OPEN_GATES:-0}\" \"${OPEN_TASKS:-0}\" \"${LAST_AGENT:-none}\"; [ \"${OPEN_GATES:-0}\" -gt 0 ] && echo '\u2192 run /inbox to see pending decisions' || true; P0_OPEN=$(bd list --status open 2>/dev/null | grep -c 'P0'); P0_OPEN=${P0_OPEN:-0}; if [ \"$P0_OPEN\" -gt 0 ]; then echo ''; printf '\\033[41;97m \ud83d\udea8 P0 OPEN: %s \u2014 run /inbox NOW \\033[0m\\n' \"$P0_OPEN\"; bd list --status open 2>/dev/null | grep 'P0' | head -3 | sed 's/^/ /'; fi; NOW_EPOCH=$(date +%s); LAST_AUDIT_FILE=$(find docs/audit -maxdepth 1 -name 'AUDIT-*.md' 2>/dev/null | sort | tail -1); if [ -n \"$LAST_AUDIT_FILE\" ]; then AUD_M=$(stat -f %m \"$LAST_AUDIT_FILE\" 2>/dev/null || stat -c %Y \"$LAST_AUDIT_FILE\" 2>/dev/null || echo $NOW_EPOCH); AUD_AGE=$(( (NOW_EPOCH - AUD_M) / 86400 )); [ \"$AUD_AGE\" -gt 30 ] && echo \"\u26a0 audit $AUD_AGE days old \u2014 run /audit\"; else echo '\u26a0 no audit artefact \u2014 run /audit or /doctor'; fi; if [ -f .great_cto/digest-latest.md ]; then DIG_M=$(stat -f %m .great_cto/digest-latest.md 2>/dev/null || stat -c %Y .great_cto/digest-latest.md 2>/dev/null || echo $NOW_EPOCH); DIG_AGE=$(( (NOW_EPOCH - DIG_M) / 86400 )); [ \"$DIG_AGE\" -gt 8 ] && echo \"\u26a0 digest $DIG_AGE days old \u2014 run /digest 7\"; fi; [ \"$PHASE\" != implementation ] && cat .great_cto/digest-latest.md 2>/dev/null | head -5 || true; [ -f \"${PLUGIN_DIR}/scripts/hooks/auto-attach-reviewers.mjs\" ] && node \"${PLUGIN_DIR}/scripts/hooks/auto-attach-reviewers.mjs\" 2>/dev/null; [ -n \"$PLUGIN_DIR\" ] && [ -f .great_cto/PROJECT.md ] && [ ! -f .great_cto/CODEBASE.md ] && node \"${PLUGIN_DIR}/scripts/first-run-codebase.mjs\" >/dev/null 2>&1 & node \"${PLUGIN_DIR}/scripts/hooks/quota-check.mjs\" 2>/dev/null || true; echo '=== PATTERNS ==='; GP_DIR=\"$HOME/.great_cto/global-patterns\"; ARCH=$(grep '^primary:' .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}' | head -1); if [ -d \"$GP_DIR\" ] && ls \"$GP_DIR\"/GP-*.md >/dev/null 2>&1; then ACTIVE_COUNT=$(grep -rl 'status: active' \"$GP_DIR\" 2>/dev/null | wc -l | tr -d ' '); echo \"Active patterns: ${ACTIVE_COUNT:-0}\"; if [ -n \"$ARCH\" ] && [ \"${ACTIVE_COUNT:-0}\" -gt 0 ]; then grep -rl 'status: active' \"$GP_DIR\" 2>/dev/null | xargs grep -lE \"applies_to:.*$ARCH\" 2>/dev/null | sort -V | tail -5 | while read f; do SLUG=$(basename \"$f\" .md); SYMPT=$(grep -m1 '^symptom:' \"$f\" 2>/dev/null | sed 's/symptom: //'); DETECT=$(grep -A 2 '^detection_order:' \"$f\" 2>/dev/null | grep '^ - ' | head -1 | sed 's/^ - //'); HITS=$(grep -m1 '^hits:' \"$f\" 2>/dev/null | awk '{print $2}'); printf ' %s (hits=%s): %s\\n \\u2192 Check first: %s\\n' \"$SLUG\" \"${HITS:-0}\" \"$SYMPT\" \"$DETECT\"; done || echo \" no patterns match archetype=$ARCH yet \u2014 /crystallize builds the library\"; fi; else echo 'no global patterns yet \u2014 run /crystallize after first incident'; fi; [ -x \"${PLUGIN_DIR}/scripts/check-update.sh\" ] && bash \"${PLUGIN_DIR}/scripts/check-update.sh\" \"${PLUGIN_DIR}/.claude-plugin/plugin.json\" 2>/dev/null || true ; # great_cto-cache-cleanup: keep the newest 3 cached versions, and never one a live session runs from (prune-versions; the check install-local --prune got after 2026-09-11). for CR in \"$HOME\"/.claude/plugins/cache/*/great_cto; do [ -d \"$CR\" ] || continue; node \"${PLUGIN_DIR}/scripts/lib/prune-versions.mjs\" --cache-root \"$CR\" --keep \"${PLUGIN_DIR:-$CR/none}\" --keep-newest 3 2>/dev/null | while IFS= read -r d; do case \"$d\" in \"$CR\"/*) rm -rf \"$d\" ;; esac; done; done 2>/dev/null || true",
|
|
63
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; export PATH=\"/opt/homebrew/bin:$HOME/.local/bin:/usr/local/bin:$PATH\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; if [ -z \"$PLUGIN_DIR\" ]; then echo 'GREAT_CTO: plugin dir not found \u2014 run /update'; else mkdir -p ~/.great_cto .great_cto; node \"${PLUGIN_DIR}/scripts/lib/sync-managed.mjs\" --plugin-dir \"${PLUGIN_DIR}\" 2>/dev/null || true; cp \"${PLUGIN_DIR}/skills/great_cto/ARCHETYPES.md\" .great_cto/ARCHETYPES.md 2>/dev/null || true; cp \"${PLUGIN_DIR}/skills/great_cto/SKILL.md\" .great_cto/SKILL.md 2>/dev/null || true; { printf \"export PATH=/opt/homebrew/bin:$HOME/.local/bin:/usr/local/bin:$PATH\\nexport ARCHETYPES_MD=.great_cto/ARCHETYPES.md\\n\"; } > .great_cto/env.sh; fi; if [ ! -d ~/.great_cto/catalog ]; then git clone --depth=1 https://github.com/davila7/claude-code-templates.git ~/.great_cto/catalog 2>/dev/null & fi; if [ ! -d ~/.great_cto/anthropic-skills ]; then git clone --depth=1 https://github.com/anthropics/skills.git ~/.great_cto/anthropic-skills 2>/dev/null & elif [ $(($(date +%s) - $(stat -f %m ~/.great_cto/anthropic-skills/.git 2>/dev/null || stat -c %Y ~/.great_cto/anthropic-skills/.git 2>/dev/null || echo 0))) -gt 604800 ]; then (cd ~/.great_cto/anthropic-skills && git pull --quiet 2>/dev/null) & fi; if [ ! -d ~/.great_cto/personal-skills ]; then git clone --depth=1 https://github.com/avelikiy/ai-agent-skills.git ~/.great_cto/personal-skills 2>/dev/null & elif [ $(($(date +%s) - $(stat -f %m ~/.great_cto/personal-skills/.git 2>/dev/null || stat -c %Y ~/.great_cto/personal-skills/.git 2>/dev/null || echo 0))) -gt 86400 ]; then (cd ~/.great_cto/personal-skills && git pull --quiet 2>/dev/null) & fi; if [ ! -f ~/.great_cto/skills-registry.json ] || [ $(($(date +%s) - $(stat -f %m ~/.great_cto/skills-registry.json 2>/dev/null || stat -c %Y ~/.great_cto/skills-registry.json 2>/dev/null || echo 0))) -gt 86400 ]; then [ -x \"${PLUGIN_DIR}/scripts/skill-discover.sh\" ] && bash \"${PLUGIN_DIR}/scripts/skill-discover.sh\" >/dev/null 2>&1 & fi; [ -x \"${PLUGIN_DIR}/scripts/hooks/welcome.sh\" ] && bash \"${PLUGIN_DIR}/scripts/hooks/welcome.sh\" 2>/dev/null || true; bash \"${PLUGIN_DIR}/scripts/apply-model-override.sh\" 2>/dev/null || true; bd prime 2>/dev/null || true; cat \"${PLUGIN_DIR}/scripts/hooks/operating-rules.md\" 2>/dev/null || true; echo '=== PREFERENCES ==='; node \"${PLUGIN_DIR}/scripts/hooks/read-global-memory.mjs\" 2>&1 || true; echo '=== PROJECT ==='; cat .great_cto/PROJECT.md 2>/dev/null || echo 'GREAT_CTO: No PROJECT.md \u2014 run /start'; PHASE=$(grep '^phase:' .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}' || echo implementation); echo '=== PHASE ==='; echo \"$PHASE\"; { [ \"$PHASE\" != review ] && [ \"$PHASE\" != release ]; } && { echo '=== BRAIN ==='; head -30 .great_cto/brain.md 2>/dev/null; } || true; [ \"$PHASE\" = implementation ] && { echo '=== CODEBASE ==='; head -20 .great_cto/CODEBASE.md 2>/dev/null; } || true; echo '=== LOCAL ==='; cat .great_cto/local.md 2>/dev/null || true; [ \"$PHASE\" != planning ] && { echo '=== HANDOFF ==='; cat .great_cto/HANDOFF.md 2>/dev/null || echo 'No handoff state \u2014 fresh session'; } || true; [ \"$PHASE\" = review ] && { echo '=== LATEST QA ==='; ls docs/qa-reports/QA-*.md 2>/dev/null | sort -V | tail -1 | xargs head -20 2>/dev/null; echo '=== LATEST CSO ==='; ls docs/security/CSO-*.md 2>/dev/null | sort -V | tail -1 | xargs head -20 2>/dev/null; } || true; [ \"$PHASE\" = release ] && { echo '=== PERF BASELINE ==='; tail -10 .great_cto/perf-baseline.log 2>/dev/null; } || true; echo '=== STATUS ==='; OPEN_GATES=$(bd list --label gate --status open 2>/dev/null | wc -l | tr -d ' '); OPEN_TASKS=$(bd list --status open 2>/dev/null | wc -l | tr -d ' '); BRANCH=$(git branch --show-current 2>/dev/null || echo '?'); LAST_AGENT=$(ls -t .great_cto/verdicts/*.log 2>/dev/null | head -1 | xargs tail -1 2>/dev/null | awk '{print $2}' || echo 'none'); printf 'branch=%s | gates=%s | tasks=%s | last_agent=%s\\n' \"$BRANCH\" \"${OPEN_GATES:-0}\" \"${OPEN_TASKS:-0}\" \"${LAST_AGENT:-none}\"; [ \"${OPEN_GATES:-0}\" -gt 0 ] && echo '\u2192 run /inbox to see pending decisions' || true; P0_OPEN=$(bd list --status open 2>/dev/null | grep -c 'P0'); P0_OPEN=${P0_OPEN:-0}; if [ \"$P0_OPEN\" -gt 0 ]; then echo ''; printf '\\033[41;97m \ud83d\udea8 P0 OPEN: %s \u2014 run /inbox NOW \\033[0m\\n' \"$P0_OPEN\"; bd list --status open 2>/dev/null | grep 'P0' | head -3 | sed 's/^/ /'; fi; NOW_EPOCH=$(date +%s); LAST_AUDIT_FILE=$(find docs/audit -maxdepth 1 -name 'AUDIT-*.md' 2>/dev/null | sort | tail -1); if [ -n \"$LAST_AUDIT_FILE\" ]; then AUD_M=$(stat -f %m \"$LAST_AUDIT_FILE\" 2>/dev/null || stat -c %Y \"$LAST_AUDIT_FILE\" 2>/dev/null || echo $NOW_EPOCH); AUD_AGE=$(( (NOW_EPOCH - AUD_M) / 86400 )); [ \"$AUD_AGE\" -gt 30 ] && echo \"\u26a0 audit $AUD_AGE days old \u2014 run /audit\"; else echo '\u26a0 no audit artefact \u2014 run /audit or /doctor'; fi; if [ -f .great_cto/digest-latest.md ]; then DIG_M=$(stat -f %m .great_cto/digest-latest.md 2>/dev/null || stat -c %Y .great_cto/digest-latest.md 2>/dev/null || echo $NOW_EPOCH); DIG_AGE=$(( (NOW_EPOCH - DIG_M) / 86400 )); [ \"$DIG_AGE\" -gt 8 ] && echo \"\u26a0 digest $DIG_AGE days old \u2014 run /digest 7\"; fi; [ \"$PHASE\" != implementation ] && cat .great_cto/digest-latest.md 2>/dev/null | head -5 || true; [ -f \"${PLUGIN_DIR}/scripts/hooks/auto-attach-reviewers.mjs\" ] && node \"${PLUGIN_DIR}/scripts/hooks/auto-attach-reviewers.mjs\" 2>/dev/null; [ -n \"$PLUGIN_DIR\" ] && [ -f .great_cto/PROJECT.md ] && [ ! -f .great_cto/CODEBASE.md ] && node \"${PLUGIN_DIR}/scripts/first-run-codebase.mjs\" >/dev/null 2>&1 & node \"${PLUGIN_DIR}/scripts/hooks/quota-check.mjs\" 2>/dev/null || true; echo '=== PATTERNS ==='; GP_DIR=\"$HOME/.great_cto/global-patterns\"; ARCH=$(grep '^primary:' .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}' | head -1); if [ -d \"$GP_DIR\" ] && ls \"$GP_DIR\"/GP-*.md >/dev/null 2>&1; then ACTIVE_COUNT=$(grep -rl 'status: active' \"$GP_DIR\" 2>/dev/null | wc -l | tr -d ' '); echo \"Active patterns: ${ACTIVE_COUNT:-0}\"; if [ -n \"$ARCH\" ] && [ \"${ACTIVE_COUNT:-0}\" -gt 0 ]; then grep -rl 'status: active' \"$GP_DIR\" 2>/dev/null | xargs grep -lE \"applies_to:.*$ARCH\" 2>/dev/null | sort -V | tail -5 | while read f; do SLUG=$(basename \"$f\" .md); SYMPT=$(grep -m1 '^symptom:' \"$f\" 2>/dev/null | sed 's/symptom: //'); DETECT=$(grep -A 2 '^detection_order:' \"$f\" 2>/dev/null | grep '^ - ' | head -1 | sed 's/^ - //'); HITS=$(grep -m1 '^hits:' \"$f\" 2>/dev/null | awk '{print $2}'); printf ' %s (hits=%s): %s\\n \\u2192 Check first: %s\\n' \"$SLUG\" \"${HITS:-0}\" \"$SYMPT\" \"$DETECT\"; done || echo \" no patterns match archetype=$ARCH yet \u2014 /crystallize builds the library\"; fi; else echo 'no global patterns yet \u2014 run /crystallize after first incident'; fi; [ -x \"${PLUGIN_DIR}/scripts/check-update.sh\" ] && bash \"${PLUGIN_DIR}/scripts/check-update.sh\" \"${PLUGIN_DIR}/.claude-plugin/plugin.json\" 2>/dev/null || true ; # great_cto-cache-cleanup: keep the newest 3 cached versions, and never one a live session runs from (prune-versions; the check install-local --prune got after 2026-09-11). for CR in \"$HOME\"/.claude/plugins/cache/*/great_cto; do [ -d \"$CR\" ] || continue; node \"${PLUGIN_DIR}/scripts/lib/prune-versions.mjs\" --cache-root \"$CR\" --keep \"${PLUGIN_DIR:-$CR/none}\" --keep-newest 3 2>/dev/null | while IFS= read -r d; do case \"$d\" in \"$CR\"/*) rm -rf \"$d\" ;; esac; done; done 2>/dev/null || true",
|
|
65
64
|
"timeout": 30,
|
|
66
65
|
"statusMessage": "Loading Great CTO context..."
|
|
67
66
|
},
|
|
68
67
|
{
|
|
69
68
|
"type": "command",
|
|
70
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/gate-expiry.mjs\" 2>/dev/null || true",
|
|
69
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/gate-expiry.mjs\" 2>/dev/null || true",
|
|
71
70
|
"timeout": 10,
|
|
72
71
|
"statusMessage": "Checking gate expiry (72h policy)..."
|
|
73
72
|
},
|
|
74
73
|
{
|
|
75
74
|
"type": "command",
|
|
76
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; [ -n \"$PLUGIN_DIR\" ] && node \"${PLUGIN_DIR}/scripts/hooks/session-pipeline-resume.mjs\" 2>/dev/null || true",
|
|
75
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; [ -n \"$PLUGIN_DIR\" ] && node \"${PLUGIN_DIR}/scripts/hooks/session-pipeline-resume.mjs\" 2>/dev/null || true",
|
|
77
76
|
"timeout": 10,
|
|
78
77
|
"statusMessage": "Checking for pipeline work in flight..."
|
|
79
78
|
},
|
|
80
79
|
{
|
|
81
80
|
"type": "command",
|
|
82
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed \"s|/$||\")}; [ -n \"$PLUGIN_DIR\" ] && node \"${PLUGIN_DIR}/scripts/hooks/register-project.mjs\" </dev/null 2>/dev/null || true",
|
|
81
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed \"s|/$||\")}; [ -n \"$PLUGIN_DIR\" ] && node \"${PLUGIN_DIR}/scripts/hooks/register-project.mjs\" </dev/null 2>/dev/null || true",
|
|
83
82
|
"timeout": 10,
|
|
84
83
|
"statusMessage": "Registering project on the board..."
|
|
85
84
|
}
|
|
@@ -92,7 +91,7 @@
|
|
|
92
91
|
"hooks": [
|
|
93
92
|
{
|
|
94
93
|
"type": "command",
|
|
95
|
-
"command": "bd prime 2>/dev/null; echo '---GREAT_CTO-CONTEXT---'; cat .great_cto/PROJECT.md 2>/dev/null | head -30; echo '---BEADS-READY---'; bd ready 2>/dev/null | head -10; BRANCH=$(git branch --show-current 2>/dev/null || echo 'unknown'); LAST_COMMIT=$(git log --oneline -1 2>/dev/null || echo 'none'); UNCOMMITTED=$(git status --short 2>/dev/null | wc -l | tr -d ' '); OPEN_GATES=$(bd list --label gate --status open 2>/dev/null | head -5 || grep '\\[GATE:' .great_cto/tasks.md 2>/dev/null | head -5 || echo 'none'); OPEN_TASKS=$(bd list --status open 2>/dev/null | head -8 || echo 'none'); LAST_VERDICT=$(ls -t .great_cto/verdicts/*.log 2>/dev/null | head -1 | xargs tail -1 2>/dev/null || echo 'none'); LATEST_ARCH=$(ls docs/architecture/ARCH-*.md 2>/dev/null | sort | tail -1 || echo 'none'); LATEST_QA=$(ls docs/qa-reports/QA-*.md 2>/dev/null | sort | tail -1 || echo 'none'); LATEST_CSO=$(ls docs/security/CSO-*.md 2>/dev/null | sort | tail -1 || echo 'none'); printf '# Auto-Handoff \u2014 %s\\n\\n> Auto-saved by PreCompact hook. Next session: read this, then /inbox.\\n\\n## Git\\n- Branch: `%s` | Last commit: `%s` | Uncommitted: %s files\\n\\n## Open Gates\\n%s\\n\\n## Open Tasks\\n%s\\n\\n## Last Agent Verdict\\n%s\\n\\n## Latest Docs\\n- Arch: %s\\n- QA: %s\\n- Security: %s\\n\\n## Resume\\nRun `/inbox` to see pending gates, then continue pipeline.\\n' \"$(date '+%Y-%m-%d %H:%M')\" \"$BRANCH\" \"$LAST_COMMIT\" \"$UNCOMMITTED\" \"$OPEN_GATES\" \"$OPEN_TASKS\" \"$LAST_VERDICT\" \"$LATEST_ARCH\" \"$LATEST_QA\" \"$LATEST_CSO\" > .great_cto/HANDOFF.md 2>/dev/null; echo '---HANDOFF-SAVED---'; mkdir -p .great_cto/logs; LOG_DATE=$(date +%Y-%m-%d); LOG_TIME=$(date +%H:%M); LOG_FILE=\".great_cto/logs/session-${LOG_DATE}-autocompact.md\"; printf '---\\ndate: %s\\ntime: %s\\nduration: auto-saved (context compact)\\n---\\n\\n# Session: context compact snapshot\\n\\n## Done\\n%s\\n\\n## Pending\\n%s\\n\\n## Commits\\n%s\\n' \"$LOG_DATE\" \"$LOG_TIME\" \"$OPEN_TASKS\" \"$OPEN_TASKS\" \"$LAST_COMMIT\" > \"$LOG_FILE\" 2>/dev/null; echo '---SESSIONLOG-SAVED---'",
|
|
94
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; bd prime 2>/dev/null; echo '---GREAT_CTO-CONTEXT---'; cat .great_cto/PROJECT.md 2>/dev/null | head -30; echo '---BEADS-READY---'; bd ready 2>/dev/null | head -10; BRANCH=$(git branch --show-current 2>/dev/null || echo 'unknown'); LAST_COMMIT=$(git log --oneline -1 2>/dev/null || echo 'none'); UNCOMMITTED=$(git status --short 2>/dev/null | wc -l | tr -d ' '); OPEN_GATES=$(bd list --label gate --status open 2>/dev/null | head -5 || grep '\\[GATE:' .great_cto/tasks.md 2>/dev/null | head -5 || echo 'none'); OPEN_TASKS=$(bd list --status open 2>/dev/null | head -8 || echo 'none'); LAST_VERDICT=$(ls -t .great_cto/verdicts/*.log 2>/dev/null | head -1 | xargs tail -1 2>/dev/null || echo 'none'); LATEST_ARCH=$(ls docs/architecture/ARCH-*.md 2>/dev/null | sort | tail -1 || echo 'none'); LATEST_QA=$(ls docs/qa-reports/QA-*.md 2>/dev/null | sort | tail -1 || echo 'none'); LATEST_CSO=$(ls docs/security/CSO-*.md 2>/dev/null | sort | tail -1 || echo 'none'); printf '# Auto-Handoff \u2014 %s\\n\\n> Auto-saved by PreCompact hook. Next session: read this, then /inbox.\\n\\n## Git\\n- Branch: `%s` | Last commit: `%s` | Uncommitted: %s files\\n\\n## Open Gates\\n%s\\n\\n## Open Tasks\\n%s\\n\\n## Last Agent Verdict\\n%s\\n\\n## Latest Docs\\n- Arch: %s\\n- QA: %s\\n- Security: %s\\n\\n## Resume\\nRun `/inbox` to see pending gates, then continue pipeline.\\n' \"$(date '+%Y-%m-%d %H:%M')\" \"$BRANCH\" \"$LAST_COMMIT\" \"$UNCOMMITTED\" \"$OPEN_GATES\" \"$OPEN_TASKS\" \"$LAST_VERDICT\" \"$LATEST_ARCH\" \"$LATEST_QA\" \"$LATEST_CSO\" > .great_cto/HANDOFF.md 2>/dev/null; echo '---HANDOFF-SAVED---'; mkdir -p .great_cto/logs; LOG_DATE=$(date +%Y-%m-%d); LOG_TIME=$(date +%H:%M); LOG_FILE=\".great_cto/logs/session-${LOG_DATE}-autocompact.md\"; printf '---\\ndate: %s\\ntime: %s\\nduration: auto-saved (context compact)\\n---\\n\\n# Session: context compact snapshot\\n\\n## Done\\n%s\\n\\n## Pending\\n%s\\n\\n## Commits\\n%s\\n' \"$LOG_DATE\" \"$LOG_TIME\" \"$OPEN_TASKS\" \"$OPEN_TASKS\" \"$LAST_COMMIT\" > \"$LOG_FILE\" 2>/dev/null; echo '---SESSIONLOG-SAVED---'",
|
|
96
95
|
"timeout": 15,
|
|
97
96
|
"statusMessage": "Saving session state to HANDOFF.md..."
|
|
98
97
|
}
|
|
@@ -105,13 +104,13 @@
|
|
|
105
104
|
"hooks": [
|
|
106
105
|
{
|
|
107
106
|
"type": "command",
|
|
108
|
-
"command": "echo '=== PROJECT ==='; cat .great_cto/PROJECT.md 2>/dev/null | head -15; echo '=== BRAIN (stable knowledge) ==='; awk '/^## SEMANTIC/,/^## EPISODIC/' .great_cto/brain.md 2>/dev/null | head -60; echo '---'; awk '/^## PROCEDURAL/,/^## EPISODIC/' .great_cto/brain.md 2>/dev/null | head -40 || head -30 .great_cto/brain.md 2>/dev/null; echo '=== HANDOFF ==='; cat .great_cto/HANDOFF.md 2>/dev/null | head -20",
|
|
107
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; echo '=== PROJECT ==='; cat .great_cto/PROJECT.md 2>/dev/null | head -15; echo '=== BRAIN (stable knowledge) ==='; awk '/^## SEMANTIC/,/^## EPISODIC/' .great_cto/brain.md 2>/dev/null | head -60; echo '---'; awk '/^## PROCEDURAL/,/^## EPISODIC/' .great_cto/brain.md 2>/dev/null | head -40 || head -30 .great_cto/brain.md 2>/dev/null; echo '=== HANDOFF ==='; cat .great_cto/HANDOFF.md 2>/dev/null | head -20",
|
|
109
108
|
"timeout": 5,
|
|
110
109
|
"statusMessage": "Injecting project + brain + handoff context into agent..."
|
|
111
110
|
},
|
|
112
111
|
{
|
|
113
112
|
"type": "command",
|
|
114
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/orchestrator-check.mjs\" 2>/dev/null || true",
|
|
113
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/orchestrator-check.mjs\" 2>/dev/null || true",
|
|
115
114
|
"timeout": 5,
|
|
116
115
|
"statusMessage": "Loading orchestrator contract..."
|
|
117
116
|
}
|
|
@@ -124,14 +123,14 @@
|
|
|
124
123
|
"hooks": [
|
|
125
124
|
{
|
|
126
125
|
"type": "command",
|
|
127
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/subagent-stop-completion.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
126
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/subagent-stop-completion.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
128
127
|
"timeout": 5,
|
|
129
128
|
"statusMessage": "Three-state completion check..."
|
|
130
129
|
},
|
|
131
130
|
{
|
|
132
131
|
"type": "command",
|
|
133
132
|
"async": true,
|
|
134
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/turn-snapshot.mjs\" 2>/dev/null || true"
|
|
133
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/turn-snapshot.mjs\" 2>/dev/null || true"
|
|
135
134
|
}
|
|
136
135
|
]
|
|
137
136
|
}
|
|
@@ -142,13 +141,13 @@
|
|
|
142
141
|
"hooks": [
|
|
143
142
|
{
|
|
144
143
|
"type": "command",
|
|
145
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/orchestrator-check.mjs\" 2>/dev/null; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
144
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/orchestrator-check.mjs\" 2>/dev/null; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
146
145
|
"timeout": 5,
|
|
147
146
|
"statusMessage": "Inline subagent check..."
|
|
148
147
|
},
|
|
149
148
|
{
|
|
150
149
|
"type": "command",
|
|
151
|
-
"command": "INPUT=$(cat 2>/dev/null); if [ -z \"$INPUT\" ]; then exit 0; fi; CMD=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('command',''))\" 2>/dev/null); if [ -z \"$CMD\" ]; then exit 0; fi; if echo \"$CMD\" | grep -qiE '(^|[;&|[:space:]])rm[[:space:]]+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r|--recursive)|(^|[;&|[:space:]])git[[:space:]]+(push[[:space:]].*(--force(-with-lease)?|-f([[:space:]]|$))|reset[[:space:]]+--hard)|(DROP[[:space:]]+(TABLE|DATABASE|SCHEMA))|(^|[;&|[:space:]])truncate[[:space:]]+--all|(^|[;&|[:space:]])(mkfs|dd)[[:space:]]|(^|[;&|[:space:]])chmod[[:space:]]+[0-7]*0{3}[[:space:]]|curl[[:space:]]+.*[|][[:space:]]*(sudo|sh|bash|zsh|python[23]?|perl|ruby)'; then echo \"BLOCKED: Dangerous command pattern detected.\"; exit 2; fi; exit 0",
|
|
150
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; INPUT=$(cat 2>/dev/null); if [ -z \"$INPUT\" ]; then exit 0; fi; CMD=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('command',''))\" 2>/dev/null); if [ -z \"$CMD\" ]; then exit 0; fi; if echo \"$CMD\" | grep -qiE '(^|[;&|[:space:]])rm[[:space:]]+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r|--recursive)|(^|[;&|[:space:]])git[[:space:]]+(push[[:space:]].*(--force(-with-lease)?|-f([[:space:]]|$))|reset[[:space:]]+--hard)|(DROP[[:space:]]+(TABLE|DATABASE|SCHEMA))|(^|[;&|[:space:]])truncate[[:space:]]+--all|(^|[;&|[:space:]])(mkfs|dd)[[:space:]]|(^|[;&|[:space:]])chmod[[:space:]]+[0-7]*0{3}[[:space:]]|curl[[:space:]]+.*[|][[:space:]]*(sudo|sh|bash|zsh|python[23]?|perl|ruby)'; then echo \"BLOCKED: Dangerous command pattern detected.\"; exit 2; fi; exit 0",
|
|
152
151
|
"timeout": 5,
|
|
153
152
|
"statusMessage": "Safety check..."
|
|
154
153
|
}
|
|
@@ -159,19 +158,19 @@
|
|
|
159
158
|
"hooks": [
|
|
160
159
|
{
|
|
161
160
|
"type": "command",
|
|
162
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/secret-scan.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
161
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/secret-scan.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
163
162
|
"timeout": 5,
|
|
164
163
|
"statusMessage": "Scanning for secrets..."
|
|
165
164
|
},
|
|
166
165
|
{
|
|
167
166
|
"type": "command",
|
|
168
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/frozen-gates-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
167
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/frozen-gates-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
169
168
|
"timeout": 5,
|
|
170
169
|
"statusMessage": "Checking frozen gates..."
|
|
171
170
|
},
|
|
172
171
|
{
|
|
173
172
|
"type": "command",
|
|
174
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/edit-scope-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
173
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/edit-scope-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
175
174
|
"timeout": 5,
|
|
176
175
|
"statusMessage": "Checking edit scope..."
|
|
177
176
|
}
|
|
@@ -184,35 +183,35 @@
|
|
|
184
183
|
"hooks": [
|
|
185
184
|
{
|
|
186
185
|
"type": "command",
|
|
187
|
-
"command": "INPUT=$(cat 2>/dev/null); FILE=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('file_path',''))\" 2>/dev/null); TOOL=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_name',''))\" 2>/dev/null); if [ -n \"$FILE\" ]; then mkdir -p .great_cto 2>/dev/null; printf '%s %s %s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" \"$FILE\" >> .great_cto/agent-writes.log 2>/dev/null; fi; true",
|
|
186
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; INPUT=$(cat 2>/dev/null); FILE=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('file_path',''))\" 2>/dev/null); TOOL=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_name',''))\" 2>/dev/null); if [ -n \"$FILE\" ]; then mkdir -p .great_cto 2>/dev/null; printf '%s %s %s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" \"$FILE\" >> .great_cto/agent-writes.log 2>/dev/null; fi; true",
|
|
188
187
|
"timeout": 3,
|
|
189
188
|
"async": true
|
|
190
189
|
},
|
|
191
190
|
{
|
|
192
191
|
"type": "command",
|
|
193
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/format-check.mjs\" 2>/dev/null; true",
|
|
192
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/format-check.mjs\" 2>/dev/null; true",
|
|
194
193
|
"timeout": 12,
|
|
195
194
|
"async": true
|
|
196
195
|
},
|
|
197
196
|
{
|
|
198
197
|
"type": "command",
|
|
199
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/summary-enforce.mjs\" 2>/dev/null; true",
|
|
198
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/summary-enforce.mjs\" 2>/dev/null; true",
|
|
200
199
|
"timeout": 5,
|
|
201
200
|
"async": true
|
|
202
201
|
},
|
|
203
202
|
{
|
|
204
203
|
"type": "command",
|
|
205
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/reviewer-nudge.mjs\" 2>/dev/null || true",
|
|
204
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/reviewer-nudge.mjs\" 2>/dev/null || true",
|
|
206
205
|
"timeout": 5,
|
|
207
206
|
"statusMessage": "Checking reviewer rules for this file..."
|
|
208
207
|
},
|
|
209
208
|
{
|
|
210
209
|
"type": "command",
|
|
211
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/lesson-rules-check.mjs\" 2>/dev/null; true"
|
|
210
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/lesson-rules-check.mjs\" 2>/dev/null; true"
|
|
212
211
|
},
|
|
213
212
|
{
|
|
214
213
|
"type": "command",
|
|
215
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/docs-reference-sync.mjs\" 2>/dev/null; true",
|
|
214
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/docs-reference-sync.mjs\" 2>/dev/null; true",
|
|
216
215
|
"timeout": 25,
|
|
217
216
|
"async": true
|
|
218
217
|
}
|
|
@@ -223,7 +222,7 @@
|
|
|
223
222
|
"hooks": [
|
|
224
223
|
{
|
|
225
224
|
"type": "command",
|
|
226
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/tool-failure.mjs\" 2>/dev/null || true",
|
|
225
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/tool-failure.mjs\" 2>/dev/null || true",
|
|
227
226
|
"timeout": 3,
|
|
228
227
|
"statusMessage": "Capturing tool failure...",
|
|
229
228
|
"async": true
|
|
@@ -235,7 +234,7 @@
|
|
|
235
234
|
"hooks": [
|
|
236
235
|
{
|
|
237
236
|
"type": "command",
|
|
238
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/pipeline-dispatcher.mjs\" 2>/dev/null || true",
|
|
237
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/pipeline-dispatcher.mjs\" 2>/dev/null || true",
|
|
239
238
|
"timeout": 5,
|
|
240
239
|
"statusMessage": "Computing next pipeline stage..."
|
|
241
240
|
}
|
|
@@ -248,13 +247,13 @@
|
|
|
248
247
|
"hooks": [
|
|
249
248
|
{
|
|
250
249
|
"type": "command",
|
|
251
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-end.mjs\" 2>/dev/null; true",
|
|
250
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-end.mjs\" 2>/dev/null; true",
|
|
252
251
|
"timeout": 8,
|
|
253
252
|
"statusMessage": "Saving session summary..."
|
|
254
253
|
},
|
|
255
254
|
{
|
|
256
255
|
"type": "command",
|
|
257
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
256
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
258
257
|
"timeout": 5
|
|
259
258
|
}
|
|
260
259
|
]
|
|
@@ -266,22 +265,22 @@
|
|
|
266
265
|
"hooks": [
|
|
267
266
|
{
|
|
268
267
|
"type": "command",
|
|
269
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; python3 \"${PLUGIN_DIR}/scripts/hooks/user-prompt-submit.py\" 2>/dev/null; true",
|
|
268
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; python3 \"${PLUGIN_DIR}/scripts/hooks/user-prompt-submit.py\" 2>/dev/null; true",
|
|
270
269
|
"timeout": 3
|
|
271
270
|
},
|
|
272
271
|
{
|
|
273
272
|
"type": "command",
|
|
274
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/cost-guard.mjs\" 2>&1 1>/dev/null; true",
|
|
273
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/cost-guard.mjs\" 2>&1 1>/dev/null; true",
|
|
275
274
|
"timeout": 3
|
|
276
275
|
},
|
|
277
276
|
{
|
|
278
277
|
"type": "command",
|
|
279
|
-
"command": "[ -n \"$GREAT_CTO_CLASS_TELEMETRY\" ] && { PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/classify-telemetry.mjs\" 2>/dev/null; }; true",
|
|
278
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -n \"$GREAT_CTO_CLASS_TELEMETRY\" ] && { PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/classify-telemetry.mjs\" 2>/dev/null; }; true",
|
|
280
279
|
"timeout": 3
|
|
281
280
|
},
|
|
282
281
|
{
|
|
283
282
|
"type": "command",
|
|
284
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
283
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
285
284
|
"timeout": 5
|
|
286
285
|
}
|
|
287
286
|
]
|
|
@@ -293,7 +292,7 @@
|
|
|
293
292
|
"hooks": [
|
|
294
293
|
{
|
|
295
294
|
"type": "command",
|
|
296
|
-
"command": "INPUT=$(cat 2>/dev/null); TOOL=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_name','unknown'))\" 2>/dev/null); mkdir -p .great_cto 2>/dev/null; printf '%s PERMISSION_DENIED tool=%s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" >> .great_cto/permission-denied.log 2>/dev/null; if [ \"$TOOL\" = \"Bash\" ] || [ \"$TOOL\" = \"Write\" ]; then printf '%s PERMISSION_DENIED %s \u2014 agent may be stuck. Run /inbox.\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" >> .great_cto/permission-denied.log 2>/dev/null; fi; true; node \"${CLAUDE_PLUGIN_ROOT}/scripts/lib/agent-events.mjs\" --emit denied --tool \"$TOOL\" 2>/dev/null || true",
|
|
295
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; INPUT=$(cat 2>/dev/null); TOOL=$(echo \"$INPUT\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_name','unknown'))\" 2>/dev/null); mkdir -p .great_cto 2>/dev/null; printf '%s PERMISSION_DENIED tool=%s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" >> .great_cto/permission-denied.log 2>/dev/null; if [ \"$TOOL\" = \"Bash\" ] || [ \"$TOOL\" = \"Write\" ]; then printf '%s PERMISSION_DENIED %s \u2014 agent may be stuck. Run /inbox.\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"$TOOL\" >> .great_cto/permission-denied.log 2>/dev/null; fi; true; node \"${CLAUDE_PLUGIN_ROOT}/scripts/lib/agent-events.mjs\" --emit denied --tool \"$TOOL\" 2>/dev/null || true",
|
|
297
296
|
"timeout": 3
|
|
298
297
|
}
|
|
299
298
|
]
|
|
@@ -304,25 +303,25 @@
|
|
|
304
303
|
"hooks": [
|
|
305
304
|
{
|
|
306
305
|
"type": "command",
|
|
307
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pipeline-stall-guard.mjs\" 2>/dev/null || true"
|
|
306
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pipeline-stall-guard.mjs\" 2>/dev/null || true"
|
|
308
307
|
},
|
|
309
308
|
{
|
|
310
309
|
"type": "command",
|
|
311
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/cross-review-gate.mjs\" 2>/dev/null || true"
|
|
310
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/cross-review-gate.mjs\" 2>/dev/null || true"
|
|
312
311
|
},
|
|
313
312
|
{
|
|
314
313
|
"type": "command",
|
|
315
314
|
"async": true,
|
|
316
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/turn-snapshot.mjs\" 2>/dev/null || true"
|
|
315
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/turn-snapshot.mjs\" 2>/dev/null || true"
|
|
317
316
|
},
|
|
318
317
|
{
|
|
319
318
|
"type": "command",
|
|
320
319
|
"async": true,
|
|
321
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/register-project.mjs\" 2>/dev/null || true"
|
|
320
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/register-project.mjs\" 2>/dev/null || true"
|
|
322
321
|
},
|
|
323
322
|
{
|
|
324
323
|
"type": "command",
|
|
325
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
324
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
326
325
|
"timeout": 5
|
|
327
326
|
}
|
|
328
327
|
]
|
|
@@ -334,7 +333,7 @@
|
|
|
334
333
|
"hooks": [
|
|
335
334
|
{
|
|
336
335
|
"type": "command",
|
|
337
|
-
"command": "PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
336
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/*/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||')}; node \"${PLUGIN_DIR}/scripts/hooks/session-status.mjs\" 2>/dev/null; true",
|
|
338
337
|
"timeout": 5
|
|
339
338
|
}
|
|
340
339
|
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-root — the directory whose .great_cto/ a hook should write to.
|
|
3
|
+
*
|
|
4
|
+
* Hooks resolved `.great_cto` against the current working directory, and a
|
|
5
|
+
* session's working directory wanders: one project on the measuring machine had
|
|
6
|
+
* `backend/.great_cto/` and `backend/src/.great_cto/` beside the real one, each
|
|
7
|
+
* with its own event log and handoff, none of them a project. The root is the
|
|
8
|
+
* nearest directory at or above `start` that holds `.great_cto/PROJECT.md`; with
|
|
9
|
+
* none, `start` itself, which is what the hooks did before.
|
|
10
|
+
*
|
|
11
|
+
* The same walk is inlined in shell at the head of every hook command in
|
|
12
|
+
* plugin.json (ROOT_SNIPPET below), so a hook is already in the root when it runs.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { dirname, join, resolve } from 'node:path';
|
|
16
|
+
|
|
17
|
+
export function projectRoot(start = process.cwd()) {
|
|
18
|
+
let dir = resolve(start);
|
|
19
|
+
for (;;) {
|
|
20
|
+
if (existsSync(join(dir, '.great_cto', 'PROJECT.md'))) return dir;
|
|
21
|
+
const up = dirname(dir);
|
|
22
|
+
if (up === dir) return resolve(start);
|
|
23
|
+
dir = up;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** POSIX sh, no subprocess: cd to the nearest ancestor with .great_cto/PROJECT.md, if any. */
|
|
28
|
+
export const ROOT_SNIPPET = '_gd="$PWD"; while [ -n "$_gd" ] && [ ! -f "$_gd/.great_cto/PROJECT.md" ]; do _gd="${_gd%/*}"; done; [ -n "$_gd" ] && cd "$_gd"; ';
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, rmSync, renameSync } from 'node:fs';
|
|
25
25
|
import { join } from 'node:path';
|
|
26
|
+
import { projectRoot } from './project-root.mjs';
|
|
26
27
|
|
|
27
28
|
export const STALE_WORKING_MS = 5 * 60 * 1000;
|
|
28
29
|
export const MOVED_ON_SLACK_MS = 2000;
|
|
@@ -34,7 +35,8 @@ const safeId = (id) => String(id || '').replace(/[^A-Za-z0-9_-]/g, '').slice(0,
|
|
|
34
35
|
|
|
35
36
|
/** Apply one hook payload. Returns what was written, or null when nothing applies. */
|
|
36
37
|
export function recordHookEvent(payload, { now = Date.now() } = {}) {
|
|
37
|
-
|
|
38
|
+
// The session's cwd wanders into subdirectories; state belongs at the project root.
|
|
39
|
+
const cwd = projectRoot(payload?.cwd || process.cwd());
|
|
38
40
|
const id = safeId(payload?.session_id);
|
|
39
41
|
if (!id) return null;
|
|
40
42
|
const dir = join(cwd, '.great_cto', 'status');
|
|
@@ -125,7 +125,13 @@ on = ["TASK_DONE", "DONE"]
|
|
|
125
125
|
# makes "the code that was reviewed is the code that shipped" answerable.
|
|
126
126
|
produces = ["receipt"]
|
|
127
127
|
gate = ["gate:code"]
|
|
128
|
-
|
|
128
|
+
# The review stage fans out (PLAN-2026-09-23-agent-speed S1): code-reviewer, QA
|
|
129
|
+
# and security all read the same finished diff and none reads another's output,
|
|
130
|
+
# yet they ran one after another. Measured medians: code-reviewer 2.6 min, QA
|
|
131
|
+
# 5.7, security 4.3 — sequential, the sum; side by side, the slowest. If one of
|
|
132
|
+
# them blocks and the code changes, the others' verdicts predate the change and
|
|
133
|
+
# gate:ship refuses them (ship-evidence), so a fan-out cannot ship a stale pass.
|
|
134
|
+
next = ["code-reviewer", "qa-engineer", "security-officer"]
|
|
129
135
|
|
|
130
136
|
# The mid-build escape hatch, and it escaped into nothing.
|
|
131
137
|
#
|
|
@@ -144,13 +150,18 @@ on = ["APPROVED", "DONE"]
|
|
|
144
150
|
# See senior-dev above: a reviewer's evidence is the tree it reviewed, recorded
|
|
145
151
|
# as a receipt, not a document it may or may not have written.
|
|
146
152
|
produces = ["receipt"]
|
|
147
|
-
|
|
153
|
+
join = ["qa-engineer", "security-officer"]
|
|
154
|
+
# Whichever of the three reviewers finishes last opens the edge to devops, so this
|
|
155
|
+
# edge carries the ship gate too — without it, a code-reviewer finishing after QA
|
|
156
|
+
# and security would reach devops with no human decision.
|
|
157
|
+
gate = ["gate:ship"]
|
|
158
|
+
next = ["devops"]
|
|
148
159
|
|
|
149
160
|
[transitions.qa-engineer]
|
|
150
161
|
on = ["PASS", "DONE"]
|
|
151
162
|
# the QA report its verdict rests on
|
|
152
163
|
produces = ["report"]
|
|
153
|
-
join = ["security-officer"]
|
|
164
|
+
join = ["security-officer", "code-reviewer"]
|
|
154
165
|
gate = ["gate:qa", "gate:ship"]
|
|
155
166
|
next = ["devops"]
|
|
156
167
|
|
|
@@ -158,7 +169,7 @@ next = ["devops"]
|
|
|
158
169
|
on = ["APPROVED", "DONE"]
|
|
159
170
|
# the security audit report
|
|
160
171
|
produces = ["report"]
|
|
161
|
-
join = ["qa-engineer"]
|
|
172
|
+
join = ["qa-engineer", "code-reviewer"]
|
|
162
173
|
gate = ["gate:security", "gate:compliance", "gate:ship"]
|
|
163
174
|
next = ["devops"]
|
|
164
175
|
|
package/package.json
CHANGED