Source profileQuality 93/100Review permissions

Jamie-BitFlight/claude_skills/plugins/plugin-creator/skills/hooks-io-api/SKILL.md

hooks-io-api

Hook JSON input/output API reference — what data hooks receive via stdin and what JSON they can return to control Claude Code behavior. Use when writing hook scripts, checking exit code behavior, building JSON output for PreToolUse permissions, or understanding event-specific input schemas.

Source repository stars
64
Declared platforms
1
Static risk flags
1
Last source update
2026-08-28
Source checked
2026-08-28

Decision brief

What it does: where it fits

JSON schemas for hook stdin input and stdout output per event. For hook system fundamentals, activate Skill(skill: "plugin-creator:hooks-core-reference"). For working examples, activate Skill(skill: "plugin-creator:hooks-patterns").

Best for

  • Use when writing hook scripts, checking exit code behavior, building JSON output for PreToolUse permissions, or understanding event-specific input schemas.

Not for

  • Tasks that require unconfirmed production actions or broad system permissions.
  • Environments where the pinned source and install steps cannot be inspected.

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeDeclaredSource recordInstall path and trigger
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/Jamie-BitFlight/claude_skills --skill "plugins/plugin-creator/skills/hooks-io-api"
Safe inspection promptEditorial

Inspect the Agent Skill "hooks-io-api" from https://github.com/Jamie-BitFlight/claude_skills/blob/a00194f25fec502d3d659b7d610369614967251e/plugins/plugin-creator/skills/hooks-io-api/SKILL.md at commit a00194f25fec502d3d659b7d610369614967251e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Setup Input

    trigger: "init" (from --init or --init-only) or "maintenance" (from --maintenance)

    trigger: "init" (from --init or --init-only) or "maintenance" (from --maintenance)Has access to CLAUDEENVFILE for persisting environment variables- trigger: "init" (from --init or --init-only) or "maintenance" (from --maintenance) - Has access to CLAUDEENVFILE for persisting environment variables
  2. 02

    Setup / SessionStart JSON Output

    additionalContext: added to Claude's context at the start of the conversation

    additionalContext: added to Claude's context at the start of the conversationinitialUserMessage: SessionStart only — used as the first user message in non-interactive (-p) modewatchPaths: SessionStart only — array of absolute paths to watch for FileChanged events
  3. 03

    Hook Input (JSON via stdin)

    permissionmode values: "default", "plan", "acceptEdits", "auto", "dontAsk", "bypassPermissions"

    stophookactive: true when Claude is already continuing due to a stop hook — check to prevent infinite loops. Claude Code overrides after 8 consecutive blocks.lastassistantmessage: text content of Claude's final response (no transcript parsing needed)backgroundtasks: in-flight background tasks (v2.1.145+). Each entry: id, type (shell/subagent/monitor/workflow/teammate/etc.), status, description, plus type-specific fields (command, agenttype, server, tool, name)
  4. 04

    Common Fields (All Events)

    permissionmode values: "default", "plan", "acceptEdits", "auto", "dontAsk", "bypassPermissions"

    permissionmode values: "default", "plan", "acceptEdits", "auto", "dontAsk", "bypassPermissions"When running with --agent or inside a subagent, two additional fields are included:
  5. 05

    PreToolUse Input

    Tool-specific toolinput fields:

    Tool-specific toolinput fields:

Permission review

Static risk signals and limitations

Runs scripts

medium · line 657

The documentation asks the agent to run terminal commands or scripts.

| `terminalSequence` | string | Terminal escape sequence emitted by Claude Code on your behalf. Restricted to OSC `0`/`1`/`2`/`9`/`99`/`777` and BEL. Use instead of writing to `/dev/tty` (hooks run without a controlling terminal) |

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score93/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars64SourceRepository attention, not individual Skill quality
Compatibility1 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
Jamie-BitFlight/claude_skills
Skill path
plugins/plugin-creator/skills/hooks-io-api/SKILL.md
Commit
a00194f25fec502d3d659b7d610369614967251e
License
MIT
Collected
2026-08-28
Default branch
main
View the original SKILL.md

Claude Code Hooks — I/O API Reference (May 2026)

