Hook Template Generator
SkillCommunicationGenerate hook script from template. Use when adding a new hook, wiring a PreToolUse/PostToolUse/Stop/Notification hook, or scaffolding hook config for settings.json.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Hook Template Generator skill
What this skill tells your AI
The instructions your AI receives, as published by claude-world/director-mode-lite in skills/hook-template/SKILL.md and read by ahel’s review.
Generate a hook script and configuration based on requirements.
Usage: /hook-template [hook-event] [purpose]
Hook Events
Claude Code defines 30 hook events. The common ones, grouped:
| Group | Events |
|---|---|
| Lifecycle | SessionStart, SessionEnd, Setup |
| Prompt | UserPromptSubmit, UserPromptExpansion |
| Tool | PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch |
| Permission | PermissionRequest, PermissionDenied |
| Subagent | SubagentStart, SubagentStop |
| Stop | Stop, StopFailure |
| Task | TaskCreated, TaskCompleted |
| Compaction | PreCompact, PostCompact |
| Notification | Notification, MessageDisplay |
| MCP elicitation | Elicitation, ElicitationResult |
| Environment | ConfigChange, CwdChanged, FileChanged, InstructionsLoaded, WorktreeCreate, WorktreeRemove, TeammateIdle |
That is all 30 events. See the official Claude Code hooks docs for the full list and each event's payload. The templates below cover the practical 90%.
Hook Config Fields
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
type | String | No | command | command, prompt, http, mcp_tool, or agent |
command | String | If type=command | - | Shell command to execute |
prompt | String | If type=prompt | - | Natural language prompt evaluated by an LLM |
matcher | String | No | - | For tool events: tool name, regex, or * |
timeout | Integer | No | 60 | Seconds, per hook |
once | Boolean | No | false | Run hook only once per session |
if | String | No | - | Conditional guard for the hook |
statusMessage | String | No | - | Message shown while the hook runs |
type: command (default) and type: prompt (LLM-evaluated) cover most needs.
The http, mcp_tool, and agent entry types also exist for calling an
endpoint, invoking an MCP tool, or dispatching a subagent.
Hook Input (stdin JSON)
All hooks receive JSON on stdin. The event name is in hook_event_name:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"permission_mode": "ask",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file" }
}
Decision output cheatsheet
- PreToolUse: return
hookSpecificOutput.permissionDecisionofallow,deny, orask. Exit code 2 with a stderr message is the shorthand fordeny. The legacy top-level{"decision": "approve" | "block"}is still accepted. - Stop / SubagentStop: to continue the loop, return
{"decision": "block", "reason": "<next prompt>"}— the key isreason. - Silent success is exit
0with no output.
Process
-
Gather Requirements
- Hook event
- Purpose
- Matcher (for tool events: tool name, regex, or
*)
-
Generate Script at
.claude/hooks/[name].sh -
Update settings.json with hook config
-
Make Executable:
chmod +x -
Validate with
/hooks-check
Templates
PreToolUse (Blocker, exit-code shorthand)
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Block edits to specific files
if [[ "$FILE" == *"package-lock.json"* ]]; then
echo "BLOCKED: Do not edit lockfiles directly" >&2
exit 2 # exit 2 = deny for PreToolUse
fi
exit 0 # Allow (no output needed)
PreToolUse (Deny via JSON permissionDecision)
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *"package-lock.json"* ]]; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Do not edit lockfiles directly"
}
}'
exit 0
fi
# permissionDecision may be "allow", "deny", or "ask"
exit 0
PreToolUse (Context Adding)
#!/bin/bash
cat > /dev/null # Consume stdin
INFO="This file requires careful review"
jq -n --arg ctx "$INFO" '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": $ctx
}
}'
exit 0
PostToolUse (Logger)
#!/bin/bash
INPUT=$(cat)
# Process and log... (no stdout needed)
exit 0
Stop (Auto-Loop)
#!/bin/bash
CHECKPOINT=".auto-loop/checkpoint.json"
if [[ ! -f "$CHECKPOINT" ]]; then
exit 0 # Allow stop
fi
# Block stop to continue loop — key is "reason", used as the next prompt
jq -n --arg reason "Continuing iteration" \
'{"decision": "block", "reason": $reason}'
exit 0
SessionStart (Context Load)
#!/bin/bash
cat > /dev/null
echo "Loading project context..." >&2
exit 0
Prompt Hook (LLM-based)
Instead of a bash script, use type: "prompt" in settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Review the work done. Return 'approve' if complete, or 'block' with reason.",
"timeout": 30
}
]
}
]
}
}
Settings.json Format
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate.sh",
"timeout": 60
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/load-context.sh",
"once": true
}
]
}
]
}
}
Example
/hook-template PreToolUse "block edits to package-lock.json"
Creates:
- .claude/hooks/protect-lockfile.sh
- Updates .claude/settings.json
Signals
- GitHub stars
- 82
- Forks
- 11
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
hook-template- Source
- github.com/claude-world/director-mode-lite