Skill

hooks-core-reference

Hook system fundamentals — all events, configuration structure, matchers per event type, environment variables, execution behavior, security, and debugging. Use when creating hooks, understanding hook events, matchers, configuration locations, environment variables, or troubleshooting hook issues.

From plugin-creator
Install
1
Run in your terminal
$
npx claudepluginhub jamie-bitflight/claude_skills --plugin plugin-creator
Tool Access

This skill uses the workspace's default tool permissions.

Skill Content

Claude Code Hooks — Core Reference (January 2026)

Hooks execute custom commands or prompts in response to Claude Code events. Use for automation, validation, formatting, and security.

For JSON input/output schemas, activate Skill(skill: "plugin-creator:hooks-io-api"). For working examples and patterns, activate Skill(skill: "plugin-creator:hooks-patterns").


All Hook Events

EventWhen FiredMatcher AppliesCommon Uses
PreToolUseBefore tool executionYes — tool nameValidation, blocking
PermissionRequestWhen user shown permission dialogYes — tool nameAuto-approval policies
PostToolUseAfter successful tool executionYes — tool nameFormatting, linting
PostToolUseFailureAfter tool failsYes — tool nameError handling
NotificationWhen Claude wants attentionYes — notification typeCustom notifications
UserPromptSubmitUser submits promptNoInput validation
StopClaude finishes responseNoCleanup, final checks
SubagentStartWhen spawning a subagentYes — agent type nameSubagent initialization
SubagentStopSubagent (Agent tool) completesYes — agent type nameResult validation
TeammateIdleAgent team teammate about to go idleNoQuality gates
TaskCompletedTask being marked as completedNoCompletion enforcement
InstructionsLoadedCLAUDE.md or rules file loaded into contextNoObservability
ConfigChangeConfiguration file changes during sessionYes — config sourceSettings auditing
WorktreeCreateWorktree being created (--worktree or isolation: worktree)NoCustom VCS integration
WorktreeRemoveWorktree being removed at session exit or subagent finishNoCustom VCS cleanup
PreCompactBefore context compactionYes — manual or autoState backup
PostCompactAfter context compaction completesYes — manual or autoReact to new state
ElicitationMCP server requests user input mid-taskYes — MCP server nameProgrammatic responses
ElicitationResultUser responds to MCP elicitationYes — MCP server nameObserve/modify response
SetupRepository setup/maintenanceYes — init or maintenanceOne-time operations
SessionStartSession begins or resumesYes — startup, resume, etc.Environment setup
SessionEndSession endsYes — exit reasonCleanup, persistence

Note: The Agent tool was renamed from Task in Claude Code v2.1.63. Use Agent as the matcher name for tool-based hooks targeting subagent operations.

For a visual overview of the full event sequence, see the ../hooks-guide/references/hooks-lifecycle.png.


Configuration

Configuration Locations (Precedence highest to lowest)

  1. Managed - managed-settings.json (enterprise)
  2. Local - .claude/settings.local.json (gitignored)
  3. Project - .claude/settings.json (shared via git)
  4. User - ~/.claude/settings.json (personal)
  5. Plugin - hooks/hooks.json or frontmatter
  6. Capability - Skill/Command/Agent frontmatter

Note: Enterprise administrators can use allowManagedHooksOnly to block user, project, and plugin hooks.

Structure

Hooks are organized by matchers, where each matcher can have multiple hooks:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}

Fields:

  • matcher: Pattern to match tool names, case-sensitive (for PreToolUse, PermissionRequest, PostToolUse); matches agent type name for SubagentStart/SubagentStop; matches exit reason for SessionEnd; matches config source for ConfigChange; matches MCP server name for Elicitation/ElicitationResult
    • Simple strings match exactly: Write matches only the Write tool
    • Supports regex: Edit|Write or Notebook.*
    • Use * to match all tools. Also accepts empty string ("") or omit matcher
  • hooks: Array of hooks to execute when pattern matches
    • type: "command" for bash commands, "prompt" for LLM evaluation, "http" for HTTP POST requests, or "agent" for agentic verifiers with tool access
    • command: (For type: "command") The bash command to execute
    • url: (For type: "http") The URL to POST the hook input to
    • prompt: (For type: "prompt" or type: "agent") The prompt to send to the model
    • timeout: (Optional) Seconds before canceling (default: 600 for commands, 30 for prompts, 60 for agent hooks)

