Claude
Skills
Sign in
Back

hook-development

Included with Lifetime
$97 forever

Implementing event hooks for PreToolUse, PostToolUse, SessionStart, and other events

General

What this skill does


# Hook Development Skill

Guide to creating event-driven hooks for Claude Code plugins.

## When to Use

Activate when:
- Creating event hooks
- User asks about automation
- Implementing validation logic
- Questions about hook events

## What Are Hooks?

Hooks are **executable scripts** that respond to events:
- PreToolUse - Before any tool execution
- PostToolUse - After tool execution
- SessionStart - Session initialization
- SessionEnd - Session cleanup
- Stop - Before session ends
- SubagentStop - When agent completes
- UserPromptSubmit - After user input
- PreCompact - Before context compression
- Notification - System notifications

**When to use hooks**:
- Automatic validation before actions
- Event-driven automation
- Logging and auditing
- Safety checks and enforcement

**When NOT to use hooks**:
- User-invoked actions → Use Commands
- Guidance and workflows → Use Skills
- Focused analysis → Use Agents

## Hook Structure

### File Location

```
plugin-name/
└── hooks/
    ├── PreToolUse.sh
    ├── PostToolUse.sh
    ├── SessionStart.sh
    └── Stop.sh
```

### Basic Hook Format

```bash
#!/bin/bash
# hooks/PreToolUse.sh

# Hook receives event data via environment variables
# Exit codes:
#   0 - Allow action to proceed
#   1 - Block action
# Output to stderr - shown to user

# Example: Block dangerous operations
if [[ "$TOOL_NAME" == "Bash" ]] && [[ "$COMMAND" == *"rm -rf"* ]]; then
  echo "⛔ Dangerous command blocked: rm -rf" >&2
  exit 1
fi

# Allow all other actions
exit 0
```

### Making Hooks Executable

**CRITICAL**: Hooks must be executable:

```bash
chmod +x hooks/PreToolUse.sh
chmod +x hooks/PostToolUse.sh
```

## Available Hooks

### PreToolUse

**Triggers**: Before any tool execution

**Use cases**:
- Validate tool arguments
- Block dangerous operations
- Enforce workflow rules
- Check prerequisites

**Environment variables**:
- `TOOL_NAME` - Tool being invoked (Bash, Read, Edit, etc.)
- `TOOL_ARGS` - Tool arguments (JSON)
- Additional tool-specific vars

**Example - Block destructive commands**:
```bash
#!/bin/bash
# hooks/PreToolUse.sh

if [[ "$TOOL_NAME" == "Bash" ]]; then
  COMMAND=$(echo "$TOOL_ARGS" | jq -r '.command')

  # Block force push to main
  if [[ "$COMMAND" == *"git push"*"--force"* ]] && [[ "$COMMAND" == *"main"* ]]; then
    echo "⛔ Force push to main blocked" >&2
    echo "This is dangerous. Use a feature branch." >&2
    exit 1
  fi
fi

exit 0
```

### PostToolUse

**Triggers**: After tool execution

**Use cases**:
- Log operations
- Trigger follow-up actions
- Update external systems
- Collect metrics

**Environment variables**:
- `TOOL_NAME` - Tool that was executed
- `TOOL_RESULT` - Tool output
- `EXIT_CODE` - Tool exit code

**Example - Log all bash commands**:
```bash
#!/bin/bash
# hooks/PostToolUse.sh

if [[ "$TOOL_NAME" == "Bash" ]]; then
  COMMAND=$(echo "$TOOL_ARGS" | jq -r '.command')
  TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

  echo "$TIMESTAMP | $COMMAND | Exit: $EXIT_CODE" >> ~/.claude/command.log
fi

exit 0
```

### SessionStart

**Triggers**: When Claude Code session begins

**Use cases**:
- Initialize environment
- Load configuration
- Check dependencies
- Set up workspace

**Example - Verify environment**:
```bash
#!/bin/bash
# hooks/SessionStart.sh

# Check required tools
MISSING=()

command -v git >/dev/null 2>&1 || MISSING+=("git")
command -v node >/dev/null 2>&1 || MISSING+=("node")
command -v jq >/dev/null 2>&1 || MISSING+=("jq")

if [ ${#MISSING[@]} -gt 0 ]; then
  echo "⚠️  Missing tools: ${MISSING[*]}" >&2
  echo "Some plugin features may not work." >&2
fi

# Load project config
if [ -f ".claude/config.json" ]; then
  echo "✓ Loaded project configuration" >&2
fi

exit 0
```

### SessionEnd

**Triggers**: When session ends normally

**Use cases**:
- Cleanup temporary files
- Save session state
- Push uncommitted work
- Generate reports

