What Is Hooks?
Hooks in Claude Code are shell commands that run automatically when specific lifecycle events occur inside a Claude Code session. Examples include before a tool call, after a tool call, when the user submits a prompt, or when a conversation ends. You configure them as JSON inside ~/.claude/settings.json or a repo-local .claude/settings.json, without writing a plugin. This makes Hooks the go-to mechanism for wiring Claude Code into existing developer workflows: linters, type checkers, Slack notifiers, audit loggers, and safety gates.
A simple mental model: think of Hooks as checkpoints placed along the paths Claude Code takes. Whenever Claude wants to edit a file, execute a Bash command, or end a turn, the checkpoint runs your shell command and can optionally block the action, annotate it, or post a message back into the conversation. You should keep this model in mind because it is what makes Hooks different from Skills or subagents. Skills inject knowledge; subagents delegate reasoning; Hooks enforce and automate. Since Hooks shipped in 2025, they have become one of the most important Claude Code extension mechanisms alongside plugins and subagents.
Why Hooks Matter in 2026
The year 2025 saw Claude Code evolve from a simple chat-style CLI into a full-blown agent platform with skills, subagents, plugins, and hooks. Among these extension points, Hooks are the ones that developers reach for first because they require zero new concepts: anyone who has used git hooks, Husky pre-commit hooks, or a CI workflow already understands the mental model. You should appreciate that familiarity — it is the reason Hooks became the most widely used Claude Code extension point within months of launch, outpacing subagents and plugins in adoption surveys.
Another reason Hooks matter in 2026 is the rise of long-running agent sessions. Claude Code tasks that last hours consume many tool calls, which means many opportunities for policy to slip. A hook that runs automatically on every tool call is the only reliable way to enforce rules at scale. Important: as agent autonomy grows, Hooks transition from a convenience feature to a governance requirement. Organizations that take Claude Code to production typically deploy a standardized hook bundle before allowing agents onto real codebases.
Hooks and the Broader Agent Ecosystem
Hooks are part of a larger family of Claude Code extension mechanisms. Plugins package reusable capabilities that are distributable via marketplaces; skills inject domain knowledge on demand; subagents delegate reasoning to specialized models; and MCP servers expose external tools. Hooks sit beneath all of them as the runtime enforcement layer. Keep in mind that a well-architected Claude Code deployment uses every layer: plugins for breadth, skills for depth, subagents for parallelism, MCP for integrations, and hooks for safety rails.
How to Pronounce Hooks
hooks (/hʊks/)
hook (/hʊk/) — singular form used for each individual event handler
How Hooks Work
Each hook configuration is a combination of three parts: a hook event, a matcher, and a command. Claude Code emits lifecycle events as users and tools operate inside a session, and Hooks attach shell commands to those events. The command receives context via stdin as JSON and can return data via stdout. The process exit code decides whether Claude Code proceeds, blocks, or forwards feedback to the model itself.
Main Hook Events
| Event | When It Fires | Typical Use |
|---|---|---|
PreToolUse |
Before any tool runs | Block unsafe commands |
PostToolUse |
Immediately after tool runs | Format, lint, test |
UserPromptSubmit |
User sends a prompt | Filter input, inject context |
Stop |
Conversation turn ends | Ship logs, send Slack |
SessionStart |
New session begins | Load env vars, mount secrets |
Notification |
Internal notification fires | OS-level alerts |
Note that Hooks are more than just callbacks. Important design idea: the exit code is the primary control surface. Exit 0 means allow, exit 2 means block and surface stderr back to Claude as feedback, and stdout can be printed as a comment that enters the conversation. You should internalize this because it means Hooks are not only passive observers; they are an active gate that can push information into the model.
Claude Code Hooks Architecture
Hook Lifecycle Flow
Structured JSON I/O
Beyond shell exit codes, hooks can emit structured JSON on stdout to influence Claude Code precisely. The official schema supports hookSpecificOutput keys like additionalContext for UserPromptSubmit, or permissionDecision for PreToolUse. A hook that injects organizational policy into every user prompt, or a hook that grants permission to tools on a case-by-case basis, both rely on this JSON channel. You should keep in mind that the schema is strict; malformed JSON will simply be ignored with a warning.
Important: combining exit code semantics (0 = allow, 2 = block) with structured JSON gives you two complementary control surfaces. Exit codes are fast and simple, while JSON output supports fine-grained adjustments. In practice, teams start with exit codes and graduate to JSON when guardrails need to carry richer information back to the model.
Scoping and Precedence
Claude Code reads three hook configuration files: user-global ~/.claude/settings.json, project-shared .claude/settings.json committed to the repository, and developer-local .claude/settings.local.json ignored by git. Later files override earlier ones. You should remember this precedence when debugging: if a hook is not firing, check whether a local override is suppressing it. In well-run teams, the project-level file carries the baseline linting and safety rules while the local file handles individual notification endpoints and debugging toggles.
Hook Debugging Tips
The most common Hooks bug is a matcher regex that silently misses its target. Note that matchers are evaluated as case-sensitive regexes against tool names, not shell globs. A matcher of edit will not match the actual tool name Edit. Run Claude Code with claude --debug to see every hook invocation and its exit code, then iterate on the matcher until it lights up.
Hooks Usage and Examples
The quickest way to try Hooks is to drop a few lines into ~/.claude/settings.json. The configuration below reformats every Python file after Claude edits it, using black.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd $CLAUDE_PROJECT_DIR && black --quiet ."
}
]
}
]
}
}
Blocking Dangerous Commands
A common safety pattern is to inspect Bash commands Claude tries to run and refuse anything dangerous. Important: returning exit code 2 forwards the stderr back to Claude, letting the model know why you blocked the action, so it can adapt.
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
if echo "$CMD" | grep -Eq "rm -rf /|git push --force|:(){ :|:& };:"; then
echo "Refused: destructive command pattern detected" >&2
exit 2
fi
exit 0
Slack Notification at Stop
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "curl -s -X POST $SLACK_URL -d 'payload={\"text\":\"Claude Code session finished\"}'"
}
]
}
]
}
}
Playwright-Backed Screenshot Hooks
Hooks are not restricted to trivial shell tasks. Teams have started combining PostToolUse hooks with Playwright or Puppeteer to capture screenshots after every edit, producing a visual audit trail. This pattern is especially useful for UI refactors where regression risk lives in layout, not logic.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "npx playwright screenshot http://localhost:3000 audit/$(date +%s).png"}
]
}
]
}
}
Branch-Aware Behavior
A hook can inspect $CLAUDE_PROJECT_DIR and the current git branch to apply different rules. For example, treat main as strict (block any direct writes) while allowing edits on feature branches. Important: this pattern elevates hooks from pure automation to policy enforcement, and it scales well as teams grow and the range of developer experience widens.
#!/bin/bash
BRANCH=$(cd "$CLAUDE_PROJECT_DIR" && git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" = "main" ]; then
echo "Direct edits on main are forbidden" >&2
exit 2
fi
exit 0
Combining Hooks With MCP Servers
Hooks and MCP servers complement each other. Hooks own the pre/post cycle of tool execution; MCP servers own external capabilities. Teams pair them by using Hooks to validate that MCP tool calls match organizational policy. For example, a PreToolUse hook can inspect arguments to an MCP slack.send_message call and block messages to restricted channels. Keep in mind that MCP tool names appear as mcp__server__tool strings, which your matcher regex should recognize.
Performance Considerations
Latency added by hooks is latency added to every tool call. You should benchmark hooks against a representative workload; anything above 200ms per PostToolUse hook becomes painful in long sessions. Keep expensive work (running a full test suite, invoking a large linter) behind gating: only run it when specific files change, and cache results when feasible.
Case Study: CI Equivalence Hooks
A common pattern at teams with mature CI pipelines is to replicate the CI checks as PostToolUse hooks. The hook runs the same tests, the same linters, and the same type checks that run in CI. If the hook fails, Claude sees the error and self-corrects before committing. You should note that this pattern dramatically shortens the feedback loop compared to waiting for remote CI — Claude fixes problems in seconds instead of minutes.
Teams that adopt this pattern typically see a drop in failed CI runs by 70–90%. Important: the trade-off is that Claude Code sessions now spend more local compute. Most teams find the trade-off worthwhile, but you should monitor session latency and adjust the hook selection if it slows interactive work.
Case Study: Secret Scrubbing Before Commit
Another production use case is running secret scanners like gitleaks or trufflehog as PostToolUse hooks. The hook scans modified files after each edit and blocks the commit if it detects secrets. Keep in mind that LLM-generated code is not immune to leaking secrets, especially when the model has seen example API keys in training data. A secret-scrubbing hook catches these leaks before they reach the repository and becomes the single most important hook in regulated deployments.
Case Study: Test Coverage Enforcement
Some teams use PostToolUse hooks to enforce coverage thresholds. After every edit to a source file, the hook runs the tests with coverage, compares against a baseline, and blocks if coverage drops. This is how human developers are often held to coverage rules; bringing Claude Code under the same rule prevents the agent from shipping uncovered code. Important: hooks of this sort need to be fast — running a full test suite on every edit is impractical. Teams typically scope coverage enforcement to the files that changed.
Advantages and Disadvantages of Hooks
Advantages
- No plugin boilerplate. A few lines of JSON plug Claude Code into your existing toolchain.
- Works as a safety net. Dangerous command patterns can be caught before Claude executes them.
- Bi-directional: stdout and exit codes let you influence Claude’s behavior.
- Project-level and user-level scoping; you can ship shared Hooks via git.
Disadvantages
- Arbitrary shell execution means misconfiguration can open security holes.
- Slow hooks slow the whole session. Keep commands sub-second.
- Wrong matchers silently skip hooks, creating false confidence in safety rules.
- Environment drift: hooks that rely on
jqor Python must document requirements.
Hooks vs Subagents
People frequently confuse Hooks with subagents. Important distinction: Hooks are shell commands with no LLM involvement; subagents are separate Claude instances that reason. They solve different problems and are often combined.
| Dimension | Hooks | Subagent |
|---|---|---|
| Runtime | Shell command | LLM instance |
| Purpose | Pre/post processing, safety | Delegated reasoning |
| Trigger | Event-driven automatic | Parent Claude’s choice |
| Cost | Local, zero tokens | Consumes API tokens |
Common Misconceptions
Misconception 1: Claude Code Hooks are the same as React Hooks
They are entirely unrelated. React Hooks (useState, useEffect, etc.) are front-end state management primitives. Claude Code Hooks are a shell command injection mechanism along Claude Code’s lifecycle. The shared name is the only thing in common; keep in mind that the design goals are completely different.
Misconception 2: Hooks replace plugins
They complement each other. In real teams, small pre/post-processing lives in Hooks while distributable capabilities are packaged as Plugins with their own MCP servers, commands, and skills. You should note that treating Hooks as a plugin replacement leads to maintenance pain once you need versioning, discovery, or marketplace distribution.
Misconception 3: Hooks are safe by default
Anything run by a hook runs with your shell’s privileges. If you clone an untrusted repository that ships a .claude/settings.json, a hook could run on session start. You should always audit .claude/settings.json before opening an unknown project with Claude Code.
Evolution of Hook Semantics
Hooks were originally introduced to Claude Code with a minimal set of events. Over successive releases the team expanded the roster, added the JSON I/O protocol, and refined the precedence model. Keep in mind that the evolution is ongoing; new hook events appear with new Claude Code features, and older behaviors are occasionally clarified. You should read the changelog when upgrading Claude Code because hook behavior is one area where small changes have outsized downstream effects.
Cross-Cutting Concerns Handled by Hooks
Several cross-cutting concerns are handled uniquely well by hooks. Compliance logging: every tool invocation can be captured to a centralized log. Metric emission: tool call counts, durations, and outcomes can be emitted to StatsD or Prometheus via a lightweight hook. Drift detection: a hook can compare the working tree against a baseline and alert on unexpected changes. Important: handling these concerns through hooks rather than wrapping Claude Code from the outside preserves the developer experience while still meeting organizational requirements.
Hook Anti-Patterns To Avoid
Certain patterns are known to cause pain. Wrapping every tool call in a hook that makes an HTTP request destroys session performance. Storing large amounts of state in hook invocations creates subtle races. Writing hooks that depend on machine-specific paths breaks team sharing. You should also avoid hooks that silently succeed without clear logging — debugging a hook that appears to do nothing is one of the most frustrating tasks in the Claude Code ecosystem.
Integrating Hooks With Monitoring Platforms
Teams that take observability seriously integrate Hooks with their monitoring stack. A Stop hook that emits a session summary to Datadog or Honeycomb gives visibility into how Claude Code is used across the organization. Important: this kind of integration becomes valuable when you have dozens of developers running hundreds of sessions per week, because the aggregate data reveals usage patterns and cost drivers that individual developers cannot see. Many teams also emit metrics on hook execution itself so they can detect when a hook’s runtime drifts.
Real-World Use Cases
In practice, four patterns dominate. Important: treat Hooks as guardrails and automation, not reasoning. Anything that requires judgement should be delegated to Claude or to a subagent, not encoded into a shell script.
- Auto-format and lint on save (black, ruff, prettier, eslint, gofmt).
- Block destructive shell commands and dangerous file paths.
- Stream session transcripts to audit logs or Slack channels.
- Mount secrets or load virtualenv on SessionStart.
Frequently Asked Questions (FAQ)
Q. Which operating systems are supported?
macOS, Linux, and Windows (including WSL). On native Windows you should adapt the command syntax to PowerShell or cmd semantics.
Q. My hook is not firing. What should I check?
Run claude --debug and watch the event log. Most failures come from an overly strict matcher regex or a shell command returning a non-zero exit code unexpectedly. Keep in mind that matcher strings support simple regex-like patterns, so escape metacharacters carefully.
Q. How do I share hooks with my team?
Commit .claude/settings.json to the project repository. Reserve ~/.claude/settings.json for personal overrides such as notification endpoints or local paths.
Q. Can hooks modify Claude’s memory or context?
Hooks cannot directly mutate conversation memory, but stdout from UserPromptSubmit is injected as a system note and can thus steer the conversation.
Q. Can hooks call APIs that take credentials?
Yes. Because hooks run as shell commands, they inherit your environment. Store API tokens in environment variables or in tools like 1Password CLI and reference them from the hook command. Important: never commit credentials inside a hook command that lives in a shared .claude/settings.json.
Q. Do hooks work in non-interactive Claude Code sessions?
Yes. Hooks fire whether Claude Code runs in an interactive terminal or as part of a CI pipeline via claude --print. This makes them suitable for enforcing policy during automated agent runs.
Q. What’s the relationship between hooks and the Claude Agent SDK?
Claude Agent SDK exposes its own Python and TypeScript hooks at the SDK level. Conceptually they mirror the CLI’s hook events, but they run as in-process callbacks rather than shell commands. Teams that build custom agents often start with CLI hooks and migrate to SDK hooks when they need programmatic access to event payloads.
Cultural Impact Inside Engineering Teams
A subtle but important effect of Hooks is cultural. Teams that adopt Hooks tend to formalize their development practices in a way that benefits human developers as well. A hook that enforces linting makes the team’s style explicit. A hook that blocks dangerous commands documents what “dangerous” means in that team’s context. Keep in mind that this second-order effect — Claude Code pushing human practices toward greater clarity — is often the most durable benefit of Hooks adoption. Important: teams that invest in well-designed hooks typically report faster onboarding for new engineers, because the rules that used to live in tribal knowledge now live in version-controlled configuration that anyone can read and reason about.
Portability Across Environments
Hooks configurations are portable across macOS, Linux, and WSL, but each platform has quirks. On macOS, shell paths differ from Linux in subtle ways, and relying on specific GNU coreutils flags can break portability. On WSL, file system performance across the Windows-Linux boundary can slow hooks that touch many files. You should design your hooks defensively — use POSIX shell features, prefer portable tools like Python for anything beyond trivial text manipulation, and document any platform-specific assumptions explicitly. Important: teams with mixed-OS development environments should test their hooks on every supported platform before declaring them production-ready, because failures in one environment are often silent for developers on another.
Conclusion
- Hooks inject shell commands into Claude Code lifecycle events.
- Events include PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, and Notification.
- Configuration lives in
~/.claude/settings.jsonor project-level.claude/settings.json. - Exit codes and stdout let hooks influence Claude’s behavior in real time.
- Primary use cases: format, lint, safety gate, audit, notifications.
- Hooks differ from Subagents, Skills, and Plugins and are often combined with them.
- Audit untrusted settings.json files before opening a project.
References
📚 References
- ・Anthropic “Claude Code Hooks Guide” https://docs.claude.com/en/docs/claude-code/hooks
- ・Anthropic “Claude Code Settings Reference” https://docs.claude.com/en/docs/claude-code/settings
- ・Anthropic “Claude Code overview” https://docs.claude.com/en/docs/claude-code/overview









































Leave a Reply