Project-Specific Hook Scripts

Use $CLAUDE_PROJECT_DIR to reference scripts in your project:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}

Events Without Matchers

For UserPromptSubmit and Stop, omit the matcher. SubagentStart and SubagentStop support matchers filtered by agent type name. SessionEnd supports matchers filtered by exit reason. Example for events that don't use matchers:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/prompt-validator.py"
          }
        ]
      }
    ]
  }
}

Event-Specific Matchers

PreToolUse / PermissionRequest / PostToolUse Common Matchers

MatcherDescription
AgentSubagent operations
BashShell commands
GlobFile pattern matching
GrepContent search
ReadFile reading
EditFile editing
WriteFile writing
WebFetchWeb fetching
WebSearchWeb search
Notebook.*NotebookEdit, NotebookRead
mcp__<server>__.*All tools from MCP server
mcp__.*__write.*MCP write tools across servers

SessionStart Matchers

MatcherTrigger
startupNew session started
resume--resume, --continue, or /resume
clear/clear command
compactAuto or manual compact

PreCompact Matchers

MatcherTrigger
manual/compact command
autoAuto-compact (full context)

Setup Matchers

MatcherTrigger
init--init or --init-only flags
maintenance--maintenance flag

SubagentStart / SubagentStop Matchers

Matched against the agent type name. Values include built-in agents (Bash, Explore, Plan) and custom agent names from .claude/agents/.

Note: The Agent tool was renamed from Task in Claude Code v2.1.63. Use Agent as the matcher name for PreToolUse/PostToolUse hooks targeting the subagent-spawning tool call.

SessionEnd Matchers

MatcherTrigger
clearSession cleared with /clear command
logoutUser logged out
prompt_input_exitUser exited while prompt input was visible
bypass_permissions_disabledBypass permissions mode was disabled
otherOther exit reasons

ConfigChange Matchers

MatcherWhen it fires
user_settings~/.claude/settings.json changes
project_settings.claude/settings.json changes
local_settings.claude/settings.local.json changes
policy_settingsManaged policy settings (non-blocking)
skillSkill file changes

PostCompact Matchers

MatcherTrigger
manualAfter /compact command
autoAfter auto-compact when the context window is full

Elicitation / ElicitationResult Matchers

Matched against the MCP server name (the server requesting or receiving user input).

Notification Matchers

MatcherTrigger
permission_promptPermission requests from Claude
idle_promptClaude waiting for input (60s+ idle)
auth_successAuthentication success
elicitation_dialogMCP tool elicitation

Important Hook Events

Setup Hook

Runs when Claude Code is invoked with repository setup and maintenance flags (--init, --init-only, or --maintenance).

Use Setup hooks for:

  • One-time or occasional operations (dependency installation, migrations, cleanup)
  • Operations you don't want on every session start

Key characteristics:

  • Requires explicit flags because running automatically would slow down every session start
  • Has access to CLAUDE_ENV_FILE for persisting environment variables
  • Output added to Claude's context

SessionStart Hook

Runs when Claude Code starts a new session or resumes an existing session.

Use SessionStart hooks for:

  • Loading development context (existing issues, recent changes)
  • Setting up environment variables

Important: For one-time operations like installing dependencies or running migrations, use Setup hooks instead. SessionStart runs on every session, so keep these hooks fast.

PostToolUseFailure Hook

Runs immediately after a tool fails (returns an error). This complements PostToolUse, which only runs on successful tool execution.

Use PostToolUseFailure hooks for:

  • Error recovery actions
  • Logging tool failures
  • Custom error handling and reporting

Recognizes the same matcher values as PreToolUse and PostToolUse.

SubagentStart Hook

Runs when a Claude Code subagent (Agent tool call) is spawned.

Use SubagentStart hooks for:

  • Subagent initialization
  • Logging subagent creation
  • Context injection for specific agent types

Input includes:

  • agent_id: Unique identifier for the subagent
  • agent_type: Agent name (built-in like "Bash", "Explore", "Plan", or custom agent names)

Environment Variables

VariableDescriptionAvailable In
CLAUDE_PROJECT_DIRProject root (absolute path)All hooks
CLAUDE_CODE_REMOTE"true" if remote, empty if localAll hooks
CLAUDE_ENV_FILEPath for persisting env varsSessionStart, Setup
CLAUDE_PLUGIN_ROOTPlugin directory (absolute)Plugin hooks

