---
name: hook-authoring
context: fork
description: |
Claude Code hook creation and configuration. PAI event-driven automation.
USE WHEN: "create hook", "hook system", "modify hooks", "add hook"
---
## Workflow Routing (SYSTEM PROMPT)
**When user requests creating a new hook:**
Examples: "create a hook for X", "add a hook", "make a SessionStart hook"
-> **READ:** ${PAI_DIR}/skills/hook-authoring/workflows/create-hook.md
-> **EXECUTE:** Follow hook creation workflow
**When user needs to debug hooks:**
Examples: "hook not working", "debug hooks", "hook troubleshooting"
-> **READ:** ${PAI_DIR}/skills/hook-authoring/workflows/debug-hooks.md
-> **EXECUTE:** Run debugging checklist
---
## Claude Code Hook Events
### SessionStart
**When:** New Claude Code session begins
**Use Cases:** Load context, initialize state, capture metadata
### Stop
**When:** Claude completes a response (not user)
**Use Cases:** Extract completion info, update tab titles, capture work
### UserPromptSubmit
**When:** User submits a prompt
**Use Cases:** Pre-processing, context injection, tab updates
### SubagentStop
**When:** A subagent (Task) completes
**Use Cases:** Agent tracking, result capture, coordination
### Notification
**When:** Notifications are triggered
**Use Cases:** Alerts, external integrations
---
## Hook Configuration
**Location:** `${PAI_DIR}/.claude/settings.json` or `~/.claude/settings.json`
```json
{
"hooks": {
"SessionStart": [
{
"matcher": {},
"hooks": [
{
"type": "command",
"command": "${PAI_DIR}/hooks/my-hook.ts"
}
]
}
]
}
}
```
---
## Hook Input/Output
### Input (stdin JSON)
```typescript
interface HookInput {
session_id: string;
transcript_path: string;
// Event-specific fields...
}
```
### Output (stdout)
- **Continue:** `{ "continue": true }`
- **Block:** `{ "continue": false, "reason": "..." }`
- **Inject content:** `{ "result": "..." }`
- **Add context (CC 2.1.9+):** `{ "decision": "continue", "additionalContext": "..." }`
### Session ID Tracking (CC 2.1.9+)
Hooks receive `session_id` in input JSON. For persistent storage:
```typescript
// Use session_id from hook input
const sessionFile = `${PAI_DIR}/state/sessions/${input.session_id}.json`;
// Or use ${CLAUDE_SESSION_ID} substitution in settings.json:
{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "${PAI_DIR}/hooks/init-session.ts --session ${CLAUDE_SESSION_ID}"
}]
}]
}
}
```
---
## PAI Active Hooks
| Hook | Event | Purpose |
|------|-------|---------|
| `session-start.ts` | SessionStart | Load CORE skill, set tab |
| `stop-hook.ts` | Stop | Extract COMPLETED, update tab |
| `subagent-stop-hook.ts` | SubagentStop | Track agent completion |
| `capture-all-events.ts` | All | JSONL logging |
---
## Hook Best Practices
1. **Fail gracefully** - Hooks should never block Claude
2. **Timeout protection** - Use timeouts for external calls
3. **Async by default** - Don't block the main process
4. **TypeScript preferred** - Use Bun for execution
5. **Log errors** - Don't fail silently
---
## Quick Hook Template
```typescript
#!/usr/bin/env bun
// ${PAI_DIR}/hooks/my-hook.ts
import { readFileSync } from 'fs';
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
// Your logic here
// Output (choose one):
console.log(JSON.stringify({ continue: true }));
// console.log(JSON.stringify({ result: "..." }));
```
Make executable: `chmod +x my-hook.ts`
---
## Related
- See `agent-observability` skill for event analysis
- See `capture-all-events.ts` for JSONL logging pattern