Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
anthropics avatar

Hook Development

  • 11.7k installs
  • 139k repo stars
  • Updated July 25, 2026
  • anthropics/claude-code

Event-driven automation scripts that execute in response to Claude Code lifecycle events (PreToolUse, PostToolUse, Stop, SessionStart, etc.) to validate operations, enforce policies, modify inputs, and integrate external

About

Hook Development enables developers to build event-driven automation scripts that execute in response to Claude Code events like PreToolUse, PostToolUse, Stop, and SessionStart. Developers use hooks to validate tool calls before execution, enforce policies, add project context, and integrate external systems into Claude Code workflows. The skill covers two hook types: prompt-based hooks (LLM-driven reasoning for context-aware decisions) and command hooks (bash scripts for deterministic checks). Key workflows include validating file writes for security, analyzing tool results for issues, verifying task completion before stopping, and loading project context at session start. Hook configuration uses JSON matchers to target specific tools, supports parallel execution, and requires restart to load changes.

  • Implement prompt-based hooks with LLM reasoning for flexible, context-aware validation logic without bash scripting
  • Configure command hooks for fast deterministic checks, file operations, and integration with external tools
  • Use matchers (exact, regex, wildcard) to target specific tools: Write|Edit, mcp__.*, or * for all tools
  • Access hook inputs via stdin JSON with fields like tool_name, tool_input, tool_result, user_prompt
  • Return structured JSON decisions (approve/deny/block) with systemMessage context fed back to Claude

Hook Development by the numbers

  • 11,743 all-time installs (skills.sh)
  • +321 installs in the week ending Jul 28, 2026 (Skillselion tracking)
  • Ranked #66 of 16,659 AI & Agent Building skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
At a glance

hook development capabilities & compatibility

Capabilities
validate tool calls before execution with pretoo · react to tool results and provide feedback with · verify task completion before stopping with stop · load and persist project context at session star · validate user prompts and add security guidance · execute bash commands for deterministic checks a · use llm driven reasoning for context aware valid · target specific tools with exact, regex, or wild
Works with
github · jira · confluence · slack
Use cases
security audit · code review · testing · debugging · orchestration
Platforms
macOS · Windows · Linux · WSL
Runs
Runs locally
Pricing
Free
From the docs

What hook development says it does

Hooks are event-driven automation scripts that execute in response to Claude Code events. Use hooks to validate operations, enforce policies, add context, and integrate external tools into workflows.
SKILL.md:Overview
Hooks are loaded when Claude Code session starts. Changes to hook configuration require restarting Claude Code.
SKILL.md:Hooks Load at Session Start
npx skills add https://github.com/anthropics/claude-code --skill hook-development

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs11.7k
repo stars139k
Security audit3 / 3 scanners passed
Last updatedJuly 25, 2026
Repositoryanthropics/claude-code

What it does

Create event-driven hooks to validate, modify, and automate Claude Code operations across tool execution and session lifecycle.

Who is it for?

Enforcing security policies on file operations, validating tool usage, ensuring task completion standards, loading project context, integrating external systems, automating multi-step workflows, blocking dangerous comman

Skip if: Real-time monitoring dashboards, agent training, modifying core Claude Code agent behavior, building new tools (use plugin development instead), learning Claude basics.

When should I use this skill?

User asks to create/add hooks, validate tool use, implement prompt-based hooks, use ${CLAUDE_PLUGIN_ROOT}, set up event-driven automation, block dangerous commands, or mentions hook events (PreToolUse, PostToolUse, Stop,

What you get

Developers can create reusable hook scripts that automatically validate, block, modify, or provide feedback on Claude Code operations, enforcing project-specific policies and automating common workflows.

  • hook configuration
  • event handler scripts

By the numbers

  • 8+ hook event types supported (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, PreCompact, Notifi
  • 2 hook type strategies: prompt-based and command-based with separate recommended timeouts (30s prompt, 60s command)
  • Hooks run in parallel when matching the same tool, requiring independent design without ordering assumptions

Files

SKILL.mdMarkdownGitHub ↗

Hook Development for Claude Code Plugins

Overview

Hooks are event-driven automation scripts that execute in response to Claude Code events. Use hooks to validate operations, enforce policies, add context, and integrate external tools into workflows.

Key capabilities:

  • Validate tool calls before execution (PreToolUse)
  • React to tool results (PostToolUse)
  • Enforce completion standards (Stop, SubagentStop)
  • Load project context (SessionStart)
  • Automate workflows across the development lifecycle

Hook Types

Prompt-Based Hooks (Recommended)

Use LLM-driven decision making for context-aware validation:

{
  "type": "prompt",
  "prompt": "Evaluate if this tool use is appropriate: $TOOL_INPUT",
  "timeout": 30
}

Supported events: Stop, SubagentStop, UserPromptSubmit, PreToolUse

Benefits:

  • Context-aware decisions based on natural language reasoning
  • Flexible evaluation logic without bash scripting
  • Better edge case handling
  • Easier to maintain and extend

Command Hooks

Execute bash commands for deterministic checks:

{
  "type": "command",
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
  "timeout": 60
}

Use for:

  • Fast deterministic validations
  • File system operations
  • External tool integrations
  • Performance-critical checks

Hook Configuration Formats

Plugin hooks.json Format

For plugin hooks in hooks/hooks.json, use wrapper format:

{
  "description": "Brief explanation of hooks (optional)",
  "hooks": {
    "PreToolUse": [...],
    "Stop": [...],
    "SessionStart": [...]
  }
}

Key points:

  • description field is optional
  • hooks field is required wrapper containing actual hook events
  • This is the plugin-specific format

Example:

{
  "description": "Validation hooks for code quality",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
          }
        ]
      }
    ]
  }
}

