# 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](https://code.claude.com/docs/en/hooks#pretooluse-decision-control).

*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*

On GitHub: [github.com/botassembly/thinkthen](https://github.com/botassembly/thinkthen)
