Initial commit
This commit is contained in:
469
skills/create-hooks/references/input-output-schemas.md
Normal file
469
skills/create-hooks/references/input-output-schemas.md
Normal file
@@ -0,0 +1,469 @@
|
||||
# Input/Output Schemas
|
||||
|
||||
Complete JSON schemas for all hook types.
|
||||
|
||||
## Common Input Fields
|
||||
|
||||
All hooks receive these fields:
|
||||
|
||||
```typescript
|
||||
{
|
||||
session_id: string // Unique session identifier
|
||||
transcript_path: string // Path to session transcript (.jsonl file)
|
||||
cwd: string // Current working directory
|
||||
permission_mode: string // "default" | "plan" | "acceptEdits" | "bypassPermissions"
|
||||
hook_event_name: string // Name of the hook event
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PreToolUse
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "PreToolUse",
|
||||
"tool_name": "Bash",
|
||||
"tool_input": {
|
||||
"command": "npm install",
|
||||
"description": "Install dependencies"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output** (optional, for control):
|
||||
```json
|
||||
{
|
||||
"decision": "approve" | "block",
|
||||
"reason": "Explanation for the decision",
|
||||
"permissionDecision": "allow" | "deny" | "ask",
|
||||
"permissionDecisionReason": "Why this permission decision",
|
||||
"updatedInput": {
|
||||
"command": "npm install --save-exact"
|
||||
},
|
||||
"systemMessage": "Message displayed to user",
|
||||
"suppressOutput": false,
|
||||
"continue": true
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `decision`: Whether to allow the tool call
|
||||
- `reason`: Explanation (required if blocking)
|
||||
- `permissionDecision`: Override permission system
|
||||
- `updatedInput`: Modified tool input (partial update)
|
||||
- `systemMessage`: Message shown to user
|
||||
- `suppressOutput`: Hide hook output from user
|
||||
- `continue`: If false, stop execution
|
||||
|
||||
---
|
||||
|
||||
## PostToolUse
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "PostToolUse",
|
||||
"tool_name": "Write",
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.js",
|
||||
"content": "const x = 1;"
|
||||
},
|
||||
"tool_output": "File created successfully at: /path/to/file.js"
|
||||
}
|
||||
```
|
||||
|
||||
**Output** (optional):
|
||||
```json
|
||||
{
|
||||
"systemMessage": "Code formatted successfully",
|
||||
"suppressOutput": false
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `systemMessage`: Additional message to display
|
||||
- `suppressOutput`: Hide tool output from user
|
||||
|
||||
---
|
||||
|
||||
## UserPromptSubmit
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "UserPromptSubmit",
|
||||
"prompt": "Write a function to calculate factorial"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"decision": "approve" | "block",
|
||||
"reason": "Prompt is clear and actionable",
|
||||
"systemMessage": "Optional message to user"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `decision`: Whether to allow the prompt
|
||||
- `reason`: Explanation (required if blocking)
|
||||
- `systemMessage`: Message shown to user
|
||||
|
||||
---
|
||||
|
||||
## Stop
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "Stop",
|
||||
"stop_hook_active": false
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"decision": "block" | undefined,
|
||||
"reason": "Tests are still failing - please fix before stopping",
|
||||
"continue": true,
|
||||
"stopReason": "Cannot stop yet",
|
||||
"systemMessage": "Additional context"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `decision`: `"block"` to prevent stopping, `undefined` to allow
|
||||
- `reason`: Why Claude should continue (required if blocking)
|
||||
- `continue`: If true and blocking, Claude continues working
|
||||
- `stopReason`: Message shown when stopping is blocked
|
||||
- `systemMessage`: Additional context for Claude
|
||||
- `stop_hook_active`: If true, don't block again (prevents infinite loops)
|
||||
|
||||
**Important**: Always check `stop_hook_active` to avoid infinite loops:
|
||||
|
||||
```javascript
|
||||
if (input.stop_hook_active) {
|
||||
return { decision: undefined }; // Don't block again
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SubagentStop
|
||||
|
||||
**Input**: Same as Stop
|
||||
|
||||
**Output**: Same as Stop
|
||||
|
||||
**Usage**: Same as Stop, but for subagent completion
|
||||
|
||||
---
|
||||
|
||||
## SessionStart
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "SessionStart",
|
||||
"source": "startup" | "continue" | "checkpoint"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "SessionStart",
|
||||
"additionalContext": "Current sprint: Sprint 23\nFocus: User authentication\nDeadline: Friday"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `additionalContext`: Text injected into session context
|
||||
- Multiple SessionStart hooks' contexts are concatenated
|
||||
|
||||
---
|
||||
|
||||
## SessionEnd
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "SessionEnd",
|
||||
"reason": "exit" | "error" | "timeout" | "compact"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: None (ignored)
|
||||
|
||||
**Usage**: Cleanup tasks only. Cannot prevent session end.
|
||||
|
||||
---
|
||||
|
||||
## PreCompact
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "PreCompact",
|
||||
"trigger": "manual" | "auto",
|
||||
"custom_instructions": "Preserve all git commit messages"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"decision": "approve" | "block",
|
||||
"reason": "Safe to compact" | "Wait until task completes"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `trigger`: How compaction was initiated
|
||||
- `custom_instructions`: User's compaction preferences (if manual)
|
||||
- `decision`: Whether to proceed with compaction
|
||||
- `reason`: Explanation
|
||||
|
||||
---
|
||||
|
||||
## Notification
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../session.jsonl",
|
||||
"cwd": "/Users/username/project",
|
||||
"permission_mode": "default",
|
||||
"hook_event_name": "Notification"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: None (hook just performs notification action)
|
||||
|
||||
**Usage**: Trigger external notifications (desktop, sound, status bar)
|
||||
|
||||
---
|
||||
|
||||
## Common Output Fields
|
||||
|
||||
These fields can be returned by any hook:
|
||||
|
||||
```json
|
||||
{
|
||||
"continue": true | false,
|
||||
"stopReason": "Reason shown when stopping",
|
||||
"suppressOutput": true | false,
|
||||
"systemMessage": "Additional context or message"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `continue`: If false, stop Claude's execution immediately
|
||||
- `stopReason`: Message displayed when execution stops
|
||||
- `suppressOutput`: If true, hide hook's stdout/stderr from user
|
||||
- `systemMessage`: Context added to Claude's next message
|
||||
|
||||
---
|
||||
|
||||
## LLM Prompt Hook Response
|
||||
|
||||
When using `type: "prompt"`, the LLM must return JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"decision": "approve" | "block",
|
||||
"reason": "Detailed explanation",
|
||||
"systemMessage": "Optional message",
|
||||
"continue": true | false,
|
||||
"stopReason": "Optional stop message"
|
||||
}
|
||||
```
|
||||
|
||||
**Example prompt**:
|
||||
```
|
||||
Evaluate this command: $ARGUMENTS
|
||||
|
||||
Check if it's safe to execute.
|
||||
|
||||
Return JSON:
|
||||
{
|
||||
"decision": "approve" or "block",
|
||||
"reason": "your explanation"
|
||||
}
|
||||
```
|
||||
|
||||
The `$ARGUMENTS` placeholder is replaced with the hook's input JSON.
|
||||
|
||||
---
|
||||
|
||||
## Tool-Specific Input Fields
|
||||
|
||||
Different tools provide different `tool_input` fields:
|
||||
|
||||
### Bash
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"command": "npm install",
|
||||
"description": "Install dependencies",
|
||||
"timeout": 120000,
|
||||
"run_in_background": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Write
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.js",
|
||||
"content": "const x = 1;"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Edit
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.js",
|
||||
"old_string": "const x = 1;",
|
||||
"new_string": "const x = 2;",
|
||||
"replace_all": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Read
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.js",
|
||||
"offset": 0,
|
||||
"limit": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Grep
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"pattern": "function.*",
|
||||
"path": "/path/to/search",
|
||||
"output_mode": "content"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MCP tools
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
// MCP tool-specific parameters
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Access these in hooks:
|
||||
```bash
|
||||
command=$(echo "$input" | jq -r '.tool_input.command')
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modifying Tool Input
|
||||
|
||||
PreToolUse hooks can modify `tool_input` before execution:
|
||||
|
||||
**Original input**:
|
||||
```json
|
||||
{
|
||||
"tool_input": {
|
||||
"command": "npm install lodash"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Hook output**:
|
||||
```json
|
||||
{
|
||||
"decision": "approve",
|
||||
"reason": "Adding --save-exact flag",
|
||||
"updatedInput": {
|
||||
"command": "npm install --save-exact lodash"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Result**: Tool executes with modified input.
|
||||
|
||||
**Partial updates**: Only specify fields you want to change:
|
||||
```json
|
||||
{
|
||||
"updatedInput": {
|
||||
"timeout": 300000 // Only update timeout, keep other fields
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Command hooks**: Return non-zero exit code to indicate error
|
||||
```bash
|
||||
if [ error ]; then
|
||||
echo '{"decision": "block", "reason": "Error occurred"}' >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
**Prompt hooks**: LLM should return valid JSON. If malformed, hook fails gracefully.
|
||||
|
||||
**Timeout**: Set `timeout` (ms) to prevent hanging:
|
||||
```json
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/slow-script.sh",
|
||||
"timeout": 30000
|
||||
}
|
||||
```
|
||||
|
||||
Default: 60000ms (60s)
|
||||
Reference in New Issue
Block a user