---
name: enhance-hooks
description: "Use when reviewing hooks for safety, timeouts, and correct frontmatter."
version: 5.1.0
argument-hint: "[path] [--fix]"
---

# enhance-hooks

Analyze hook definitions and scripts for safety, correctness, and best practices.

## Parse Arguments

```javascript
const args = '$ARGUMENTS'.split(' ').filter(Boolean);
const targetPath = args.find(a => !a.startsWith('--')) || '.';
const fix = args.includes('--fix');
```

## Workflow

1. **Discover** - Find hook files (.md, .sh, .json)
2. **Classify** - Identify hook type and event
3. **Parse** - Extract frontmatter and script content
4. **Check** - Run all pattern checks against knowledge below
5. **Filter** - Apply certainty filtering
6. **Report** - Generate markdown output
7. **Fix** - Apply auto-fixes if --fix flag present

---

## Hook Knowledge Reference

### What Are Hooks

Hooks are automated actions triggered at specific points in a Claude Code session. They enable validation, monitoring, and control of Claude's actions through bash commands or LLM-based evaluation.

### Hook Lifecycle (Complete Reference)

Hooks fire in this sequence:

| Order | Event | Description | Matcher Required |
|-------|-------|-------------|------------------|
| 1 | `SessionStart` | Session begins or resumes | No |
| 2 | `UserPromptSubmit` | User submits a prompt | No |
| 3 | `PreToolUse` | Before tool execution (can modify/block) | Yes |
| 4 | `PermissionRequest` | When permission dialog appears | Yes |
| 5 | `PostToolUse` | After tool succeeds | Yes |
| 6 | `SubagentStart` | When spawning a subagent | No |
| 7 | `SubagentStop` | When subagent finishes | No |
| 8 | `Stop` | Claude finishes responding | No |
| 9 | `PreCompact` | Before context compaction | No |
| 10 | `SessionEnd` | Session terminates | No |
| 11 | `Notification` | Claude Code sends notifications | No |

### Hook Types

**Command Hooks** (`type: "command"`):
- Execute bash commands with full stdin/stdout control
- Available for all events

**Prompt Hooks** (`type: "prompt"`):
- Use LLM evaluation for intelligent, context-aware decisions
- **Only supported for `Stop` and `SubagentStop` events**

### Configuration Locations

| File | Location | Scope | Committed |
|------|----------|-------|-----------|
| User settings | `~/.claude/settings.json` | All projects | No |
| Project settings | `.claude/settings.json` | Current project | Yes |
| Local settings | `.claude/settings.local.json` | Current project | No |

### Configuration Structure

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/validate-bash.sh",
            "timeout": 30
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all requested tasks are complete.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

### Matcher Syntax

| Pattern | Description |
|---------|-------------|
| `Write` | Match exact tool name |
| `Edit\|Write` | Match multiple tools (regex OR) |
| `Notebook.*` | Regex pattern matching |
| `*` or `""` | Match all tools |
| (omitted) | Required for Stop, SubagentStop, UserPromptSubmit |

### Input Schema (JSON via stdin)

All hooks receive this JSON structure:

```json
{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript",
  "cwd": "/project/root",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  }
}
```

### Exit Codes

| Exit Code | Behavior |
|-----------|----------|
| 0 | Success - stdout shown to user or added as context |
| 2 | Blocking error - stderr shown, action blocked |
| Other | Non-blocking error - stderr shown in verbose mode |

### Output Schemas

**PreToolUse Decision Control:**
```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "Reason for decision",
    "updatedInput": {
      "command": "modified command"
    },
    "additionalContext": "Context for Claude"
  }
}
```

**Stop/SubagentStop Control:**
```json
{
  "decision": "block",
  "reason": "Tasks incomplete: missing test coverage"
}
```

### Environment Variables

| Variable | Description | Available In |
|----------|-------------|--------------|
| `CLAUDE_PROJECT_DIR` | Absolute path to project root | All hooks |
| `CLAUDE_CODE_REMOTE` | "true" if remote session | All hooks |
| `CLAUDE_ENV_FILE` | Path to persist env vars | SessionStart only |
| `CLAUDE_FILE_PATHS` | Space-separated file paths | PostToolUse (Write/Edit) |

### Practical Hook Examples

