boma/.claude/hooks/guard-execution-mode-menu.sh

47 lines
2 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
#
# Stop guard: block ending the turn when the assistant's final message presents the
# execution-mode menu. The writing-plans / subagent-driven-development skills script a
# "Subagent-Driven vs Inline Execution — which approach?" menu at the plan→execution
# handoff. boma's standing preference (docs/FRICTION.md + the
# always-subagent-driven-execution memory) is to NEVER present it and proceed
# subagent-driven. Prose reminders failed four times (06-05/06/09/10); this is the
# mechanical guard recorded by the 2026-06-10 kaizen review.
#
# Fails OPEN: any parse/read problem → allow the stop. Respects stop_hook_active so a
# block can never loop. The match signature is deliberately tight ("inline execution"
# AND "which approach"/"two execution options") so it fires on the actual menu, not on
# meta-discussion of it.
#
set -uo pipefail
input=$(cat 2>/dev/null) || exit 0
# Loop guard: if we already blocked once for this stop, let it through.
active=$(printf '%s' "$input" | jq -r '.stop_hook_active // false' 2>/dev/null) || exit 0
[ "$active" = "true" ] && exit 0
transcript=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null) || exit 0
[ -z "$transcript" ] || [ ! -r "$transcript" ] && exit 0
# Last assistant message's text blocks, joined.
text=$(jq -rs '
([ .[] | select(.type=="assistant") ] | last) as $a
| ($a.message.content // [])
| if type=="array" then [ .[] | select(.type=="text") | .text ] | join("\n")
elif type=="string" then .
else "" end
' "$transcript" 2>/dev/null) || exit 0
low="${text,,}"
if [[ "$low" == *"inline execution"* \
&& ( "$low" == *"which approach"* || "$low" == *"two execution options"* ) ]]; then
cat <<'JSON'
{"decision":"block","reason":"Execution-mode menu detected in your final message. boma standing preference (docs/FRICTION.md + always-subagent-driven-execution memory): never present the subagent-driven-vs-inline menu. Drop the menu and proceed with subagent-driven execution directly (superpowers:subagent-driven-development)."}
JSON
exit 0
fi
exit 0