@goodfoot/agent-hooks 1.0.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 (68) hide show
  1. package/README.md +137 -0
  2. package/dist/agents/antigravity/events.js +42 -0
  3. package/dist/agents/antigravity/hooks.js +59 -0
  4. package/dist/agents/antigravity/index.js +28 -0
  5. package/dist/agents/antigravity/outputs.js +81 -0
  6. package/dist/agents/antigravity/transport.js +84 -0
  7. package/dist/agents/antigravity/types.js +11 -0
  8. package/dist/agents/claude-code/env.js +27 -0
  9. package/dist/agents/claude-code/events.js +87 -0
  10. package/dist/agents/claude-code/hooks.js +1000 -0
  11. package/dist/agents/claude-code/index.js +57 -0
  12. package/dist/agents/claude-code/outputs.js +579 -0
  13. package/dist/agents/claude-code/tool-helpers.js +840 -0
  14. package/dist/agents/claude-code/transport.js +254 -0
  15. package/dist/agents/claude-code/types.js +11 -0
  16. package/dist/agents/codex/cli-support.js +318 -0
  17. package/dist/agents/codex/constants.js +29 -0
  18. package/dist/agents/codex/events.js +68 -0
  19. package/dist/agents/codex/hooks.js +93 -0
  20. package/dist/agents/codex/index.js +21 -0
  21. package/dist/agents/codex/outputs.js +189 -0
  22. package/dist/agents/codex/scaffold.js +273 -0
  23. package/dist/agents/codex/transport.js +139 -0
  24. package/dist/agents/codex/types.js +1 -0
  25. package/dist/cli.js +1357 -0
  26. package/dist/constants.js +39 -0
  27. package/dist/core/define-hook.js +61 -0
  28. package/dist/core/env.js +186 -0
  29. package/dist/core/logger.js +508 -0
  30. package/dist/core/stdin.js +38 -0
  31. package/dist/core/transport.js +231 -0
  32. package/dist/core/types.js +10 -0
  33. package/dist/index.js +17 -0
  34. package/dist/scaffold.js +512 -0
  35. package/package.json +61 -0
  36. package/types/agents/antigravity/events.d.ts +50 -0
  37. package/types/agents/antigravity/hooks.d.ts +76 -0
  38. package/types/agents/antigravity/index.d.ts +27 -0
  39. package/types/agents/antigravity/outputs.d.ts +107 -0
  40. package/types/agents/antigravity/transport.d.ts +47 -0
  41. package/types/agents/antigravity/types.d.ts +58 -0
  42. package/types/agents/claude-code/env.d.ts +27 -0
  43. package/types/agents/claude-code/events.d.ts +69 -0
  44. package/types/agents/claude-code/hooks.d.ts +1182 -0
  45. package/types/agents/claude-code/index.d.ts +23 -0
  46. package/types/agents/claude-code/outputs.d.ts +1160 -0
  47. package/types/agents/claude-code/tool-helpers.d.ts +807 -0
  48. package/types/agents/claude-code/transport.d.ts +119 -0
  49. package/types/agents/claude-code/types.d.ts +557 -0
  50. package/types/agents/codex/cli-support.d.ts +105 -0
  51. package/types/agents/codex/constants.d.ts +10 -0
  52. package/types/agents/codex/events.d.ts +63 -0
  53. package/types/agents/codex/hooks.d.ts +135 -0
  54. package/types/agents/codex/index.d.ts +19 -0
  55. package/types/agents/codex/outputs.d.ts +190 -0
  56. package/types/agents/codex/scaffold.d.ts +14 -0
  57. package/types/agents/codex/transport.d.ts +74 -0
  58. package/types/agents/codex/types.d.ts +131 -0
  59. package/types/cli.d.ts +315 -0
  60. package/types/constants.d.ts +9 -0
  61. package/types/core/define-hook.d.ts +39 -0
  62. package/types/core/env.d.ts +150 -0
  63. package/types/core/logger.d.ts +476 -0
  64. package/types/core/stdin.d.ts +21 -0
  65. package/types/core/transport.d.ts +131 -0
  66. package/types/core/types.d.ts +137 -0
  67. package/types/index.d.ts +18 -0
  68. package/types/scaffold.d.ts +46 -0
@@ -0,0 +1,1000 @@
1
+ /**
2
+ * Hook factory functions for Claude Code hooks.
3
+ *
4
+ * Provides typed factory functions for all 30 hook types that handle:
5
+ * - Input type narrowing based on hook event type
6
+ * - Output type enforcement via return types
7
+ * - Error wrapping with automatic logging
8
+ * - Logger context injection
9
+ *
10
+ * Each factory accepts a HookConfig with optional matcher and timeout settings,
11
+ * and returns a function that the runtime invokes when the hook file executes.
12
+ * @module
13
+ * @example
14
+ * ```typescript
15
+ * import { preToolUseHook, preToolUseOutput } from '@goodfoot/agent-hooks/claude-code';
16
+ *
17
+ * export default preToolUseHook({ matcher: 'Bash' }, async (input, { logger }) => {
18
+ * logger.info('Processing Bash command');
19
+ * return preToolUseOutput({ allow: true });
20
+ * });
21
+ * ```
22
+ * @see https://code.claude.com/docs/en/hooks
23
+ */
24
+ import { defineHook } from "../../core/define-hook.js";
25
+ import { persistEnvVar, persistEnvVars } from "../../core/env.js";
26
+ import { logger } from "../../core/logger.js";
27
+ import { ADVISORY_EVENTS } from "./events.js";
28
+ // ============================================================================
29
+ // Generic Factory
30
+ // ============================================================================
31
+ /**
32
+ * The runtime policy gate injected into every Claude Code factory: rejects
33
+ * `unexpectedError: "continue"` at factory-call time for any event not on
34
+ * the advisory allow-list ({@link ADVISORY_EVENTS}). Every other policy
35
+ * value (including undefined, meaning the default `"error"`) is accepted
36
+ * for every event. {@link defineHook} fails closed — throws instead of
37
+ * creating the hook — when this gate returns false.
38
+ */
39
+ const advisoryPolicyGate = (eventName, policy) => policy !== "continue" || ADVISORY_EVENTS.includes(eventName);
40
+ /**
41
+ * Builds the handler context for a SessionStart hook: the extended context
42
+ * carries the env-persistence utilities alongside the logger. Bound at
43
+ * factory time via {@link defineHook}'s `createContext` seam.
44
+ */
45
+ function createSessionStartContext() {
46
+ return { logger, persistEnvVar, persistEnvVars };
47
+ }
48
+ /**
49
+ * Creates a hook function for a specific hook type on the shared core.
50
+ *
51
+ * This is the internal implementation used by all typed factories. It binds
52
+ * the event name, injects the advisory allow-list policy gate, and attaches
53
+ * the SessionStart-specific context factory where applicable.
54
+ * @param hookEventName - The hook event name
55
+ * @param config - Hook configuration
56
+ * @param handler - The handler function to wrap
57
+ * @returns A wrapped hook function carrying its metadata for the driver
58
+ * @internal
59
+ */
60
+ function createHookFunction(hookEventName, config, handler) {
61
+ const isSessionStart = hookEventName === "SessionStart";
62
+ return defineHook(hookEventName, isSessionStart ? { ...config, createContext: createSessionStartContext } : config, handler, advisoryPolicyGate);
63
+ }
64
+ /** @inheritdoc */
65
+ export function preToolUseHook(config, handler) {
66
+ return createHookFunction("PreToolUse", config, handler);
67
+ }
68
+ /** @inheritdoc */
69
+ export function postToolUseHook(config, handler) {
70
+ return createHookFunction("PostToolUse", config, handler);
71
+ }
72
+ /** @inheritdoc */
73
+ export function postToolUseFailureHook(config, handler) {
74
+ return createHookFunction("PostToolUseFailure", config, handler);
75
+ }
76
+ // ============================================================================
77
+ // PostToolBatch Hook Factory
78
+ // ============================================================================
79
+ /**
80
+ * Creates a PostToolBatch hook handler.
81
+ *
82
+ * PostToolBatch hooks fire exactly once after every tool call in a batch has
83
+ * resolved, before the next model request. Unlike PostToolUse — which fires per
84
+ * tool and may run concurrently for parallel tool calls — PostToolBatch receives
85
+ * the full batch via `input.tool_calls`, allowing you to:
86
+ * - Inspect or summarize all tool calls in a single turn together
87
+ * - Inject additional context once per batch instead of once per tool
88
+ *
89
+ * **Matcher**: No matcher support - fires once per batch
90
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
91
+ * @param handler - The handler function to execute
92
+ * @returns A hook function that can be exported as the default export
93
+ * @example
94
+ * ```typescript
95
+ * import { postToolBatchHook, postToolBatchOutput } from '@goodfoot/agent-hooks/claude-code';
96
+ *
97
+ * export default postToolBatchHook({}, async (input, { logger }) => {
98
+ * logger.info('Tool batch completed', { count: input.tool_calls.length });
99
+ *
100
+ * return postToolBatchOutput({
101
+ * hookSpecificOutput: {
102
+ * additionalContext: `Reviewed ${input.tool_calls.length} tool calls`
103
+ * }
104
+ * });
105
+ * });
106
+ * ```
107
+ * @see https://code.claude.com/docs/en/hooks#posttoolbatch
108
+ */
109
+ export function postToolBatchHook(config, handler) {
110
+ return createHookFunction("PostToolBatch", config, handler);
111
+ }
112
+ // ============================================================================
113
+ // Notification Hook Factory
114
+ // ============================================================================
115
+ /**
116
+ * Creates a Notification hook handler.
117
+ *
118
+ * Notification hooks fire when Claude Code sends a notification, allowing you to:
119
+ * - Forward notifications to external systems
120
+ * - Log important events
121
+ * - Trigger custom alerting
122
+ *
123
+ * **Matcher**: Matches against `notification_type`
124
+ * @param config - Hook configuration with optional matcher and timeout
125
+ * @param handler - The handler function to execute
126
+ * @returns A hook function that can be exported as the default export
127
+ * @example
128
+ * ```typescript
129
+ * import { notificationHook, notificationOutput } from '@goodfoot/agent-hooks/claude-code';
130
+ *
131
+ * // Forward notifications to Slack
132
+ * export default notificationHook({}, async (input, { logger }) => {
133
+ * logger.info('Notification received', {
134
+ * type: input.notification_type,
135
+ * title: input.title
136
+ * });
137
+ *
138
+ * await sendSlackMessage(input.title ?? 'Notification', input.message);
139
+ *
140
+ * return notificationOutput({});
141
+ * });
142
+ * ```
143
+ * @see https://code.claude.com/docs/en/hooks#notification
144
+ */
145
+ export function notificationHook(config, handler) {
146
+ return createHookFunction("Notification", config, handler);
147
+ }
148
+ // ============================================================================
149
+ // UserPromptSubmit Hook Factory
150
+ // ============================================================================
151
+ /**
152
+ * Creates a UserPromptSubmit hook handler.
153
+ *
154
+ * UserPromptSubmit hooks fire when a user submits a prompt, allowing you to:
155
+ * - Add additional context or instructions
156
+ * - Log user interactions
157
+ * - Validate or transform prompts
158
+ *
159
+ * **Matcher**: No matcher support - fires on all prompt submissions
160
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
161
+ * @param handler - The handler function to execute
162
+ * @returns A hook function that can be exported as the default export
163
+ * @example
164
+ * ```typescript
165
+ * import { userPromptSubmitHook, userPromptSubmitOutput } from '@goodfoot/agent-hooks/claude-code';
166
+ *
167
+ * // Add project context to every prompt
168
+ * export default userPromptSubmitHook({}, async (input, { logger }) => {
169
+ * logger.debug('User prompt submitted', { promptLength: input.prompt.length });
170
+ *
171
+ * const projectContext = await getProjectContext();
172
+ *
173
+ * return userPromptSubmitOutput({
174
+ * additionalContext: projectContext
175
+ * });
176
+ * });
177
+ * ```
178
+ * @see https://code.claude.com/docs/en/hooks#userpromptsubmit
179
+ */
180
+ export function userPromptSubmitHook(config, handler) {
181
+ return createHookFunction("UserPromptSubmit", config, handler);
182
+ }
183
+ // ============================================================================
184
+ // UserPromptExpansion Hook Factory
185
+ // ============================================================================
186
+ /**
187
+ * Creates a UserPromptExpansion hook handler.
188
+ *
189
+ * UserPromptExpansion hooks fire when a user prompt is expanded from a slash
190
+ * command or MCP prompt, allowing you to:
191
+ * - Add context based on the command being invoked
192
+ * - Log slash command and MCP prompt usage
193
+ * - Observe prompt expansion events
194
+ *
195
+ * **Matcher**: No matcher support - fires on all prompt expansions
196
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
197
+ * @param handler - The handler function to execute
198
+ * @returns A hook function that can be exported as the default export
199
+ * @example
200
+ * ```typescript
201
+ * import { userPromptExpansionHook, userPromptExpansionOutput } from '@goodfoot/agent-hooks/claude-code';
202
+ *
203
+ * // Add context when a slash command is invoked
204
+ * export default userPromptExpansionHook({}, async (input, { logger }) => {
205
+ * logger.debug('Prompt expanded', { type: input.expansion_type, command: input.command_name });
206
+ *
207
+ * return userPromptExpansionOutput({
208
+ * hookSpecificOutput: {
209
+ * additionalContext: `Command: ${input.command_name}`
210
+ * }
211
+ * });
212
+ * });
213
+ * ```
214
+ * @see https://code.claude.com/docs/en/hooks#userpromptexpansion
215
+ */
216
+ export function userPromptExpansionHook(config, handler) {
217
+ return createHookFunction("UserPromptExpansion", config, handler);
218
+ }
219
+ // ============================================================================
220
+ // SessionStart Hook Factory
221
+ // ============================================================================
222
+ /**
223
+ * Creates a SessionStart hook handler.
224
+ *
225
+ * SessionStart hooks fire when a Claude Code session starts or restarts,
226
+ * allowing you to:
227
+ * - Initialize session state
228
+ * - Inject context or instructions
229
+ * - Persist environment variables for subsequent bash commands
230
+ * - Set up logging or monitoring
231
+ *
232
+ * **Matcher**: Matches against `source` ('startup', 'resume', 'clear', 'compact')
233
+ *
234
+ * **Context**: SessionStart hooks receive an extended context with `persistEnvVar`
235
+ * and `persistEnvVars` functions for setting environment variables.
236
+ * @param config - Hook configuration with optional matcher and timeout
237
+ * @param handler - The handler function to execute
238
+ * @returns A hook function that can be exported as the default export
239
+ * @example
240
+ * ```typescript
241
+ * import { sessionStartHook, sessionStartOutput } from '@goodfoot/agent-hooks/claude-code';
242
+ *
243
+ * // Persist environment variables for the session
244
+ * export default sessionStartHook({ matcher: 'startup' }, async (input, { logger, persistEnvVar }) => {
245
+ * logger.info('New session started', {
246
+ * sessionId: input.session_id,
247
+ * cwd: input.cwd
248
+ * });
249
+ *
250
+ * // Set environment variables for all subsequent bash commands
251
+ * persistEnvVar('NODE_ENV', 'development');
252
+ * persistEnvVar('DEBUG', 'true');
253
+ *
254
+ * return sessionStartOutput({});
255
+ * });
256
+ * ```
257
+ * @example
258
+ * ```typescript
259
+ * // Set multiple environment variables at once
260
+ * export default sessionStartHook({}, async (input, { persistEnvVars }) => {
261
+ * persistEnvVars({
262
+ * NODE_ENV: 'production',
263
+ * API_KEY: 'secret',
264
+ * DEBUG: 'false'
265
+ * });
266
+ *
267
+ * return sessionStartOutput({});
268
+ * });
269
+ * ```
270
+ * @see https://code.claude.com/docs/en/hooks#sessionstart
271
+ */
272
+ export function sessionStartHook(config, handler) {
273
+ return createHookFunction("SessionStart", config, handler);
274
+ }
275
+ // ============================================================================
276
+ // SessionEnd Hook Factory
277
+ // ============================================================================
278
+ /**
279
+ * Creates a SessionEnd hook handler.
280
+ *
281
+ * SessionEnd hooks fire when a Claude Code session ends, allowing you to:
282
+ * - Clean up session resources
283
+ * - Log session metrics
284
+ * - Persist session state
285
+ *
286
+ * **Matcher**: Matches against `reason` (the exit reason string)
287
+ * @param config - Hook configuration with optional matcher and timeout
288
+ * @param handler - The handler function to execute
289
+ * @returns A hook function that can be exported as the default export
290
+ * @example
291
+ * ```typescript
292
+ * import { sessionEndHook, sessionEndOutput } from '@goodfoot/agent-hooks/claude-code';
293
+ *
294
+ * // Log session end and clean up
295
+ * export default sessionEndHook({}, async (input, { logger }) => {
296
+ * logger.info('Session ended', {
297
+ * sessionId: input.session_id,
298
+ * reason: input.reason
299
+ * });
300
+ *
301
+ * await cleanupSessionResources(input.session_id);
302
+ *
303
+ * return sessionEndOutput({});
304
+ * });
305
+ * ```
306
+ * @see https://code.claude.com/docs/en/hooks#sessionend
307
+ */
308
+ export function sessionEndHook(config, handler) {
309
+ return createHookFunction("SessionEnd", config, handler);
310
+ }
311
+ // ============================================================================
312
+ // Stop Hook Factory
313
+ // ============================================================================
314
+ /**
315
+ * Creates a Stop hook handler.
316
+ *
317
+ * Stop hooks fire when Claude Code is about to stop, allowing you to:
318
+ * - Block the stop and require additional action
319
+ * - Confirm the user wants to stop
320
+ * - Clean up resources before stopping
321
+ *
322
+ * **Matcher**: No matcher support - fires on all stop events
323
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
324
+ * @param handler - The handler function to execute
325
+ * @returns A hook function that can be exported as the default export
326
+ * @example
327
+ * ```typescript
328
+ * import { stopHook, stopOutput } from '@goodfoot/agent-hooks/claude-code';
329
+ *
330
+ * // Block stop if there are pending changes
331
+ * export default stopHook({}, async (input, { logger }) => {
332
+ * const pendingChanges = await checkPendingChanges();
333
+ *
334
+ * if (pendingChanges.length > 0) {
335
+ * logger.warn('Blocking stop due to pending changes', {
336
+ * count: pendingChanges.length
337
+ * });
338
+ *
339
+ * return stopOutput({
340
+ * decision: 'block',
341
+ * reason: `There are ${pendingChanges.length} uncommitted changes`,
342
+ * systemMessage: 'Please commit or discard changes before stopping'
343
+ * });
344
+ * }
345
+ *
346
+ * logger.info('Approving stop');
347
+ * return stopOutput({ decision: 'approve' });
348
+ * });
349
+ * ```
350
+ * @see https://code.claude.com/docs/en/hooks#stop
351
+ */
352
+ export function stopHook(config, handler) {
353
+ return createHookFunction("Stop", config, handler);
354
+ }
355
+ // ============================================================================
356
+ // StopFailure Hook Factory
357
+ // ============================================================================
358
+ /**
359
+ * Creates a StopFailure hook handler.
360
+ *
361
+ * StopFailure hooks fire when Claude Code encounters an error while stopping
362
+ * (e.g., API errors, authentication failures, rate limits), allowing you to:
363
+ * - Log stop failure events and error details
364
+ * - Alert on unexpected session termination errors
365
+ * - Observe what error caused the failure
366
+ *
367
+ * **Matcher**: No matcher support - fires on all stop failure events
368
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
369
+ * @param handler - The handler function to execute
370
+ * @returns A hook function that can be exported as the default export
371
+ * @example
372
+ * ```typescript
373
+ * import { stopFailureHook, stopFailureOutput } from '@goodfoot/agent-hooks/claude-code';
374
+ *
375
+ * export default stopFailureHook({}, async (input, { logger }) => {
376
+ * logger.error('Session stopped due to error', {
377
+ * error: input.error,
378
+ * details: input.error_details
379
+ * });
380
+ * return stopFailureOutput({});
381
+ * });
382
+ * ```
383
+ * @see https://code.claude.com/docs/en/hooks#stopfailure
384
+ */
385
+ export function stopFailureHook(config, handler) {
386
+ return createHookFunction("StopFailure", config, handler);
387
+ }
388
+ // ============================================================================
389
+ // SubagentStart Hook Factory
390
+ // ============================================================================
391
+ /**
392
+ * Creates a SubagentStart hook handler.
393
+ *
394
+ * SubagentStart hooks fire when a subagent (Agent tool) starts, allowing you to:
395
+ * - Inject context for the subagent
396
+ * - Log subagent invocations
397
+ * - Configure subagent behavior
398
+ *
399
+ * **Matcher**: Matches against `agent_type` (e.g., 'explore', 'codebase-analysis')
400
+ * @param config - Hook configuration with optional matcher and timeout
401
+ * @param handler - The handler function to execute
402
+ * @returns A hook function that can be exported as the default export
403
+ * @example
404
+ * ```typescript
405
+ * import { subagentStartHook, subagentStartOutput } from '@goodfoot/agent-hooks/claude-code';
406
+ *
407
+ * // Add context for explore subagents
408
+ * export default subagentStartHook({ matcher: 'explore' }, async (input, { logger }) => {
409
+ * logger.info('Explore subagent starting', {
410
+ * agentId: input.agent_id,
411
+ * agentType: input.agent_type
412
+ * });
413
+ *
414
+ * return subagentStartOutput({
415
+ * additionalContext: 'Focus on finding patterns and conventions'
416
+ * });
417
+ * });
418
+ * ```
419
+ * @see https://code.claude.com/docs/en/hooks#subagentstart
420
+ */
421
+ export function subagentStartHook(config, handler) {
422
+ return createHookFunction("SubagentStart", config, handler);
423
+ }
424
+ // ============================================================================
425
+ // SubagentStop Hook Factory
426
+ // ============================================================================
427
+ /**
428
+ * Creates a SubagentStop hook handler.
429
+ *
430
+ * SubagentStop hooks fire when a subagent completes or stops, allowing you to:
431
+ * - Block the subagent from stopping
432
+ * - Process subagent results
433
+ * - Clean up subagent resources
434
+ * - Log subagent completion
435
+ *
436
+ * **Matcher**: Matches against `agent_type` (e.g., 'explore', 'codebase-analysis')
437
+ * @param config - Hook configuration with optional matcher and timeout
438
+ * @param handler - The handler function to execute
439
+ * @returns A hook function that can be exported as the default export
440
+ * @example
441
+ * ```typescript
442
+ * import { subagentStopHook, subagentStopOutput } from '@goodfoot/agent-hooks/claude-code';
443
+ *
444
+ * // Block explore subagents if task incomplete
445
+ * export default subagentStopHook({ matcher: 'explore' }, async (input, { logger }) => {
446
+ * logger.info('Subagent stopping', {
447
+ * agentId: input.agent_id,
448
+ * agentType: input.agent_type
449
+ * });
450
+ *
451
+ * // Block if transcript shows incomplete work
452
+ * return subagentStopOutput({
453
+ * decision: 'block',
454
+ * reason: 'Please verify exploration is complete'
455
+ * });
456
+ * });
457
+ * ```
458
+ * @see https://code.claude.com/docs/en/hooks#subagentstop
459
+ */
460
+ export function subagentStopHook(config, handler) {
461
+ return createHookFunction("SubagentStop", config, handler);
462
+ }
463
+ // ============================================================================
464
+ // PreCompact Hook Factory
465
+ // ============================================================================
466
+ /**
467
+ * Creates a PreCompact hook handler.
468
+ *
469
+ * PreCompact hooks fire before context compaction occurs, allowing you to:
470
+ * - Preserve important information before compaction
471
+ * - Log compaction events
472
+ * - Modify custom instructions for the compacted context
473
+ *
474
+ * **Matcher**: Matches against `trigger` ('manual', 'auto')
475
+ * @param config - Hook configuration with optional matcher and timeout
476
+ * @param handler - The handler function to execute
477
+ * @returns A hook function that can be exported as the default export
478
+ * @example
479
+ * ```typescript
480
+ * import { preCompactHook, preCompactOutput } from '@goodfoot/agent-hooks/claude-code';
481
+ *
482
+ * // Log compaction events and preserve context
483
+ * export default preCompactHook({}, async (input, { logger }) => {
484
+ * logger.info('Context compaction triggered', {
485
+ * trigger: input.trigger,
486
+ * hasCustomInstructions: input.custom_instructions !== null
487
+ * });
488
+ *
489
+ * return preCompactOutput({
490
+ * systemMessage: 'Remember: strict mode is enabled'
491
+ * });
492
+ * });
493
+ * ```
494
+ * @example
495
+ * ```typescript
496
+ * // Only handle manual compaction
497
+ * export default preCompactHook({ matcher: 'manual' }, async (input, { logger }) => {
498
+ * logger.info('Manual compaction requested');
499
+ * return preCompactOutput({});
500
+ * });
501
+ * ```
502
+ * @see https://code.claude.com/docs/en/hooks#precompact
503
+ */
504
+ export function preCompactHook(config, handler) {
505
+ return createHookFunction("PreCompact", config, handler);
506
+ }
507
+ // ============================================================================
508
+ // PostCompact Hook Factory
509
+ // ============================================================================
510
+ /**
511
+ * Creates a PostCompact hook handler.
512
+ *
513
+ * PostCompact hooks fire after context compaction completes, allowing you to:
514
+ * - Observe the compaction summary and details
515
+ * - Log compaction events
516
+ * - React to the new compacted state
517
+ *
518
+ * **Matcher**: Matches against `trigger` ('manual', 'auto')
519
+ * @param config - Hook configuration with optional matcher and timeout
520
+ * @param handler - The handler function to execute
521
+ * @returns A hook function that can be exported as the default export
522
+ * @example
523
+ * ```typescript
524
+ * import { postCompactHook, postCompactOutput } from '@goodfoot/agent-hooks/claude-code';
525
+ *
526
+ * export default postCompactHook({}, async (input, { logger }) => {
527
+ * logger.info('Context compaction completed', {
528
+ * trigger: input.trigger,
529
+ * summary: input.compact_summary
530
+ * });
531
+ * return postCompactOutput({});
532
+ * });
533
+ * ```
534
+ * @see https://code.claude.com/docs/en/hooks#postcompact
535
+ */
536
+ export function postCompactHook(config, handler) {
537
+ return createHookFunction("PostCompact", config, handler);
538
+ }
539
+ /** @inheritdoc */
540
+ export function permissionRequestHook(config, handler) {
541
+ return createHookFunction("PermissionRequest", config, handler);
542
+ }
543
+ // ============================================================================
544
+ // PermissionDenied Hook Factory
545
+ // ============================================================================
546
+ /**
547
+ * Creates a PermissionDenied hook handler.
548
+ *
549
+ * PermissionDenied hooks fire when a permission request is denied (either by the
550
+ * user or by a PermissionRequest hook), allowing you to:
551
+ * - Log permission denials for auditing
552
+ * - React to denied tool executions
553
+ * - Optionally request a retry via the output
554
+ *
555
+ * **Matcher**: Matches against `tool_name`
556
+ * @param config - Hook configuration with optional matcher and timeout
557
+ * @param handler - The handler function to execute
558
+ * @returns A hook function that can be exported as the default export
559
+ * @example
560
+ * ```typescript
561
+ * import { permissionDeniedHook, permissionDeniedOutput } from '@goodfoot/agent-hooks/claude-code';
562
+ *
563
+ * // Log all permission denials
564
+ * export default permissionDeniedHook({}, async (input, { logger }) => {
565
+ * logger.warn('Permission denied', {
566
+ * toolName: input.tool_name,
567
+ * reason: input.reason
568
+ * });
569
+ * return permissionDeniedOutput({});
570
+ * });
571
+ * ```
572
+ * @see https://code.claude.com/docs/en/hooks#permissiondenied
573
+ */
574
+ export function permissionDeniedHook(config, handler) {
575
+ return createHookFunction("PermissionDenied", config, handler);
576
+ }
577
+ // ============================================================================
578
+ // Setup Hook Factory
579
+ // ============================================================================
580
+ /**
581
+ * Creates a Setup hook handler.
582
+ *
583
+ * Setup hooks fire during initialization or maintenance, allowing you to:
584
+ * - Configure initial session state
585
+ * - Perform setup tasks before the session starts
586
+ * - Add context for maintenance operations
587
+ *
588
+ * **Matcher**: Matches against `trigger` ('init' or 'maintenance')
589
+ * @param config - Hook configuration with optional matcher and timeout
590
+ * @param handler - The handler function to execute
591
+ * @returns A hook function that can be exported as the default export
592
+ * @example
593
+ * ```typescript
594
+ * import { setupHook, setupOutput } from '@goodfoot/agent-hooks/claude-code';
595
+ *
596
+ * // Handle all setup events
597
+ * export default setupHook({}, async (input, { logger }) => {
598
+ * logger.info('Setup triggered', { trigger: input.trigger });
599
+ * return setupOutput({});
600
+ * });
601
+ *
602
+ * // Only handle initialization
603
+ * export default setupHook({ matcher: 'init' }, async (input, { logger }) => {
604
+ * logger.info('Initializing session');
605
+ * return setupOutput({
606
+ * hookSpecificOutput: {
607
+ * additionalContext: 'Session initialized with custom configuration'
608
+ * }
609
+ * });
610
+ * });
611
+ * ```
612
+ * @see https://code.claude.com/docs/en/hooks#setup
613
+ */
614
+ export function setupHook(config, handler) {
615
+ return createHookFunction("Setup", config, handler);
616
+ }
617
+ // ============================================================================
618
+ // TeammateIdle Hook Factory
619
+ // ============================================================================
620
+ /**
621
+ * Creates a TeammateIdle hook handler.
622
+ *
623
+ * TeammateIdle hooks fire when a teammate in a team is about to go idle,
624
+ * allowing you to:
625
+ * - Assign work to idle teammates
626
+ * - Log team activity
627
+ * - Coordinate multi-agent workflows
628
+ *
629
+ * **Matcher**: No matcher support - fires on all teammate idle events
630
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
631
+ * @param handler - The handler function to execute
632
+ * @returns A hook function that can be exported as the default export
633
+ * @example
634
+ * ```typescript
635
+ * import { teammateIdleHook, teammateIdleOutput } from '@goodfoot/agent-hooks/claude-code';
636
+ *
637
+ * // Log when teammates go idle
638
+ * export default teammateIdleHook({}, async (input, { logger }) => {
639
+ * logger.info('Teammate going idle', {
640
+ * teammateName: input.teammate_name,
641
+ * teamName: input.team_name
642
+ * });
643
+ *
644
+ * return teammateIdleOutput({});
645
+ * });
646
+ * ```
647
+ * @see https://code.claude.com/docs/en/hooks#teammateidle
648
+ */
649
+ export function teammateIdleHook(config, handler) {
650
+ return createHookFunction("TeammateIdle", config, handler);
651
+ }
652
+ // ============================================================================
653
+ // TaskCreated Hook Factory
654
+ // ============================================================================
655
+ /**
656
+ * Creates a TaskCreated hook handler.
657
+ *
658
+ * TaskCreated hooks fire when a new task is created and assigned to a teammate,
659
+ * allowing you to:
660
+ * - Observe task creation events
661
+ * - Log task assignments for auditing
662
+ * - React to new work being assigned
663
+ *
664
+ * **Matcher**: No matcher support - fires on all task creation events
665
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
666
+ * @param handler - The handler function to execute
667
+ * @returns A hook function that can be exported as the default export
668
+ * @example
669
+ * ```typescript
670
+ * import { taskCreatedHook, taskCreatedOutput } from '@goodfoot/agent-hooks/claude-code';
671
+ *
672
+ * // Log task creation
673
+ * export default taskCreatedHook({}, async (input, { logger }) => {
674
+ * logger.info('Task created', {
675
+ * taskId: input.task_id,
676
+ * taskSubject: input.task_subject
677
+ * });
678
+ *
679
+ * return taskCreatedOutput({});
680
+ * });
681
+ * ```
682
+ * @see https://code.claude.com/docs/en/hooks#taskcreated
683
+ */
684
+ export function taskCreatedHook(config, handler) {
685
+ return createHookFunction("TaskCreated", config, handler);
686
+ }
687
+ // ============================================================================
688
+ // TaskCompleted Hook Factory
689
+ // ============================================================================
690
+ /**
691
+ * Creates a TaskCompleted hook handler.
692
+ *
693
+ * TaskCompleted hooks fire when a task is being marked as completed,
694
+ * allowing you to:
695
+ * - Verify task completion
696
+ * - Log task metrics
697
+ * - Trigger follow-up actions
698
+ *
699
+ * **Matcher**: No matcher support - fires on all task completion events
700
+ * @param config - Hook configuration with optional timeout (matcher is ignored)
701
+ * @param handler - The handler function to execute
702
+ * @returns A hook function that can be exported as the default export
703
+ * @example
704
+ * ```typescript
705
+ * import { taskCompletedHook, taskCompletedOutput } from '@goodfoot/agent-hooks/claude-code';
706
+ *
707
+ * // Log task completion
708
+ * export default taskCompletedHook({}, async (input, { logger }) => {
709
+ * logger.info('Task completed', {
710
+ * taskId: input.task_id,
711
+ * taskSubject: input.task_subject
712
+ * });
713
+ *
714
+ * return taskCompletedOutput({});
715
+ * });
716
+ * ```
717
+ * @see https://code.claude.com/docs/en/hooks#taskcompleted
718
+ */
719
+ export function taskCompletedHook(config, handler) {
720
+ return createHookFunction("TaskCompleted", config, handler);
721
+ }
722
+ // ============================================================================
723
+ // Elicitation Hook Factory
724
+ // ============================================================================
725
+ /**
726
+ * Creates an Elicitation hook handler.
727
+ *
728
+ * Elicitation hooks fire when an MCP server requests user input, allowing you to:
729
+ * - Accept, decline, or cancel elicitation requests programmatically
730
+ * - Provide structured form input or URL-based auth responses
731
+ * - Log or audit elicitation requests
732
+ *
733
+ * **Matcher**: No matcher support - fires on all elicitation events
734
+ * @param config - Hook configuration with optional timeout
735
+ * @param handler - The handler function to execute
736
+ * @returns A hook function that can be exported as the default export
737
+ * @example
738
+ * ```typescript
739
+ * import { elicitationHook, elicitationOutput } from '@goodfoot/agent-hooks/claude-code';
740
+ *
741
+ * export default elicitationHook({}, async (input, { logger }) => {
742
+ * logger.info('Elicitation request', { server: input.mcp_server_name });
743
+ * return elicitationOutput({
744
+ * hookSpecificOutput: { action: 'accept', content: { approved: true } }
745
+ * });
746
+ * });
747
+ * ```
748
+ * @see https://code.claude.com/docs/en/hooks#elicitation
749
+ */
750
+ export function elicitationHook(config, handler) {
751
+ return createHookFunction("Elicitation", config, handler);
752
+ }
753
+ // ============================================================================
754
+ // ElicitationResult Hook Factory
755
+ // ============================================================================
756
+ /**
757
+ * Creates an ElicitationResult hook handler.
758
+ *
759
+ * ElicitationResult hooks fire with the result of an MCP elicitation request,
760
+ * allowing you to:
761
+ * - Observe elicitation outcomes
762
+ * - Modify the result before it is returned to the MCP server
763
+ * - Log elicitation completions
764
+ *
765
+ * **Matcher**: No matcher support - fires on all elicitation result events
766
+ * @param config - Hook configuration with optional timeout
767
+ * @param handler - The handler function to execute
768
+ * @returns A hook function that can be exported as the default export
769
+ * @example
770
+ * ```typescript
771
+ * import { elicitationResultHook, elicitationResultOutput } from '@goodfoot/agent-hooks/claude-code';
772
+ *
773
+ * export default elicitationResultHook({}, async (input, { logger }) => {
774
+ * logger.info('Elicitation result', { action: input.action });
775
+ * return elicitationResultOutput({});
776
+ * });
777
+ * ```
778
+ * @see https://code.claude.com/docs/en/hooks#elicitationresult
779
+ */
780
+ export function elicitationResultHook(config, handler) {
781
+ return createHookFunction("ElicitationResult", config, handler);
782
+ }
783
+ // ============================================================================
784
+ // ConfigChange Hook Factory
785
+ // ============================================================================
786
+ /**
787
+ * Creates a ConfigChange hook handler.
788
+ *
789
+ * ConfigChange hooks fire when Claude Code configuration changes, allowing you to:
790
+ * - React to settings file changes
791
+ * - Log or audit configuration changes
792
+ * - Apply custom logic when settings are updated
793
+ *
794
+ * **Matcher**: Matches against `source` ('user_settings', 'project_settings', etc.)
795
+ * @param config - Hook configuration with optional matcher and timeout
796
+ * @param handler - The handler function to execute
797
+ * @returns A hook function that can be exported as the default export
798
+ * @example
799
+ * ```typescript
800
+ * import { configChangeHook, configChangeOutput } from '@goodfoot/agent-hooks/claude-code';
801
+ *
802
+ * export default configChangeHook({}, async (input, { logger }) => {
803
+ * logger.info('Config changed', { source: input.source, file: input.file_path });
804
+ * return configChangeOutput({});
805
+ * });
806
+ * ```
807
+ * @see https://code.claude.com/docs/en/hooks#configchange
808
+ */
809
+ export function configChangeHook(config, handler) {
810
+ return createHookFunction("ConfigChange", config, handler);
811
+ }
812
+ // ============================================================================
813
+ // InstructionsLoaded Hook Factory
814
+ // ============================================================================
815
+ /**
816
+ * Creates an InstructionsLoaded hook handler.
817
+ *
818
+ * InstructionsLoaded hooks fire when a CLAUDE.md or similar instructions file
819
+ * is loaded, allowing you to:
820
+ * - React to instructions being applied
821
+ * - Log which instruction files are active
822
+ * - Observe the instruction loading hierarchy
823
+ *
824
+ * **Matcher**: No matcher support - fires on all instruction load events
825
+ * @param config - Hook configuration with optional timeout
826
+ * @param handler - The handler function to execute
827
+ * @returns A hook function that can be exported as the default export
828
+ * @example
829
+ * ```typescript
830
+ * import { instructionsLoadedHook, instructionsLoadedOutput } from '@goodfoot/agent-hooks/claude-code';
831
+ *
832
+ * export default instructionsLoadedHook({}, async (input, { logger }) => {
833
+ * logger.info('Instructions loaded', { file: input.file_path, type: input.memory_type });
834
+ * return instructionsLoadedOutput({});
835
+ * });
836
+ * ```
837
+ * @see https://code.claude.com/docs/en/hooks#instructionsloaded
838
+ */
839
+ export function instructionsLoadedHook(config, handler) {
840
+ return createHookFunction("InstructionsLoaded", config, handler);
841
+ }
842
+ // ============================================================================
843
+ // WorktreeCreate Hook Factory
844
+ // ============================================================================
845
+ /**
846
+ * Creates a WorktreeCreate hook handler.
847
+ *
848
+ * WorktreeCreate hooks fire when a git worktree is created, allowing you to:
849
+ * - Set up worktree-specific configuration
850
+ * - Log worktree creation events
851
+ * - Initialize worktree resources
852
+ *
853
+ * **Matcher**: No matcher support - fires on all worktree creation events
854
+ * @param config - Hook configuration with optional timeout
855
+ * @param handler - The handler function to execute
856
+ * @returns A hook function that can be exported as the default export
857
+ * @example
858
+ * ```typescript
859
+ * import { worktreeCreateHook, worktreeCreateOutput } from '@goodfoot/agent-hooks/claude-code';
860
+ *
861
+ * export default worktreeCreateHook({}, async (input, { logger }) => {
862
+ * const worktreePath = `${input.cwd}/.worktrees/${input.name}`;
863
+ * logger.info('Worktree created', { name: input.name, worktreePath });
864
+ * // WorktreeCreate is a command hook: the path is written to stdout as plain text.
865
+ * return worktreeCreateOutput({ worktreePath });
866
+ * });
867
+ * ```
868
+ * @see https://code.claude.com/docs/en/hooks#worktreecreate
869
+ */
870
+ export function worktreeCreateHook(config, handler) {
871
+ return createHookFunction("WorktreeCreate", config, handler);
872
+ }
873
+ // ============================================================================
874
+ // WorktreeRemove Hook Factory
875
+ // ============================================================================
876
+ /**
877
+ * Creates a WorktreeRemove hook handler.
878
+ *
879
+ * WorktreeRemove hooks fire when a git worktree is removed, allowing you to:
880
+ * - Clean up worktree-specific resources
881
+ * - Log worktree removal events
882
+ *
883
+ * **Matcher**: No matcher support - fires on all worktree removal events
884
+ * @param config - Hook configuration with optional timeout
885
+ * @param handler - The handler function to execute
886
+ * @returns A hook function that can be exported as the default export
887
+ * @example
888
+ * ```typescript
889
+ * import { worktreeRemoveHook, worktreeRemoveOutput } from '@goodfoot/agent-hooks/claude-code';
890
+ *
891
+ * export default worktreeRemoveHook({}, async (input, { logger }) => {
892
+ * logger.info('Worktree removed', { path: input.worktree_path });
893
+ * return worktreeRemoveOutput({});
894
+ * });
895
+ * ```
896
+ * @see https://code.claude.com/docs/en/hooks#worktreeremove
897
+ */
898
+ export function worktreeRemoveHook(config, handler) {
899
+ return createHookFunction("WorktreeRemove", config, handler);
900
+ }
901
+ // ============================================================================
902
+ // CwdChanged Hook Factory
903
+ // ============================================================================
904
+ /**
905
+ * Creates a CwdChanged hook handler.
906
+ *
907
+ * CwdChanged hooks fire when Claude Code's current working directory changes,
908
+ * allowing you to:
909
+ * - React to directory changes within a session
910
+ * - Update file watchers or environment state
911
+ * - Return `watchPaths` via `hookSpecificOutput` to register paths for FileChanged events
912
+ *
913
+ * **Matcher**: No matcher support - fires on all cwd change events
914
+ * @param config - Hook configuration with optional timeout
915
+ * @param handler - The handler function to execute
916
+ * @returns A hook function that can be exported as the default export
917
+ * @example
918
+ * ```typescript
919
+ * import { cwdChangedHook, cwdChangedOutput } from '@goodfoot/agent-hooks/claude-code';
920
+ *
921
+ * export default cwdChangedHook({}, async (input, { logger }) => {
922
+ * logger.info('Working directory changed', { from: input.old_cwd, to: input.new_cwd });
923
+ * return cwdChangedOutput({});
924
+ * });
925
+ * ```
926
+ * @see https://code.claude.com/docs/en/hooks#cwdchanged
927
+ */
928
+ export function cwdChangedHook(config, handler) {
929
+ return createHookFunction("CwdChanged", config, handler);
930
+ }
931
+ // ============================================================================
932
+ // FileChanged Hook Factory
933
+ // ============================================================================
934
+ /**
935
+ * Creates a FileChanged hook handler.
936
+ *
937
+ * FileChanged hooks fire when a watched file changes on disk, allowing you to:
938
+ * - React to file system changes during a session
939
+ * - Invalidate caches or reload configuration
940
+ * - Return `watchPaths` via `hookSpecificOutput` to update the set of watched paths
941
+ *
942
+ * The input `event` field indicates the type of change:
943
+ * - `'change'` - File contents changed
944
+ * - `'add'` - File was created
945
+ * - `'unlink'` - File was deleted
946
+ *
947
+ * **Matcher**: No matcher support - fires on all file change events
948
+ * @param config - Hook configuration with optional timeout
949
+ * @param handler - The handler function to execute
950
+ * @returns A hook function that can be exported as the default export
951
+ * @example
952
+ * ```typescript
953
+ * import { fileChangedHook, fileChangedOutput } from '@goodfoot/agent-hooks/claude-code';
954
+ *
955
+ * export default fileChangedHook({}, async (input, { logger }) => {
956
+ * logger.info('File changed', { path: input.file_path, event: input.event });
957
+ * return fileChangedOutput({});
958
+ * });
959
+ * ```
960
+ * @see https://code.claude.com/docs/en/hooks#filechanged
961
+ */
962
+ export function fileChangedHook(config, handler) {
963
+ return createHookFunction("FileChanged", config, handler);
964
+ }
965
+ // ============================================================================
966
+ // MessageDisplay Hook Factory
967
+ // ============================================================================
968
+ /**
969
+ * Creates a MessageDisplay hook handler.
970
+ *
971
+ * MessageDisplay hooks fire with each batch of newly completed lines while an
972
+ * assistant message streams. Display-only: the stored message and what the model
973
+ * sees are untouched. Allows you to:
974
+ * - Replace the delta shown on screen with custom content via `displayContent`
975
+ * - Observe and log message streaming events
976
+ *
977
+ * The input carries `turn_id`, `message_id`, `index`, `final`, and `delta` fields.
978
+ * The `final` flag indicates the last flush of a message — its `delta` is empty
979
+ * when the message ends on a newline; treat `final` as the end-of-message signal.
980
+ *
981
+ * **Matcher**: No matcher support - fires on all message display events
982
+ * @param config - Hook configuration with optional timeout
983
+ * @param handler - The handler function to execute
984
+ * @returns A hook function that can be exported as the default export
985
+ * @example
986
+ * ```typescript
987
+ * import { messageDisplayHook, messageDisplayOutput } from '@goodfoot/agent-hooks/claude-code';
988
+ *
989
+ * export default messageDisplayHook({}, async (input, { logger }) => {
990
+ * if (input.final) {
991
+ * logger.info('Message complete', { messageId: input.message_id });
992
+ * }
993
+ * return messageDisplayOutput({});
994
+ * });
995
+ * ```
996
+ * @see https://code.claude.com/docs/en/hooks#messagedisplay
997
+ */
998
+ export function messageDisplayHook(config, handler) {
999
+ return createHookFunction("MessageDisplay", config, handler);
1000
+ }