Home › Rules › Claude Code hooks examples

Claude Code hooks examples: 7 beginner-safe hooks to copy (and how they work)

Built from Anthropic's and Cursor's own docs, checked Oct 25, 2026. Sources below.

Short answer: a hook is a shell command Claude Code runs on its own at a set moment, like right after it edits a file or right before it runs a command. You add hooks to a "hooks" block in .claude/settings.json (one project) or ~/.claude/settings.json (all your projects). Anthropic's docs describe the point plainly: certain actions always happen, rather than relying on the AI to choose to run them. Type /hooks in Claude Code to see what's set up.

Hooks vs CLAUDE.md vs skills

What it isUse it for
CLAUDE.mdInstructions Claude reads every sessionHow you want things done: style, commands, project notes
SkillsExtra instructions loaded when they fit, or when you type /nameLonger, repeatable step-by-step jobs
HooksShell commands Claude Code runs automatically at fixed pointsThings that must happen every time: formatting, blocking risky actions, alerts

Rule of thumb: if you keep writing "always run Prettier" in CLAUDE.md and hoping, make it a hook instead. Anthropic's docs also say that for adding context on every session start, CLAUDE.md is the simpler choice.

Where hooks go

FileApplies toShared with your team?
~/.claude/settings.jsonAll your projectsNo, only your computer
.claude/settings.jsonThis projectYes, you can commit it to git
.claude/settings.local.jsonThis projectNo (gitignored when Claude Code saves a setting there)

Hooks can also come from plugins, from a skill's frontmatter (once the skill is used), or from a subagent's file while it runs.

Every hook has the same shape: an event name, an optional matcher that narrows it down (for tool events, the tool name, like Edit|Write or Bash; matchers are case-sensitive), and the command to run:

The basic shape of a hook
{
  "hooks": {
    "EventName": [
      {
        "matcher": "which tool or type it applies to",
        "hooks": [
          { "type": "command", "command": "the shell command to run" }
        ]
      }
    ]
  }
}

If your settings file already has a "hooks" block, add the new event next to the ones already there instead of replacing the whole block. The file must be valid JSON: no trailing commas, no comments.

The events you'll actually use

Claude Code has many more events (the full list is in the hooks reference). These cover most beginner needs:

EventWhen it firesGood for
SessionStartWhen a session begins or resumes (matcher: startup, resume, clear, compact, fork)Add reminders to Claude's context, load environment variables
UserPromptSubmitWhen you submit a prompt, before Claude processes itAdd extra context to every prompt (no matcher support)
PreToolUseBefore a tool call runs. Can block itBlock risky commands or edits to protected files
PermissionRequestWhen a tool call needs a permission decisionAuto-approve one specific prompt you always allow
PostToolUseAfter a tool call succeedsFormat a file after Claude edits it, log commands
NotificationWhen Claude Code sends a notification (waiting for input or permission)Desktop alert so you don't have to watch the terminal
StopWhen Claude finishes respondingCheck the work is really done before it stops
PreCompact / PostCompactBefore and after the context is summarizedLog or react when the chat gets summarized
SessionEndWhen a session endsClean up temp files (these hooks get a short time budget)

How a hook talks back: exit codes

When the event fires, Claude Code sends your command the details as JSON on stdin (for example tool_name and tool_input, where tool_input.file_path is the file being edited and tool_input.command is the shell command). Your command answers with its exit code:

Several examples below use jq to read that JSON. Install it with brew install jq on a Mac or apt-get install jq on Debian and Ubuntu.

7 hooks to copy

Examples 1 to 3 and 5 to 7 come straight from Anthropic's hooks guide, lightly trimmed. Example 4 uses the same pattern as the guide's blocking examples. Paste each block into the settings file named under it, merging with any hooks you already have.

1. Get a desktop alert when Claude needs you

Uses Notification, which fires when Claude is waiting for your input or permission. The empty matcher means every kind of notification.

macOS: add to ~/.claude/settings.json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
Linux: add to ~/.claude/settings.json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}

On a Mac, if nothing shows up, run osascript -e 'display notification "test"' once in Terminal, then turn on notifications for Script Editor in System Settings, Notifications. On Linux, notify-send needs the libnotify-bin package and a desktop session. Windows has a PowerShell version in Anthropic's guide (linked below). To fire only on permission prompts, set the matcher to permission_prompt; for "finished and waiting about 60 seconds", use idle_prompt.

2. Format every file Claude edits

Add to .claude/settings.json in your project
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Runs Prettier on the file after each Edit or Write. When it works, Claude Code shows nothing; you'll just see the file reformatted. Swap npx prettier --write for your project's formatter if you use something else.

3. Block edits to .env and other protected files

Save this script, then run chmod +x .claude/hooks/protect-files.sh on Mac or Linux so it can run:

.claude/hooks/protect-files.sh
#!/bin/bash
# protect-files.sh: stop Claude from editing secret or generated files

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0
Then register it in .claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

Test it by asking Claude to add a comment to your .env file. The edit is blocked and Claude gets the "Blocked:" message. Note: this only watches the Edit and Write tools. Claude can also change files with shell commands, which this matcher doesn't see, so treat it as a seatbelt, not a lock. Pairs with our vibe coding security checklist.

4. Block a few risky commands

