Skip to content
Back to skills

Hook Authoring

ASecurity

Creates and configures Claude Code hooks for lifecycle automation. Covers all 17 hook events, 4 hook types (command, prompt, agent, http), matchers, input/output formats, and exit codes. Follows official Anthropic best practices. USE WHEN: user mentions "hook", "hooks", "auto-format", "pre tool use", "post tool use", "session start", "notification hook", "block command", "validate tool", "lifecycle event", "PostToolUse", "PreToolUse" DO NOT USE FOR: creating skills - use `skill-authoring`; ...

  • 31 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
ai-agentspythonshellbashnodegit

Works with

  • claude code

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 8, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill hook-authoring --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hook Authoring?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hook Authoring
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-hook-authoring/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-hook-authoring)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hook-authoring
description: |
  Creates and configures Claude Code hooks for lifecycle automation. Covers all
  17 hook events, 4 hook types (command, prompt, agent, http), matchers,
  input/output formats, and exit codes. Follows official Anthropic best practices.

  USE WHEN: user mentions "hook", "hooks", "auto-format", "pre tool use",
  "post tool use", "session start", "notification hook", "block command",
  "validate tool", "lifecycle event", "PostToolUse", "PreToolUse"

  DO NOT USE FOR: creating skills - use `skill-authoring`;
  creating agents - use `agent-authoring`; webhook endpoints - different concept
allowed-tools: Read, Grep, Glob, Write, Edit
---
# Hook Authoring — Official Best Practices

## What Hooks Do

Hooks are deterministic shell commands (or LLM prompts) that execute at specific lifecycle points. They provide guaranteed behavior — not relying on the LLM to choose to run them.

## Configuration Locations

| Location | Scope | Shareable |
|----------|-------|-----------|
| `~/.claude/settings.json` | All projects | No |
| `.claude/settings.json` | Single project | Yes (commit) |
| `.claude/settings.local.json` | Single project | No (gitignored) |
| Agent/skill frontmatter | While component active | Yes |
| Plugin `hooks/hooks.json` | When plugin enabled | Yes |

## Hook Types

| Type | How it works | Use when |
|------|-------------|----------|
| `command` | Runs shell command, reads stdin JSON, uses exit codes | Deterministic validation, formatting, logging |
| `prompt` | Single-turn LLM call, returns `{ok, reason}` | Judgment-based decisions without tool access |
| `agent` | Multi-turn subagent with tool access | Verification requiring file reads or commands |
| `http` | POSTs event data to URL endpoint | External service integration, audit logging |

## Hook Events

See [quick-ref/events-reference.md](quick-ref/events-reference.md) for full input/output schemas.

| Event | Matcher input | Can block? | Common use |
|-------|--------------|------------|------------|
| `SessionStart` | startup/resume/clear/compact | No | Re-inject context after compaction |
| `UserPromptSubmit` | (none) | Yes | Validate/transform user input |
| `PreToolUse` | Tool name | Yes | Block commands, validate operations |
| `PermissionRequest` | Tool name | Yes | Auto-allow/deny permissions |
| `PostToolUse` | Tool name | No* | Auto-format files, logging |
| `PostToolUseFailure` | Tool name | No | Error handling |
| `Notification` | Notification type | No | Desktop alerts |
| `SubagentStart` | Agent type | No | Setup before agent runs |
| `SubagentStop` | Agent type | No | Cleanup after agent |
| `Stop` | (none) | Yes | Verify completeness |
| `ConfigChange` | Config source | Yes | Audit, block unauthorized changes |
| `PreCompact` | manual/auto | No | Save context before compaction |
| `SessionEnd` | Exit reason | No | Cleanup |

*PostToolUse `Stop` hooks can return `{"decision": "block"}` to keep Claude working.

## Configuration Format

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

## Input/Output Protocol

### Input (stdin JSON)
Every hook receives JSON on stdin with common fields + event-specific data:
```json
{
  "session_id": "abc123",
  "cwd": "/path/to/project",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" }
}
```

### Output (exit codes)
| Exit code | Effect |
|-----------|--------|
| `0` | Allow — action proceeds. Stdout added to context (SessionStart, UserPromptSubmit) |
| `2` | Block — action cancelled. Stderr sent to Claude as feedback |
| Other | Allow — stderr logged (visible in verbose mode Ctrl+O) |

### Structured JSON output (exit 0 + JSON on stdout)
```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep"
  }
}
```

PreToolUse decisions: `"allow"`, `"deny"`, `"ask"`.

## Common Patterns

### Auto-format after edits
```json
{
  "PostToolUse": [{
    "matcher": "Edit|Write",
    "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
  }]
}
```

### Block protected files
```bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "package-lock.json" ".git/"; do
  if [[ "$FILE" == *"$pattern"* ]]; then
    echo "Blocked: matches protected pattern '$pattern'" >&2
    exit 2
  fi
done
exit 0
```

### Re-inject context after compaction
```json
{
  "SessionStart": [{
    "matcher": "compact",
    "hooks": [{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run tests before commits.'" }]
  }]
}
```

### Notification on idle
```json
{
  "Notification": [{
    "matcher": "",
    "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"'" }]
  }]
}
```

## Stop Hook Infinite Loop Prevention

Always check `stop_hook_active` to avoid loops:
```bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Let Claude stop
fi
# ... your logic
```

## Anti-Patterns

| Anti-Pattern | Fix |
|--------------|-----|
| Shell profile `echo` breaks JSON | Wrap in `if [[ $- == *i* ]]` |
| Stop hook without loop guard | Check `stop_hook_active` field |
| Using PostToolUse to undo actions | Too late — use PreToolUse to block instead |
| Relying on PermissionRequest in headless mode | Doesn't fire in `-p` mode. Use PreToolUse |

## Checklist

- [ ] Correct event chosen for the use case
- [ ] Matcher pattern tested (case-sensitive, regex)
- [ ] Script is executable (`chmod +x`)
- [ ] Uses `jq` for JSON parsing (or Python/Node)
- [ ] Exit code 2 for blocking, 0 for allowing
- [ ] Stop hooks check `stop_hook_active`
- [ ] Tested with sample JSON piped to stdin
- [ ] Hook script uses absolute paths or `$CLAUDE_PROJECT_DIR`

## Reference
- [All events with input/output schemas](quick-ref/events-reference.md)
- [Hook types comparison](quick-ref/hook-types.md)

Files in this skill

  • SKILL.md6.2 KB
  • quick-ref/events-reference.md3.8 KB
  • quick-ref/hook-types.md3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.