Skip to content

Claude Code hooks: rules Claude can't forget to follow

What Claude Code hooks are, which events you can hook into, how exit code 2 blocks an action, and five hooks worth adding to a business setup.

Short answer

Claude hooks are shell commands that Claude Code runs automatically at fixed moments, such as before a tool runs, after a file is edited or when Claude finishes. You set them in settings.json under an event like PreToolUse or PostToolUse. Unlike an instruction in CLAUDE.md, a hook always runs, and a PreToolUse hook that exits with code 2 blocks the action.

Key takeaways

  1. A hook is your own command that Claude Code runs at a set moment. It does not depend on Claude remembering anything.
  2. The events you will use most are PreToolUse, PostToolUse, Notification, Stop and SessionStart.
  3. Hooks get JSON on stdin. Exit code 0 means carry on, exit code 2 blocks the action and sends your message back to Claude.
  4. Use hooks for rules that must hold every time: block risky commands, protect secret files, format code, log what happened.
  5. Hooks run with your own permissions. Read every hook before you add it, the same way you would read a script from the internet.

You can tell Claude Code a hundred times to never touch your .env file. Most of the time it listens. Most of the time is not good enough for a file full of API keys.

That gap is what hooks close. A hook is a command you write, and Claude Code runs it at a fixed moment. It does not depend on what the model remembers, how long the session is, or how clever the prompt was. It just runs.

This guide covers what hooks are, which events exist, how to write your first one, and the five I would add to any setup that touches real business data.

What are Claude Code hooks?

Claude Code hooks are shell commands that Claude Code runs automatically at specific points in a session, such as right before a tool is used or right after Claude finishes. You define them once in a settings file, and they fire every time that moment comes.

The key word is automatically. An instruction in CLAUDE.md is advice the model weighs. A hook is code that runs outside the model. Claude cannot skip it, forget it or decide it does not apply this time.

That matters more than it sounds. In Stack Overflow's 2025 Developer Survey, 84% of developers were using AI tools or planning to, yet more of them distrusted the accuracy of AI output (46%) than trusted it (33%). Hooks are a practical answer to that distrust. You stop hoping the AI does the right thing and make the important checks run by themselves.

Which hook events can you use?

Hook events are the moments in a session where Claude Code can run your command. These are the ones I use or see used most:

Event When it fires Good for
PreToolUse Before Claude runs a tool Blocking risky commands, protecting files
PostToolUse After a tool finishes successfully Formatting code, running checks, logging edits
UserPromptSubmit When you send a prompt, before Claude sees it Adding context, filtering prompts
Notification When Claude needs your permission or input Desktop or phone alerts
Stop When Claude finishes its response Logging, final checks, a sound when done
SubagentStop When a subagent finishes Checking delegated work
PreCompact Before the conversation gets compacted Saving notes before context is summarized
SessionStart When a session starts or resumes Loading fresh context, like open tasks

Anthropic adds events over time, so check the hooks reference in the Claude Code docs for the full current list.

For tool events you add a matcher. It is the tool name, or a pattern like Edit|Write. Use Bash to catch shell commands, or mcp__crm__.* to catch every tool from an MCP server called crm.

How does a hook decide what happens?

A hook decides what happens through its exit code: 0 lets Claude Code carry on, 2 blocks the action, and anything else is reported as a non-blocking error. Claude Code sends the hook a JSON object on stdin with details like the session id, the working directory, the tool name and the tool input.

Exit code What Claude Code does
0 Continues. For UserPromptSubmit and SessionStart, your stdout is added as context for Claude.
2 Blocks. In PreToolUse the tool call is cancelled and your stderr goes to Claude. In Stop, Claude keeps working instead of stopping.
Other Continues, and shows your stderr to you as an error.

Exit code 2 is the one to remember. It turns a hook from a side effect into a gate. Hooks can also print structured JSON for finer control, but you can get far with exit codes alone.

Each hook has a timeout. By default it is 60 seconds, according to Anthropic's documentation, and you can set your own per hook.

How do you add your first hook?

You add a hook by putting it in a settings file under the event name, with a matcher and a command. The steps:

  1. Pick one rule that must always hold. Not a preference. Something that would hurt if it was skipped once.
  2. Choose the event. Stopping something means PreToolUse. Cleaning up after means PostToolUse.
  3. Write the script in .claude/hooks/ and make it executable with chmod +x.
  4. Register it in .claude/settings.json, or in ~/.claude/settings.json if you want it in every project.
  5. Check it in Claude Code with the /hooks menu. Settings are loaded when a session starts, so restart Claude Code or review the change in /hooks after editing the file.
  6. Test it on purpose. Ask Claude to do the thing the hook should stop, and confirm it gets stopped.

Here is a full example. The script blocks a few commands I never want an AI to run without me:

#!/bin/bash
# .claude/hooks/block-risky-commands.sh
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf|git push.*(--force|-f)|DROP TABLE'; then
  echo "Blocked by hook: '$cmd' needs a human. Ask before running it." >&2
  exit 2
