Scaffold Claude Code hooks (PreToolUse, Stop, Notification, etc.) from a natural language description. Use when creating hooks.json, hook scripts, or event-driven automation. Triggers on /factory:hook.
Official specification: @../../references/hook-spec.md JSON template: @../../templates/hook-command.json Node.js script template: @../../templates/hook-script.js Bash script template: @../../templates/hook-script.sh
/factory:hook "description of the hook behavior"
/factory:hook "block rm -rf commands"
/factory:hook "notify on Slack when tests fail"
/factory:hook "play a sound when Claude finishes a turn"
$ARGUMENTS contains the user's description of the desired hook behavior.
If $ARGUMENTS is empty, ask: "What should this hook do? Describe the behavior in natural language."
Check if hooks already exist at the target location:
# Look for existing hooks.json in plugin context
cat <target-plugin>/hooks/hooks.json 2>/dev/null
From the description, determine:
Match the description to the most appropriate hook event:
| User Intent | Event | Matcher |
|---|---|---|
| Block/allow a tool action | PreToolUse | Tool name regex (e.g., Bash, Read|Write, Edit) |
| React after a tool runs | PostToolUse | Tool name regex |
| React to tool failure | PostToolUseFailure | Tool name regex |
| Do something when Claude finishes | Stop | — |
| Play sound/notify on permission prompt | Notification | permission_prompt, idle_prompt |
| Run something at session start | SessionStart | startup, resume |
| Validate user input | UserPromptSubmit | — |
| React to config changes | ConfigChange | Setting type |
| React to file changes | FileChanged | Filename basename |
If the intent is ambiguous, ask the user to clarify.
| Type | When to Use |
|---|---|
command | Most common. Shell script with exit code logic. Use for file checks, sound playback, notifications, blocking. |
http | POST to a URL. Use for webhooks, Slack notifications, external APIs. |
prompt | LLM evaluation. Use for nuanced decisions that need reasoning. |
agent | Subagent verification. Use for complex multi-step validation. |
PreToolUse/PostToolUse: regex matching tool names (e.g., Bash, Read\|Write\|Edit, Bash\(rm\|mv\))Notification: event type stringSessionStart: trigger typecommand type)Review what was determined in Step 2. If the description is precise enough to resolve all decisions, skip this step entirely.
Otherwise, use AskUserQuestion to ask only about decisions that remain ambiguous:
Rules:
Resolution logic:
*/plugins/<plugin-name>/* → write to plugin's hooks/ directory.claude-plugin/marketplace.json → ask which plugin to target.claude/settings.json or .claude/settings.local.json)For plugin context: generate hooks/hooks.json + scripts/<script-name>.js|sh
For standalone context: generate the hook config to be added to settings.json
# Check for existing hooks.json in plugin context
cat <target-plugin>/hooks/hooks.json 2>/dev/null
If hooks.json already exists:
If no hooks.json exists:
command type hooks:Generate/update hooks.json:
hook-command.json template as baseGenerate the script:
scripts/<descriptive-name>.js or scripts/<descriptive-name>.shhook-script.js or hook-script.sh)http type hooks:Generate hooks.json with:
{
"type": "http",
"url": "{{URL}}",
"headers": { "Content-Type": "application/json" },
"timeout": 10000
}
prompt type hooks:Generate hooks.json with:
{
"type": "prompt",
"prompt": "{{EVALUATION_PROMPT}}"
}
agent type hooks:Generate hooks.json with:
{
"type": "agent",
"agent": "{{AGENT_NAME}}",
"prompt": "{{VERIFICATION_PROMPT}}"
}
For scripts that use system commands:
| Feature | macOS | Linux | Windows |
|---|---|---|---|
| Sound | afplay | paplay / aplay | PowerShell [System.Media.SoundPlayer] |
| Notification | osascript -e 'display notification' | notify-send | PowerShell toast |
| Clipboard | pbcopy | xclip | clip.exe |
Include platform detection in scripts:
case "$(uname -s)" in
Darwin*) # macOS ;;
Linux*) # Linux ;;
MINGW*|MSYS*|CYGWIN*) # Windows ;;
esac
When Step 1.5 detected an existing hooks.json:
cat <target-plugin>/hooks/hooks.json
Parse all existing hook entries grouped by event type.
Display the existing hooks to the user:
Current hooks in <plugin-name>:
1. [PreToolUse] matcher: "Bash" → scripts/block-dangerous.sh
2. [Stop] → scripts/notify-done.js
...
Use AskUserQuestion to ask:
Based on the user's answer:
For modifications:
AskUserQuestion:
For additions:
Edit (not Write) to modify hooks.json entriesEdit to update only the affected sectionsDisplay:
Hook updated: <event-type> [<matcher>]
Location: <path-to-hooks.json>
Script: <path-to-script> (if modified)
Changes: <summary of what changed>
Display:
Hook created:
Event: <event-type>
Matcher: <matcher-pattern>
Type: <hook-type>
Location: <path-to-hooks.json>
Script: <path-to-script> (if applicable)
Exit codes: 0 = allow | 2 = block
Tip: Run /factory:audit <hooks.json-path> to validate.