<< All versions

Skill v1.0.2

currentAutomated scan100/100
softspark/ai-toolkit/hook-creator

3 files

──Details
PublishedOctober 1, 2026 at 08:13 PM
Content Hashsha256:db9574375262684a...
Git SHA0f09fecb3f39
Bump Typepatch
Compare with v1.0.1
──Files
Files (1 file, 12.2 KB)
SKILL.md12.2 KBactive
SKILL.md · 236 lines · 12.2 KB

version: "1.0.2" 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

EventFires WhenMatcherTypical Use
SessionStartSession begins, resumes, or clears`startup\resume\clear`Context injection, rules reminder
SessionEndSession is closinganyFlush logs, save transcripts
UserPromptSubmitUser submits a promptanyPrompt governance, usage tracking
NotificationClaude sends a notificationanyOS alerts, Slack pings
MessageDisplayAssistant message text streams in completed-line batchesnoneReturn displayContent to replace the rendered batch without changing the transcript

Tool lifecycle

EventFires WhenMatcherTypical Use
PreToolUseBefore a tool executestool name (e.g. Bash) or if: ruleSafety guards, validation, "defer" for headless
PostToolUseAfter a tool executestool nameFeedback loops, logging, format-on-save
PostToolUseFailureAfter a tool failstool nameFailure telemetry, recovery hints
PostToolBatchAfter a batch of tool calls completesanyBatch summaries, aggregate validation

Turn lifecycle

EventFires WhenMatcherTypical Use
StopClaude finishes respondinganyQuality checks, session save
StopFailureTurn ends due to an API error (rate limit, auth)anyAlerting, fallback behavior
UserPromptExpansionClaude expands or rewrites a submitted promptanyPrompt policy and context shaping

Subagent lifecycle

EventFires WhenMatcherTypical Use
SubagentStartSubagent launchesanyObservability
SubagentStopSubagent completesanyResult validation

Compaction

EventFires WhenMatcherTypical Use
PreCompactBefore context compaction; can block with exit 2 or {"decision":"block"}anyContext preservation
PostCompactAfter compaction completesanyRe-inject state that was summarized away

Model changes

EventFires WhenMatcherTypical Use
PreModelSwitchBefore a user/client model switch; exit 2 or decision: "block" cancels itcanonical target modelModel policy; requires Claude Code 2.1.251+
PostModelSwitchAfter the session model changes; cannot blockcanonical target modelModel-specific context; requires Claude Code 2.1.251+

Permissions & elicitation

EventFires WhenMatcherTypical Use
PermissionRequestTool awaiting permission; can return updatedInputanyHeadless approval flows
PermissionDeniedAuto-mode classifier denied a tool call; return {retry: true} to allow retryanyCoach the model, log denials
ElicitationMCP elicitation/create request arrivesanyIntercept / override MCP UI prompts
ElicitationResultElicitation response ready to be sent backanyValidate / transform elicitation replies

Agent Teams

EventFires WhenMatcherTypical Use
TaskCreatedNew task registered via TaskCreateanyAudit, assignment routing
TaskCompletedAgent Teams task finishedanyLint, type check, notify
TeammateIdleAgent Teams member idleanyCompleteness reminder

Worktrees & environment

EventFires WhenMatcherTypical Use
WorktreeCreateWorktree is being created; type: "http" can return hookSpecificOutput.worktreePathanyProvision worktree dirs
WorktreeRemoveWorktree is being removedanyCleanup
CwdChangedWorking directory changes during a sessionanyReactive env management (e.g., direnv)
DirectoryAddedA working directory was added with /add-dir or SDK register_repo_root; runs asynchronously after the add and cannot block it`slash_command\register_repo_root`Prepare a newly added repository
FileChangedTracked file is modified on diskanyRe-lint, reload config
ConfigChangeSettings / config file changedanyRe-validate, warn on drift

Setup / bootstrap

EventFires WhenMatcherTypical Use
SetupFirst-run / initializationanyProject bootstrap
InstructionsLoadedCLAUDE.md or .claude/rules/*.md loaded into contextload reasonVerify presence of mandatory rules

Hook Handler Types

Claude Code supports five handler type values in hooks.json:

TypePurposeRequired fields
commandRun a shell script / binarycommand (path + args)
httpCall a local or remote HTTP endpointurl
promptInject a prompt to the fast inline model and use its verdictprompt
agentSpawn an experimental tool-using subagent to evaluate the eventprompt
mcp_toolInvoke an already-connected MCP toolserver, tool; optional input object

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.

PermissionRequest accepts prompt hooks but skips agent hooks. Both model-switch events accept command, http, and mcp_tool only. MCP tool hooks use input for arguments; arguments is not a supported handler field. MCP hooks on SessionStart cannot run at launch before servers connect, and Setup always skips them. Use command hooks for required initialization.

Source: https://code.claude.com/docs/en/hooks (reviewed 2026-09-23).

Workflow

  1. Capture intent -- ask: what should the hook do? Which lifecycle event?
  2. Select event -- pick from the Supported Hook Events table above
  3. Define matcher -- tool name for PreToolUse/PostToolUse, empty for global
  4. Write script -- create app/hooks/{event-name-kebab}.sh
  5. Register in hooks.json -- add entry to app/hooks.json
  6. 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)
  • Exit 0 on success; use exit 2 for a blocking verdict on supported events. Other non-zero codes generally report an error without blocking.
  • 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

json
{
"_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):

Common to every handler type:

  • hooks[].if: one permission-rule filter (e.g. "Bash(git push*)"); evaluated only for tool events
  • hooks[].timeout: seconds to wait before canceling the handler (type and event defaults apply if omitted)
  • hooks[].statusMessage: short message surfaced in the UI while the hook runs
  • hooks[].once: run once per session; only honored in skill frontmatter and ignored in settings files or agent frontmatter

Command-handler fields:

  • hooks[].args: argument vector for exec form. Prefer exec form when a command uses ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT}, or ${CLAUDE_PLUGIN_DATA} path placeholders
  • hooks[].async: run in the background without blocking; background hooks cannot return decisions
  • hooks[].asyncRewake: run in the background and wake Claude on exit code 2; implies async
  • hooks[].shell: choose bash or powershell for shell form; ignored when args selects exec form

Prompt and agent handlers both require hooks[].prompt; type: agent does not accept an agent name field. Agent handlers are experimental, so prefer command for production enforcement.

MessageDisplay has no matcher, runs once per rendered batch in interactive sessions, and defaults to a 10-second timeout. Its displayContent output changes only the screen text; Claude and the transcript retain the original response. DirectoryAdded is always asynchronous and non-blocking regardless of the handler configuration.

Script Template

bash
#!/bin/bash
# {script-name}.sh — {One-line purpose}.
#
# Fires on: {EventName}
# Matcher: {matcher or "all"}
# Skipped when TOOLKIT_HOOK_PROFILE=minimal.
PROFILE="${TOOLKIT_HOOK_PROFILE:-standard}"
[ "$PROFILE" = "minimal" ] && exit 0
# --- Hook logic here ---
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:

  • [ ] Script exists in app/hooks/ and is executable (chmod +x)
  • [ ] Entry added to app/hooks.json with _source: "ai-toolkit"
  • [ ] Event name matches a supported lifecycle event
  • [ ] scripts/validate.py passes
  • [ ] Script runs without errors: bash app/hooks/{name}.sh
  • [ ] Hook count in README.md and docs updated if needed

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
← v1.0.1All versions