▸ TLDR
CHECK 11 15 MIN · HANDS-ON

Hooks

What you’ll know by the end of this check

  • Why hooks are the only mechanism in Claude Code that’s deterministic
  • The five hook events and what each one is good for
  • How to block dangerous operations with a PreToolUse hook

The shortest possible answer

Hooks always run. That’s the whole pitch.

You can tell Claude in CLAUDE.md to format your code after every edit. Most of the time it will. Sometimes it won’t. A hook makes it happen every single time, no exceptions.

Hooks are shell commands Claude Code executes at specific points in its lifecycle. They’re configured in settings.json (project or user scope) and fire regardless of what the model decides.

The five events

EventFiresGood for
PreToolUseBefore any tool callBlocking dangerous operations, guardrails
PostToolUseAfter a tool call completesAuto-formatting, logging, audit trails
UserPromptSubmitWhen you hit send, before Claude sees itPreprocessing, prompt injection, guards
StopWhen Claude finishes respondingNotifications, cleanup, session summaries
NotificationWhen Claude sends a notificationRouting to Slack, desktop alerts

Matchers let you scope a hook to specific tools — e.g., "Edit\|MultiEdit\|Write" fires only on file-modification tools.

The killer pattern: auto-format on edit

A PostToolUse hook with matcher "Edit|MultiEdit|Write" that runs Prettier (or gofmt, or black, or whatever your project uses) after every file change. Configure once, and formatting is solved forever.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          { "type": "command", "command": "sh .claude/hooks/format.sh \"$FILE_PATH\"" }
        ]
      }
    ]
  }
}

The script inside checks the file extension and calls the right formatter. Claude doesn’t have to remember. It’s just done.

Blocking with PreToolUse

PreToolUse hooks can block a tool call before it runs. Your hook receives the tool name and input as JSON on stdin. The exit code is the signal:

Exit codeMeaning
0Proceed normally
2Block. stderr message gets fed back to Claude as feedback so it can adjust.
Anything elseNon-blocking error — shown to you but doesn’t stop the call

This is how you enforce hard rules Claude can’t talk its way out of:

  • Block writes to src/schema/ or config/production/
  • Block rm -rf in Bash commands
  • Block commits or pushes to main
  • Block any operation touching a prod database connection string

Sharing hooks with your team

Hooks in .claude/settings.json are project-level and check into the repo. Every teammate gets the same hooks automatically. Use the CLAUDE_PROJECT_DIR environment variable in your commands so scripts reference the right paths regardless of where Claude is running.

The rule that captures the whole lesson

If something needs to happen every time without fail, don’t put it in a prompt. Put it in a hook.

Prompts are suggestions. CLAUDE.md is a suggestion. Hooks are law.

Things to try right now (15 minutes)

  1. Pick one thing you always want to happen after a file edit (format, lint, git add, whatever).
  2. Create .claude/settings.json in your repo if it doesn’t exist.
  3. Add a PostToolUse hook with a matcher and command.
  4. Edit a file with Claude. Confirm the hook fires.
  5. Commit .claude/settings.json so the team benefits too.

The canonical version

Full official lesson is at anthropic.skilljar.com/claude-code-101/469798.

Ready to verify this check?

You have at least one hook configured in a real project. You can name the five events. You know the exit-code semantics for PreToolUse blocking. Mark it cleared — you’ve finished Claude Code 101.