> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/obra/superpowers/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks System

> How hooks inject context at lifecycle events

# Hooks System

Hooks are lifecycle event handlers that execute at specific points in the platform's lifecycle, allowing Superpowers to inject context, run setup scripts, or perform initialization tasks.

## What Are Hooks?

Hooks are **automatic scripts** that run when certain events occur in your AI coding platform. They enable:

* **Context Injection**: Adding information to the agent's context at session start
* **Setup Tasks**: Running initialization scripts before work begins
* **Environment Configuration**: Setting up project-specific requirements
* **Custom Workflows**: Triggering platform-specific automation

<Note>
  Hooks are **transparent to users** - they run automatically without user intervention.
</Note>

## Hook Configuration

Hooks are defined in **`hooks/hooks.json`**:

```json theme={null}
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "'${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd' session-start",
            "async": false
          }
        ]
      }
    ]
  }
}
```

### Configuration Anatomy

<Accordion title="Hook Event Types">
  Different platforms support different hook events:

  * **`SessionStart`**: Runs when a new session begins (supported by Claude Code, Cursor)
  * **`SessionResume`**: Runs when resuming a saved session
  * **`ProjectOpen`**: Runs when opening a project
  * **Custom events**: Platform-specific hooks

  Currently, Superpowers only implements **SessionStart**.
</Accordion>

<Accordion title="Matcher Pattern">
  The `matcher` field filters when hooks run:

  ```json theme={null}
  "matcher": "startup|resume|clear|compact"
  ```

  This regex matches session states:

  * `startup`: New session
  * `resume`: Resumed session
  * `clear`: Cleared session
  * `compact`: Compacted session

  The hook runs if the session state matches any of these patterns.
</Accordion>

<Accordion title="Hook Commands">
  Each hook has:

  * **`type`**: Command type (usually `"command"`)
  * **`command`**: Shell command to execute
  * **`async`**: Whether to run asynchronously (`false` = blocking)

  **Environment variables** available:

  * `${CLAUDE_PLUGIN_ROOT}`: Path to plugin root directory
  * `${CURSOR_PLUGIN_ROOT}`: Path to plugin root (Cursor)
  * Standard shell variables (`$HOME`, `$USER`, etc.)
</Accordion>

## SessionStart Hook

The SessionStart hook is the **most important hook** in Superpowers - it injects the `using-superpowers` skill content into every session.

### Hook Script: `hooks/session-start`

```bash theme={null}
#!/usr/bin/env bash
# SessionStart hook for superpowers plugin

set -euo pipefail

# Determine plugin root directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

# Check if legacy skills directory exists and build warning
warning_message=""
legacy_skills_dir="${HOME}/.config/superpowers/skills"
if [ -d "$legacy_skills_dir" ]; then
    warning_message="\n\n<important-reminder>⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in ~/.config/superpowers/skills will not be read. Move custom skills to ~/.claude/skills instead.</important-reminder>"
fi

# Read using-superpowers content
using_superpowers_content=$(cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" 2>&1 || echo "Error reading using-superpowers skill")

# Escape string for JSON embedding
escape_for_json() {
    local s="$1"
    s="${s//\\/\\\\}"  # Backslashes
    s="${s//\"/\\\"}"  # Quotes
    s="${s//$'\n'/\\n}"  # Newlines
    s="${s//$'\r'/\\r}"  # Carriage returns
    s="${s//$'\t'/\\t}"  # Tabs
    printf '%s' "$s"
}

using_superpowers_escaped=$(escape_for_json "$using_superpowers_content")
warning_escaped=$(escape_for_json "$warning_message")
session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n${using_superpowers_escaped}\n\n${warning_escaped}\n</EXTREMELY_IMPORTANT>"

# Output context injection as JSON
cat <<EOF
{
  "additional_context": "${session_context}",
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "${session_context}"
  }
}
EOF

exit 0
```

### What the Hook Does