Settings Format (Direct)

For user settings in .claude/settings.json, use direct format:

{
  "PreToolUse": [...],
  "Stop": [...],
  "SessionStart": [...]
}

Key points:

  • No wrapper - events directly at top level
  • No description field
  • This is the settings format

Important: The examples below show the hook event structure that goes inside either format. For plugin hooks.json, wrap these in {"hooks": {...}}.

Hook Events

PreToolUse

Execute before any tool runs. Use to approve, deny, or modify tool calls.

Example (prompt-based):

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Validate file write safety. Check: system paths, credentials, path traversal, sensitive content. Return 'approve' or 'deny'."
        }
      ]
    }
  ]
}

Output for PreToolUse:

{
  "hookSpecificOutput": {
    "permissionDecision": "allow|deny|ask",
    "updatedInput": {"field": "modified_value"}
  },
  "systemMessage": "Explanation for Claude"
}

PostToolUse

Execute after tool completes. Use to react to results, provide feedback, or log.

Example:

{
  "PostToolUse": [
    {
      "matcher": "Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Analyze edit result for potential issues: syntax errors, security vulnerabilities, breaking changes. Provide feedback."
        }
      ]
    }
  ]
}

Output behavior:

  • Exit 0: stdout shown in transcript
  • Exit 2: stderr fed back to Claude
  • systemMessage included in context

Stop

Execute when main agent considers stopping. Use to validate completeness.

Example:

{
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion: tests run, build succeeded, questions answered. Return 'approve' to stop or 'block' with reason to continue."
        }
      ]
    }
  ]
}

Decision output:

{
  "decision": "approve|block",
  "reason": "Explanation",
  "systemMessage": "Additional context"
}

SubagentStop

Execute when subagent considers stopping. Use to ensure subagent completed its task.

Similar to Stop hook, but for subagents.

UserPromptSubmit

Execute when user submits a prompt. Use to add context, validate, or block prompts.

Example:

{
  "UserPromptSubmit": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Check if prompt requires security guidance. If discussing auth, permissions, or API security, return relevant warnings."
        }
      ]
    }
  ]
}

SessionStart

Execute when Claude Code session begins. Use to load context and set environment.

Example:

{
  "SessionStart": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
        }
      ]
    }
  ]
}

Special capability: Persist environment variables using $CLAUDE_ENV_FILE:

echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"

See examples/load-context.sh for complete example.

SessionEnd

Execute when session ends. Use for cleanup, logging, and state preservation.

PreCompact

Execute before context compaction. Use to add critical information to preserve.

Notification

Execute when Claude sends notifications. Use to react to user notifications.

Hook Output Format

Standard Output (All Hooks)

{
  "continue": true,
  "suppressOutput": false,
  "systemMessage": "Message for Claude"
}
  • continue: If false, halt processing (default true)
  • suppressOutput: Hide output from transcript (default false)
  • systemMessage: Message shown to Claude

Exit Codes

  • 0 - Success (stdout shown in transcript)
  • 2 - Blocking error (stderr fed back to Claude)
  • Other - Non-blocking error

Hook Input Format

All hooks receive JSON via stdin with common fields:

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.txt",
  "cwd": "/current/working/dir",
  "permission_mode": "ask|allow",
  "hook_event_name": "PreToolUse"
}

Event-specific fields:

  • PreToolUse/PostToolUse: tool_name, tool_input, tool_result
  • UserPromptSubmit: user_prompt
  • Stop/SubagentStop: reason

