| name | hook-creator |
| description | Create new Claude Code lifecycle hook (PreToolUse/PostToolUse/Stop/SessionStart) with bash + hooks.json. Triggers: create hook, lifecycle hook, PreToolUse, PostToolUse, hook event. |
| effort | high |
| disable-model-invocation | true |
| argument-hint | [hook event or description] |
| allowed-tools | Read, Write, Edit, Bash, Grep, Glob |
Hook Creator
$ARGUMENTS
Create a new Claude Code hook following ai-toolkit conventions.
Supported Hook Events
Core lifecycle
| Event | Fires When | Matcher | Typical Use |
|---|
SessionStart | Session begins, resumes, or clears | startup|resume|clear | Context injection, rules reminder |
SessionEnd | Session is closing | any | Flush logs, save transcripts |
UserPromptSubmit | User submits a prompt | any | Prompt governance, usage tracking |
Notification | Claude sends a notification | any | OS alerts, Slack pings |
MessageDisplay | Assistant message is about to be shown to the user | any | Transform or hide assistant message text before display |
Tool lifecycle
| Event | Fires When | Matcher | Typical Use |
|---|
PreToolUse | Before a tool executes | tool name (e.g. Bash) or if: rule | Safety guards, validation, "defer" for headless |
PostToolUse | After a tool executes | tool name | Feedback loops, logging, format-on-save |
PostToolUseFailure | After a tool fails | tool name | Failure telemetry, recovery hints |
PostToolBatch | After a batch of tool calls completes | any | Batch summaries, aggregate validation |
Turn lifecycle
| Event | Fires When | Matcher | Typical Use |
|---|
Stop | Claude finishes responding | any | Quality checks, session save |
StopFailure | Turn ends due to an API error (rate limit, auth) | any | Alerting, fallback behavior |
UserPromptExpansion | Claude expands or rewrites a submitted prompt | any | Prompt policy and context shaping |
Subagent lifecycle
| Event | Fires When | Matcher | Typical Use |
|---|
SubagentStart | Subagent launches | any | Observability |
SubagentStop | Subagent completes | any | Result validation |
Compaction
| Event | Fires When | Matcher | Typical Use |
|---|
PreCompact | Before context compaction; can block with exit 2 or {"decision":"block"} | any | Context preservation |
PostCompact | After compaction completes | any | Re-inject state that was summarized away |
Permissions & elicitation
| Event | Fires When | Matcher | Typical Use |
|---|
PermissionRequest | Tool awaiting permission; can return updatedInput | any | Headless approval flows |
PermissionDenied | Auto-mode classifier denied a tool call; return {retry: true} to allow retry | any | Coach the model, log denials |
Elicitation | MCP elicitation/create request arrives | any | Intercept / override MCP UI prompts |
ElicitationResult | Elicitation response ready to be sent back | any | Validate / transform elicitation replies |
Agent Teams
| Event | Fires When | Matcher | Typical Use |
|---|
TaskCreated | New task registered via TaskCreate | any | Audit, assignment routing |
TaskCompleted | Agent Teams task finished | any | Lint, type check, notify |
TeammateIdle | Agent Teams member idle | any | Completeness reminder |
Worktrees & environment
| Event | Fires When | Matcher | Typical Use |
|---|
WorktreeCreate | Worktree is being created; type: "http" can return hookSpecificOutput.worktreePath | any | Provision worktree dirs |
WorktreeRemove | Worktree is being removed | any | Cleanup |
CwdChanged | Working directory changes during a session | any | Reactive env management (e.g., direnv) |
FileChanged | Tracked file is modified on disk | any | Re-lint, reload config |
ConfigChange | Settings / config file changed | any | Re-validate, warn on drift |
Setup / bootstrap
| Event | Fires When | Matcher | Typical Use |
|---|
Setup | First-run / initialization | any | Project bootstrap |
InstructionsLoaded | CLAUDE.md / AGENTS.md loaded into context | any | Verify presence of mandatory rules |
Hook Handler Types
Claude Code supports five handler type values in hooks.json:
| Type | Purpose | Required fields |
|---|
command | Run a shell script / binary | command (path + args) |
http | Call a local or remote HTTP endpoint | url |
prompt | Inject a prompt to the fast inline model and use its verdict | prompt |
agent | Spawn a full subagent to evaluate the event (must target Stop / SubagentStop) | agent (agent name) |
mcp_tool | Invoke an MCP tool directly (no subprocess) | server, tool, arguments |
command remains the default and ai-toolkit's hook entries all use it. The other types are documented here so you can author them by hand when needed.
Workflow
- Capture intent -- ask: what should the hook do? Which lifecycle event?
- Select event -- pick from the Supported Hook Events table above
- Define matcher -- tool name for PreToolUse/PostToolUse, empty for global
- Write script -- create
app/hooks/{event-name-kebab}.sh
- Register in hooks.json -- add entry to
app/hooks.json
- Validate -- run
scripts/validate.py
Hook Script Conventions
- Location:
app/hooks/{event-name-kebab}.sh
- Shebang:
#!/bin/bash
- Header comment: script name, purpose, event, matcher
- Respect
TOOLKIT_HOOK_PROFILE env var (minimal = skip non-essential hooks)
- Always
exit 0 on success (non-zero blocks the operation for Pre* hooks)
- Output goes to Claude's context as plain text
- No external dependencies -- bash builtins and coreutils only
- Keep output concise -- hooks fire frequently
hooks.json Entry Format
{
"_source": "ai-toolkit",
"matcher": "",
"hooks": [
{
"type": "command",
"command": "\"$HOME/.softspark/ai-toolkit/hooks/{script-name}.sh\""
}
]
}
Required fields:
_source: always "ai-toolkit" (used by merge/strip logic)
matcher: tool name or regex for Pre/PostToolUse, empty string for global events
hooks[].type: "command", "http", "prompt", "agent", or "mcp_tool" (ai-toolkit uses "command")
hooks[].command: path to script using $HOME/.softspark/ai-toolkit/hooks/ prefix (for type: command)
Optional fields (read from Claude Code docs, not emitted by ai-toolkit by default):
hooks[].timeout: seconds to wait before killing the hook (global default applies if omitted)
hooks[].if: permission-rule filter (e.g. "Bash(git push*)") to reduce process spawning
hooks[].statusMessage: short message surfaced in the UI while the hook runs
Script Template
#!/bin/bash
PROFILE="${TOOLKIT_HOOK_PROFILE:-standard}"
[ "$PROFILE" = "minimal" ] && exit 0
exit 0
Rules
- MUST use one script per hook entry — no inline multi-line commands inside
hooks.json
- MUST keep
Pre* hooks fast and deterministic — they gate every matching tool call, slow hooks throttle the whole agent
- NEVER write secrets, tokens, or credentials to stdout — hook output is injected into LLM context and can be extracted
- NEVER exit non-zero from a
Post* or Stop hook unless you intend to block further processing; exit 0 is the safe default
- CRITICAL: respect the
TOOLKIT_HOOK_PROFILE env var. Profile minimal must be a no-op for non-essential hooks.
- MANDATORY: test the script standalone (
bash app/hooks/{name}.sh) before adding it to hooks.json
Gotchas
PreToolUse hooks that exit non-zero block the tool call. A slow or flaky hook (network call, lock contention) becomes a DoS against Claude's own workflow. Keep Pre hooks to pure-bash checks of local state.
- Hook output (stdout) is injected verbatim into the model's context. A hook that runs
git log --all prints hundreds of lines the model then has to wade through — be surgical, print only what matters.
- The path in
hooks.json is resolved relative to the user's machine, not the ai-toolkit repo. Use $HOME/.softspark/ai-toolkit/hooks/<name>.sh as the canonical location (installer symlinks there).
SessionStart with matcher startup|compact fires on both fresh starts AND after context compaction. Hooks that assume "new session" will mis-fire after compaction — check for explicit context markers if the distinction matters.
- Bash hooks on Windows (without WSL) will not run. If the hook must work cross-platform, wrap it in a Node or Python script and call from the bash stub — or flag the hook as
posix-only in the description.
Validation Checklist
After creating the hook:
When NOT to Use
- For a skill (slash command) — use
/skill-creator
- For an agent definition — use
/agent-creator
- For a git pre-commit hook (not a Claude Code hook) — use
/git-mastery or scripts/install_git_hooks.py
- For one-off automation that is not tied to a Claude Code event — use a plain shell script outside the toolkit
- To modify an existing toolkit hook — edit the file directly; this skill is create-only