1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#!/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
|