Claude
Skills
Sign in
Back

building-hooks

Included with Lifetime
$97 forever

Expert at creating Claude Code event hooks for automation and policy enforcement. Auto-invokes when creating/updating hooks, modifying hooks.json, designing event-driven automation, or writing hook configurations.

AI Agentsscripts

What this skill does


# Building Hooks Skill

You are an expert at creating Claude Code event hooks. Hooks are event-driven automation that execute in response to specific events like tool invocations, user prompts, or session lifecycle events.

## When to Create Hooks

**Use HOOKS when:**
- You need event-driven automation
- You want to validate or block tool usage
- You need to enforce policies automatically
- You want to log or audit Claude's actions
- You need pre/post-processing for tool invocations

**Use COMMANDS instead when:**
- The user explicitly triggers an action
- You need manual invocation

**Use AGENTS/SKILLS instead when:**
- You need Claude's reasoning and generation
- The task requires LLM capabilities

## Hook Schema & Structure

### File Location
- **Project-level**: `.claude/hooks.json`
- **Project settings**: `.claude/settings.json` (hooks section)
- **Directory-specific**: `.claude-hooks.json` (in any directory)
- **Plugin-level**: `plugin-dir/hooks/hooks.json`

### File Format
JSON configuration file.

### Schema Structure

```json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "bash command to execute"
          }
        ]
      }
    ]
  }
}
```

## Event Types

### Events WITH Matchers (Tool-Specific)

**PreToolUse**: Before a tool runs
```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{"type": "command", "command": "bash validate.sh"}]
      }
    ]
  }
}
```

**PostToolUse**: After a tool completes successfully
```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{"type": "command", "command": "bash format.sh"}]
      }
    ]
  }
}
```

### Events WITHOUT Matchers (Lifecycle Events)

**UserPromptSubmit**: When user submits a prompt
```json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{"type": "command", "command": "bash log-prompt.sh"}]
      }
    ]
  }
}
```

**Stop**: When Claude finishes responding
```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [{"type": "command", "command": "bash cleanup.sh"}]
      }
    ]
  }
}
```

**SessionStart**: When session starts
```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{"type": "command", "command": "bash setup.sh"}]
      }
    ]
  }
}
```

**Other Events**:
- **Notification**: When Claude sends an alert
- **SubagentStop**: When a subagent completes
- **PreCompact**: Before transcript compaction

## Matcher Patterns

For `PreToolUse` and `PostToolUse` events:

| Pattern | Matches | Example |
|---------|---------|---------|
| `"Write"` | Exact tool name | Matches only Write tool |
| `"Edit\|Write"` | Regex OR | Matches Edit or Write |
| `"Bash"` | Single tool | Matches Bash tool |
| `"*"` | Wildcard | Matches ALL tools |
| `"Notebook.*"` | Regex pattern | Matches NotebookEdit, etc. |
| `""` | Empty (for non-tool events) | For lifecycle events |

## Hook Types

### Type 1: Command Hook

Execute a bash command:

```json
{
  "type": "command",
  "command": "bash /path/to/script.sh"
}
```

**Use for:**
- Validation scripts
- Formatting tools
- Logging and auditing
- File system operations

### Type 2: Prompt Hook (LLM-based)

Use LLM for evaluation:

```json
{
  "type": "prompt",
  "prompt": "Analyze the tool usage and determine if it's safe"
}
```

**Use for:**
- Complex policy evaluation
- Context-aware decisions
- Natural language analysis

## Hook Return Values

Hooks can return structured JSON to control behavior:

```json
{
  "continue": true,
  "decision": "approve",
  "reason": "Explanation for the decision",
  "suppressOutput": false,
  "systemMessage": "Optional message shown to user",
  "hookSpecificOutput": {
    "permissionDecision": "approve",
    "permissionDecisionReason": "Safe operation",
    "additionalContext": "Extra context for Claude"
  }
}
```

### Key Fields

- **`continue`**: `true` to proceed, `false` to stop
- **`decision`**: `"approve"`, `"block"`, or `"warn"`
- **`reason`**: Explanation for the decision
- **`suppressOutput`**: Hide hook output from transcript
- **`systemMessage`**: Message displayed to user
- **`permissionDecision`**: For tool permission hooks
- **`additionalContext`**: Context added to Claude's knowledge

### ⚠️ IMPORTANT: Event-Specific Output Formats

**`hookSpecificOutput` is ONLY valid for:**
- `PreToolUse` - use with `permissionDecision`, `updatedInput`
- `UserPromptSubmit` - use with `additionalContext`
- `PostToolUse` - use with `additionalContext`

**For SessionStart/SessionEnd hooks, use `systemMessage` instead:**
```json
// ✅ CORRECT for SessionStart/SessionEnd
{"decision": "approve", "systemMessage": "Your context here"}

// ❌ WRONG - will cause validation error
{"decision": "approve", "hookSpecificOutput": {"additionalContext": "..."}}
```

**Valid SessionStart/SessionEnd outputs:**
```json
// Simple approval
{"decision": "approve", "suppressOutput": true}

// With reason
{"decision": "approve", "reason": "Setup complete"}

// With message for Claude
{"decision": "approve", "systemMessage": "Session guidance or context"}
```

### Exit Codes

- **`0`**: Success (stdout shown in transcript mode)
- **`2`**: Blocking error (stderr fed to Claude)
- **Other**: Non-blocking error

## Common Hook Patterns

### Pattern 1: Validation Hook (PreToolUse)

Validate tool usage before execution:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/validate-write.sh"
          }
        ]
      }
    ]
  }
}
```

**Example validate-write.sh**:
```bash
#!/bin/bash
# Check if writing to protected directory

FILE_PATH="$1"

if [[ "$FILE_PATH" == /protected/* ]]; then
  echo '{"decision": "block", "reason": "Cannot write to protected directory"}'
  exit 2
fi

echo '{"decision": "approve", "reason": "Path is valid"}'
exit 0
```

### Pattern 2: Formatting Hook (PostToolUse)

Auto-format files after writing:

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/format-file.sh"
          }
        ]
      }
    ]
  }
}
```

**Example format-file.sh**:
```bash
#!/bin/bash
FILE_PATH="$1"

if [[ "$FILE_PATH" == *.py ]]; then
  black "$FILE_PATH"
elif [[ "$FILE_PATH" == *.js ]]; then
  prettier --write "$FILE_PATH"
fi

exit 0
```

### Pattern 3: Logging Hook (All Tools)

Log all tool usage:

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/log-tool.sh"
          }
        ]
      }
    ]
  }
}
```

### Pattern 4: Security Hook (Bash Commands)

Validate bash commands for security:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/validate-bash.sh"
          }
        ]
      }
    ]
  }
}
```

**Example validate-bash.sh**:
```bash
#!/bin/bash
COMMAND="$1"

# Block dangerous commands
if echo "$COMMAND" | grep -qE "rm -rf /|dd if="; then
  echo '{"decision": "block", "reason": "Dangerous command detected"}'
  exit 2
fi

echo '{"decision": "approve"}'
exit 0
```

### Pattern 5: Session Setup Hook

Initialize environment on session start:

```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/setup-session.sh"
          }
        ]
      }
    ]
  }
}
```

**Example setup-session.sh**:
```bash
#!/bin/bash
# Load environment, start services, etc.
export PROJECT_ROOT=$(pwd)
echo "Session initialized for project: $PROJECT_ROOT"
exit 0
```

## Creating Hooks

### Step 1: Identify the Need
Ask the user:
1. What

Related in AI Agents