🎯

hook-authoring

🎯Skill

from multicam/qara

VibeIndex|
What it does

hook-authoring skill from multicam/qara

hook-authoring

Installation

Install skill:
npx skills add https://github.com/multicam/qara --skill hook-authoring
1
AddedJan 29, 2026

Skill Details

SKILL.md

|

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