#!/usr/bin/env bash # # Stop guard for two external-skill gates that conflict with boma conventions, where # prose reminders repeatedly failed to hold (docs/FRICTION.md): # # 1. The execution-mode menu — writing-plans / subagent-driven-development script a # "Subagent-Driven vs Inline Execution — which approach?" menu at the plan→execution # handoff. boma's standing preference is to NEVER present it and proceed # subagent-driven. (Recorded by the 2026-06-10 kaizen review; the 2026-06-17 review # widened the matcher to also catch free-form *prose* re-asks of the same choice — # e.g. "which execution approach?" — which the literal-menu matcher missed. The # sibling push-vs-not re-ask is deliberately NOT hooked: a genuine "should I push?" # is sometimes legitimate, so it stays a soft default via the # dont-reask-settled-defaults memory rather than a hard block.) # 2. The brainstorming spec-review gate — the brainstorming skill scripts "Spec written # and committed … please review it before … the implementation plan." The standing # agreement is to move directly from the committed spec to writing-plans. (Recorded # by the 2026-06-14 kaizen review; 06-10/06-14 recurrences.) # # Fails OPEN: any parse/read problem → allow the stop. Respects stop_hook_active so a # block can never loop. Match signatures are deliberately tight so they fire on the # actual gate text, 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"* ) ]] \ || [[ "$low" == *"subagent-driven or inline"* || "$low" == *"inline or subagent"* ]] \ || [[ "$low" == *"subagent-driven vs inline"* || "$low" == *"subagent vs inline"* \ || "$low" == *"inline vs subagent"* ]] \ || [[ "$low" == *"execution approach"* && "$low" == *"?"* ]]; 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 # Brainstorming spec-review gate: asking the user to review the committed spec before # the implementation plan. Tight signature: "implementation plan" present, plus either the # skill's literal "spec written and committed" line, or the review+spec+before combination. if [[ "$low" == *"implementation plan"* \ && ( "$low" == *"spec written and committed"* \ || ( "$low" == *"review"* && "$low" == *"the spec"* && "$low" == *"before"* ) ) ]]; then cat <<'JSON' {"decision":"block","reason":"Brainstorming spec-review gate detected in your final message. boma standing agreement (docs/FRICTION.md): once the spec is written and committed, move directly to the implementation plan (superpowers:writing-plans) — do not stop to ask the user to review the spec first. Drop the gate and proceed."} JSON exit 0 fi exit 0