Claude Code Hands-On (7): Ten Hooks I Actually Use, with the Code
Picking ten hooks out of the 100 in the reference repo and walking through each: what it does, the actual JS, the settings.json wire-up, and where it bites. PreToolUse for safety, PostToolUse for hygiene, the boring ones that earn their keep.
Chapter 5 was the conceptual tour of hooks. This one is the field guide. Out of the 100-script reference repo, ten earn their place in every serious project I run. Those are the ten I will walk through, with code.
All examples assume Node 18+, save scripts to ./hooks/, mark them chmod +x, and wire them in .claude/settings.json like:
| |
1. block-env-read — protect secrets
The single highest-ROI hook. Stops Read and Grep from touching .env, id_rsa, credentials.json:
| |
Wire on Read|Grep|MultiEdit|Edit|Write. Exit code 2 in PreToolUse blocks the call; the stderr text is fed back to the model so it sees why.
2. bash-blacklist — stop rm -rf /
The most common foot-gun. PreToolUse on Bash:
| |
The regex list is short on purpose. Long blocklists get ignored when they cause false positives.
3. bash-whitelist — for production-adjacent boxes
The inverse, for repos that touch production. Allow only an explicit set of binaries:
| |
Whitelists win where blocklists lose: you cannot accidentally allow something new.
4. block-git-push — no surprise pushes
I have never wanted Claude to push without asking. PreToolUse on Bash:
| |
The cost of being wrong is so much worse than the friction of typing git push myself.
5. format-on-write — Prettier as a PostToolUse
PostToolUse on Write|Edit|MultiEdit:
| |
PostToolUse runs after the edit, so exit code 2 doesn’t roll anything back. The point is hygiene, not policy.
6. test-on-edit — fail fast
PostToolUse on Edit|MultiEdit for source files:
| |
Exit code 1 surfaces the failure to the model, which then sees the test output and tries again. This is the single hook that taught Claude to write better code over time on my repos.
7. backup-before-edit — the safety net
PreToolUse on Edit|Write|MultiEdit:
| |
Cheap insurance. I have recovered files from /tmp exactly twice, both times worth a year of cron job pay.
8. log-tool-calls — observability
PostToolUse on *:
| |
You will not look at this file every day. The day you do, you will be glad it exists.
9. read-before-write — no blind edits
PreToolUse on Edit|MultiEdit:
| |
Forces the model to read a file before editing it. Catches the subtle bug where the model edits based on its prior, not the file’s current state.
10. work-hours-only — humane boundaries
PreToolUse on Bash:
| |
I run this on the box that handles after-hours pings. If the bot tries to do something destructive at 2am, that is almost certainly a misfire.
What ties them together
Three rules I picked up the hard way:
- PreToolUse for policy, PostToolUse for hygiene. Don’t try to undo things in PostToolUse — the side-effect already happened.
- Stderr is feedback, exit code is verdict. Exit 2 blocks (PreToolUse only). Anything in stderr gets fed back to Claude verbatim. Use both.
- Hooks fail closed. A misbehaving hook will block all your tool calls. Test the script with
echo '{"tool_name":"Read","tool_input":{"file_path":"/tmp/x"}}' | node hook.jsbefore wiring it in.
Ten hooks does not sound like much. It is enough to make a Yolo-mode session feel responsible.