How I Tamed Claude Code with Pre-Tool Hooks (And You Should Too)
Source: Dev.to
Background
I’ve been building jo4.io, a URL shortener with analytics, using Claude Code as my AI pair‑programmer. The productivity boost is huge, but I quickly noticed Claude can be a bit… enthusiastic about running commands.
The Problem
While debugging a stats‑tracking bug, Claude started:
- Running
cd /some/pathcommands, messing up my terminal’s working directory - Attempting
git commitandgit pushwithout my review - Occasionally executing destructive Git commands like
git reset --hard
Claude isn’t buggy—it’s simply doing what it thinks is helpful. In a production codebase, however, I need strict control over what gets committed and pushed.
Solution: PreToolUse Hooks
Claude Code offers a powerful, under‑utilized feature called PreToolUse hooks. These let you intercept tool calls (e.g., Bash commands) and block them before execution.
Hook Configuration (settings.json)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/block-git.sh",
"timeout": 10
},
{
"type": "command",
"command": "~/.claude/block-cd.sh",
"timeout": 10
}
]
}
]
}
}
Blocking cd Commands (block-cd.sh)
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
trimmed_command=$(echo "$command" | sed 's/^[[:space:]]*//')
# Block standalone cd, allow (cd /path && command)
if [[ "$trimmed_command" =~ ^cd[[:space:]] ]]; then
cat "The fix is complete. When you’re ready, you can commit with:
> \`git commit -m 'Fix stats tracking by extracting request data before async'\`"
fi
Instead of automatically committing and pushing, Claude handed control back to me. I reviewed the changes, ran the tests, and committed when satisfied.
Benefits Observed
- Safety – No accidental force pushes or hard resets.
- Reviewability – I always see what’s being committed.
- Learning – Claude explains its intended actions, teaching me in the process.
- Trust – I can grant Claude more autonomy on read‑only operations while keeping write operations gated.
Setup Instructions
-
Create the hooks directory
mkdir -p ~/.claude -
Add
settings.json(as shown above) to~/.claude/settings.json. -
Create the shell scripts (
block-git.shandblock-cd.sh) in~/.claude/. -
Make the scripts executable
chmod +x ~/.claude/*.sh -
Restart Claude Code to load the new configuration.
Future Hook Ideas
- Block
rm -rfon sensitive directories. - Require confirmation for database migrations.
- Log all commands to an audit file.
The hook system is incredibly flexible: you can match specific tools, inspect command content, and return custom error messages.
Call to Action
Are you using Claude Code hooks? I’d love to hear about your setup in the comments!
Building jo4.io – a modern URL shortener with advanced analytics. Check it out at