skills/multicam/qara/hook-authoring

hook-authoring

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

PreToolUse

When: Before a tool executes Use Cases: Security checks, permission enforcement, input validation

PostToolUse

When: After a tool executes Use Cases: Logging, metrics, audit trail

UserPromptSubmit

When: User submits a prompt Use Cases: Pre-processing, context injection, tab updates

Stop

When: Claude completes a response (not user) Use Cases: Extract completion info, update tab titles, capture work

SubagentStop

When: A subagent (Task) completes Use Cases: Agent tracking, result capture, coordination

ConfigChange

When: Settings or config files change Use Cases: Sync validation, drift detection


Hook Configuration

Location: ${PAI_DIR}/.claude/settings.json or ~/.claude/settings.json

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": {},
        "hooks": [
          {
            "type": "command",
            "command": "${PAI_DIR}/hooks/my-hook.ts"
          }
        ]
      }
    ]
  }
}

Hook Input/Output

Input (stdin JSON)

interface HookInput {
  session_id: string;
  transcript_path: string;
  // Event-specific fields...
}

Output (stdout)

  • Continue: { "continue": true }
  • Block: { "continue": false, "reason": "..." }
  • Inject content: { "result": "<system-reminder>...</system-reminder>" }
  • 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:

// 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, initialize session
pre-tool-use-security.ts PreToolUse Block dangerous Bash commands
post-tool-use.ts PostToolUse JSONL tool usage logging
update-tab-titles.ts UserPromptSubmit Set terminal tab titles
stop-hook.ts Stop Checkpoint logging, tab update
config-change.ts ConfigChange Settings sync validation

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

#!/usr/bin/env bun
// ${PAI_DIR}/hooks/my-hook.ts

import { readFileSync } from 'fs';

const input = JSON.parse(readFileSync(0, 'utf-8'));

// Your logic here

// Output (choose one):
console.log(JSON.stringify({ continue: true }));
// console.log(JSON.stringify({ result: "<system-reminder>...</system-reminder>" }));

Make executable: chmod +x my-hook.ts


Related

  • See post-tool-use.ts for JSONL logging pattern
  • See hook-test skill for hook health checking
Weekly Installs
19
Repository
multicam/qara
GitHub Stars
2
First Seen
Jan 27, 2026
Installed on
trae18
gemini-cli18
antigravity18
claude-code18
github-copilot18
cursor18