Claude Code Hooks: Automate and Control the Agent (2026)

Claude Code hooks — shell commands wired into the agent's lifecycle events to format, block, and notify
In one line

Claude Code hooks are shell commands wired to lifecycle events in settings.json that run every single time, so you can format code, block dangerous shell commands, or ping yourself on finish without trusting the model to remember.

A prompt asks the agent nicely. A Claude Code hook makes it certain. When you tell the model "always run the linter after editing," it usually does, and then on a long session it forgets once and ships a formatting diff into your PR. A hook removes the "usually." It is a shell command Claude Code runs at a fixed point in its lifecycle, every time, whether the model planned to or not. That turns probabilistic behavior into deterministic behavior, which is exactly what you want for anything that must happen. This guide covers the eight events you can hook, how to wire one up in settings.json, three hooks worth adding this afternoon, and the security tradeoff you are signing up for.

What Hooks Do

A Claude Code hook is a shell command tied to a named moment in the agent's run, defined under a hooks key in your settings file. When that moment arrives, Claude Code runs your command and passes it a JSON payload on standard input describing what just happened, such as which tool is about to run or what the edited file path was. Your command can act on that payload and, for some events, send a result back that changes what the agent does next.

The reason this is a separate feature from skills or instructions: hooks do not pass through the model. The model cannot skip a hook, reinterpret it, or decide today is different. Tom, a backend engineer on a four-person team, spent two weeks adding "remember to run gofmt" to his project's CLAUDE.md memory file and still found unformatted code in three commits. He replaced the instruction with a one-line PostToolUse hook that calls gofmt -w on the edited file. The unformatted commits stopped that day.

It helps to browse the skill registry alongside the hooks docs, because the two layers complement each other: skills shape what the agent tries to do, hooks guarantee what happens around it.

The Hook Events

Claude Code exposes eight hook events, each firing at a distinct point in the run. Picking the right one is most of the work, because the event decides what JSON your command receives and whether it can change the outcome.

Event Fires when Can it block?
PreToolUse Before the agent runs a tool (Bash, Edit, Write, and others) Yes, can deny the call
PostToolUse Right after a tool call succeeds No, but can feed feedback back
UserPromptSubmit When you submit a message, before the model sees it Yes, can reject the prompt
Stop When the main agent finishes responding Yes, can force it to continue
SubagentStop When a subagent finishes its task Yes, can force it to continue
Notification When Claude Code would notify you (idle, needs input) No
PreCompact Before the context window gets compacted No
SessionStart When a session starts or resumes No

The two you will reach for first are PreToolUse and PostToolUse. PreToolUse is your gate: it runs before a tool executes, so it can inspect the command and stop it. PostToolUse is your reaction, where formatters, linters, and "regenerate the types" steps live. The Stop and Notification events are about you, not the code, and they are where finish-notifications go.

Configuring a Hook

Hooks live in a hooks key in your settings.json. You can edit .claude/settings.json in your project (shared with the team through git) or your user-level ~/.claude/settings.json (yours across every project), or run /hooks inside Claude Code to add one through a guided menu. The structure groups commands by event, then by a matcher that picks which tools the hook applies to.

Here is a PostToolUse hook that runs Prettier on any file the agent edits or writes:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
          }
        ]
      }
    ]
  }
}

The matcher is a regular expression against the tool name, so "Edit|Write" catches both. The command is plain shell; Claude Code runs it and exposes details through environment variables and the JSON it sends on stdin. Save the file and the hook is live on the next matching edit, with no restart. If a hook misbehaves, run /hooks to see what is registered and where it came from.

Three Hooks Worth Adding Today

You do not need a wall of hooks. Three earn their keep for almost any project, and each is a few lines.

1. Auto-format on every edit. The PostToolUse example above is the highest-value hook for most teams, because it kills formatting churn in review. Swap prettier for gofmt, black, rustfmt, or whatever your language uses, and the agent's edits land already formatted.

2. Block a dangerous command. A PreToolUse hook can read the Bash command the agent wants to run and stop it. This script denies anything matching rm -rf by exiting with code 2, which Claude Code treats as a block:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' | grep -qE 'rm +-rf' && { echo 'Blocked: rm -rf is not allowed' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

When this hook exits 2, the tool call never runs and the message on stderr goes back to the model as the reason. Maya, who runs a data team, added a variant that blocks any write to a production config path; in her words, it let her stop watching every Bash call over the agent's shoulder.

