Bash · Techniques
Guard a coding agent tool call
This Claude Code PreToolUse hook reads a proposed Bash command as text. It runs none of the proposals. A yes gives allow, a no gives deny, and not sure gives ask. A failed call also gives deny. The hook answers in JSON. In ThinkThen, exit 2 means a usage or input error. In a hook, exit 2 blocks the tool call.
Host contract: Claude Code hooks reference, checked 2026-09-28.
guard.txt, a file the script reads
question='Does this command only read, and leave every file'
question+=' and every setting on the machine unchanged?'
set -o pipefail
jq -erj '.tool_input.command |
select(type == "string" and length > 0)' |
thinkthen decide \
"$question" \
--threshold 0.1:0.8 --quiet \
--replay recording && rc=0 || rc=$?
case $rc in
0) decision=allow; reason='Read-only proposal' ;;
1) decision=deny; reason='Changes the machine' ;;
3) decision=ask; reason='A person should decide' ;;
*) decision=deny; reason='The judge did not answer' ;;
esac
jq -cn --arg decision "$decision" \
--arg reason "$reason" \
'{hookSpecificOutput:{
hookEventName:"PreToolUse",
permissionDecision:$decision,
permissionDecisionReason:$reason
}}'One recorded proposal is allowed, one asks a person, and one is denied.
for note in list fetch wipe; do
jq -Rs '{tool_input:{command:.}}' "proposed/$note.txt" |
bash guard.txt |
jq -r '.hookSpecificOutput.permissionDecision'
doneOutput
allow ask deny
exit 0
On GitHub: github.com/botassembly/thinkthen