fi
exit 0

And the settings that register it:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-risky-commands.sh"
          }
        ]
      }
    ]
  }
}

$CLAUDE_PROJECT_DIR points to the root of your project, so the path works no matter which folder Claude is in. Claude reads that message and adjusts its plan.

Which hooks are worth adding to a business setup?

The hooks worth adding are the ones that guard what you cannot undo and remove what you keep asking for by hand. These five cover most of it:

  1. Block risky commands. The script above. Force pushes, mass deletes and database drops go through you.
  2. Protect secret files. A PreToolUse hook on Read|Edit|Write that exits 2 when the file path contains .env or a secrets folder. This pairs well with deny rules in your permissions.
  3. Format after every edit. A PostToolUse hook on Edit|Write that runs your formatter on the changed file. You can copy a ready version from my Claude Code templates.
  4. Get pinged when Claude waits. A Notification hook that sends a desktop alert, so you stop checking the terminal every two minutes. On a Mac: osascript -e 'display notification "Claude needs you" with title "Claude Code"'.
  5. Keep a record. A Stop hook that appends a timestamp and the session id to a log file. When something looks off later, you know which session to check.

Notice what these have in common. None of them make Claude smarter. They make the setup safer and quieter. The smart part still lives in CLAUDE.md and in skills that hold your procedures.

When should you use a hook instead of CLAUDE.md or permissions?

Use a hook when a rule needs logic or an action, use permissions when a simple allow or deny is enough, and use CLAUDE.md for everything that is context or preference. They work best together.

Need Best place
"We write in American English" CLAUDE.md
"Never read .env" Permissions deny list, plus a hook if you want a clear message
"Never force push, but normal pushes are fine" Hook, because it needs a pattern check
"Run the formatter after every edit" Hook
"Here is how we write proposals" Skill

A simple test: if Claude ignoring the rule once would cost you money, data or trust, it does not belong only in prose.

Missing guardrails show up in two of the five patterns behind agents that break after launch: no human approving risky actions, and access that is far too wide. The full list is in my breakdown of the patterns that break agents. Hooks are one of the cheapest ways to fix both.

What are the risks of Claude Code hooks?

The main risk of Claude Code hooks is that they run automatically with your own user permissions, so a careless or malicious hook can do the same damage you could. Anthropic's documentation says this plainly: you are responsible for the commands you configure.

A few habits keep this safe:

  • Read every hook before adding it. Especially hooks that arrive in a shared repo or a template from someone else.
  • Quote your variables. "$cmd", not $cmd. Unquoted variables break on spaces and invite injection.
  • Use absolute paths or $CLAUDE_PROJECT_DIR, so the hook runs the script you meant.
  • Keep hooks fast. A slow PostToolUse hook runs after every edit and makes the whole session drag.
  • Fail loudly. A hook that silently does nothing gives you false confidence. Test that it blocks what it should.

Are hooks worth the effort?

Yes, if Claude Code touches anything that matters, and the first one takes about as long to write as reading this guide. I run my business with Claude Code doing real work, and I still review what goes out. Hooks make that review easier, because the things that must never happen are already taken off the table. I described that daily setup in this walkthrough.

Start with one hook that blocks the action you would hate most. Test it. Then add the formatter or the notification, whichever saves you more annoyance. Small rules that always run beat long instructions that usually work.

Frequently asked questions

What are Claude Code hooks?

Claude Code hooks are user-defined shell commands that run automatically at specific points in a Claude Code session, for example before a tool call, after a file edit, when Claude needs your input or when it finishes responding. They are configured in settings.json and give you deterministic control instead of relying on the model to follow an instruction.

Where do I put hooks in Claude Code?

Hooks live in a settings file: ~/.claude/settings.json for every project, .claude/settings.json for a shared project setup, or .claude/settings.local.json for personal project settings that stay out of git. You can also open the /hooks menu inside Claude Code to view and edit them.

How do I block a command with a Claude Code hook?

Add a PreToolUse hook with a matcher such as Bash. Your script reads the tool input from stdin, checks the command, and exits with code 2 when it should be blocked. Claude Code then cancels the tool call and shows your stderr message to Claude, so it can choose another approach.

What is the difference between hooks and CLAUDE.md?

CLAUDE.md contains instructions that Claude reads and usually follows. A hook is code that Claude Code runs every time the event happens, whatever the model decides. Put preferences and context in CLAUDE.md, and put rules that must never be broken in hooks or permission settings.

Are Claude Code hooks safe?

Hooks run automatically with your user account's permissions, so a bad hook can do real damage. Only add hooks you have read and understand, quote shell variables, use absolute paths, and be careful with hooks from shared repositories or other people.

Robin van Veen

Robin van Veen is the founder of Arcgent. He helps companies become AI-native, process by process, and shares what he builds with AI agents and Claude Code in public.