ThinkThen

Bash techniques
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'
done
Output
allow
ask
deny
exit 0