Skip to main content

Command Palette

Search for a command to run...

5 Claude Code Hooks Examples That Prevent Real Mistakes

Updated
•3 min read•View as Markdown
K
Kitforge builds practical tooling for AI-assisted development. Maker of The Agentic Coding Kit.

Hooks are Claude Code's most underused feature. A hook is a shell command that runs automatically at a specific point in the agent's lifecycle - before a tool runs, after a file is written, when the agent tries to stop. They live in .claude/settings.json and they turn "please remember to" rules into actual enforcement.

Here are five hooks worth copying and what each one fixes.

How hooks work in 60 seconds

Hooks are defined per event. The two you'll use most:

  • PreToolUse - runs before a tool call. Exit code 2 blocks the call and feeds your message back to the agent.

  • PostToolUse - runs after a tool call. Perfect for formatters and tests.

Each hook gets a matcher (a regex against the tool name) and a shell command that receives JSON about the call on stdin.

1. Block edits to .env files

Fixes: the agent cheerfully pasting a live API key into a committed file.

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path' | grep -q '\\.env' && echo 'Blocked: never edit .env files' >&2 && exit 2 || exit 0"
      }]
    }]
  }
}

Exit code 2 means "block this and tell the agent why." The agent adjusts immediately instead of you discovering the problem after commit.

2. Run the test suite after every code edit

Fixes: the agent declaring victory with broken tests.

"PostToolUse": [{
  "matcher": "Edit|Write",
  "hooks": [{
    "type": "command",
    "command": "jq -r '.tool_input.file_path' | grep -qE '\\.(ts|js|py)$' && npm test --silent || exit 0"
  }]
}]

Scope it to your fast unit tests, or pair it with a Stop hook for the slow pass.

3. Auto-format on write

Fixes: style drift and lint noise in review.

"PostToolUse": [{
  "matcher": "Edit|Write",
  "hooks": [{
    "type": "command",
    "command": "f=$(jq -r '.tool_input.file_path'); npx prettier --write \"$f\" >/dev/null 2>&1; exit 0"
  }]
}]

The agent never sees this happen. Your diffs stay clean.

4. Require a plan file before edits

Fixes: the agent spray-editing eight files when you wanted a two-line change.

"PreToolUse": [{
  "matcher": "Edit",
  "hooks": [{
    "type": "command",
    "command": "test -f .claude/plan.md || { echo 'Blocked: write .claude/plan.md first' >&2; exit 2; }"
  }]
}]

Brutal, but effective for big refactors. Delete the plan file when you want freeform mode back.

5. Log every bash command

Fixes: the "what did it actually do?" archaeology session.

"PreToolUse": [{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "command": "jq -r '.tool_input.command' >> .claude/bash-history.log; exit 0"
  }]
}]

Add .claude/*.log to .gitignore and you get a full audit trail for free.

The gotchas nobody mentions

  • Hooks run with your permissions. A bad hook can break every tool call. Test the command standalone first.

  • Keep them fast. A 10-second test suite on every edit makes the agent feel broken. Move slow checks to the Stop event.

  • Only stderr + exit 2 reaches the agent. Write for that channel.


Want these without the config archaeology? The Agentic Coding Kit ships a ready-made hooks pack plus 29 other templates - $19 one-time, drop into any repo. Or generate a free CLAUDE.md baseline with the Kitforge generator.

Cross-posted from the Kitforge blog.

More from this blog

K

Kitforge

23 posts