**Example - Cleanup**:
```bash
#!/bin/bash
# hooks/SessionEnd.sh

# Clean up temp files
rm -rf /tmp/claude-session-*

# Save session summary
SUMMARY_FILE="$HOME/.claude/sessions/$(date +%Y%m%d-%H%M%S).log"
echo "Session ended at $(date)" > "$SUMMARY_FILE"

exit 0
```

### Stop

**Triggers**: Before session stops (user can cancel)

**Use cases**:
- Warn about uncommitted changes
- Prompt for cleanup
- Check for running processes
- Save work in progress

**Example - Check uncommitted changes**:
```bash
#!/bin/bash
# hooks/Stop.sh

# Check for uncommitted changes
if git rev-parse --git-dir > /dev/null 2>&1; then
  if ! git diff-index --quiet HEAD --; then
    echo "⚠️  You have uncommitted changes" >&2
    echo "Consider committing or stashing before exiting." >&2

    # Don't block, just warn
    exit 0
  fi
fi

exit 0
```

### SubagentStop

**Triggers**: When a spawned agent completes

**Use cases**:
- Process agent results
- Trigger next step in workflow
- Log agent completion
- Update task status

**Environment variables**:
- `AGENT_NAME` - Agent that completed
- `AGENT_RESULT` - Agent output
- `AGENT_STATUS` - Success/failure

**Example - Log agent completion**:
```bash
#!/bin/bash
# hooks/SubagentStop.sh

TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "$TIMESTAMP | Agent: $AGENT_NAME | Status: $AGENT_STATUS" >> ~/.claude/agents.log

exit 0
```

### UserPromptSubmit

**Triggers**: After user submits a prompt

**Use cases**:
- Track user requests
- Analyze patterns
- Trigger automations
- Log interactions

**Environment variables**:
- `USER_PROMPT` - The user's input

**Example - Track prompts**:
```bash
#!/bin/bash
# hooks/UserPromptSubmit.sh

TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "$TIMESTAMP | $USER_PROMPT" >> ~/.claude/prompts.log

exit 0
```

### PreCompact

**Triggers**: Before context window compression

**Use cases**:
- Save context snapshot
- Export important data
- Preserve state
- Generate checkpoint

**Example - Save context snapshot**:
```bash
#!/bin/bash
# hooks/PreCompact.sh

SNAPSHOT_DIR="$HOME/.claude/snapshots"
mkdir -p "$SNAPSHOT_DIR"

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
SNAPSHOT_FILE="$SNAPSHOT_DIR/context-$TIMESTAMP.json"

# Save context metadata
cat > "$SNAPSHOT_FILE" <<EOF
{
  "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
  "working_directory": "$(pwd)",
  "git_branch": "$(git branch --show-current 2>/dev/null || echo 'none')",
  "git_commit": "$(git rev-parse HEAD 2>/dev/null || echo 'none')"
}
EOF

exit 0
```

### Notification

**Triggers**: System notifications

**Use cases**:
- Send alerts
- Update external systems
- Trigger integrations
- Custom notifications

**Example - Send notification**:
```bash
#!/bin/bash
# hooks/Notification.sh

# Forward to system notification
osascript -e "display notification \"$NOTIFICATION_MESSAGE\" with title \"Claude Code\""

exit 0
```

## Hook Patterns

### Validation Hook

**Purpose**: Enforce rules and prevent mistakes

```bash
#!/bin/bash
# hooks/PreToolUse.sh

# Enforce TDD: Block code changes without tests
if [[ "$TOOL_NAME" == "Edit" ]] || [[ "$TOOL_NAME" == "Write" ]]; then
  FILE_PATH=$(echo "$TOOL_ARGS" | jq -r '.file_path')

  # If editing source code
  if [[ "$FILE_PATH" == *"/src/"* ]] && [[ "$FILE_PATH" != *".test."* ]]; then
    # Check if corresponding test exists
    TEST_FILE="${FILE_PATH/.ts/.test.ts}"

    if [ ! -f "$TEST_FILE" ]; then
      echo "⛔ TDD Violation" >&2
      echo "No test file found for: $FILE_PATH" >&2
      echo "Create test first: $TEST_FILE" >&2
      exit 1
    fi
  fi
fi

exit 0
```

### Logging Hook

**Purpose**: Audit trail of operations

```bash
#!/bin/bash
# hooks/PostToolUse.sh

LOG_FILE="$HOME/.claude/audit.log"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Log all tool usage
echo "$TIMESTAMP | $TOOL_NAME | $TOOL_ARGS" >> "$LOG_FILE"

# Rotate log if too large
if [ -f "$LOG_FILE" ] && [ $(wc -l < "$LOG_FILE") -gt 10000 ]; then
  mv "$LOG_FILE" "$LOG_FILE.old"
fi

exit 0
```

### Environment Setup Hook

**Purpose**: Prepare workspace

```bash
#!/bin/bash
# hooks/SessionStart.sh

# Load environment variables
if [ -f ".env" ]; then
  

Related in General