Access fields in prompts using $TOOL_INPUT, $TOOL_RESULT, $USER_PROMPT, etc.

Environment Variables

Available in all command hooks:

  • $CLAUDE_PROJECT_DIR - Project root path
  • $CLAUDE_PLUGIN_ROOT - Plugin directory (use for portable paths)
  • $CLAUDE_ENV_FILE - SessionStart only: persist env vars here
  • $CLAUDE_CODE_REMOTE - Set if running in remote context

Always use ${CLAUDE_PLUGIN_ROOT} in hook commands for portability:

{
  "type": "command",
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}

Plugin Hook Configuration

In plugins, define hooks in hooks/hooks.json:

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Validate file write safety"
        }
      ]
    }
  ],
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion"
        }
      ]
    }
  ],
  "SessionStart": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
          "timeout": 10
        }
      ]
    }
  ]
}

Plugin hooks merge with user's hooks and run in parallel.

Matchers

Tool Name Matching

Exact match:

"matcher": "Write"

Multiple tools:

"matcher": "Read|Write|Edit"

Wildcard (all tools):

"matcher": "*"

Regex patterns:

"matcher": "mcp__.*__delete.*"  // All MCP delete tools

Note: Matchers are case-sensitive.

Common Patterns

// All MCP tools
"matcher": "mcp__.*"

// Specific plugin's MCP tools
"matcher": "mcp__plugin_asana_.*"

// All file operations
"matcher": "Read|Write|Edit"

// Bash commands only
"matcher": "Bash"

Security Best Practices

Input Validation

Always validate inputs in command hooks:

#!/bin/bash
set -euo pipefail

input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