.claude/hooks/block-risky-commands.sh
#!/bin/bash
# block-risky-commands.sh: stop a few commands a beginner never wants run by surprise

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

BLOCKED=("rm -rf" "git push --force" "git reset --hard" "drop table" "DROP TABLE")

for pattern in "${BLOCKED[@]}"; do
  if [[ "$COMMAND" == *"$pattern"* ]]; then
    echo "Blocked: '$pattern' is not allowed here. Explain what you wanted to do and ask me first." >&2
    exit 2
  fi
done

exit 0
Register it in .claude/settings.json (and chmod +x the script)
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-risky-commands.sh"
          }
        ]
      }
    ]
  }
}

Same idea for the Bash tool: if the command contains one of the listed phrases, the hook exits 2 and Claude is told to ask you first. It's a plain text match, so a command written a different way can slip past. Add or remove phrases to fit your project. Anthropic's docs note that a PreToolUse block applies in every permission mode, even bypassPermissions.

5. Remind Claude of your rules after it compacts

Add to .claude/settings.json in your project
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: explain changes in plain English. Run the tests before saying a task is done. Never edit .env files.'"
          }
        ]
      }
    ]
  }
}

When the context fills up, Claude Code summarizes the chat to free space, and details can get lost. A SessionStart hook with the compact matcher runs right after that, and whatever it prints is added to Claude's context. Put your two or three most important rules in the echo.

6. Keep a log of every command Claude runs

Add to ~/.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

After each Bash command, its text is added to ~/.claude/command-log.txt. Handy when something changed and you want to know what ran.

7. Don't let Claude stop until the job is done

Add to .claude/settings.json in your project
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

This is a prompt hook: instead of running a script, Claude Code asks a Claude model to check, using your prompt. If the answer is "ok": false, Claude keeps working and uses the reason as its next step. If a Stop hook blocks eight times in a row with no tool call from Claude in between, Claude Code overrides it, so it can't loop forever.

Hooks in Cursor

Cursor has its own hooks, set in a hooks.json file: .cursor/hooks.json in a project, or ~/.cursor/hooks.json for everything. The format is a little different. Here's the formatter from example 2, Cursor-style:

.cursor/hooks.json
{
  "version": 1,
  "hooks": {
    "afterFileEdit": [{ "command": ".cursor/hooks/format.sh" }]
  }
}
.cursor/hooks/format.sh (then chmod +x it)
#!/bin/bash
# format.sh: run Prettier on the file the Agent just edited
FILE_PATH=$(jq -r '.file_path // empty')
if [ -n "$FILE_PATH" ]; then
  npx prettier --write "$FILE_PATH" > /dev/null 2>&1
fi
exit 0

Before you add a hook from the internet

Anthropic's docs are blunt about this: command hooks run with your full user permissions and can change, delete or read any file your account can. Read every command before you paste it. Their advice: quote shell variables ("$VAR", not $VAR), use full paths to scripts (like "$CLAUDE_PROJECT_DIR"/.claude/hooks/...), and skip sensitive files like .env and .git/. In a normal Claude Code session, hooks don't run until you accept the folder's workspace trust prompt.

You can also tell Claude about your hooks in CLAUDE.md, so a block doesn't confuse it:

Add this to your CLAUDE.md
## Hooks
- This project has hooks in .claude/settings.json. They format files after edits and block edits to .env.
- If a hook blocks you, read its message and ask me before trying another way.

Hook not working? Check these

Next step for Claude Code: Agent Skills

Skills are folders your AI coding agent loads only when the job comes up, and Claude Code, Cursor and OpenAI Codex all read the same SKILL.md format. The Vibe Coder Skills Pack ($9) is 6 ready-to-copy SKILL.md skills (fix-one-bug, stop-the-loop, leak-check, ship-check, explain-project, builder-handoff) for Claude Code, Cursor and OpenAI Codex. Also included in the 6-kit bundle.

Get the Skills Pack for $9 on Gumroad → See what's inside

One .zip of skill folders plus one markdown file. 7-day refund, no questions asked.

Best value

All 6 kits for $29 — or $20.30 with code WELCOME30 (through Nov 30)

Hooks keep Claude in line. The kits tell it what to build, step by step: a small game, a Discord bot, an email-triage agent, an MVP, or a cleanup of messy code.

Get all 6 kits for $29 → See what's in each kit

$29 instead of $48 for the six kits bought separately; WELCOME30 takes 30% off that at checkout ($20.30). Instant download from Gumroad. 7-day refund, no questions asked.

Related

Sources

Checked Oct 25, 2026. These tools change often; if something here no longer matches, tell us at [email protected].

Get a free starter kit: the Discord Bot Builder ($6)

  • The Discord Bot Builder kit, free (it's $6 on Gumroad): from the Discord Developer Portal to a running bot, an agent brief (AGENTS.md / CLAUDE.md) with token-safety rules, a starter bot with /roll and /ping, and six feature prompts. Emailed with your download link.
  • Bonus: the whole rules library in one zip: 137 ready-to-use rules files (AGENTS.md, CLAUDE.md, Cursor .mdc rules, Lovable/Bolt Project Knowledge) for 30 projects.

After that, an occasional email when we publish a new guide, rules file or kit.

Free. No spam. Unsubscribe anytime. See our privacy note.