Why hooks?
Instructions in CLAUDE.md are requests. Hooks are guarantees: commands Claude Code runs automatically at lifecycle events, every time. Here are eight worth adding today. Each goes under the hooks key in .claude/settings.json (project) or ~/.claude/settings.json (personal).
1. Auto-format every edit (PostToolUse)
{ "PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "f=$(jq -r '.tool_input.file_path') && npx prettier --write --ignore-unknown \"$f\"" }] }] }2. Block edits to protected files (PreToolUse)
Point a PreToolUse hook on Edit|Write at a script that checks .tool_input.file_path against a list and exits 2 with a reason on stderr. Pair it with permissions.deny rules for defense in depth.
3. Typecheck gate (Stop)
{ "Stop": [{ "hooks": [{ "type": "command", "command": "pnpm tsc --noEmit 1>&2 || exit 2" }] }] }Claude can't finish its turn until the typecheck passes.
4. Desktop notification (Notification)
{ "Notification": [{ "matcher": "", "hooks": [{ "type": "command", "command": "notify-send 'Claude Code' 'Claude Code needs your attention'" }] }] }On macOS, use osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'.
5. Fresh context at startup (SessionStart)
{ "SessionStart": [{ "matcher": "startup", "hooks": [{ "type": "command", "command": "echo 'Open PRs:'; gh pr list --limit 5" }] }] }Stdout from a SessionStart hook is added to Claude's context.
6. Re-inject key context after compaction
Use the compact matcher on SessionStart to print critical reminders after a compaction, so summaries never drop them.
7. Block risky shell commands
A PreToolUse hook on Bash can inspect .tool_input.command and block patterns you never want an agent to run, like force-pushing to main.
8. Lint only what changed
{ "PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "f=$(jq -r '.tool_input.file_path'); case \"$f\" in *.py) ruff check \"$f\" 1>&2 || exit 2;; esac" }] }] }The exit-code rule
Exit 0 = no objection. Exit 2 = block, and your stderr becomes Claude's feedback. Learn the full model, including JSON decisions and matchers, in Hooks Fundamentals.