From plugin-creator
References JSON input/output schemas for Claude Code hooks per event (PreToolUse, PostToolUse, etc.). Use for scripting hooks, permissions JSON, exit codes, event data.
npx claudepluginhub jamie-bitflight/claude_skills --plugin plugin-creatorThis skill uses the workspace's default tool permissions.
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")`.
Develops Claude Code plugin hooks for event-driven automation, validating tool use with prompt-based, command, and agent types for events like PreToolUse, Stop, and SessionStart.
Author Claude Code hooks for events like PreToolUse and PostToolUse using command, prompt, or agent types to automate workflows and validate operations.
Details Claude Code hook events, matchers, configuration, env vars, execution, security, and debugging. Use for creating hooks, understanding system, or troubleshooting issues.
Share bugs, ideas, or general feedback.
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").
{
"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", "dontAsk", "bypassPermissions"
{
"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:
| Tool | Fields |
|---|---|
Bash | command, description, timeout, run_in_background |
Write | file_path, content |
Edit | file_path, old_string, new_string, replace_all |
Read | file_path, offset, limit |
{
"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"
}
{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "Notification",
"message": "Claude needs your permission to use Bash",
"notification_type": "permission_prompt"
}
{
"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"
}
{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true
}
stop_hook_active: True when Claude is already continuing due to a stop hook. Check this to prevent infinite loops.
{
"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"
}
Fields:
agent_id: Unique identifier for the subagentagent_type: Agent name (built-in like "Bash", "Explore", "Plan", or custom agent names){
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false,
"agent_id": "def456",
"agent_transcript_path": "/path/to/subagents/agent-def456.jsonl"
}
Fields:
agent_id: Unique identifier for the subagentagent_transcript_path: Path to the subagent's own transcript in nested subagents/ folderstop_hook_active: True when already continuing due to a stop hook{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}
{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "Setup",
"trigger": "init"
}
Fields:
trigger: Either "init" (from --init or --init-only) or "maintenance" (from --maintenance)CLAUDE_ENV_FILE for persisting environment variables{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-20250514"
}
Fields:
source: Indicates how session started: "startup" (new), "resume" (resumed), "clear" (after /clear), or "compact" (after compaction)model: Model identifier when availableagent_type: (Optional) Present when Claude Code started with claude --agent <name>{
"session_id": "abc123",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/path/to/project",
"permission_mode": "default",
"hook_event_name": "SessionEnd",
"reason": "exit"
}
reason values: clear, logout, prompt_input_exit, other
| Code | Behavior |
|---|---|
| 0 | Success. stdout processed (JSON or plain text) |
| 2 | Blocking error. stderr used as error message, fed back to Claude |
| Other | Non-blocking error. stderr shown in verbose mode (Ctrl+O) |
Important: Claude Code does not see stdout if exit code is 0, except for UserPromptSubmit and SessionStart where stdout is added to context.
| Event | Exit Code 2 Behavior |
|---|---|
PreToolUse | Blocks tool call, shows stderr to Claude |
PermissionRequest | Denies permission, shows stderr to Claude |
PostToolUse | Shows stderr to Claude (tool already ran) |
PostToolUseFailure | Shows stderr to Claude (tool already failed) |
Notification | Shows stderr to user only |
UserPromptSubmit | Blocks prompt, erases it, shows stderr to user |
Stop | Blocks stoppage, shows stderr to Claude |
SubagentStart | Shows stderr to user only |
SubagentStop | Blocks stoppage, shows stderr to Claude subagent |
PreCompact | Shows stderr to user only |
Setup | Shows stderr to user only |
SessionStart | Shows stderr to user only |
SessionEnd | Shows stderr to user only |
Important: JSON output only processed with exit code 0. Exit code 2 uses stderr only.
{
"continue": true,
"stopReason": "Message shown when continue is false",
"suppressOutput": false,
"systemMessage": "Optional warning message shown to user"
}
| Field | Type | Effect |
|---|---|---|
continue | boolean | false stops Claude (takes precedence over all) |
stopReason | string | Shown to user when continue is false |
suppressOutput | boolean | Hide stdout from transcript mode |
systemMessage | string | Warning message shown to user |
Precedence: continue: false takes precedence over any decision: "block" output.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Auto-approved documentation file",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
| Field | Values | Effect |
|---|---|---|
permissionDecision | allow, deny, ask | Controls tool execution |
permissionDecisionReason | string | Shown to user (allow/ask) or Claude (deny) |
updatedInput | object | Modifies tool input before execution |
additionalContext | string | Added to Claude's context |
Note: decision and reason fields are deprecated. Use hookSpecificOutput.permissionDecision and hookSpecificOutput.permissionDecisionReason. Deprecated "approve" and "block" map to "allow" and "deny".
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
}
}
}
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
"block" automatically prompts Claude with reasonundefined does nothing, reason is ignored{
"decision": "block",
"reason": "Prompt contains sensitive data",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here"
}
}
Two ways to add context (exit code 0):
additionalContext: More structured controlBlocking prompts:
"decision": "block" prevents prompt processing, erases prompt from context"reason" shown to user but not added to context{
"decision": "block",
"reason": "Must be provided when Claude is blocked from stopping"
}
"block" prevents Claude from stopping. Must populate reasonundefined allows Claude to stop. reason is ignored{
"hookSpecificOutput": {
"hookEventName": "Setup",
"additionalContext": "Repository initialized with custom configuration"
}
}
Note: Multiple hooks' additionalContext values are concatenated. Setup hooks have access to CLAUDE_ENV_FILE for persisting environment variables.
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Project context loaded successfully"
}
}
Note: Multiple hooks' additionalContext values are concatenated.