Module 03 walkthrough · ~3 minutes · three parts: anatomy, the trap, and two more locks
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command",
"command": "./scripts/check-command.sh" }
]
}
]
}
}
#!/bin/bash
# refuse destructive shell commands
input=$(cat)
if echo "$input" | grep -qE "rm -rf|--force|push.*main"
then
echo "Blocked: destructive command." >&2
echo "Archive instead of deleting." >&2
exit 2 # 2 = block. 0 = proceed.
fi
exit 0
[hook] Blocked: destructive command.
Archive instead of deleting.
Claude: That delete was blocked by a project
guardrail. Instead, I'll move the old files
to archive/2026-pre-june/ so nothing is
lost. Want me to go ahead?
[hook] Blocked: this write contains something
that looks like a credential.
Claude: The config had the API key pasted in
directly. I'll reference it from an
environment variable instead, so the actual
key never lands in the repo.
2026-08-21 14:02:11 Bash git status
2026-08-21 14:02:40 Bash BLOCKED rm -rf deliverables/old
2026-08-21 14:03:05 Bash mv deliverables/old archive/
2026-08-21 14:09:18 Write BLOCKED settings.py (credential)
2026-08-21 14:09:52 Write settings.py (env reference)
2026-08-21 14:11:30 Edit deliverables/2026-08-21-brief.md
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash",
"hooks": [{ "type": "command",
"command": "./scripts/check-command.sh" }] },
{ "matcher": "Write|Edit",
"hooks": [{ "type": "command",
"command": "./scripts/scan-secrets.sh" }] }
],
"PostToolUse": [
{ "hooks": [{ "type": "command",
"command": "./scripts/audit-log.sh" }] }
]
}
}