#!/usr/bin/env bash # Stop hook: soft-nudge the agent to process pending inbox/ handoffs before it # yields control back to Craig. # # The "check inbox/ at every task boundary" rule (protocols.org, Inbox # Monitoring Cadence) is otherwise prose-only, so it holds only as well as the # agent remembers it. A Stop is the agent finishing a turn and about to yield, # which is the harness's closest event to the rule's own trigger ("after # finishing a unit of work, before reporting back"). When handoffs are pending # this hook blocks the yield once and injects a reason, so the agent processes # them instead of returning with items unseen. # # Soft-nudge, not hard-block: on the harness re-entry (stop_hook_active: true) # the hook steps aside and lets the turn end. An item the agent genuinely can't # process, or a mid-task pause to ask Craig a question (also a Stop), never # wedges the session. # # Self-skips everywhere it doesn't apply: no inbox/ dir, no inbox-status, or a # clean inbox each exit 0 silently. One hook, every project, no config. # # Wire in ~/.claude/settings.json (see hooks/settings-snippet.json), Stop array. set -u payload="$(cat)" # Re-entry after our own nudge: don't nudge twice, let the turn end. active="$(printf '%s' "$payload" | jq -r '.stop_hook_active // false' 2>/dev/null)" [ "$active" = "true" ] && exit 0 cwd="$(printf '%s' "$payload" | jq -r '.cwd // empty' 2>/dev/null)" [ -z "$cwd" ] && cwd="$PWD" cd "$cwd" 2>/dev/null || exit 0 # Nothing to enforce without an inbox to watch. [ -d inbox ] || exit 0 # Prefer the project-local inbox-status; fall back to one on PATH. Absent both, # degrade to a no-op rather than block on a check we can't run. status_bin=".ai/scripts/inbox-status" [ -x "$status_bin" ] || status_bin="$(command -v inbox-status 2>/dev/null)" || exit 0 [ -n "$status_bin" ] || exit 0 # inbox-status -q: exit 1 = pending, 0 = clean, 2 = no inbox. Only 1 nudges. out="$("$status_bin" -q 2>/dev/null)" [ $? -eq 1 ] || exit 0 count="$(printf '%s' "$out" | grep -oE '[0-9]+' | head -1)" [ -n "$count" ] || count="Some" reason="$count pending inbox handoff(s) in $(basename "$cwd"). Process per inbox.org before yielding." printf '{"decision":"block","reason":%s}\n' "$(printf '%s' "$reason" | jq -Rs .)" exit 0