# Validate tool name format
if [[ ! "$tool_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
  echo '{"decision": "deny", "reason": "Invalid tool name"}' >&2
  exit 2
fi

Path Safety

Check for path traversal and sensitive files:

file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# Deny path traversal
if [[ "$file_path" == *".."* ]]; then
  echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
  exit 2
fi

# Deny sensitive files
if [[ "$file_path" == *".env"* ]]; then
  echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
  exit 2
fi

See examples/validate-write.sh and examples/validate-bash.sh for complete examples.

Quote All Variables

# GOOD: Quoted
echo "$file_path"
cd "$CLAUDE_PROJECT_DIR"

# BAD: Unquoted (injection risk)
echo $file_path
cd $CLAUDE_PROJECT_DIR

Set Appropriate Timeouts

{
  "type": "command",
  "command": "bash script.sh",
  "timeout": 10
}

Defaults: Command hooks (60s), Prompt hooks (30s)

Performance Considerations

Parallel Execution

All matching hooks run in parallel:

{
  "PreToolUse": [
    {
      "matcher": "Write",
      "hooks": [
        {"type": "command", "command": "check1.sh"},  // Parallel
        {"type": "command", "command": "check2.sh"},  // Parallel
        {"type": "prompt", "prompt": "Validate..."}   // Parallel
      ]
    }
  ]
}

Design implications:

  • Hooks don't see each other's output
  • Non-deterministic ordering
  • Design for independence

Optimization

1. Use command hooks for quick deterministic checks 2. Use prompt hooks for complex reasoning 3. Cache validation results in temp files 4. Minimize I/O in hot paths

Temporarily Active Hooks

Create hooks that activate conditionally by checking for a flag file or configuration:

Pattern: Flag file activation

#!/bin/bash
# Only active when flag file exists
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-strict-validation"

if [ ! -f "$FLAG_FILE" ]; then
  # Flag not present, skip validation
  exit 0
fi

# Flag present, run validation
input=$(cat)
# ... validation logic ...

Pattern: Configuration-based activation

#!/bin/bash
# Check configuration for activation
CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/plugin-config.json"

if [ -f "$CONFIG_FILE" ]; then
  enabled=$(jq -r '.strictMode // false' "$CONFIG_FILE")
  if [ "$enabled" != "true" ]; then
    exit 0  # Not enabled, skip
  fi
fi

# Enabled, run hook logic
input=$(cat)
# ... hook logic ...

Use cases:

  • Enable strict validation only when needed
  • Temporary debugging hooks
  • Project-specific hook behavior
  • Feature flags for hooks

Best practice: Document activation mechanism in plugin README so users know how to enable/disable temporary hooks.

Hook Lifecycle and Limitations

Hooks Load at Session Start

Important: Hooks are loaded when Claude Code session starts. Changes to hook configuration require restarting Claude Code.

Cannot hot-swap hooks:

  • Editing hooks/hooks.json won't affect current session
  • Adding new hook scripts won't be recognized
  • Changing hook commands/prompts won't update
  • Must restart Claude Code: exit and run claude again

To test hook changes: 1. Edit hook configuration or scripts 2. Exit Claude Code session 3. Restart: claude or cc 4. New hook configuration loads 5. Test hooks with claude --debug

Hook Validation at Startup

Hooks are validated when Claude Code starts:

  • Invalid JSON in hooks.json causes loading failure
  • Missing scripts cause warnings
  • Syntax errors reported in debug mode

Use /hooks command to review loaded hooks in current session.

Debugging Hooks

Enable Debug Mode

claude --debug

Look for hook registration, execution logs, input/output JSON, and timing information.

Test Hook Scripts

Test command hooks directly:

echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
  bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh

echo "Exit code: $?"

Validate JSON Output

Ensure hooks output valid JSON:

output=$(./your-hook.sh < test-input.json)
echo "$output" | jq .

Quick Reference

Hook Events Summary

EventWhenUse For
PreToolUseBefore toolValidation, modification
PostToolUseAfter toolFeedback, logging
UserPromptSubmitUser inputContext, validation
StopAgent stoppingCompleteness check
SubagentStopSubagent doneTask validation
SessionStartSession beginsContext loading
SessionEndSession endsCleanup, logging
PreCompactBefore compactPreserve context
NotificationUser notifiedLogging, reactions

Best Practices

DO:

  • ✅ Use prompt-based hooks for complex logic
  • ✅ Use ${CLAUDE_PLUGIN_ROOT} for portability
  • ✅ Validate all inputs in command hooks
  • ✅ Quote all bash variables
  • ✅ Set appropriate timeouts
  • ✅ Return structured JSON output
  • ✅ Test hooks thoroughly

DON'T:

  • ❌ Use hardcoded paths
  • ❌ Trust user input without validation
  • ❌ Create long-running hooks
  • ❌ Rely on hook execution order
  • ❌ Modify global state unpredictably
  • ❌ Log sensitive information

Additional Resources

Reference Files

For detailed patterns and advanced techniques, consult:

  • `references/patterns.md` - Common hook patterns (8+ proven patterns)
  • `references/migration.md` - Migrating from basic to advanced hooks
  • `references/advanced.md` - Advanced use cases and techniques

Example Hook Scripts

Working examples in examples/:

  • `validate-write.sh` - File write validation example
  • `validate-bash.sh` - Bash command validation example
  • `load-context.sh` - SessionStart context loading example

Utility Scripts

Development tools in scripts/:

  • `validate-hook-schema.sh` - Validate hooks.json structure and syntax
  • `test-hook.sh` - Test hooks with sample input before deployment
  • `hook-linter.sh` - Check hook scripts for common issues and best practices

External Resources

  • Official Docs: https://docs.claude.com/en/docs/claude-code/hooks
  • Examples: See security-guidance plugin in marketplace
  • Testing: Use claude --debug for detailed logs
  • Validation: Use jq to validate hook JSON output

Implementation Workflow

To implement hooks in a plugin:

1. Identify events to hook into (PreToolUse, Stop, SessionStart, etc.) 2. Decide between prompt-based (flexible) or command (deterministic) hooks 3. Write hook configuration in hooks/hooks.json 4. For command hooks, create hook scripts 5. Use ${CLAUDE_PLUGIN_ROOT} for all file references 6. Validate configuration with scripts/validate-hook-schema.sh hooks/hooks.json 7. Test hooks with scripts/test-hook.sh before deployment 8. Test in Claude Code with claude --debug 9. Document hooks in plugin README

Focus on prompt-based hooks for most use cases. Reserve command hooks for performance-critical or deterministic checks.

Related skills

Forks & variants (3)

Hook Development has 3 known copies in the catalog totaling 320 installs. They canonicalize to this original listing.

How it compares

Choose hook development over generic shell scripting when automation must run inside Claude Code on plugin lifecycle and tool-call events.

FAQ

Do hooks update without restarting Claude Code?

No. Hooks load at session start. Changes to hooks.json or hook scripts require restarting Claude Code (exit and run claude or cc again).

What is the difference between prompt-based and command hooks?

Prompt-based hooks use LLM reasoning for context-aware decisions (flexible, natural language logic). Command hooks execute bash scripts for deterministic checks (fast, file operations, external tools).

How do I target specific tools with hooks?

Use matchers: exact match ("Write"), multiple tools ("Read|Write|Edit"), wildcard ("*"), or regex ("mcp__.*__delete.*"). Matchers are case-sensitive.

Is Hook Development safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

AI & Agent Buildingagentsautomation

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.