JSON schemas for hook stdin input and stdout output per event. For hook system fundamentals, activate Skill(skill: "plugin-creator:hooks-core-reference"). For working examples, activate Skill(skill: "plugin-creator:hooks-patterns").


Hook Input (JSON via stdin)

Common Fields (All Events)

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/current/working/directory",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse"
}

permission_mode values: "default", "plan", "acceptEdits", "auto", "dontAsk", "bypassPermissions"

When running with --agent or inside a subagent, two additional fields are included:

FieldDescription
agent_idUnique identifier for the subagent. Present only when the hook fires inside a subagent call
agent_typeAgent name (e.g. "Explore" or "security-reviewer"). For custom subagents, this is the name field from the agent's frontmatter, not the filename

PreToolUse Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "psql -c 'SELECT * FROM users'",
    "description": "Query the users table",
    "timeout": 120000
  },
  "tool_use_id": "toolu_01ABC123"
}

Tool-specific tool_input fields:

ToolFields
Bashcommand, description, timeout, run_in_background
Writefile_path, content
Editfile_path, old_string, new_string, replace_all
Readfile_path, offset, limit
Globpattern, path
Greppattern, path, glob, output_mode, -i, multiline
WebFetchurl, prompt
WebSearchquery, allowed_domains, blocked_domains
Agentprompt, description, subagent_type, model

PostToolUse Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123",
  "duration_ms": 12
}

duration_ms: tool execution time in milliseconds, excluding permission prompts and PreToolUse hooks.

PostToolUseFailure Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "tool_use_id": "toolu_01ABC123",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false,
  "duration_ms": 4187
}

PostToolBatch Input

Fires once after every tool call in a parallel batch resolves, before the next model call. No matcher support.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PostToolBatch",
  "tool_calls": [
    {
      "tool_name": "Read",
      "tool_input": { "file_path": "/path/to/file.py" },
      "tool_use_id": "toolu_01...",
      "tool_response": "     1\tfile contents..."
    }
  ]
}

Note: tool_response in PostToolBatch is the serialized tool_result content the model sees — different from PostToolUse's structured Output object.

PermissionRequest Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules"
  },
  "permission_suggestions": [
    {
      "type": "addRules",
      "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
      "behavior": "allow",
      "destination": "localSettings"
    }
  ]
}

PermissionDenied Input

Fires when the auto mode classifier denies a tool call. Does not fire on manual denials or PreToolUse blocks.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "auto",
  "hook_event_name": "PermissionDenied",
  "tool_name": "Bash",
  "tool_input": { "command": "rm -rf /tmp/build" },
  "tool_use_id": "toolu_01ABC123",
  "reason": "Auto mode denied: command targets a path outside the project"
}

Notification Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}

notification_type values: permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response

UserPromptSubmit Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate factorial"
}

UserPromptExpansion Input

Fires when a user-typed slash command expands into a prompt, before it reaches Claude.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "UserPromptExpansion",
  "expansion_type": "slash_command",
  "command_name": "example-skill",
  "command_args": "arg1 arg2",
  "command_source": "plugin",
  "prompt": "/example-skill arg1 arg2"
}

Matcher filters on command_name. expansion_type is slash_command for skills/commands or mcp_prompt for MCP server prompts.

Stop Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary...",
  "background_tasks": [
    {
      "id": "task-001",
      "type": "shell",
      "status": "running",
      "description": "tail logs",
      "command": "tail -f /var/log/syslog"
    }
  ],
  "session_crons": [
    {
      "id": "cron-001",
      "schedule": "0 9 * * 1-5",
      "recurring": true,
      "prompt": "check the build"
    }
  ]
}
  • stop_hook_active: true when Claude is already continuing due to a stop hook — check to prevent infinite loops. Claude Code overrides after 8 consecutive blocks.
  • last_assistant_message: text content of Claude's final response (no transcript parsing needed)
  • background_tasks: in-flight background tasks (v2.1.145+). Each entry: id, type (shell/subagent/monitor/workflow/teammate/etc.), status, description, plus type-specific fields (command, agent_type, server, tool, name)
  • session_crons: scheduled session wakeups (v2.1.145+). Each entry: id, schedule, recurring, prompt

StopFailure Input

Fires instead of Stop when the turn ends due to an API error. Output and exit code are ignored.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}

error values: rate_limit, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, unknown