**Security Firewall (PreToolUse):**
```bash
#!/usr/bin/env bash
set -euo pipefail

cmd=$(jq -r '.tool_input.command // ""')

# Block dangerous patterns
if echo "$cmd" | grep -qE 'rm -rf|git reset --hard|curl.*\|.*sh'; then
  echo '{"decision": "block", "reason": "Dangerous command blocked"}' >&2
  exit 2
fi

exit 0
```

**Auto-Formatter (PostToolUse):**
```bash
#!/usr/bin/env bash
set -euo pipefail

files=$(jq -r '.tool_input.file_path // ""')

for file in $files; do
  case "$file" in
    *.py) black "$file" 2>/dev/null || true ;;
    *.js|*.ts) prettier --write "$file" 2>/dev/null || true ;;
  esac
done

exit 0
```

**Command Logger (PreToolUse):**
```bash
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
printf '%s %s\n' "$(date -Is)" "$cmd" >> .claude/bash-commands.log
exit 0
```

**Workflow Orchestration (SubagentStop - prompt type):**
```json
{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review the subagent's work. Did it complete all tasks?"
          }
        ]
      }
    ]
  }
}
```

---

## Detection Patterns

### 1. Frontmatter Validation (HIGH Certainty)

**Required:**
- YAML frontmatter with `---` delimiters
- `name` field in frontmatter
- `description` field in frontmatter

**Recommended:**
- `timeout` for command hooks (default: 30s)
- Hook type specification

**Flag:**
- Missing frontmatter delimiters
- Missing name or description

### 2. Script Safety (HIGH Certainty)

**Required Safety Patterns:**
- `set -euo pipefail` at script start
- Error handling for jq/JSON parsing
- Proper quoting of variables

**Dangerous Patterns to Flag:**

| Pattern | Risk | Certainty |
|---------|------|-----------|
| `rm -rf` | Destructive without confirmation | HIGH |
| `git reset --hard` | Data loss risk | HIGH |
| `curl \| sh` | Remote code execution | HIGH |
| `eval "$input"` | Arbitrary code execution | HIGH |
| `rm -r` | Recursive delete (may be intentional) | MEDIUM |
| `git push --force` | Force push (may be intentional) | MEDIUM |

### 3. Exit Code Handling (HIGH Certainty)

**Check:** Scripts use correct exit codes

**Flag:**
- Missing `exit 0` for success path
- Using exit code 1 for blocking (should be 2)
- No exit code at end of script

### 4. Hook Type Appropriateness (HIGH Certainty)

**Check:** Hook type matches event

**Flag:**
- Prompt hooks used for events other than Stop/SubagentStop
- Missing type specification

### 5. Lifecycle Event Appropriateness (MEDIUM Certainty)

| Event | Appropriate Use Cases |
|-------|----------------------|
| `PreToolUse` | Security validation, command blocking, input modification |
| `PostToolUse` | Formatting, logging, notifications |
| `Stop` | Completion checks, cleanup, summary |
| `SubagentStop` | Workflow orchestration, result validation |
| `SessionStart` | Environment setup, initialization |

**Flag:**
- PostToolUse hooks trying to block actions (too late)
- PreToolUse hooks doing heavy processing (should be fast)
- Prompt hooks on unsupported events

### 6. Timeout Configuration (MEDIUM Certainty)

**Guidelines:**
- Default: 30 seconds for command hooks
- Network operations: Always set explicit timeout
- External service calls: Set timeout based on expected latency

**Flag:**
- No timeout for network operations
- Timeout missing for external service calls
- Unreasonably long timeouts (>60s without justification)

### 7. Output Format (MEDIUM Certainty)

**PreToolUse Output Fields:**
- `permissionDecision`: allow, deny, or ask
- `permissionDecisionReason`: Explanation for decision
- `updatedInput`: Modified tool input (optional)
- `additionalContext`: Context for Claude (optional)

**Flag:**
- Invalid permissionDecision values
- Missing reason for deny decisions
- Malformed JSON output

### 8. Matcher Patterns (MEDIUM Certainty)

**Check:** Matcher syntax is valid

**Flag:**
- Invalid regex patterns
- Too broad matchers (`*` without justification)
- Matcher on events that don't support it (Stop, SubagentStop)

### 9. Anti-Patterns (LOW Certainty)

