@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.
- package/README.md +137 -0
- package/dist/agents/antigravity/events.js +42 -0
- package/dist/agents/antigravity/hooks.js +59 -0
- package/dist/agents/antigravity/index.js +28 -0
- package/dist/agents/antigravity/outputs.js +81 -0
- package/dist/agents/antigravity/transport.js +84 -0
- package/dist/agents/antigravity/types.js +11 -0
- package/dist/agents/claude-code/env.js +27 -0
- package/dist/agents/claude-code/events.js +87 -0
- package/dist/agents/claude-code/hooks.js +1000 -0
- package/dist/agents/claude-code/index.js +57 -0
- package/dist/agents/claude-code/outputs.js +579 -0
- package/dist/agents/claude-code/tool-helpers.js +840 -0
- package/dist/agents/claude-code/transport.js +254 -0
- package/dist/agents/claude-code/types.js +11 -0
- package/dist/agents/codex/cli-support.js +318 -0
- package/dist/agents/codex/constants.js +29 -0
- package/dist/agents/codex/events.js +68 -0
- package/dist/agents/codex/hooks.js +93 -0
- package/dist/agents/codex/index.js +21 -0
- package/dist/agents/codex/outputs.js +189 -0
- package/dist/agents/codex/scaffold.js +273 -0
- package/dist/agents/codex/transport.js +139 -0
- package/dist/agents/codex/types.js +1 -0
- package/dist/cli.js +1357 -0
- package/dist/constants.js +39 -0
- package/dist/core/define-hook.js +61 -0
- package/dist/core/env.js +186 -0
- package/dist/core/logger.js +508 -0
- package/dist/core/stdin.js +38 -0
- package/dist/core/transport.js +231 -0
- package/dist/core/types.js +10 -0
- package/dist/index.js +17 -0
- package/dist/scaffold.js +512 -0
- package/package.json +61 -0
- package/types/agents/antigravity/events.d.ts +50 -0
- package/types/agents/antigravity/hooks.d.ts +76 -0
- package/types/agents/antigravity/index.d.ts +27 -0
- package/types/agents/antigravity/outputs.d.ts +107 -0
- package/types/agents/antigravity/transport.d.ts +47 -0
- package/types/agents/antigravity/types.d.ts +58 -0
- package/types/agents/claude-code/env.d.ts +27 -0
- package/types/agents/claude-code/events.d.ts +69 -0
- package/types/agents/claude-code/hooks.d.ts +1182 -0
- package/types/agents/claude-code/index.d.ts +23 -0
- package/types/agents/claude-code/outputs.d.ts +1160 -0
- package/types/agents/claude-code/tool-helpers.d.ts +807 -0
- package/types/agents/claude-code/transport.d.ts +119 -0
- package/types/agents/claude-code/types.d.ts +557 -0
- package/types/agents/codex/cli-support.d.ts +105 -0
- package/types/agents/codex/constants.d.ts +10 -0
- package/types/agents/codex/events.d.ts +63 -0
- package/types/agents/codex/hooks.d.ts +135 -0
- package/types/agents/codex/index.d.ts +19 -0
- package/types/agents/codex/outputs.d.ts +190 -0
- package/types/agents/codex/scaffold.d.ts +14 -0
- package/types/agents/codex/transport.d.ts +74 -0
- package/types/agents/codex/types.d.ts +131 -0
- package/types/cli.d.ts +315 -0
- package/types/constants.d.ts +9 -0
- package/types/core/define-hook.d.ts +39 -0
- package/types/core/env.d.ts +150 -0
- package/types/core/logger.d.ts +476 -0
- package/types/core/stdin.d.ts +21 -0
- package/types/core/transport.d.ts +131 -0
- package/types/core/types.d.ts +137 -0
- package/types/index.d.ts +18 -0
- 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
|
+
}
|
package/dist/core/env.js
ADDED
|
@@ -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
|
+
}
|