agentsclimarketplace

Plugin hooks

Skill viktorbezdek/skillstack/plugin-dev/skills/plugin-hooks

Authoritative guide to Claude Code hooks — event-driven scripts that execute before or after tool calls, session events, file changes, and more. Use when writing a PreToolUse hook to block dangerous commands, a PostToolUse hook to auto-format after edits, a SessionStart hook to inject context, a Stop hook for session loops, a Notification hook for desktop alerts, a FileChanged hook for reactive environments, a WorktreeCreate hook for custom worktree provisioning, or current documented hook events. Covers handler types (command, http, mcp_tool, prompt, agent), matcher syntax (exact/OR-list/regex), exit code semantics, and JSON output schema. NOT for designing hook script content for a specific domain (use the domain skill) — this skill covers hook mechanics and authoring only.From its SKILL.md

Install
npx -y skills add viktorbezdek/skillstack --skill plugin-hooks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 10 stars10 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.1 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

Plugin Hooks

The most powerful and most trap-laden extension type. Exit code 1 does not block — only exit code 2 blocks. Matcher syntax has three modes. updatedInput replaces the entire tool input object, not just one field. These three facts cause most hook bugs. Learn them first.

Authoritative source: https://code.claude.com/docs/en/hooks


When to use this skill

  • Writing any hook configuration (hooks/hooks.json, settings.json)
  • Debugging "my hook isn't blocking" or "my hook isn't firing"
  • Choosing between hook event types for a specific capability
  • Understanding exit code behavior (exit 1 vs exit 2 distinction)
  • Designing safe auto-approve flows for PermissionRequest
  • Setting up file-watching or cwd-change hooks

When NOT to use this skill

  • What hook script logic to write (domain-specific) → use the domain skill
  • MCP server authoring → use plugin-architecture or mcp-server
  • Full plugin structure → use plugin-composition

Core principles

  1. Exit code 1 does not block. Only exit code 2 blocks a tool call, prompt, or stop. Exit 1 is a non-blocking error — the action proceeds. This is the #1 source of hook bugs.
  2. Matcher syntax has three modes. * or omitted = match all. Letters/digits/underscore/pipe only = exact string or |-separated list. Any other character = JavaScript regex. mcp__memory is an exact match (matches no real tool); use mcp__memory__.* for regex.
  3. updatedInput replaces the entire tool input. In PreToolUse, returning updatedInput in hookSpecificOutput replaces ALL fields. You must echo back unchanged fields or they are lost.
  4. Async hooks cannot block. If async: true, the hook runs after the action has already proceeded. Never use async for security/guardrail hooks.

Core events (most common in practice)

The full documented event set changes over time. references/hook-event-reference.md tracks the current catalog; this section covers the events practitioners reach for most often.

PreToolUse

Fires before any tool call. Can block, modify input, or allow. The most powerful event.

{
  "matcher": "Bash",
  "hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check.sh"}]
}

Input includes: tool_name, tool_input (full schema), tool_use_id.

Decision output:

{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Blocked"}}

Precedence when multiple hooks run: deny > defer > ask > allow.

PostToolUse

Fires after a tool succeeds. Cannot block. Common use: auto-format, logging, notifications.

{"matcher": "Edit|Write", "hooks": [{"type": "command", "async": true, "command": "prettier --write \"$file_path\""}]}

PermissionRequest

Fires when a permission dialog would appear. Can auto-approve or deny without showing the dialog.

{"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}}

UserPromptSubmit

Fires before Claude processes a user prompt. Can block or add context. No matcher support — fires on every prompt.

SessionStart

Fires at session start, resume, clear, or compact. Can inject context Claude will see.

{"matcher": "compact", "hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/inject-context.sh"}]}

Exit 0 stdout is added to Claude's context for SessionStart.

Stop

Fires when Claude finishes responding. Can prevent Claude from stopping to continue working.

⚠️ Infinite loop trap: if your Stop hook always signals "keep going", Claude loops forever. Check stop_hook_active in input:

input=$(cat)
if echo "$input" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get('stop_hook_active') else 1)" 2>/dev/null; then
    exit 0  # already running a stop hook, don't loop
fi

Notification

Fires for: permission_prompt, idle_prompt, auth_success, elicitation_dialog. Common use: desktop notifications.

FileChanged

Fires when a watched file changes on disk. The matcher both builds the watch list (literal filenames split by |) and filters which handlers run. Example watching .envrc:

{"matcher": ".envrc", "hooks": [{"type": "command", "command": "direnv export bash >> \"$CLAUDE_ENV_FILE\""}]}

SubagentStop

Fires when a subagent finishes. Useful for aggregating subagent results.

WorktreeCreate

Fires when a worktree is created. Any non-zero exit fails worktree creation. The hook must print the absolute path of the created worktree to stdout:

echo "/absolute/path/to/worktree"

Hook handler types (quick reference)

TypeUse whenKey fields
commandShell script, full OS accesscommand, async, shell, timeout
httpRemote service, shared stateurl, headers, allowedEnvVars, timeout
mcp_toolDelegate the hook decision to a configured MCP toolserver, tool, arguments, timeout
promptLLM evaluationprompt (use $ARGUMENTS), model, timeout (30s default)
agentMulti-step tool-using verificationprompt (use $ARGUMENTS), model, timeout (60s default)

See references/hook-handler-types.md for full schemas and examples.


Matcher syntax (quick reference)

ValueInterpreted as
*, "", or omittedMatch all — fires on every occurrence
Letters/digits/_/| onlyExact string or |-separated list: Bash, Edit|Write
Any other characterJavaScript regex: ^Notebook, mcp__memory__.*

Common mistake: mcp__memory (underscores only) is evaluated as an exact string. It never matches any real tool because real tools are mcp__memory__create_entities etc. Use mcp__memory__.* (has .*) for regex.


Exit code contract

Exit 0  → success; parse stdout for JSON output
Exit 2  → blocking error; ignore stdout; use stderr as the error message
Exit 1  → NON-BLOCKING error; the action proceeds anyway; stderr shown in transcript
Timeout → treated as non-blocking error

Not every event can be blocked. See references/hook-event-reference.md for the per-event blocking table.


Plugin hooks configuration

Hooks ship with a plugin in hooks/hooks.json at the plugin root. Example:

{
  "description": "Format TypeScript files after edits",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"}]
      }
    ]
  }
}

Use ${CLAUDE_PLUGIN_ROOT} for paths to scripts bundled with the plugin. Use ${CLAUDE_PLUGIN_DATA} for state that should survive plugin updates.


References

FileContents
references/hook-event-reference.mdCurrent hook event catalog with schemas, blocking behavior, matcher semantics
references/hook-handler-types.mdCommand, http, mcp_tool, prompt, agent — full schemas, examples, security
references/hook-anti-patterns.mdExit code 1 trap, infinite Stop loops, shell profile pollution, partial updatedInput, and 10+ more
references/hook-testing-patterns.md/hooks menu, debug log, test_hook.sh usage, stdin replay

Plugin-Dev Authoring Toolkit by Viktor Bezdek — licensed under MIT.

What ships with it: 6 files

42.9 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,452. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.