- Complex logic in hooks (should be simple and fast)
- Missing documentation/comments
- Hardcoded paths (should use `$CLAUDE_PROJECT_DIR`)
- Network calls without error handling
- Secrets/credentials in hook scripts

---

## Auto-Fix Implementations

### 1. Missing safety header
```bash
#!/usr/bin/env bash
set -euo pipefail
```

### 2. Missing exit code
Add `exit 0` at end of script

### 3. Missing frontmatter fields
```yaml
---
name: hook-name
description: Hook description
timeout: 30
---
```

### 4. Wrong blocking exit code
Replace `exit 1` with `exit 2` for blocking errors

---

## Output Format

```markdown
## Hook Analysis: {hook-name}

**File**: {path}
**Type**: {command|prompt|config}
**Event**: {PreToolUse|PostToolUse|Stop|...}

### Summary
- HIGH: {count} issues
- MEDIUM: {count} issues

### Frontmatter Issues ({n})
| Issue | Fix | Certainty |

### Safety Issues ({n})
| Issue | Fix | Certainty |

### Exit Code Issues ({n})
| Issue | Fix | Certainty |

### Lifecycle Issues ({n})
| Issue | Fix | Certainty |

### Output Format Issues ({n})
| Issue | Fix | Certainty |
```

---

## Pattern Statistics

| Category | Patterns | Auto-Fixable |
|----------|----------|--------------|
| Frontmatter | 3 | 2 |
| Safety | 6 | 2 |
| Exit Code | 3 | 2 |
| Hook Type | 2 | 0 |
| Lifecycle | 5 | 0 |
| Timeout | 3 | 0 |
| Output | 3 | 0 |
| Matcher | 3 | 0 |
| Anti-Pattern | 5 | 0 |
| **Total** | **33** | **6** |

---

<examples>
### Example: Missing Safety Header

<bad_example>
```bash
#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // ""')
```
**Why it's bad**: Missing `set -euo pipefail` means errors may silently pass.
</bad_example>

<good_example>
```bash
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
```
**Why it's good**: Fails fast on errors, unset variables, and pipe failures.
</good_example>

### Example: Wrong Exit Code for Blocking

<bad_example>
```bash
if [[ "$cmd" == *"rm -rf"* ]]; then
  echo "Blocked dangerous command" >&2
  exit 1  # Wrong!
fi
```
**Why it's bad**: Exit code 1 is non-blocking. Action will still proceed.
</bad_example>

<good_example>
```bash
if [[ "$cmd" == *"rm -rf"* ]]; then
  echo '{"decision": "block", "reason": "Dangerous command"}' >&2
  exit 2  # Correct blocking exit code
fi
```
**Why it's good**: Exit code 2 blocks the action. JSON output provides context.
</good_example>

### Example: Prompt Hook on Wrong Event

<bad_example>
```json
{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [{ "type": "prompt", "prompt": "Is this safe?" }]
      }
    ]
  }
}
```
**Why it's bad**: Prompt hooks only work for Stop and SubagentStop events.
</bad_example>

<good_example>
```json
{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [{ "type": "command", "command": "./validate.sh" }]
      }
    ]
  }
}
```
**Why it's good**: Command hooks work for all events.
</good_example>

### Example: Dangerous Command Pattern

<bad_example>
```bash
if echo "$cmd" | grep -q 'rm'; then
  exit 2
fi
```
**Why it's bad**: Too broad - blocks legitimate `rm file.tmp`.
</bad_example>

<good_example>
```bash
if echo "$cmd" | grep -qE 'rm\s+(-rf|-fr)\s+/'; then
  exit 2
fi
```
**Why it's good**: Specific pattern targets actual dangerous commands.
</good_example>

### Example: Hardcoded Path

<bad_example>
```bash
log_file="/home/user/project/.claude/commands.log"
```
**Why it's bad**: Hardcoded path breaks on other machines.
</bad_example>

<good_example>
```bash
log_file="$CLAUDE_PROJECT_DIR/.claude/commands.log"
```
**Why it's good**: Uses environment variable for portability.
</good_example>
</examples>

---

## Constraints

- Only apply auto-fixes for HIGH certainty issues
- Be cautious about security patterns - false negatives worse than false positives
- Never remove content, only suggest improvements
- Validate against embedded knowledge reference above
