hook-authoring

Hook Authoring — Official Best Practices

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "hook-authoring" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-hook-authoring

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 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

{ "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:

{ "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)

{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Use rg instead of grep" } }

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

Common Patterns

Auto-format after edits

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

Block protected files

#!/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

{ "SessionStart": [{ "matcher": "compact", "hooks": [{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run tests before commits.'" }] }] }

Notification on idle

{ "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:

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

  • Hook types comparison

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review