3. Desktop notification on finish. A Stop hook fires when the agent is done. On macOS, this pings you so you can step away during a long run:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code is done\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Start with these three, live with them for a week, and add a fourth only when a repeated annoyance asks for one. If you want ready-made bundles, the Claude Code marketplace and the registry both list packaged hook configurations you can install instead of hand-writing.

Hooks vs Skills vs Subagents: Deterministic vs Probabilistic

Hooks, skills, and subagents are not competitors; they sit at different points on a control axis. The single question that tells you which to reach for is: does this need to happen every time, or does it need judgment?

Mechanism Runs Best for
Hook Deterministically, on a lifecycle event Guarantees: format, block, notify, validate
Skill Probabilistically, when the model judges it relevant Know-how: how to use a tool or follow a workflow
Subagent Probabilistically, in its own context window Side-quests: research, review, a self-contained task

Put the formatter in a hook, because it must run on every edit. Put "how our codebase does database migrations" in a skill, because the agent should apply that judgment when it is migrating and ignore it otherwise. Push a noisy code review into a subagent so it does not flood your main context. A common stack uses all three: a skill teaches the workflow, a subagent does the heavy lifting, and a hook guarantees the cleanup afterward.

The Security Side of Hooks

Hooks run with your full shell access, so a hook is exactly as dangerous as the command inside it. This is the one part of the feature where "most guides only sell the upside" is itself a risk. A hook command can read your files, hit the network, and delete things, all without a confirmation prompt, because the whole point is that it runs automatically.

Two facts make this manageable. First, Claude Code captures your hook configuration when a session starts, so a hook changed mid-session has no effect until the next session, which closes one path where a malicious edit could take over a running agent. Second, hooks are plain shell, so you can read exactly what one does before you trust it. That is the rule with any hook you did not write: open it, read the command, and only then save it.

The real exposure is copy-pasting hook configs from the internet without reading them. A hook that quietly exfiltrates an environment variable on every tool call looks almost identical to a benign logging hook. Treat a hook config like a dependency, not a snippet: prefer scanned or official sources, and on PolySkill every published skill, including any that ship hook configuration, is security-scanned before it is listed.

Add One Hook, Then Trust It

The failure mode with hooks is the opposite of the one with prompts. Prompts fail by being too soft; hooks fail by being too eager, because a single bad matcher can run a command on every edit you did not intend. So do the cautious version: add one hook, run /hooks to confirm exactly what it executes and where it is registered, watch it fire once on a real action, and only then forget about it. The auto-format hook is the right first one for almost everyone. Once you have read what it runs and seen it behave, you have a piece of your workflow that no longer depends on the model remembering, which is the entire reason hooks exist.

Browse next: Claude Code Subagents (2026) | Claude Code Commands (2026) | Claude Code MCP (2026) | Claude Code Memory (2026)

Hook Questions, Answered

What are Claude Code hooks?

Claude Code hooks are shell commands that run automatically at fixed points in the agent's lifecycle, configured under a hooks key in settings.json. Where a prompt asks the model to behave a certain way, a hook enforces it: the command fires every time the matching event occurs, regardless of what the model decides. Common uses are auto-formatting edited files, blocking risky shell commands, and notifying you when a task finishes.

How do I add a hook in Claude Code?

Run /hooks inside Claude Code for a guided menu, or edit .claude/settings.json directly: add a hooks key, pick an event like PostToolUse, set a matcher for the tools it applies to, and give it a command. The command gets JSON about the event on stdin. Save the file and the hook is active on the next matching action, no restart needed.

What events can a Claude Code hook fire on?

Eight events: PreToolUse and PostToolUse (before and after a tool call), UserPromptSubmit (when you send a message), Stop and SubagentStop (when the agent or a subagent finishes), Notification (when Claude Code would alert you), PreCompact (before context is compacted), and SessionStart (when a session begins or resumes).

Can a hook block Claude Code from running a command?

Yes. A PreToolUse hook runs before the tool call and can stop it. Exit with code 2 to deny the action and send the hook's stderr back to the model as the reason, or print JSON with a permissionDecision of deny. Teams use this to hard-block patterns like rm -rf or writes to a .env file no matter how the prompt is worded.

Are Claude Code hooks safe?

Hooks run arbitrary shell with your full permissions, so a hook is exactly as safe as the command inside it. Claude Code captures hook configuration at session start, so editing a hook mid-session has no effect until the next session, which closes one injection path. Read any hook you copy from someone else before saving it, the way you would read a shell script before running it.

Back to Blog