SessionStart Environment Persistence

Example: Setting individual environment variables

#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0

Example: Persisting all environment changes (e.g., nvm use)

#!/bin/bash
ENV_BEFORE=$(export -p | sort)

# Run setup commands that modify environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0

Variables in $CLAUDE_ENV_FILE are sourced before each Bash command.


Working with MCP Tools

MCP tools follow the pattern mcp__<server>__<tool>:

  • mcp__memory__create_entities - Memory server's create entities tool
  • mcp__filesystem__read_file - Filesystem server's read file tool
  • mcp__github__search_repositories - GitHub server's search tool
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Execution Details

AspectBehavior
Timeout600 seconds default for commands, 30s for prompts, 60s for agent hooks, configurable
ParallelizationAll matching hooks run in parallel
DeduplicationIdentical commands deduplicated
EnvironmentRuns in cwd with Claude Code's env
Hook orderHooks from all sources execute together

Output Handling by Event

Eventstdout Handling
UserPromptSubmit, SessionStart, SetupAdded to Claude's context
PreToolUse, PostToolUse, StopShown in verbose mode (Ctrl+O)
Notification, SessionEnd, SubagentStart, InstructionsLoadedLogged to debug only (--debug)
PostCompactLogged to debug only (--debug)
TeammateIdle, TaskCompleted, ConfigChangeShown in verbose mode (Ctrl+O)
WorktreeCreateStdout must be the absolute path to the created worktree directory
WorktreeRemoveNon-blocking; logged to debug
Elicitation, ElicitationResultJSON response controls MCP input

Security Considerations

Disclaimer

USE AT YOUR OWN RISK: Claude Code hooks execute arbitrary shell commands on your system automatically. By using hooks, you acknowledge that:

  • You are solely responsible for the commands you configure
  • Hooks can modify, delete, or access any files your user account can access
  • Malicious or poorly written hooks can cause data loss or system damage
  • Anthropic provides no warranty and assumes no liability for any damages
  • You should thoroughly test hooks in a safe environment before production use

Security Best Practices

  1. Validate and sanitize inputs - Never trust input data blindly
  2. Always quote shell variables - Use "$VAR" not $VAR
  3. Block path traversal - Check for .. in file paths
  4. Use absolute paths via the $CLAUDE_PROJECT_DIR variable - Specify full paths for scripts (use $CLAUDE_PROJECT_DIR)
  5. Skip sensitive files - Avoid .env, .git/, keys, etc.

Configuration Safety

Direct edits to hooks in settings files don't take effect immediately:

  1. Hooks snapshot captured at startup
  2. Snapshot used throughout the session
  3. Warns if hooks are modified externally
  4. Requires review in /hooks menu for changes to apply

This prevents malicious hook modifications from affecting your current session.


Debugging

Enable Debug Mode

claude --debug
claude --debug "hooks"  # Filter to hooks only

Debug Output Example

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

Basic Troubleshooting

  1. Check configuration - Run /hooks to see if your hook is registered
  2. Verify syntax - Ensure your JSON settings are valid
  3. Test commands - Run hook commands manually first
  4. Check permissions - Make sure scripts are executable
  5. Review logs - Use claude --debug to see hook execution details
  6. Validate plugin hooks - Use claude plugin validate or /plugin validate for plugin-level hooks

Common Issues

ProblemCauseFix
Hook not runningWrong matcher patternCheck case-sensitivity, regex
Command not foundRelative pathUse $CLAUDE_PROJECT_DIR
JSON not processedNon-zero exit codeExit 0 for JSON processing
Hook times outSlow scriptOptimize or increase timeout
Quotes breakingUnescaped in JSONUse \" inside JSON strings
Plugin hook not loadInvalid plugin.json hooks configValidate with claude plugin validate .
Path not foundMissing ${CLAUDE_PLUGIN_ROOT}Use variable for plugin scripts

Validation Commands

For plugin hooks:

# CLI (from terminal)
claude plugin validate .
claude plugin validate ./path/to/plugin

# In Claude Code session
/plugin validate .
/plugin validate ./path/to/plugin

For settings hooks: JSON validation with:

python3 -m json.tool .claude/settings.json

Test Commands Manually

echo '{"tool_name":"Write","tool_input":{"file_path":"test.txt"}}' | ./your-hook.sh

Sources

Stats
Parent Repo Stars30
Parent Repo Forks4
Last CommitMar 24, 2026