Skip to main content
NeVibe · guide · 2026-10-24

Claude Code hooks: what they are, where to configure them, how to block a risky command

A Claude Code hook is a command, HTTP call or MCP tool call that the harness runs on its own when an agent event fires. Unlike CLAUDE.md, it is not a request to the model: it always runs and it can block an action.

Why hooks when you already have CLAUDE.md

The model reads CLAUDE.md and usually follows it. Usually. The Claude Code docs say plainly that it is context, not enforced configuration, and point to a PreToolUse hook for hard rules.

Mistakes here get expensive. "An AI agent deleted our production database" hit 860 points and over a thousand comments on Hacker News (2026-04-26), and in July 2025 a Replit agent wiped Jason Lemkin's database. A line saying "never touch prod" will not stop that. A hook that exits 2 on DROP or rm -rf will.

The other everyday job: run the formatter or linter after every edit without hoping the agent remembers.

Hook events: when hooks fire

There are 33 events. These cover most needs:

EventFiresTypical usePreToolUsebefore a tool callblocking, argument checksPostToolUseafter a successful callformatter, linter, testsUserPromptSubmitbefore your message is processedadd context, filter inputSessionStart / SessionEndsession start and endload state, clean upStop / SubagentStopagent or subagent finishednotifications, final checksPreCompactbefore context compactionsave what mattersInstructionsLoadedinstruction files loadedaudit what entered context

The rest (permissions, tasks, worktrees, model switches and more) are in the hooks reference.

Example: block rm -rf with PreToolUse

In the project .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/no-rm.sh", "timeout": 30 }
        ]
      }
    ]
  }
}

The script, .claude/hooks/no-rm.sh:

#!/bin/sh
cmd=$(jq -r '.tool_input.command')
case "$cmd" in
  *"rm -rf"*) echo "Blocked: rm -rf is not allowed here" >&2; exit 2 ;;
esac
exit 0

Treat it as a sketch and test it. The mechanics, per the docs: the command gets the event JSON on stdin (tool_name, tool_input, plus common fields such as cwd). Exit code 2 blocks the call and stderr goes to Claude as the reason. Exit 0 means "no decision" and the action proceeds; Claude never sees stderr from a hook that exits 0.

A matcher made of letters, digits, | and commas is an exact match: Edit|Write catches exactly those two tools. Any other character turns it into a regex, such as mcp__memory__.*.

Where hooks live and how levels merge

  • ~/.claude/settings.json for every project;
  • .claude/settings.json for the project, committed;
  • .claude/settings.local.json for you only, not in git;
  • organization managed settings;
  • hooks/hooks.json inside a plugin;
  • skill or subagent frontmatter.

Levels add up rather than override: a user hook and a project hook on the same event both run. To see what is actually configured and where it came from, type /hooks; it opens a read-only hook browser.

Handler types and timeouts

TypeWhat it doesDefault timeoutcommandshell command600 s (30 s on UserPromptSubmit)httpPOST with the event JSON as body600 smcp_toolcalls an MCP server tool600 sprompta model decides from a prompt30 sagentexperimental, an agent runs the check60 s

Use command for anything you want enforced: a deterministic script does not change its mind. prompt helps when a rule is hard to express as a regex, but then you are back to model judgment.

Hooks in Codex, Cursor and Gemini CLI

All three have hooks. None reads the Claude Code format directly.

  • Codex: hooks.json or [hooks] tables in config.toml, under ~/.codex/ or the repo .codex/. Event names match Claude Code: PreToolUse, PostToolUse, Stop, PreCompact and others. Unmanaged hooks must be reviewed and trusted via /hooks. Docs.
  • Cursor: .cursor/hooks.json and ~/.cursor/hooks.json, camelCase events such as beforeShellExecution, afterFileEdit, beforeMCPExecution. Docs.
  • Gemini CLI: a block in settings.json, 11 events with their own names: BeforeTool, AfterTool, BeforeModel. Docs.

The handler script usually ports; the config around it has to be rewritten. Ready-made hooks are in the Hooks section.

FAQ

What are hooks in Claude Code?

Commands, HTTP calls or MCP tool calls that Claude Code runs on agent events: before and after a tool, at session start, on stop. The model cannot skip them.

What is the primary purpose of hooks in Claude Code?

To guarantee behavior you cannot leave to instructions: block a dangerous command with a PreToolUse hook that exits 2, or run a formatter after every edit with PostToolUse.

Where do I configure Claude Code hooks?

In ~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json, managed settings, a plugin hooks/hooks.json, or skill and subagent frontmatter. Entries from all levels are merged.

How does a hook block an action?

The script exits with code 2. Claude Code cancels the tool call and passes the stderr text to the model as the reason.

NeVibe catalog

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor