@tekmidian/pai 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/cli/index.mjs +279 -21
  2. package/dist/cli/index.mjs.map +1 -1
  3. package/dist/hooks/capture-all-events.mjs +238 -0
  4. package/dist/hooks/capture-all-events.mjs.map +7 -0
  5. package/dist/hooks/capture-session-summary.mjs +198 -0
  6. package/dist/hooks/capture-session-summary.mjs.map +7 -0
  7. package/dist/hooks/capture-tool-output.mjs +105 -0
  8. package/dist/hooks/capture-tool-output.mjs.map +7 -0
  9. package/dist/hooks/cleanup-session-files.mjs +129 -0
  10. package/dist/hooks/cleanup-session-files.mjs.map +7 -0
  11. package/dist/hooks/context-compression-hook.mjs +283 -0
  12. package/dist/hooks/context-compression-hook.mjs.map +7 -0
  13. package/dist/hooks/initialize-session.mjs +206 -0
  14. package/dist/hooks/initialize-session.mjs.map +7 -0
  15. package/dist/hooks/load-core-context.mjs +110 -0
  16. package/dist/hooks/load-core-context.mjs.map +7 -0
  17. package/dist/hooks/load-project-context.mjs +548 -0
  18. package/dist/hooks/load-project-context.mjs.map +7 -0
  19. package/dist/hooks/security-validator.mjs +159 -0
  20. package/dist/hooks/security-validator.mjs.map +7 -0
  21. package/dist/hooks/stop-hook.mjs +625 -0
  22. package/dist/hooks/stop-hook.mjs.map +7 -0
  23. package/dist/hooks/subagent-stop-hook.mjs +152 -0
  24. package/dist/hooks/subagent-stop-hook.mjs.map +7 -0
  25. package/dist/hooks/sync-todo-to-md.mjs +322 -0
  26. package/dist/hooks/sync-todo-to-md.mjs.map +7 -0
  27. package/dist/hooks/update-tab-on-action.mjs +90 -0
  28. package/dist/hooks/update-tab-on-action.mjs.map +7 -0
  29. package/dist/hooks/update-tab-titles.mjs +55 -0
  30. package/dist/hooks/update-tab-titles.mjs.map +7 -0
  31. package/package.json +4 -2
  32. package/scripts/build-hooks.mjs +51 -0
  33. package/src/hooks/ts/capture-all-events.ts +179 -0
  34. package/src/hooks/ts/lib/detect-environment.ts +53 -0
  35. package/src/hooks/ts/lib/metadata-extraction.ts +144 -0
  36. package/src/hooks/ts/lib/pai-paths.ts +124 -0
  37. package/src/hooks/ts/lib/project-utils.ts +914 -0
  38. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +78 -0
  39. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +230 -0
  40. package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +145 -0
  41. package/src/hooks/ts/pre-compact/context-compression-hook.ts +155 -0
  42. package/src/hooks/ts/pre-tool-use/security-validator.ts +258 -0
  43. package/src/hooks/ts/session-end/capture-session-summary.ts +185 -0
  44. package/src/hooks/ts/session-start/initialize-session.ts +155 -0
  45. package/src/hooks/ts/session-start/load-core-context.ts +104 -0
  46. package/src/hooks/ts/session-start/load-project-context.ts +394 -0
  47. package/src/hooks/ts/stop/stop-hook.ts +407 -0
  48. package/src/hooks/ts/subagent-stop/subagent-stop-hook.ts +212 -0
  49. package/src/hooks/ts/user-prompt/cleanup-session-files.ts +45 -0
  50. package/src/hooks/ts/user-prompt/update-tab-titles.ts +88 -0
  51. package/tab-color-command.sh +24 -0
  52. package/templates/skills/createskill-skill.template.md +78 -0
  53. package/templates/skills/history-system.template.md +371 -0
  54. package/templates/skills/hook-system.template.md +913 -0
  55. package/templates/skills/sessions-skill.template.md +102 -0
  56. package/templates/skills/skill-system.template.md +214 -0
  57. package/templates/skills/terminal-tabs.template.md +120 -0
@@ -0,0 +1,913 @@
1
+ <!-- Generated by PAI Setup -->
2
+ # Hook System
3
+
4
+ **Event-Driven Automation Infrastructure**
5
+
6
+ **Location:** `${PAI_DIR}/Hooks/`
7
+ **Configuration:** `~/.claude/settings.json`
8
+ **Status:** Configure during `pai setup`
9
+
10
+ ---
11
+
12
+ ## Overview
13
+
14
+ The PAI hook system is an event-driven automation infrastructure built on Claude Code's native hook support. Hooks are executable scripts (TypeScript/Python) that run automatically in response to specific events during Claude Code sessions.
15
+
16
+ **Core Capabilities:**
17
+ - **Session Management** - Auto-load context, capture summaries, manage state
18
+ - **Voice Notifications** - Text-to-speech announcements for task completions
19
+ - **History Capture** - Automatic work/learning documentation to `${PAI_DIR}/History/`
20
+ - **Multi-Agent Support** - Agent-specific hooks with voice routing
21
+ - **Observability** - Real-time event streaming to dashboard
22
+ - **Tab Titles** - Dynamic terminal tab updates with task context
23
+
24
+ **Key Principle:** Hooks run asynchronously and fail gracefully. They enhance the user experience but never block Claude Code's core functionality.
25
+
26
+ ---
27
+
28
+ ## Available Hook Types
29
+
30
+ Claude Code supports the following hook events:
31
+
32
+ ### 1. **SessionStart**
33
+ **When:** Claude Code session begins (new conversation)
34
+ **Use Cases:**
35
+ - Load PAI context from `skills/CORE/SKILL.md`
36
+ - Initialize session state
37
+ - Capture session metadata
38
+
39
+ **Example Configuration:**
40
+ ```json
41
+ {
42
+ "SessionStart": [
43
+ {
44
+ "hooks": [
45
+ {
46
+ "type": "command",
47
+ "command": "${PAI_DIR}/Hooks/load-core-context.ts"
48
+ },
49
+ {
50
+ "type": "command",
51
+ "command": "${PAI_DIR}/Hooks/initialize-pai-session.ts"
52
+ },
53
+ {
54
+ "type": "command",
55
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type SessionStart"
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ **What They Do:**
64
+ - `load-core-context.ts` - Reads `skills/CORE/SKILL.md` and injects PAI context as `<system-reminder>` at session start
65
+ - `initialize-pai-session.ts` - Sets up session state and environment
66
+ - `capture-all-events.ts` - Logs event to `${PAI_DIR}/History/raw-outputs/YYYY-MM/YYYY-MM-DD_all-events.jsonl`
67
+
68
+ ---
69
+
70
+ ### 2. **SessionEnd**
71
+ **When:** Claude Code session terminates (conversation ends)
72
+ **Use Cases:**
73
+ - Generate session summaries
74
+ - Save session metadata
75
+ - Cleanup temporary state
76
+
77
+ **Example Configuration:**
78
+ ```json
79
+ {
80
+ "SessionEnd": [
81
+ {
82
+ "hooks": [
83
+ {
84
+ "type": "command",
85
+ "command": "${PAI_DIR}/Hooks/capture-session-summary.ts"
86
+ },
87
+ {
88
+ "type": "command",
89
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type SessionEnd"
90
+ }
91
+ ]
92
+ }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ **What They Do:**
98
+ - `capture-session-summary.ts` - Analyzes session activity and creates summary document in `${PAI_DIR}/History/sessions/YYYY-MM/`
99
+ - Captures: files changed, commands executed, tools used, session focus, duration
100
+
101
+ ---
102
+
103
+ ### 3. **UserPromptSubmit**
104
+ **When:** User submits a new prompt to Claude
105
+ **Use Cases:**
106
+ - Update UI indicators
107
+ - Pre-process user input
108
+ - Capture prompts for analysis
109
+
110
+ **Example Configuration:**
111
+ ```json
112
+ {
113
+ "UserPromptSubmit": [
114
+ {
115
+ "hooks": [
116
+ {
117
+ "type": "command",
118
+ "command": "${PAI_DIR}/Hooks/update-tab-titles.ts"
119
+ },
120
+ {
121
+ "type": "command",
122
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type UserPromptSubmit"
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ }
128
+ ```
129
+
130
+ **What They Do:**
131
+ - `update-tab-titles.ts` - Updates terminal tab title with task summary
132
+ - Launches background summarization for better tab titles
133
+ - Sets a processing indicator prefix
134
+
135
+ ---
136
+
137
+ ### 4. **Stop**
138
+ **When:** Main agent completes a response
139
+ **Use Cases:**
140
+ - Voice notifications for task completion
141
+ - Capture work summaries and learnings
142
+ - Update terminal tab with completion status
143
+
144
+ **Example Configuration:**
145
+ ```json
146
+ {
147
+ "Stop": [
148
+ {
149
+ "hooks": [
150
+ {
151
+ "type": "command",
152
+ "command": "${PAI_DIR}/Hooks/stop-hook.ts"
153
+ },
154
+ {
155
+ "type": "command",
156
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type Stop"
157
+ }
158
+ ]
159
+ }
160
+ ]
161
+ }
162
+ ```
163
+
164
+ **What They Do:**
165
+ - `stop-hook.ts` - THE CRITICAL HOOK for main agent completions
166
+ - Extracts `🎯 COMPLETED:` line from response
167
+ - Sends to voice server for TTS announcement
168
+ - Captures work summaries to `${PAI_DIR}/History/sessions/YYYY-MM/` or learnings to `${PAI_DIR}/History/learnings/YYYY-MM/`
169
+ - Updates terminal tab with completion indicator
170
+ - Sends event to observability dashboard
171
+
172
+ **Learning Detection:** Automatically identifies learning moments (2+ indicators: problem/issue/bug, fixed/solved, troubleshoot/debug, lesson/takeaway)
173
+
174
+ ---
175
+
176
+ ### 5. **SubagentStop**
177
+ **When:** Subagent (Task tool) completes execution
178
+ **Use Cases:**
179
+ - Agent-specific voice notifications
180
+ - Capture agent outputs
181
+ - Track multi-agent workflows
182
+
183
+ **Example Configuration:**
184
+ ```json
185
+ {
186
+ "SubagentStop": [
187
+ {
188
+ "hooks": [
189
+ {
190
+ "type": "command",
191
+ "command": "${PAI_DIR}/Hooks/subagent-stop-hook.ts"
192
+ },
193
+ {
194
+ "type": "command",
195
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type SubagentStop"
196
+ }
197
+ ]
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ **What They Do:**
204
+ - `subagent-stop-hook.ts` - Agent-specific completion handling
205
+ - Waits for Task tool result in transcript
206
+ - Extracts `[AGENT:type]` tag and completion message
207
+ - Routes to agent-specific voice (via agent's own voice notification in response)
208
+ - Captures agent output to appropriate history category
209
+ - Sends to observability dashboard
210
+
211
+ **Agent-Specific Routing:**
212
+ - `[AGENT:engineer]` → Engineer voice
213
+ - `[AGENT:researcher]` → Researcher voice
214
+ - `[AGENT:pentester]` → Pentester voice
215
+
216
+ ---
217
+
218
+ ### 6. **PreToolUse**
219
+ **When:** Before Claude executes any tool
220
+ **Use Cases:**
221
+ - Tool usage analytics
222
+ - Pre-execution validation
223
+ - Performance monitoring
224
+
225
+ **Example Configuration:**
226
+ ```json
227
+ {
228
+ "PreToolUse": [
229
+ {
230
+ "matcher": "*",
231
+ "hooks": [
232
+ {
233
+ "type": "command",
234
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type PreToolUse"
235
+ }
236
+ ]
237
+ }
238
+ ]
239
+ }
240
+ ```
241
+
242
+ **What They Do:**
243
+ - Captures tool name, input parameters, timestamp
244
+ - Logs to daily events file for analysis
245
+
246
+ ---
247
+
248
+ ### 7. **PostToolUse**
249
+ **When:** After Claude executes any tool
250
+ **Use Cases:**
251
+ - Capture tool outputs
252
+ - Error tracking
253
+ - Performance metrics
254
+
255
+ **Example Configuration:**
256
+ ```json
257
+ {
258
+ "PostToolUse": [
259
+ {
260
+ "matcher": "*",
261
+ "hooks": [
262
+ {
263
+ "type": "command",
264
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type PostToolUse"
265
+ }
266
+ ]
267
+ }
268
+ ]
269
+ }
270
+ ```
271
+
272
+ **What They Do:**
273
+ - Captures tool output, execution time, success/failure
274
+ - Logs to `${PAI_DIR}/History/raw-outputs/YYYY-MM/YYYY-MM-DD_all-events.jsonl`
275
+ - Powers observability dashboard
276
+
277
+ ---
278
+
279
+ ### 8. **PreCompact**
280
+ **When:** Before Claude compacts context (long conversations)
281
+ **Use Cases:**
282
+ - Preserve important context
283
+ - Log compaction events
284
+ - Pre-compaction cleanup
285
+
286
+ **Example Configuration:**
287
+ ```json
288
+ {
289
+ "PreCompact": [
290
+ {
291
+ "matcher": "",
292
+ "hooks": [
293
+ {
294
+ "type": "command",
295
+ "command": "${PAI_DIR}/Hooks/context-compression-hook.ts"
296
+ },
297
+ {
298
+ "type": "command",
299
+ "command": "${PAI_DIR}/Hooks/capture-all-events.ts --event-type PreCompact"
300
+ }
301
+ ]
302
+ }
303
+ ]
304
+ }
305
+ ```
306
+
307
+ **What They Do:**
308
+ - `context-compression-hook.ts` - Handles context preservation before compaction
309
+ - Captures metadata about compaction events
310
+
311
+ ---
312
+
313
+ ## Configuration
314
+
315
+ ### Location
316
+ **File:** `~/.claude/settings.json`
317
+ **Section:** `"hooks": { ... }`
318
+
319
+ ### Environment Variables
320
+ Hooks have access to all environment variables from `~/.claude/settings.json` `"env"` section:
321
+
322
+ ```json
323
+ {
324
+ "env": {
325
+ "PAI_DIR": "${HOME}/.claude",
326
+ "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000"
327
+ }
328
+ }
329
+ ```
330
+
331
+ **Key Variables:**
332
+ - `PAI_DIR` - PAI installation directory (always `${HOME}/.claude` or `~/.claude`)
333
+ - Hook scripts reference `${PAI_DIR}` in command paths
334
+
335
+ ### Hook Configuration Structure
336
+
337
+ ```json
338
+ {
339
+ "hooks": {
340
+ "HookEventName": [
341
+ {
342
+ "matcher": "pattern",
343
+ "hooks": [
344
+ {
345
+ "type": "command",
346
+ "command": "${PAI_DIR}/Hooks/my-hook.ts --arg value"
347
+ }
348
+ ]
349
+ }
350
+ ]
351
+ }
352
+ }
353
+ ```
354
+
355
+ **Fields:**
356
+ - `HookEventName` - One of: SessionStart, SessionEnd, UserPromptSubmit, Stop, SubagentStop, PreToolUse, PostToolUse, PreCompact
357
+ - `matcher` - Pattern to match (use `"*"` for all tools, or specific tool names)
358
+ - `type` - Always `"command"` (executes external script)
359
+ - `command` - Path to executable hook script (TypeScript/Python/Bash)
360
+
361
+ ### Hook Input (stdin)
362
+ All hooks receive JSON data on stdin:
363
+
364
+ ```typescript
365
+ {
366
+ session_id: string; // Unique session identifier
367
+ transcript_path: string; // Path to JSONL transcript
368
+ hook_event_name: string; // Event that triggered hook
369
+ prompt?: string; // User prompt (UserPromptSubmit only)
370
+ tool_name?: string; // Tool name (PreToolUse/PostToolUse)
371
+ tool_input?: any; // Tool parameters (PreToolUse)
372
+ tool_output?: any; // Tool result (PostToolUse)
373
+ // ... event-specific fields
374
+ }
375
+ ```
376
+
377
+ ---
378
+
379
+ ## Common Patterns
380
+
381
+ ### 1. Voice Notifications
382
+
383
+ **Pattern:** Extract completion message → Send to voice server
384
+
385
+ ```typescript
386
+ // stop-hook.ts pattern
387
+ const completionMessage = extractCompletion(lastMessage);
388
+
389
+ const payload = {
390
+ title: 'PAI',
391
+ message: completionMessage,
392
+ voice_enabled: true,
393
+ voice_id: 'YOUR_VOICE_ID' // Configure with your ElevenLabs voice
394
+ };
395
+
396
+ await fetch('http://localhost:8888/notify', {
397
+ method: 'POST',
398
+ headers: { 'Content-Type': 'application/json' },
399
+ body: JSON.stringify(payload)
400
+ });
401
+ ```
402
+
403
+ Configure voice IDs in your `~/.claude/settings.json` env section.
404
+
405
+ ---
406
+
407
+ ### 2. History Capture (UOCS Pattern)
408
+
409
+ **Pattern:** Parse structured response → Save to appropriate history directory
410
+
411
+ **File Naming Convention:**
412
+ ```
413
+ YYYY-MM-DD-HHMMSS_TYPE_description.md
414
+ ```
415
+
416
+ **Types:**
417
+ - `WORK` - General task completions
418
+ - `LEARNING` - Problem-solving learnings
419
+ - `SESSION` - Session summaries
420
+ - `RESEARCH` - Research findings (from agents)
421
+ - `FEATURE` - Feature implementations (from agents)
422
+ - `DECISION` - Architectural decisions (from agents)
423
+
424
+ **Structured Sections Parsed:**
425
+ - `📋 SUMMARY:` - Brief overview
426
+ - `🔍 ANALYSIS:` - Key findings
427
+ - `⚡ ACTIONS:` - Steps taken
428
+ - `✅ RESULTS:` - Outcomes
429
+ - `📊 STATUS:` - Current state
430
+ - `➡️ NEXT:` - Follow-up actions
431
+ - `🎯 COMPLETED:` - **Voice notification line**
432
+
433
+ ---
434
+
435
+ ### 3. Agent Type Detection
436
+
437
+ **Pattern:** Identify which agent is executing → Route appropriately
438
+
439
+ ```typescript
440
+ // Agent detection patterns
441
+ let agentName = 'main';
442
+
443
+ // Detect from Task tool
444
+ if (hookData.tool_name === 'Task' && hookData.tool_input?.subagent_type) {
445
+ agentName = hookData.tool_input.subagent_type;
446
+ }
447
+
448
+ // Detect from env variable
449
+ else if (process.env.CLAUDE_CODE_AGENT) {
450
+ agentName = process.env.CLAUDE_CODE_AGENT;
451
+ }
452
+
453
+ // Detect from path
454
+ else if (hookData.cwd && hookData.cwd.includes('/Agents/')) {
455
+ const agentMatch = hookData.cwd.match(/\/agents\/([^\/]+)/i);
456
+ if (agentMatch) agentName = agentMatch[1];
457
+ }
458
+ ```
459
+
460
+ **Session Mapping:** `${PAI_DIR}/agent-sessions.json`
461
+ ```json
462
+ {
463
+ "session-id-abc123": "engineer",
464
+ "session-id-def456": "researcher"
465
+ }
466
+ ```
467
+
468
+ ---
469
+
470
+ ### 4. Observability Integration
471
+
472
+ **Pattern:** Send event to dashboard → Fail silently if offline
473
+
474
+ ```typescript
475
+ await sendEventToObservability({
476
+ source_app: agentName,
477
+ session_id: hookInput.session_id,
478
+ hook_event_type: 'Stop',
479
+ timestamp: getCurrentTimestamp(),
480
+ transcript_path: hookInput.transcript_path,
481
+ summary: completionMessage,
482
+ }).catch(() => {
483
+ // Silently fail - dashboard may not be running
484
+ });
485
+ ```
486
+
487
+ **Dashboard URLs:**
488
+ - Server: `http://localhost:4000`
489
+ - Client: `http://localhost:5173`
490
+
491
+ ---
492
+
493
+ ### 5. Async Non-Blocking Execution
494
+
495
+ **Pattern:** Hook executes quickly → Launch background processes for slow operations
496
+
497
+ ```typescript
498
+ // Set immediate tab title (fast)
499
+ process.stderr.write(`\x1b]0;${titleWithEmoji}\x07`);
500
+
501
+ // Launch background process for AI summary (slow)
502
+ Bun.spawn(['bun', `${paiDir}/Hooks/update-tab-title.ts`, prompt], {
503
+ stdout: 'ignore',
504
+ stderr: 'ignore',
505
+ stdin: 'ignore'
506
+ });
507
+
508
+ process.exit(0); // Exit immediately
509
+ ```
510
+
511
+ **Key Principle:** Hooks must never block Claude Code. Always exit quickly, use background processes for slow work.
512
+
513
+ ---
514
+
515
+ ### 6. Graceful Failure
516
+
517
+ **Pattern:** Wrap everything in try/catch → Log errors → Exit successfully
518
+
519
+ ```typescript
520
+ async function main() {
521
+ try {
522
+ // Hook logic here
523
+ } catch (error) {
524
+ // Log but don't fail
525
+ console.error('Hook error:', error);
526
+ }
527
+
528
+ process.exit(0); // Always exit 0
529
+ }
530
+ ```
531
+
532
+ **Why:** If hooks crash, Claude Code may freeze. Always exit cleanly.
533
+
534
+ ---
535
+
536
+ ## Creating Custom Hooks
537
+
538
+ ### Step 1: Choose Hook Event
539
+ Decide which event should trigger your hook (SessionStart, Stop, PostToolUse, etc.)
540
+
541
+ ### Step 2: Create Hook Script
542
+ **Location:** `${PAI_DIR}/Hooks/my-custom-hook.ts`
543
+
544
+ **Template:**
545
+ ```typescript
546
+ #!/usr/bin/env bun
547
+
548
+ interface HookInput {
549
+ session_id: string;
550
+ transcript_path: string;
551
+ hook_event_name: string;
552
+ // ... event-specific fields
553
+ }
554
+
555
+ async function main() {
556
+ try {
557
+ // Read stdin
558
+ const input = await Bun.stdin.text();
559
+ const data: HookInput = JSON.parse(input);
560
+
561
+ // Your hook logic here
562
+ console.log(`Hook triggered: ${data.hook_event_name}`);
563
+
564
+ // Example: Read transcript
565
+ const fs = require('fs');
566
+ const transcript = fs.readFileSync(data.transcript_path, 'utf-8');
567
+
568
+ // Do something with the data
569
+
570
+ } catch (error) {
571
+ // Log but don't fail
572
+ console.error('Hook error:', error);
573
+ }
574
+
575
+ process.exit(0); // Always exit 0
576
+ }
577
+
578
+ main();
579
+ ```
580
+
581
+ ### Step 3: Make Executable
582
+ ```bash
583
+ chmod +x ${PAI_DIR}/Hooks/my-custom-hook.ts
584
+ ```
585
+
586
+ ### Step 4: Add to settings.json
587
+ ```json
588
+ {
589
+ "hooks": {
590
+ "Stop": [
591
+ {
592
+ "hooks": [
593
+ {
594
+ "type": "command",
595
+ "command": "${PAI_DIR}/Hooks/my-custom-hook.ts"
596
+ }
597
+ ]
598
+ }
599
+ ]
600
+ }
601
+ }
602
+ ```
603
+
604
+ ### Step 5: Test
605
+ ```bash
606
+ # Test hook directly
607
+ echo '{"session_id":"test","transcript_path":"/tmp/test.jsonl","hook_event_name":"Stop"}' | bun ${PAI_DIR}/Hooks/my-custom-hook.ts
608
+ ```
609
+
610
+ ### Step 6: Restart Claude Code
611
+ Hooks are loaded at startup. Restart to apply changes.
612
+
613
+ ---
614
+
615
+ ## Hook Development Best Practices
616
+
617
+ ### 1. **Fast Execution**
618
+ - Hooks should complete in < 500ms
619
+ - Use background processes for slow work (AI API calls, file processing)
620
+ - Exit immediately after launching background work
621
+
622
+ ### 2. **Graceful Failure**
623
+ - Always wrap in try/catch
624
+ - Log errors to stderr (available in hook debug logs)
625
+ - Always `process.exit(0)` - never throw or exit(1)
626
+
627
+ ### 3. **Non-Blocking**
628
+ - Never wait for external services (unless they respond quickly)
629
+ - Use `.catch(() => {})` for async operations
630
+ - Fail silently if optional services are offline
631
+
632
+ ### 4. **Stdin Reading**
633
+ - Use timeout when reading stdin (Claude Code may not send data immediately)
634
+ - Handle empty/invalid input gracefully
635
+
636
+ ```typescript
637
+ const decoder = new TextDecoder();
638
+ const reader = Bun.stdin.stream().getReader();
639
+
640
+ const timeoutPromise = new Promise<void>((resolve) => {
641
+ setTimeout(() => resolve(), 500); // 500ms timeout
642
+ });
643
+
644
+ await Promise.race([readPromise, timeoutPromise]);
645
+ ```
646
+
647
+ ### 5. **File I/O**
648
+ - Check `existsSync()` before reading files
649
+ - Create directories with `{ recursive: true }`
650
+
651
+ ### 6. **Environment Access**
652
+ - All `settings.json` env vars available via `process.env`
653
+ - Use `${PAI_DIR}` in settings.json for portability
654
+ - Access in code via `process.env.PAI_DIR`
655
+
656
+ ### 7. **Observability**
657
+ - Send events to dashboard for visibility
658
+ - Include all relevant metadata (session_id, tool_name, etc.)
659
+ - Use `.catch(() => {})` - dashboard may be offline
660
+
661
+ ---
662
+
663
+ ## Troubleshooting
664
+
665
+ ### Hook Not Running
666
+
667
+ **Check:**
668
+ 1. Is hook script executable? `chmod +x ${PAI_DIR}/Hooks/my-hook.ts`
669
+ 2. Is path correct in settings.json? Use `${PAI_DIR}/Hooks/...`
670
+ 3. Is settings.json valid JSON? `jq . ~/.claude/settings.json`
671
+ 4. Did you restart Claude Code after editing settings.json?
672
+
673
+ **Debug:**
674
+ ```bash
675
+ # Test hook directly
676
+ echo '{"session_id":"test","transcript_path":"/tmp/test.jsonl","hook_event_name":"Stop"}' | bun ${PAI_DIR}/Hooks/my-hook.ts
677
+ ```
678
+
679
+ ---
680
+
681
+ ### Hook Hangs/Freezes Claude Code
682
+
683
+ **Cause:** Hook not exiting (infinite loop, waiting for input, blocking operation)
684
+
685
+ **Fix:**
686
+ 1. Add timeouts to all blocking operations
687
+ 2. Ensure `process.exit(0)` is always reached
688
+ 3. Use background processes for long operations
689
+ 4. Check stdin reading has timeout
690
+
691
+ **Prevention:**
692
+ ```typescript
693
+ // Always use timeout
694
+ setTimeout(() => {
695
+ console.error('Hook timeout - exiting');
696
+ process.exit(0);
697
+ }, 5000); // 5 second max
698
+ ```
699
+
700
+ ---
701
+
702
+ ### Voice Notifications Not Working
703
+
704
+ **Check:**
705
+ 1. Is voice server running? `curl http://localhost:8888/health`
706
+ 2. Is voice_id correct? See your voice configuration
707
+ 3. Is message format correct? `{"message":"...", "voice_id":"...", "title":"..."}`
708
+
709
+ **Debug:**
710
+ ```bash
711
+ # Test voice server directly
712
+ curl -X POST http://localhost:8888/notify \
713
+ -H "Content-Type: application/json" \
714
+ -d '{"message":"Test message","voice_id":"YOUR_VOICE_ID","title":"Test"}'
715
+ ```
716
+
717
+ **Common Issues:**
718
+ - Wrong voice_id → Silent failure (invalid ID)
719
+ - Voice server offline → Hook continues (graceful failure)
720
+ - No `🎯 COMPLETED:` line → No voice notification extracted
721
+
722
+ ---
723
+
724
+ ### History Not Capturing
725
+
726
+ **Check:**
727
+ 1. Does `${PAI_DIR}/History/` directory exist?
728
+ 2. Are structured sections present in response? (`📋 SUMMARY:`, `🎯 COMPLETED:`, etc.)
729
+ 3. Is hook actually running? Check `${PAI_DIR}/History/raw-outputs/` for events
730
+ 4. File permissions? `ls -la ${PAI_DIR}/History/sessions/`
731
+
732
+ **Debug:**
733
+ ```bash
734
+ # Check recent captures
735
+ ls -lt ${PAI_DIR}/History/sessions/$(date +%Y-%m)/ | head -10
736
+
737
+ # Check raw events
738
+ tail ${PAI_DIR}/History/raw-outputs/$(date +%Y-%m)/$(date +%Y-%m-%d)_all-events.jsonl
739
+ ```
740
+
741
+ ---
742
+
743
+ ### Agent Detection Failing
744
+
745
+ **Check:**
746
+ 1. Is `${PAI_DIR}/agent-sessions.json` writable?
747
+ 2. Is `[AGENT:type]` tag in `🎯 COMPLETED:` line?
748
+ 3. Is agent running from correct directory?
749
+
750
+ **Fix:**
751
+ - Ensure agents include `[AGENT:type]` in completion line
752
+ - Verify Task tool passes `subagent_type` parameter
753
+
754
+ ---
755
+
756
+ ## Advanced Topics
757
+
758
+ ### Multi-Hook Execution Order
759
+
760
+ Hooks in same event execute **sequentially** in order defined in settings.json:
761
+
762
+ ```json
763
+ {
764
+ "Stop": [
765
+ {
766
+ "hooks": [
767
+ { "command": "${PAI_DIR}/Hooks/stop-hook.ts" }, // Runs first
768
+ { "command": "${PAI_DIR}/Hooks/capture-all-events.ts" } // Runs second
769
+ ]
770
+ }
771
+ ]
772
+ }
773
+ ```
774
+
775
+ **Note:** If first hook hangs, second won't run. Keep hooks fast!
776
+
777
+ ---
778
+
779
+ ### Matcher Patterns
780
+
781
+ `"matcher"` field filters which events trigger hook:
782
+
783
+ ```json
784
+ {
785
+ "PostToolUse": [
786
+ {
787
+ "matcher": "Bash", // Only Bash tool executions
788
+ "hooks": [...]
789
+ },
790
+ {
791
+ "matcher": "*", // All tool executions
792
+ "hooks": [...]
793
+ }
794
+ ]
795
+ }
796
+ ```
797
+
798
+ **Patterns:**
799
+ - `"*"` - All events
800
+ - `"Bash"` - Specific tool name
801
+ - `""` - Empty (all events, same as `*`)
802
+
803
+ ---
804
+
805
+ ### Hook Data Payloads by Event Type
806
+
807
+ **SessionStart:**
808
+ ```typescript
809
+ {
810
+ session_id: string;
811
+ transcript_path: string;
812
+ hook_event_name: "SessionStart";
813
+ cwd: string;
814
+ }
815
+ ```
816
+
817
+ **UserPromptSubmit:**
818
+ ```typescript
819
+ {
820
+ session_id: string;
821
+ transcript_path: string;
822
+ hook_event_name: "UserPromptSubmit";
823
+ prompt: string; // The user's prompt text
824
+ }
825
+ ```
826
+
827
+ **PreToolUse:**
828
+ ```typescript
829
+ {
830
+ session_id: string;
831
+ transcript_path: string;
832
+ hook_event_name: "PreToolUse";
833
+ tool_name: string;
834
+ tool_input: any; // Tool parameters
835
+ }
836
+ ```
837
+
838
+ **PostToolUse:**
839
+ ```typescript
840
+ {
841
+ session_id: string;
842
+ transcript_path: string;
843
+ hook_event_name: "PostToolUse";
844
+ tool_name: string;
845
+ tool_input: any;
846
+ tool_output: any; // Tool result
847
+ error?: string; // If tool failed
848
+ }
849
+ ```
850
+
851
+ **Stop:**
852
+ ```typescript
853
+ {
854
+ session_id: string;
855
+ transcript_path: string;
856
+ hook_event_name: "Stop";
857
+ }
858
+ ```
859
+
860
+ **SubagentStop:**
861
+ ```typescript
862
+ {
863
+ session_id: string;
864
+ transcript_path: string;
865
+ hook_event_name: "SubagentStop";
866
+ }
867
+ ```
868
+
869
+ **SessionEnd:**
870
+ ```typescript
871
+ {
872
+ conversation_id: string; // Note: different field name
873
+ timestamp: string;
874
+ }
875
+ ```
876
+
877
+ ---
878
+
879
+ ## Quick Reference Card
880
+
881
+ ```
882
+ HOOK LIFECYCLE:
883
+ 1. Event occurs (SessionStart, Stop, etc.)
884
+ 2. Claude Code writes hook data to stdin
885
+ 3. Hook script executes
886
+ 4. Hook reads stdin (with timeout)
887
+ 5. Hook performs actions (voice, capture, etc.)
888
+ 6. Hook exits 0 (always succeeds)
889
+ 7. Claude Code continues
890
+
891
+ KEY FILES:
892
+ ~/.claude/settings.json Hook configuration
893
+ ${PAI_DIR}/Hooks/ Hook scripts
894
+ ${PAI_DIR}/History/raw-outputs/ Event logs (JSONL)
895
+ ${PAI_DIR}/History/sessions/ Work summaries
896
+ ${PAI_DIR}/History/learnings/ Learning captures
897
+ ${PAI_DIR}/agent-sessions.json Session→Agent mapping
898
+
899
+ CRITICAL HOOKS:
900
+ stop-hook.ts Voice + history capture (main agent)
901
+ subagent-stop-hook.ts Voice + history capture (subagents)
902
+ load-core-context.ts PAI context loading
903
+ capture-all-events.ts Universal event logger
904
+
905
+ VOICE SERVER:
906
+ URL: http://localhost:8888/notify
907
+ Payload: {"message":"...", "voice_id":"...", "title":"..."}
908
+
909
+ OBSERVABILITY:
910
+ Server: http://localhost:4000
911
+ Client: http://localhost:5173
912
+ Events: All hooks send to /events endpoint
913
+ ```