Claude Code Plugins

Community-maintained marketplace

Feedback
1
0

Create event handler hooks for Claude Code plugins that respond to tool use, sessions, and notifications. Use when creating hooks, event handlers, or automation for plugins.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name hook-creator
description Create event handler hooks for Claude Code plugins that respond to tool use, sessions, and notifications. Use when creating hooks, event handlers, or automation for plugins.

Hook Creator

Create event handler hooks for Claude Code plugins that execute automatically at specific points in the workflow.

When to Use This Skill

Use when:

  • Creating hooks for plugins
  • User requests "create a hook" or "add event handler"
  • Automating responses to tool use
  • Setting up validation or formatting workflows

What Hooks Are

Hooks are shell commands that execute automatically at various points in Claude Code's lifecycle:

  • PreToolUse: Before tool calls (can block them)
  • PostToolUse: After tool calls complete
  • UserPromptSubmit: When user submits prompt
  • Notification: When notifications are sent
  • Stop: When Claude finishes responding
  • SubagentStop: When subagents complete
  • PreCompact: Before compaction operations
  • SessionStart: When session starts/resumes
  • SessionEnd: When session ends

Hook Configuration Format

Hooks are configured in hooks/hooks.json:

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

For plugins, hooks can also be defined inline in plugin.json.

Instructions

Step 1: Identify Hook Trigger

Determine which event should trigger the hook:

Tool-related:

  • PreToolUse - Before tools execute (can block)
  • PostToolUse - After tools complete

Session-related:

  • SessionStart - At session beginning
  • SessionEnd - At session end

User interaction:

  • UserPromptSubmit - When user submits prompt
  • Notification - When notifications sent

Workflow:

  • Stop - When Claude stops
  • SubagentStop - When subagent stops
  • PreCompact - Before compaction

Step 2: Define Matcher (For Tool Hooks)

For PreToolUse and PostToolUse:

Specify which tools to match:

  • Exact: "Write" matches only Write tool
  • Multiple: "Edit|Write" matches Edit or Write
  • Pattern: "Notebook.*" matches NotebookEdit, etc.
  • All: "*" or "" matches all tools

For other events: Matcher can be omitted or used for event subtypes.

Step 3: Create Hook Command

Write shell command or script to execute:

Simple command:

{
  "type": "command",
  "command": "echo 'Tool used' >> log.txt"
}

Script execution:

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

With timeout:

{
  "type": "command",
  "command": "python script.py",
  "timeout": 30
}

Step 4: Create Hook Configuration

Add to hooks/hooks.json:

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
          "timeout": 30
        }
      ]
    }
  ]
}

Step 5: Handle Hook Input/Output

Input: Hooks receive JSON via stdin

Exit codes:

  • 0: Success (stdout shown to user in transcript mode)
  • 2: Blocking error (stderr sent to Claude)
  • Other: Non-blocking error (stderr to user)

JSON output: For advanced control

{
  "decision": "approve"|"block",
  "reason": "Explanation",
  "continue": false,
  "stopReason": "Message to user"
}

Step 6: Use Environment Variables

Available variables:

  • ${CLAUDE_PLUGIN_ROOT} - Plugin directory path
  • ${CLAUDE_PROJECT_DIR} - Project root directory
  • ${CLAUDE_ENV_FILE} - (SessionStart only) File for env vars

Example:

{
  "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check.py"
}

Hook Event Reference

PreToolUse

When: Before tool parameters are processed

Use for:

  • Validate tool inputs
  • Block dangerous operations
  • Auto-approve safe operations
  • Modify tool parameters

Exit code 2 behavior: Blocks tool, shows stderr to Claude

Example:

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "python scripts/validate_bash.py"
        }
      ]
    }
  ]
}

PostToolUse

When: After tool completes successfully

Use for:

  • Format generated code
  • Run linters
  • Log operations
  • Provide feedback

Exit code 2 behavior: Shows stderr to Claude (tool already ran)

Example:

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format-code.sh"
        }
      ]
    }
  ]
}

SessionStart

When: Session starts or resumes

Use for:

  • Load development context
  • Install dependencies
  • Set environment variables
  • Initialize state

Special: Can persist env vars via ${Claude_ENV_FILE}

Example:

{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh"
        }
      ]
    }
  ]
}

Other Events

UserPromptSubmit: Validate/augment user prompts

Notification: Custom notifications

Stop/SubagentStop: Prevent premature stopping

PreCompact: Pre-compaction operations

SessionEnd: Cleanup and logging

Prompt-Based Hooks

For Stop and SubagentStop, hooks can use LLM evaluation:

{
  "Stop": [
    {
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Evaluate if Claude should stop: $ARGUMENTS",
          "timeout": 30
        }
      ]
    }
  ]
}

LLM responds with decision JSON automatically.

Example Hooks

Auto-Format Code

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "jq -r '.tool_input.file_path' | { read file; if echo \"$file\" | grep -q '\\.ts$'; then npx prettier --write \"$file\"; fi; }"
        }
      ]
    }
  ]
}

Validate Bash Commands

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate_bash.py",
          "timeout": 10
        }
      ]
    }
  ]
}

Load Session Context

{
  "SessionStart": [
    {
      "matcher": "startup",
      "hooks": [
        {
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/load_context.sh"
        }
      ]
    }
  ]
}

Log Commands

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "jq -r '.tool_input.command' >> ~/.claude/bash-log.txt"
        }
      ]
    }
  ]
}

Plugin Hooks

Plugin hooks in hooks/hooks.json or inline in plugin.json:

File-based:

{
  "hooks": "./hooks/hooks.json"
}

Inline:

{
  "hooks": {
    "PostToolUse": [...]
  }
}

With description:

{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [...]
  }
}

Best Practices

  1. Use appropriate events: Choose the right trigger point
  2. Handle errors gracefully: Exit codes and stderr
  3. Set reasonable timeouts: Default 60s, adjust as needed
  4. Use environment variables: ${Claude_PLUGIN_ROOT} for portability
  5. Test thoroughly: Verify hooks work as expected
  6. Document behavior: Explain what hooks do
  7. Avoid blocking operations: Keep hooks fast

Security Considerations

IMPORTANT:

  • Hooks execute with your credentials
  • Malicious hooks can exfiltrate data
  • Always review hook code before adding
  • Validate inputs in hook scripts
  • Use restricted permissions where possible

Troubleshooting

Hook not executing:

  • Check event name is correct
  • Verify matcher pattern
  • Ensure script is executable
  • Check command path
  • Review timeout setting

Hook blocks incorrectly:

  • Check exit code logic
  • Verify stderr output
  • Test hook script standalone
  • Review matcher specificity

Hook too slow:

  • Reduce timeout
  • Optimize script
  • Move to background if possible
  • Consider async operations

Reference

For complete hook documentation, see references/hook-format.md.