Claude Code Hooks
Claude Code Hooks
This project uses Claude Code's hook system to enforce development guardrails at the AI tool level. Hooks are shell scripts that run before Claude executes a tool (Edit, Write, Bash), and can block the action if it violates project rules.
Hook Overview
| Hook | Matcher | Purpose |
|---|---|---|
prevent-main-commit.sh | Bash | Blocks git commit and git push on main/master |
prevent-main-edit.sh | Edit, Write | Blocks file edits on main/master |
prevent-generated-edit.sh | Edit, Write | Blocks edits to auto-generated files |
How It Works
Hooks are configured in .claude/settings.json under hooks.PreToolUse. Each hook receives the tool input as the $TOOL_INPUT environment variable (JSON) and controls execution via exit codes:
| Exit Code | Behavior |
|---|---|
0 | Allow — tool execution proceeds |
2 | Block — tool execution is rejected, stderr message shown to user |
SSOT Protection: prevent-generated-edit.sh
The most notable hook enforces the SSOT principle by preventing Claude from directly editing generated files. It reads docsgen.yaml at runtime to build the list of protected files:
# Extract output paths from docsgen.yaml
GENERATED=$(yq -r '.targets[].output' docsgen.yaml)
# Block if the target file matches any generated output
for gen in $GENERATED; do
if [ "$REL_PATH" = "$gen" ]; then
echo "BLOCKED: '$gen' is auto-generated. Edit template/ instead." >&2
exit 2
fi
doneThis means the protection list stays in sync with docsgen.yaml automatically — no hardcoded file lists to maintain.
When blocked, Claude sees a message like:
BLOCKED: 'README.md' is auto-generated by docs-ssot.
Edit the source in template/ instead, then run 'make docs'.Branch Protection: prevent-main-edit.sh / prevent-main-commit.sh
These hooks enforce GitHub Flow by preventing any file modifications or git commits directly on main or master. Claude is forced to create a feature branch first.
Adding New Hooks
- Create a shell script in
.claude/hooks/:sh#!/bin/sh # Your validation logic here # Exit 0 to allow, exit 2 to block (message via stderr) - Make it executable:
chmod +x .claude/hooks/your-hook.sh - Register it in
.claude/settings.json:json{ "matcher": "Edit", "hooks": [ { "type": "command", "command": ".claude/hooks/your-hook.sh" } ] }