SubagentStart Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
  • agent_type: built-in agent names ("general-purpose", "Explore", "Plan") or, for custom agents, the name field from the agent's frontmatter (not the filename). Matcher filters on this value.

SubagentStop Input

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "rewrite-room-auditor",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues...",
  "background_tasks": [],
  "session_crons": []
}
  • agent_type: the name field from the agent's frontmatter (not the filename). Matcher filters on this value — same matcher values as SubagentStart.
  • agent_transcript_path: path to the subagent's own transcript in nested subagents/ folder
  • last_assistant_message: text content of the subagent's final response (no transcript parsing needed)
  • background_tasks, session_crons: scoped to the parent session, not the subagent (v2.1.145+)

TaskCreated Input

Fires when a task is being created via TaskCreate. No matcher support.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}

TaskCompleted Input

Fires when a task is being marked as completed. No matcher support.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}

TeammateIdle Input

Fires when an agent team teammate is about to go idle. No matcher support.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project"
}

ConfigChange Input

Fires when a configuration file changes during a session. Matcher filters on source.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/path/to/.claude/settings.json"
}

source / matcher values: user_settings, project_settings, local_settings, policy_settings, skills

CwdChanged Input

Fires when working directory changes. No matcher support. Has access to CLAUDE_ENV_FILE.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/new/dir",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/path/to/project",
  "new_cwd": "/path/to/project/src"
}

FileChanged Input

Fires when a watched file changes. Matcher serves dual role: builds the watch list (literal filenames split on |) AND filters which hooks run.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "FileChanged",
  "file_path": "/path/to/project/.envrc",
  "event": "change"
}

event values: change (modified), add (created), unlink (deleted)

WorktreeCreate Input

Fires when a worktree is being created. Replaces default git worktree behavior when configured.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

WorktreeRemove Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/path/to/.claude/worktrees/feature-auth"
}

PreCompact Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

trigger / matcher values: manual, auto

PostCompact Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}

InstructionsLoaded Input