<Steps>
  <Step title="Determine Plugin Root">
    Finds the plugin installation directory using `SCRIPT_DIR`:

    ```bash theme={null}
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
    PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
    ```
  </Step>

  <Step title="Check for Legacy Configuration">
    Detects old installation paths and warns users:

    ```bash theme={null}
    if [ -d "$legacy_skills_dir" ]; then
        warning_message="⚠️ WARNING: Move skills to new location"
    fi
    ```
  </Step>

  <Step title="Read using-superpowers Skill">
    Loads the core skill that teaches agents how to use skills:

    ```bash theme={null}
    using_superpowers_content=$(cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md")
    ```
  </Step>

  <Step title="Escape for JSON">
    Properly escapes content for JSON embedding (critical for special characters):

    ```bash theme={null}
    using_superpowers_escaped=$(escape_for_json "$using_superpowers_content")
    ```

    **Performance note**: Uses bash parameter substitution instead of character-by-character loops for 10-100x speedup.
  </Step>

  <Step title="Output JSON Context">
    Returns JSON with dual format for compatibility:

    ```json theme={null}
    {
      "additional_context": "...",
      "hookSpecificOutput": {
        "hookEventName": "SessionStart",
        "additionalContext": "..."
      }
    }
    ```

    * **Cursor**: Uses `additional_context`
    * **Claude Code**: Uses `hookSpecificOutput.additionalContext`
  </Step>
</Steps>

## Cross-Platform Hook Wrapper

The **`hooks/run-hook.cmd`** script is a **polyglot** (runs on both Windows and Unix):

```bash theme={null}
: << 'CMDBLOCK'
@echo off
REM Windows batch script portion
if "%~1"=="" (
    echo run-hook.cmd: missing script name >&2
    exit /b 1
)

set "HOOK_DIR=%~dp0"

# Try Git for Windows bash
if exist "C:\Program Files\Git\bin\bash.exe" (
    "C:\Program Files\Git\bin\bash.exe" "%HOOK_DIR%%~1"
    exit /b %ERRORLEVEL%
)

# Try bash on PATH
where bash >nul 2>nul
if %ERRORLEVEL% equ 0 (
    bash "%HOOK_DIR%%~1"
    exit /b %ERRORLEVEL%
)

# No bash found - exit silently
exit /b 0
CMDBLOCK

# Unix portion
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPT_NAME="$1"
shift
exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
```

### How the Polyglot Works

<Accordion title="Windows Execution">
  When `cmd.exe` runs the script:

  1. The `: << 'CMDBLOCK'` is ignored (`:` is a no-op in batch)
  2. Batch portion executes normally
  3. Searches for Git Bash in standard locations
  4. Falls back to `bash` on PATH
  5. If no bash found, **exits silently** (plugin still works without hooks)
</Accordion>

<Accordion title="Unix Execution">
  When bash/sh runs the script:

  1. The `: << 'CMDBLOCK'` starts a heredoc that ignores the batch portion
  2. `CMDBLOCK` closes the heredoc
  3. Unix portion executes normally
  4. Directly calls bash with the hook script
</Accordion>

<Note>
  **Why extensionless filenames?** Claude Code's Windows auto-detection prepends `bash` to any command with `.sh`, which would double-invoke bash.
</Note>

## Creating Custom Hooks

### Step 1: Define Hook in `hooks.json`

```json theme={null}
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "'${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd' my-custom-hook",
            "async": false
          }
        ]
      }
    ]
  }
}
```

### Step 2: Create Hook Script

Create **`hooks/my-custom-hook`** (no extension):

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

# Your custom logic here
echo "Running custom hook..."

# Output JSON for context injection
cat <<EOF
{
  "additional_context": "Custom context from my hook",
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Custom context"
  }
}
EOF

