@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,137 @@
1
+ /**
2
+ * Shared core types for `@goodfoot/agent-hooks`.
3
+ *
4
+ * Core is agent-neutral: it owns the policy vocabulary, the hook-function
5
+ * shape, and the context contract, but never an event list — each agent
6
+ * (Claude Code, Codex, Antigravity) injects its own event names and its own
7
+ * advisory allow-list through {@link HookPolicyGate}.
8
+ * @module
9
+ */
10
+ import type { Logger } from "./logger.js";
11
+ /**
12
+ * Policy governing what happens when an *unexpected* error occurs.
13
+ *
14
+ * `"error"` (default) preserves existing behavior: an unhandled exception
15
+ * anywhere in the runtime (stdin read/parse already fail open unconditionally;
16
+ * this governs handler throws, output serialization, the stdout write, and
17
+ * logger cleanup) surfaces the failure to the agent transport, which owns the
18
+ * wire rules for it.
19
+ *
20
+ * `"continue"` is an opt-in fail-open policy for advisory hooks whose only
21
+ * purpose is to enrich context. Under this policy, unexpected runtime
22
+ * failures are swallowed, the empty response is produced if no response was
23
+ * already produced, and the process exits 0 instead of surfacing a failed-hook
24
+ * banner to the user. A {@link HookBlockError} thrown by a handler is
25
+ * unaffected: it is always classified as a block outcome regardless of policy.
26
+ */
27
+ export type UnexpectedErrorPolicy = "error" | "continue";
28
+ /**
29
+ * The runtime phase in which an unexpected error occurred.
30
+ */
31
+ export type HookErrorPhase = "read" | "parse" | "handler" | "serialize" | "write" | "cleanup";
32
+ /**
33
+ * Best-effort diagnostic sink for unexpected errors under the "continue"
34
+ * policy. Called in addition to (not instead of) the runtime's own logger.
35
+ * Errors thrown by this handler are swallowed — a broken diagnostic sink
36
+ * must never itself fail the invocation.
37
+ */
38
+ export type UnexpectedErrorHandler = (error: unknown, phase: HookErrorPhase) => void;
39
+ /**
40
+ * Configuration options shared by every agent's hook factories.
41
+ *
42
+ * Agent-specific config fields (e.g. Claude Code's typed matchers) extend
43
+ * this interface; core only owns the fields both source packages share.
44
+ */
45
+ export interface HookConfig<TInput = unknown> {
46
+ /**
47
+ * Regular expression pattern for matching hook events, if the agent's
48
+ * event model supports matching.
49
+ */
50
+ matcher?: string;
51
+ /**
52
+ * Handler execution timeout in milliseconds.
53
+ */
54
+ timeout?: number;
55
+ /**
56
+ * Opt-in fail-open policy for unexpected runtime failures. Defaults to
57
+ * `"error"`. See {@link UnexpectedErrorPolicy} for the full contract.
58
+ */
59
+ unexpectedError?: UnexpectedErrorPolicy;
60
+ /**
61
+ * Best-effort diagnostic callback invoked when `unexpectedError: "continue"`
62
+ * swallows an unexpected runtime failure. See {@link UnexpectedErrorHandler}.
63
+ */
64
+ onUnexpectedError?: UnexpectedErrorHandler;
65
+ /**
66
+ * Optional per-event context factory. When provided, the driver calls it
67
+ * with the parsed input to build the context handed to the handler;
68
+ * otherwise handlers receive the base context (`{ logger }`).
69
+ */
70
+ createContext?: (input: TInput) => HookContext;
71
+ }
72
+ /**
73
+ * The base execution context passed to every hook handler.
74
+ *
75
+ * Agents may extend the context per event (e.g. Claude Code's SessionStart
76
+ * context adds env-persistence utilities) via their own factory typing on top
77
+ * of {@link defineHook}'s `createContext` seam.
78
+ */
79
+ export interface HookContext {
80
+ logger: Logger;
81
+ }
82
+ /**
83
+ * A user-provided hook handler.
84
+ */
85
+ export type HookHandler<TInput, TOutput, TContext extends HookContext = HookContext> = (input: TInput, context: TContext) => TOutput | null | Promise<TOutput | null>;
86
+ /**
87
+ * The result of a hook factory — a function that wraps the handler.
88
+ *
89
+ * This is what gets exported from hook files and invoked by the driver.
90
+ * @template TInput - The input type for this hook
91
+ * @template TOutput - The output type for this hook
92
+ * @template TContext - The context type (defaults to {@link HookContext})
93
+ */
94
+ export interface HookFunction<TInput, TOutput, TContext extends HookContext = HookContext> {
95
+ /**
96
+ * Execute the hook handler with the given input and context.
97
+ * @param input - The hook input data
98
+ * @param context - The hook execution context
99
+ * @returns The hook output, or null for an empty response
100
+ */
101
+ (input: TInput, context: TContext): Promise<TOutput | null>;
102
+ /**
103
+ * The agent event name this hook is for.
104
+ */
105
+ eventName: string;
106
+ /**
107
+ * The matcher pattern, if configured.
108
+ */
109
+ matcher?: string;
110
+ /**
111
+ * The timeout in milliseconds, if configured.
112
+ */
113
+ timeout?: number;
114
+ /**
115
+ * The fail-open policy, if configured. See {@link UnexpectedErrorPolicy}.
116
+ */
117
+ unexpectedError?: UnexpectedErrorPolicy;
118
+ /**
119
+ * The diagnostic callback for the "continue" policy, if configured.
120
+ * See {@link UnexpectedErrorHandler}.
121
+ */
122
+ onUnexpectedError?: UnexpectedErrorHandler;
123
+ /**
124
+ * Optional per-event context factory. See {@link HookConfig.createContext}.
125
+ */
126
+ createContext?: (input: TInput) => HookContext;
127
+ }
128
+ /**
129
+ * Per-event policy-validation seam injected by each agent.
130
+ *
131
+ * An agent's per-event factory passes its gate closure; core invokes it with
132
+ * the event name and the configured (possibly undefined) policy. A gate
133
+ * rejects by returning `false`, or by throwing — either way {@link defineHook}
134
+ * fails closed and throws before any hook is created. Core never hardcodes an
135
+ * event list: the advisory allow-list lives entirely in the agent's gate.
136
+ */
137
+ export type HookPolicyGate = (eventName: string, policy: UnexpectedErrorPolicy | undefined) => boolean;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@goodfoot/agent-hooks` root export.
3
+ *
4
+ * Exposes the agent-neutral core only: the {@link defineHook} factory
5
+ * primitive, the transport/driver pair ({@link Transport}, {@link drive}),
6
+ * {@link HookBlockError}, the Logger, env utilities, and stdin helpers.
7
+ *
8
+ * Per-agent entry points (`@goodfoot/agent-hooks/claude-code`, `/codex`,
9
+ * `/antigravity`) are added to the package `exports` map only in the step
10
+ * that lands their implementation — never pre-declared.
11
+ * @module
12
+ */
13
+ export { defineHook } from "./core/define-hook.js";
14
+ export { CLAUDE_ENV_VARS, getEnvFilePath, getProjectDir, isRemoteEnvironment, persistEnvVar, persistEnvVars, } from "./core/env.js";
15
+ export { LOG_LEVELS, type LogEvent, type LogEventError, type LogEventHandler, Logger, type LoggerConfig, type LogLevel, logger, type Unsubscribe, } from "./core/logger.js";
16
+ export { parseStdinJson, readStdin } from "./core/stdin.js";
17
+ export { type FinalizedResult, HookBlockError, type HookOutcome, type Transport, } from "./core/transport.js";
18
+ export type { HookConfig, HookContext, HookErrorPhase, HookFunction, HookHandler, HookPolicyGate, UnexpectedErrorHandler, UnexpectedErrorPolicy, } from "./core/types.js";
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Scaffold module for generating new Claude Code hook projects.
3
+ *
4
+ * Generates a complete TypeScript project structure with:
5
+ * - package.json with dependencies and scripts
6
+ * - tsconfig.json with ESM/Node20 configuration
7
+ * - biome.json for linting/formatting
8
+ * - Hook template files for each requested hook type
9
+ * - Vitest test files for each hook
10
+ * - vitest.config.ts for test configuration
11
+ * @module
12
+ * @example
13
+ * ```bash
14
+ * npx @goodfoot/agent-hooks --agent claude-code --scaffold ./my-hooks --hooks Stop,SubagentStop -o dist/hooks.json
15
+ * ```
16
+ */
17
+ /**
18
+ * Options for scaffolding a new hook project.
19
+ */
20
+ export interface ScaffoldOptions {
21
+ /** Directory path where the project will be created. */
22
+ directory: string;
23
+ /** Array of hook event names to generate (e.g., ['Stop', 'SubagentStop']). */
24
+ hooks: string[];
25
+ /** Relative path for hooks.json output in the build script. */
26
+ outputPath: string;
27
+ }
28
+ /**
29
+ * Scaffolds a new Claude Code hook project.
30
+ *
31
+ * Creates the complete project structure including:
32
+ * - package.json, tsconfig.json, biome.json, vitest.config.ts
33
+ * - src/ directory with hook implementations
34
+ * - test/ directory with vitest tests
35
+ * @param options - Scaffold configuration options
36
+ * @throws Exits with code 1 if directory exists or hook names are invalid
37
+ * @example
38
+ * ```typescript
39
+ * scaffoldProject({
40
+ * directory: './my-hooks',
41
+ * hooks: ['Stop', 'SubagentStop'],
42
+ * outputPath: 'dist/hooks.json'
43
+ * });
44
+ * ```
45
+ */
46
+ export declare function scaffoldProject(options: ScaffoldOptions): void;