@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,39 @@
1
+ /**
2
+ * Shared constants for the CLI and scaffold modules.
3
+ * @module
4
+ */
5
+ /**
6
+ * Maps hook factory function names to their event names.
7
+ */
8
+ export const HOOK_FACTORY_TO_EVENT = {
9
+ preToolUseHook: "PreToolUse",
10
+ postToolUseHook: "PostToolUse",
11
+ postToolUseFailureHook: "PostToolUseFailure",
12
+ postToolBatchHook: "PostToolBatch",
13
+ notificationHook: "Notification",
14
+ userPromptExpansionHook: "UserPromptExpansion",
15
+ userPromptSubmitHook: "UserPromptSubmit",
16
+ sessionStartHook: "SessionStart",
17
+ sessionEndHook: "SessionEnd",
18
+ stopHook: "Stop",
19
+ stopFailureHook: "StopFailure",
20
+ subagentStartHook: "SubagentStart",
21
+ subagentStopHook: "SubagentStop",
22
+ preCompactHook: "PreCompact",
23
+ postCompactHook: "PostCompact",
24
+ permissionRequestHook: "PermissionRequest",
25
+ permissionDeniedHook: "PermissionDenied",
26
+ setupHook: "Setup",
27
+ teammateIdleHook: "TeammateIdle",
28
+ taskCreatedHook: "TaskCreated",
29
+ taskCompletedHook: "TaskCompleted",
30
+ elicitationHook: "Elicitation",
31
+ elicitationResultHook: "ElicitationResult",
32
+ configChangeHook: "ConfigChange",
33
+ instructionsLoadedHook: "InstructionsLoaded",
34
+ worktreeCreateHook: "WorktreeCreate",
35
+ worktreeRemoveHook: "WorktreeRemove",
36
+ cwdChangedHook: "CwdChanged",
37
+ fileChangedHook: "FileChanged",
38
+ messageDisplayHook: "MessageDisplay",
39
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Generic hook factory primitive.
3
+ *
4
+ * `defineHook` is the agent-neutral seam every agent's per-event factory
5
+ * builds on: the agent supplies its event name, its config shape, and an
6
+ * optional policy gate enforcing that event's advisory allow-list. Core never
7
+ * hardcodes an event list — validation is fully injected via
8
+ * {@link HookPolicyGate} and fails closed when a gate rejects.
9
+ * @module
10
+ * @example
11
+ * ```typescript
12
+ * const hook = defineHook(
13
+ * "UserPromptSubmit",
14
+ * { unexpectedError: "continue" },
15
+ * async (input, { logger }) => {
16
+ * logger.info("Enriching prompt");
17
+ * return { additionalContext: "..." };
18
+ * },
19
+ * (eventName, policy) => policy === undefined || policy === "continue",
20
+ * );
21
+ * ```
22
+ */
23
+ /**
24
+ * Creates a hook function for a specific agent event.
25
+ *
26
+ * @param eventName - The agent's event name (e.g. `"PreToolUse"`); attached
27
+ * to the returned {@link HookFunction} as `eventName` for runtime inspection.
28
+ * @param config - Hook configuration (matcher, timeout, unexpected-error
29
+ * policy and diagnostics, optional context factory).
30
+ * @param handler - The handler function to wrap.
31
+ * @param policyGate - Optional per-event policy-validation closure injected
32
+ * by the agent. Called with `(eventName, config.unexpectedError)`; returning
33
+ * `false` or throwing rejects the configuration, and this function throws
34
+ * (fail closed) instead of creating the hook.
35
+ * @returns A wrapped hook function carrying its metadata for the driver.
36
+ * @throws Error when `policyGate` rejects `(eventName, policy)`.
37
+ */
38
+ export function defineHook(eventName, config, handler, policyGate) {
39
+ if (policyGate !== undefined) {
40
+ let accepted;
41
+ try {
42
+ accepted = policyGate(eventName, config.unexpectedError);
43
+ }
44
+ catch (error) {
45
+ throw new Error(`Policy gate rejected "${eventName}": ${error instanceof Error ? error.message : String(error)}`);
46
+ }
47
+ if (!accepted) {
48
+ throw new Error(`Policy gate rejected "${eventName}"`);
49
+ }
50
+ }
51
+ const hookFn = async (input, context) => {
52
+ return await handler(input, context);
53
+ };
54
+ hookFn.eventName = eventName;
55
+ hookFn.matcher = config.matcher;
56
+ hookFn.timeout = config.timeout;
57
+ hookFn.unexpectedError = config.unexpectedError;
58
+ hookFn.onUnexpectedError = config.onUnexpectedError;
59
+ hookFn.createContext = config.createContext;
60
+ return hookFn;
61
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Environment variable utilities for Claude Code hooks.
3
+ *
4
+ * Provides typed access to Claude Code's environment variables and utilities
5
+ * for persisting environment variables in SessionStart hooks.
6
+ *
7
+ * ## Environment Variables
8
+ *
9
+ * Claude Code sets these environment variables when running hooks:
10
+ *
11
+ * | Variable | Description | Available In |
12
+ * |----------|-------------|--------------|
13
+ * | `CLAUDE_PROJECT_DIR` | Absolute path to project root | All hooks |
14
+ * | `CLAUDE_ENV_FILE` | Path to file for persisting env vars | SessionStart only |
15
+ * | `CLAUDE_CODE_REMOTE` | `"true"` if running remotely | All hooks |
16
+ * @module
17
+ * @example
18
+ * ```typescript
19
+ * import { getProjectDir, persistEnvVar, isRemoteEnvironment } from '@goodfoot/agent-hooks';
20
+ *
21
+ * // Get project directory
22
+ * const projectDir = getProjectDir();
23
+ *
24
+ * // Check if running remotely
25
+ * if (isRemoteEnvironment()) {
26
+ * // Handle remote-specific logic
27
+ * }
28
+ *
29
+ * // In SessionStart hook: persist environment variables
30
+ * persistEnvVar('NODE_ENV', 'production');
31
+ * persistEnvVar('API_KEY', 'secret-key');
32
+ * ```
33
+ * @see https://code.claude.com/docs/en/hooks#hook-execution-details
34
+ */
35
+ import * as fs from "node:fs";
36
+ /**
37
+ * Claude Code environment variable names.
38
+ *
39
+ * These are the environment variables that Claude Code sets when running hooks.
40
+ */
41
+ export const CLAUDE_ENV_VARS = {
42
+ /**
43
+ * Absolute path to the project root directory where Claude Code was started.
44
+ * Available in all hooks.
45
+ */
46
+ PROJECT_DIR: "CLAUDE_PROJECT_DIR",
47
+ /**
48
+ * Path to a file where SessionStart hooks can persist environment variables.
49
+ * Variables written to this file will be available in all subsequent bash commands.
50
+ * Only available in SessionStart hooks.
51
+ */
52
+ ENV_FILE: "CLAUDE_ENV_FILE",
53
+ /**
54
+ * Set to "true" when running in a remote (web) environment.
55
+ * Not set or empty when running in local CLI environment.
56
+ */
57
+ REMOTE: "CLAUDE_CODE_REMOTE",
58
+ };
59
+ /**
60
+ * Gets the Claude Code project directory.
61
+ *
62
+ * This is the absolute path to the project root where Claude Code was started.
63
+ * The value comes from the `CLAUDE_PROJECT_DIR` environment variable.
64
+ * @returns The project directory path, or undefined if not set
65
+ * @example
66
+ * ```typescript
67
+ * const projectDir = getProjectDir();
68
+ * if (projectDir) {
69
+ * const configPath = `${projectDir}/.claude/config.json`;
70
+ * }
71
+ * ```
72
+ */
73
+ export function getProjectDir() {
74
+ return process.env[CLAUDE_ENV_VARS.PROJECT_DIR];
75
+ }
76
+ /**
77
+ * Gets the Claude Code env file path for persisting environment variables.
78
+ *
79
+ * This is only available in SessionStart hooks. The path points to a file
80
+ * where you can write shell export statements to persist environment variables
81
+ * for all subsequent bash commands in the session.
82
+ * @returns The env file path, or undefined if not set (not a SessionStart hook)
83
+ * @example
84
+ * ```typescript
85
+ * const envFile = getEnvFilePath();
86
+ * if (envFile) {
87
+ * // We're in a SessionStart hook and can persist env vars
88
+ * persistEnvVar('MY_VAR', 'my-value');
89
+ * }
90
+ * ```
91
+ */
92
+ export function getEnvFilePath() {
93
+ return process.env[CLAUDE_ENV_VARS.ENV_FILE];
94
+ }
95
+ /**
96
+ * Checks if the hook is running in a remote (web) environment.
97
+ *
98
+ * Remote environments may have different capabilities or restrictions
99
+ * compared to local CLI environments.
100
+ * @returns true if running remotely, false if running locally
101
+ * @example
102
+ * ```typescript
103
+ * if (isRemoteEnvironment()) {
104
+ * // Use web-compatible approaches
105
+ * } else {
106
+ * // Can use local CLI features
107
+ * }
108
+ * ```
109
+ */
110
+ export function isRemoteEnvironment() {
111
+ return process.env[CLAUDE_ENV_VARS.REMOTE] === "true";
112
+ }
113
+ /**
114
+ * Persists an environment variable for use in subsequent bash commands.
115
+ *
116
+ * This function writes a shell export statement to the `CLAUDE_ENV_FILE`,
117
+ * which Claude Code sources before running bash commands. This allows
118
+ * SessionStart hooks to configure the environment for the entire session.
119
+ *
120
+ * **Important**: This function only works in SessionStart hooks where
121
+ * `CLAUDE_ENV_FILE` is set. In other hooks, it will throw an error.
122
+ * @param name - The environment variable name
123
+ * @param value - The environment variable value (will be shell-escaped)
124
+ * @throws Error if CLAUDE_ENV_FILE is not set (not in a SessionStart hook)
125
+ * @example
126
+ * ```typescript
127
+ * import { sessionStartHook, sessionStartOutput, persistEnvVar } from '@goodfoot/agent-hooks';
128
+ *
129
+ * export default sessionStartHook({}, async (input) => {
130
+ * // Persist environment variables for the session
131
+ * persistEnvVar('NODE_ENV', 'production');
132
+ * persistEnvVar('API_KEY', process.env.MY_API_KEY ?? 'default');
133
+ * persistEnvVar('PATH', `${process.env.PATH}:./node_modules/.bin`);
134
+ *
135
+ * return sessionStartOutput({});
136
+ * });
137
+ * ```
138
+ * @see https://code.claude.com/docs/en/hooks#persisting-environment-variables
139
+ */
140
+ export function persistEnvVar(name, value) {
141
+ const envFile = getEnvFilePath();
142
+ if (envFile === undefined) {
143
+ throw new Error("persistEnvVar can only be used in SessionStart hooks. " + "CLAUDE_ENV_FILE environment variable is not set.");
144
+ }
145
+ // Shell-escape the value to handle special characters
146
+ const escapedValue = escapeShellValue(value);
147
+ // Write the export statement
148
+ const exportStatement = `export ${name}=${escapedValue}\n`;
149
+ fs.appendFileSync(envFile, exportStatement, "utf-8");
150
+ }
151
+ /**
152
+ * Persists multiple environment variables at once.
153
+ *
154
+ * This is a convenience wrapper around `persistEnvVar` for setting
155
+ * multiple variables in a single call.
156
+ * @param vars - Object mapping variable names to values
157
+ * @throws Error if CLAUDE_ENV_FILE is not set (not in a SessionStart hook)
158
+ * @example
159
+ * ```typescript
160
+ * persistEnvVars({
161
+ * NODE_ENV: 'production',
162
+ * API_KEY: 'secret',
163
+ * DEBUG: 'false'
164
+ * });
165
+ * ```
166
+ */
167
+ export function persistEnvVars(vars) {
168
+ for (const [name, value] of Object.entries(vars)) {
169
+ persistEnvVar(name, value);
170
+ }
171
+ }
172
+ /**
173
+ * Escapes a value for safe use in a shell export statement.
174
+ *
175
+ * Uses single quotes and escapes any embedded single quotes.
176
+ * This prevents shell injection and handles special characters.
177
+ * @param value - The value to escape
178
+ * @returns The shell-escaped value (with quotes)
179
+ * @internal
180
+ */
181
+ function escapeShellValue(value) {
182
+ // Use single quotes and escape any embedded single quotes
183
+ // 'value' -> 'val'\''ue' for values containing single quotes
184
+ const escaped = value.replace(/'/g, "'\\''");
185
+ return `'${escaped}'`;
186
+ }