exit 0
```

### Step 3: Make Executable

```bash theme={null}
chmod +x hooks/my-custom-hook
```

### Step 4: Test Locally

```bash theme={null}
./hooks/run-hook.cmd my-custom-hook
```

## Hook Output Format

Hooks must output **valid JSON** to stdout:

```json theme={null}
{
  "additional_context": "<string>",
  "hookSpecificOutput": {
    "hookEventName": "<event-name>",
    "additionalContext": "<string>",
    "customField": "<any-value>"
  }
}
```

<Accordion title="Field Descriptions">
  * **`additional_context`**: Text injected into agent context (Cursor format)
  * **`hookSpecificOutput.hookEventName`**: Name of triggering event
  * **`hookSpecificOutput.additionalContext`**: Text injected into agent context (Claude Code format)
  * **Custom fields**: Platform-specific extensions
</Accordion>

<Warning>
  **Invalid JSON will cause hook failures.** Use tools like `jq` to validate:

  ```bash theme={null}
  ./hooks/run-hook.cmd session-start | jq .
  ```
</Warning>

## Platform Support

| Platform    | Hook Support | SessionStart | Custom Hooks |
| ----------- | ------------ | ------------ | ------------ |
| Claude Code | ✅ Yes        | ✅ Yes        | ✅ Yes        |
| Cursor      | ✅ Yes        | ✅ Yes        | ✅ Yes        |
| OpenCode    | ❌ No         | ✅ Via Plugin | ❌ No         |
| Codex       | ❌ No         | ❌ No         | ❌ No         |

<Note>
  **OpenCode alternative**: Uses plugin system with `experimental.chat.system.transform` hook to inject context at session start. See [Plugin System](/development/plugin-system).
</Note>

## Debugging Hooks

### Enable Verbose Logging

Add debugging to your hook script:

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

# Log to file for debugging
exec 2>>/tmp/superpowers-hook-debug.log
echo "[$(date)] Hook started" >&2

# Your hook logic...

echo "[$(date)] Hook completed" >&2
```

### Check Platform Logs

* **Claude Code**: Check debug logs via `/debug logs`
* **Cursor**: Check console in developer tools
* **Manual test**: Run hook directly: `./hooks/run-hook.cmd session-start`

### Common Issues

<Accordion title="Hook doesn't run">
  * Verify `hooks.json` syntax with `jq`
  * Check file permissions: `chmod +x hooks/session-start`
  * Ensure matcher pattern matches session state
  * Verify plugin is properly installed
</Accordion>

<Accordion title="Invalid JSON output">
  * Test JSON with: `./hooks/run-hook.cmd session-start | jq .`
  * Check for unescaped special characters
  * Verify all quotes are properly escaped
  * Ensure heredoc EOF is on its own line
</Accordion>

<Accordion title="Context not injected">
  * Verify both `additional_context` AND `hookSpecificOutput.additionalContext` are set
  * Check platform-specific field requirements
  * Test with minimal content first
  * Review platform logs for errors
</Accordion>

## Best Practices

<Steps>
  <Step title="Keep Hooks Fast">
    Hooks block session start - keep execution under 1 second:

    ```bash theme={null}
    # Add timeout for network operations
    timeout 3s git fetch origin || true
    ```
  </Step>

  <Step title="Handle Errors Gracefully">
    Don't fail the entire session if hook fails:

    ```bash theme={null}
    content=$(cat file.md 2>&1 || echo "Error: file not found")
    ```
  </Step>

  <Step title="Use Efficient String Escaping">
    Use bash parameter substitution instead of loops:

    ```bash theme={null}
    # Fast: single pass
    s="${s//\\/\\\\}"

    # Slow: character by character
    for char in $s; do ...; done
    ```
  </Step>

  <Step title="Test Cross-Platform">
    Test on both Windows and Unix:

    ```bash theme={null}
    # Unix
    bash hooks/run-hook.cmd session-start

    # Windows
    cmd /c hooks\run-hook.cmd session-start
    ```
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Architecture" icon="diagram-project" href="/development/architecture">
    Understand overall system architecture
  </Card>

  <Card title="Plugin System" icon="puzzle-piece" href="/development/plugin-system">
    Learn how plugins integrate hooks
  </Card>

  <Card title="Creating Skills" icon="wand-magic-sparkles" href="/guides/creating-skills">
    Write custom skills that hooks can inject
  </Card>

  <Card title="Contributing" icon="code-pull-request" href="/contributing">
    Contribute hook improvements
  </Card>
</CardGroup>