Fires when a CLAUDE.md or .claude/rules/*.md file is loaded. Matcher filters on load_reason.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/path/to/project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

load_reason / matcher values: session_start, nested_traversal, path_glob_match, include, compact

Elicitation Input

Fires when an MCP server requests user input. Matcher filters on MCP server name.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}

ElicitationResult Input

Fires after a user responds to an MCP elicitation. Matcher filters on MCP server name.

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form",
  "elicitation_id": "elicit-123"
}

Setup Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "Setup",
  "trigger": "init"
}
  • trigger: "init" (from --init or --init-only) or "maintenance" (from --maintenance)
  • Has access to CLAUDE_ENV_FILE for persisting environment variables

SessionStart Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-6"
}
  • source / matcher values: startup, resume, clear, compact
  • model: model identifier
  • agent_type: present when Claude Code started with claude --agent <name>
  • Has access to CLAUDE_ENV_FILE for persisting environment variables

SessionEnd Input

{
  "session_id": "abc123",
  "transcript_path": "/path/to/session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

reason / matcher values: clear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other


Hook Output

Exit Codes

CodeBehavior
0Success. stdout processed (JSON or plain text)
2Blocking error. stderr fed back to Claude (or shown to user, event-dependent)
OtherNon-blocking error. stderr shown in verbose mode (Ctrl+O). Execution continues

Important: JSON output is only processed on exit 0. Exit code 2 uses stderr only.

Note: Exit code 1 is non-blocking for most events — execution continues. Only exit code 2 blocks. Exception: WorktreeCreate — any non-zero exit code fails worktree creation.

Exit Code 2 Behavior Per Event

EventCan Block?Exit Code 2 Effect
PreToolUseYesBlocks tool call, shows stderr to Claude
PermissionRequestYesDenies permission, shows stderr to Claude
UserPromptSubmitYesBlocks prompt, erases it, shows stderr to user
UserPromptExpansionYesBlocks the expansion
StopYesBlocks stoppage, shows stderr to Claude
SubagentStopYesBlocks stoppage, shows stderr to Claude subagent
TeammateIdleYesPrevents teammate going idle, shows stderr as feedback
TaskCreatedYesRolls back task creation, shows stderr as feedback
TaskCompletedYesPrevents task completion, shows stderr as feedback
ConfigChangeYesBlocks config change (except policy_settings)
PreCompactYesBlocks compaction
PostToolBatchYesStops agentic loop before next model call
ElicitationYesDenies the elicitation
ElicitationResultYesBlocks the response (action becomes decline)
WorktreeCreateYesAny non-zero exit fails worktree creation
PostToolUseNoShows stderr to Claude (tool already ran)
PostToolUseFailureNoShows stderr to Claude (tool already failed)
PermissionDeniedNoExit code ignored. Use JSON hookSpecificOutput.retry: true to allow retry
NotificationNoShows stderr to user only
SubagentStartNoShows stderr to user only
StopFailureNoOutput and exit code ignored
PreCompactYesBlocks compaction, stderr shown to user
PostCompactNoShows stderr to user only
SetupNoShows stderr to user only
SessionStartNoShows stderr to user only
SessionEndNoShows stderr to user only
CwdChangedNoShows stderr to user only
FileChangedNoShows stderr to user only
WorktreeRemoveNoFailures logged in debug mode only
InstructionsLoadedNoExit code ignored

JSON Output Control

Important: JSON output only processed with exit code 0. Exit code 2 uses stderr only.

Common JSON Fields (All Events)

{
  "continue": true,
  "stopReason": "Message shown when continue is false",
  "suppressOutput": false,
  "systemMessage": "Optional warning message shown to user",
  "terminalSequence": "]777;notify;Claude Code;Needs attention"
}
FieldTypeEffect
continuebooleanfalse stops Claude entirely (takes precedence over all other fields)
stopReasonstringShown to user when continue is false
suppressOutputbooleanHide stdout from transcript mode
systemMessagestringWarning message shown to user
terminalSequencestringTerminal escape sequence emitted by Claude Code on your behalf. Restricted to OSC 0/1/2/9/99/777 and BEL. Use instead of writing to /dev/tty (hooks run without a controlling terminal)

PreToolUse JSON Output

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "Auto-approved documentation file",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
FieldValuesEffect
permissionDecisionallow, deny, ask, deferControls tool execution. defer only valid in non-interactive -p mode — pauses for resume
permissionDecisionReasonstringShown to user (allow/ask) or Claude (deny). Ignored for defer
updatedInputobjectModifies tool input before execution. Include all fields, not just changed ones
additionalContextstringAdded to Claude's context alongside the tool result

Precedence when multiple hooks return different decisions: deny > defer > ask > allow

Note: Top-level decision and reason fields are deprecated for PreToolUse. Use hookSpecificOutput.permissionDecision and hookSpecificOutput.permissionDecisionReason. Deprecated "approve" maps to "allow", "block" maps to "deny".

PermissionRequest JSON Output

Allow with modified input:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": { "command": "npm run lint" }
    }
  }
}

Deny with message:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "deny",
      "message": "Command not allowed by policy",
      "interrupt": true
    }
  }
}

PermissionDenied JSON Output

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionDenied",
    "retry": true
  }
}

retry: true tells the model it may retry the denied tool call. Only command hooks can return this — prompt/agent hook output is discarded for this event.

PostToolUse JSON Output

{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude",
    "updatedToolOutput": {
      "stdout": "[redacted]",
      "stderr": "",
      "interrupted": false,
      "isImage": false
    }
  }
}
  • decision: "block" adds reason next to the tool result as feedback to Claude
  • updatedToolOutput: replaces what Claude sees (tool has already run — files written, commands executed are not reversed). Value must match the tool's output shape exactly. For Bash: { stdout, stderr, interrupted, isImage }
  • updatedMCPToolOutput: same as updatedToolOutput but for MCP tools only

UserPromptSubmit JSON Output

{
  "decision": "block",
  "reason": "Prompt contains sensitive data",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here",
    "sessionTitle": "My session title"
  }
}
  • decision: "block" prevents prompt processing, erases prompt from context
  • reason shown to user but not added to context
  • sessionTitle: sets the session title automatically based on prompt content
  • Plain text stdout also adds context (no JSON needed for simple use cases)

UserPromptExpansion JSON Output

{
  "decision": "block",
  "reason": "This slash command is not available",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptExpansion",
    "additionalContext": "Additional context for this expansion"
  }
}

Stop / SubagentStop JSON Output

{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}
  • decision: "block" prevents stopping. reason is required and becomes Claude's next instruction
  • SubagentStop does not support additionalContext. To inject context into the parent session after a subagent returns, use a PostToolUse hook on the Agent tool instead

TeammateIdle / TaskCreated / TaskCompleted JSON Output

Two control options:

  • Exit code 2: feeds stderr back to the model as feedback; action is blocked
  • JSON {"continue": false, "stopReason": "..."}: stops the teammate entirely

PostToolBatch JSON Output

{
  "decision": "block",
  "reason": "Reason to stop before next model call",
  "hookSpecificOutput": {
    "hookEventName": "PostToolBatch",
    "additionalContext": "Context injected once before the next model call"
  }
}

ConfigChange JSON Output

{
  "decision": "block",
  "reason": "Configuration changes require admin approval"
}

policy_settings changes cannot be blocked — hooks fire but blocking decisions are ignored.

WorktreeCreate JSON Output

Hook replaces default git worktree behavior. Must return the absolute path to the created worktree directory:

  • Command hooks: print path on stdout
  • HTTP hooks: return { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }

Elicitation JSON Output

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": { "username": "alice" }
  }
}

action values: accept, decline, cancel

ElicitationResult JSON Output

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline",
    "content": {}
  }
}

PreCompact JSON Output

{
  "decision": "block",
  "reason": "Cannot compact — CI check in progress"
}

Setup / SessionStart JSON Output

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Project context loaded successfully",
    "initialUserMessage": "Check the build status",
    "watchPaths": ["/path/to/watch"]
  }
}
  • additionalContext: added to Claude's context at the start of the conversation
  • initialUserMessage: SessionStart only — used as the first user message in non-interactive (-p) mode
  • watchPaths: SessionStart only — array of absolute paths to watch for FileChanged events
  • Multiple hooks' additionalContext values are concatenated

Events With No Decision Control

These events support side-effects only — logging, notifications, cleanup. No blocking or decision output is processed:

StopFailure, PostCompact, Notification, WorktreeRemove, SessionEnd, InstructionsLoaded, CwdChanged, FileChanged


Sources

Frequently asked questions

What to verify before installation and use

What does the hooks-io-api source document cover?

JSON schemas for hook stdin input and stdout output per event. For hook system fundamentals, activate Skill(skill: "plugin-creator:hooks-core-reference"). For working examples, activate Skill(skill: "plugin-creator:hooks-patterns").

How do I install hooks-io-api?

The source record exposes this install command: npx skills add https://github.com/Jamie-BitFlight/claude_skills --skill "plugins/plugin-creator/skills/hooks-io-api". Inspect the command and pinned source before running it.

Which Agent platforms does the source record declare?

The pinned source record declares support for: claude code.

Which permission-related actions were detected?

Static rules flagged exec-script in the source; the page lists the matching lines and excerpts.

Alternatives

Compare before choosing

Computed 100147

oaustegard/claude-skills

featuring

Generate hierarchical _FEATURES.md files that describe what a codebase DOES from a user/consumer perspective, anchored to source symbols via tree-sitting. Supports large complex codebases through feature-driven decomposition into sub-feature files. Uses a multi-pass synthesis: orientation → detail → overview rewrite. Use when someone says "what does this do", "document features", "feature inventory", "_FEATURES.md", or needs to understand a codebase's purpose before modifying it. Complements tre

Computed 100108

apollographql/skills

skill-creator

Guide for creating effective skills for Apollo GraphQL and GraphQL development. Use this skill when: (1) users want to create a new skill, (2) users want to update an existing skill, (3) users ask about skill structure or best practices, (4) users need help writing SKILL.md files.

Computed 10061

terrylica/cc-skills

draft-park

Park a draft message/text in macOS Notes for the operator to review and edit, then read it back before acting (e.g. before sending to a real person). Notes is the source of truth (AppleScript CRUD, iCloud-synced, provenance-stamped with the Claude Code session UUID); Stickies is a best-effort view-only desktop mirror. Use whenever you draft something a human should confirm/edit before it is sent or committed — messages, replies, announcements, anything outbound. TRIGGERS - park this draft, park

Computed 1008

narrative-io/narrative-skills-marketplace

design-analysis

Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "