AI CODING ANALYTICS

Claude Code hooks
explained, with a real plugin.

Claude Code hooks are handlers, usually shell commands, that run at fixed points in a session, such as before a tool call or when it ends; you set them under hooks in settings.json. A hook always runs, and only exit code 2 or a JSON deny blocks the action.

Get started for free ↗

Free for 3 seats · No credit card

Zest lemon in its mech suit

01See your team’s AI stack

Winding / Teams Last 30 days

Zest

3 Members 1 Agent
Team overview
67%
AI Adoption▲ 34 pts
240
PRs Merged▲ 101
96.1h
Median AI Time▲ 9.5h
57.2h
Build Hours▲ 20.3h
This week
Member AI PRs/wk AI Task Plan Impl Last Standup AI Stack
22.8/wk28 min ago
15.8/wk12 min ago
4/wkAug 14
—2 hours ago
Current AI models & harnesses As of Sep 8
Claude CodeFable 5.1 (max) (with fallback)70.4 · Coding Agent Index
CodexGPT-6 Astra (max)67.0 · Coding Agent Index
Muse CodeMuse Spark 1.3 (xhigh)64.2 · Coding Agent Index
Grok BuildGrok 4.5 (high)64.1 · Coding Agent Index
Kimi Code CLIKimi K362.6 · Coding Agent Index
OpencodeGemini 3.8 Flash (high)61.2 · Coding Agent Index
Cursor CLIComposer 2.5 Fast38.3 · Coding Agent Index

Demo setups use current model families. The pairings above are the combinations benchmarked by Artificial Analysis; rankings measure performance, not popularity.

Intelligence Index

Illustrative daily trends · Other totals: last 30 days. Build Hours: this week, model-estimated activity including overlap; not engineer hours.

↓ Scroll to explore

USED BY ENGINEERING TEAMS AT

FlypsCharmWildfiresGeoforgeMisaLabs

What are Claude Code hooks?

Hooks are handlers you define that run automatically at specific points in a Claude Code session: before a tool runs, after it finishes, when you submit a prompt, when the session ends. A CLAUDE.md instruction is something Claude may follow; a hook always runs. Anthropic describes them as deterministic control over Claude Code's behavior.

A handler is usually a shell command, but there are five types: command, http, mcp_tool, prompt and agent, the last still experimental.

Claude Code hook events

The hooks reference lists every event with the input it receives. The ones most teams start with:

  • SessionStart and SessionEnd: load context when a session opens, or record it when it closes.
  • UserPromptSubmit: check or enrich a prompt before Claude sees it.
  • PreToolUse: allow, deny or ask before a tool call runs.
  • PostToolUse: act on a tool's result, such as running a formatter after an edit or logging the call.
  • Stop and SubagentStop: check the work when Claude or a subagent finishes.
  • PreCompact, Notification, PermissionRequest and the rest cover compaction, notifications and permission prompts.

Where hooks are configured

Hooks live under a hooks key in settings files: ~/.claude/settings.json for you, .claude/settings.json for everyone on the project, .claude/settings.local.json for you in this project. They also live in a plugin's hooks/hooks.json, in a skill's or subagent's frontmatter, and in managed policy settings. /hooks opens a read-only view of what is active.

Each entry names an event, an optional matcher and the handlers to run. Tool events match on the tool name, such as Bash or Edit|Write, and SessionStart matches startup, resume, clear, compact or fork.

How a hook allows or blocks

Command hooks run with your full user permissions, so read one before you add it, especially one that arrives inside a plugin.

A command hook reports back through its exit code and its output:

  • Exit 0 is success, and JSON on stdout is parsed for decisions.
  • Exit 2 is a blocking error on events that can block, and even a JSON allow cannot override it. Use it when a hook enforces a policy.
  • Any other exit code, including 1, doesn't block on its own for most events: the action goes ahead, unless stdout holds valid JSON, which then decides alone.
  • For finer control, a PreToolUse hook returns JSON with permissionDecision set to allow, deny, ask or defer, and can rewrite the tool's input with updatedInput.

When a hook fails quietly

Because only exit code 2 blocks, a broken hook does not stop anything. One of our repositories runs its formatter in a PostToolUse hook after every Edit and Write. On a laptop without pnpm it failed 124 times in two days (exit code 127, command not found), and Claude carried on each time, so the formatter never ran. Run a hook's command in your own shell before you commit it.

Does Codex have hooks?

Yes, on by default. Codex reads them from a hooks.json or [hooks] tables in config.toml, in ~/.codex or the repository's .codex folder, and a Codex plugin can ship its own. The events mirror Claude Code's: SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, Stop, the subagent and compaction events, and a few more. A hook you add yourself runs only after you review and trust it in /hooks.

Blocking works the same way: exit code 2 with a reason on stderr, or JSON with permissionDecision set to deny. An ask decision is parsed but not supported yet. Zest's Codex plugin uses two hooks, SessionStart and UserPromptSubmit, to keep its login fresh.

Does Cursor have hooks?

Yes. Cursor reads .cursor/hooks.json in the project and ~/.cursor/hooks.json, plus enterprise and team hooks that take precedence. Its events have their own names: beforeShellExecution, beforeMCPExecution, beforeReadFile, afterFileEdit, beforeSubmitPrompt, stop, and generic preToolUse and postToolUse.

Exit code 2 blocks here too, and other failures let the action go ahead unless the hook sets failClosed. Cursor also runs the hooks in Claude Code's settings files, mapping their events to its own, while its setting for third-party configs is on, which it is by default. A hook written for Claude Code can then fire in both tools, so test it in each.

Hook examples from a real plugin: Zest

Zest's Claude Code plugin is built on three hooks in its hooks/hooks.json: SessionStart and SessionEnd each run a Zest command when a session opens and closes, and PostToolUse runs one after every tool call, each with a timeout. All three exit 0 and return no decision: they record, and never block or change what Claude does.

From those hooks Zest builds AI coding analytics for the team: each session's models, tokens, skills, subagents and MCP tools, and the branch, commits and pull request it touched. A lead sees Claude Code usage per engineer next to what merged, without anyone changing how they work.

TRY IT ON YOUR TEAM

See what your team ships with AI.

Starter is free (3 seats, no credit card). Pro is $49 a month with 5 seats and Max is $149 a month with 10 seats — priced per workspace, not per engineer, with extra seats at $19 a month.

A FEW THINGS TO KNOW

Questions

What is the difference between hooks and skills?+

A skill is instructions Claude chooses to use when a request matches it. A hook is code Claude Code runs every time its event fires, whatever Claude decides. Use a hook for anything that must always happen.

Can I turn all hooks off?+

Yes. Set disableAllHooks to true in your settings to switch off every hook except those an administrator set in managed settings. Administrators can go the other way with allowManagedHooksOnly, which blocks user, project, local and plugin hooks.