@agiflowai/hooks-adapter 0.0.14 → 0.0.16
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/dist/index.cjs +125 -19
- package/dist/index.d.cts +127 -48
- package/dist/index.d.mts +127 -48
- package/dist/index.mjs +119 -20
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -42,6 +42,7 @@ node_crypto = __toESM(node_crypto);
|
|
|
42
42
|
* DESIGN PATTERNS:
|
|
43
43
|
* - Strongly-typed constant exports for compile-time safety
|
|
44
44
|
* - Immutable by default (as const assertions)
|
|
45
|
+
* - Related constants grouped into const objects
|
|
45
46
|
*
|
|
46
47
|
* CODING STANDARDS:
|
|
47
48
|
* - Primitive constants: UPPER_SNAKE_CASE or PascalCase for event names
|
|
@@ -52,15 +53,36 @@ node_crypto = __toESM(node_crypto);
|
|
|
52
53
|
* - Magic strings without explanation
|
|
53
54
|
*/
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
+
* Grouped hook type identifiers for Claude Code hook events
|
|
56
57
|
*/
|
|
57
|
-
const
|
|
58
|
-
|
|
58
|
+
const ClaudeCodeHookTypes = {
|
|
59
|
+
PRE_TOOL_USE: "PreToolUse",
|
|
60
|
+
POST_TOOL_USE: "PostToolUse",
|
|
61
|
+
STOP: "Stop",
|
|
62
|
+
USER_PROMPT_SUBMIT: "UserPromptSubmit",
|
|
63
|
+
TASK_COMPLETED: "TaskCompleted"
|
|
64
|
+
};
|
|
59
65
|
/**
|
|
60
|
-
*
|
|
66
|
+
* Grouped hook type identifiers for Gemini CLI hook events
|
|
61
67
|
*/
|
|
62
|
-
const
|
|
63
|
-
|
|
68
|
+
const GeminiCliHookTypes = {
|
|
69
|
+
BEFORE_TOOL_USE: "BeforeTool",
|
|
70
|
+
AFTER_TOOL_USE: "AfterTool"
|
|
71
|
+
};
|
|
72
|
+
/** Hook event fired before a tool is executed in Claude Code */
|
|
73
|
+
const PRE_TOOL_USE = ClaudeCodeHookTypes.PRE_TOOL_USE;
|
|
74
|
+
/** Hook event fired after a tool has executed in Claude Code */
|
|
75
|
+
const POST_TOOL_USE = ClaudeCodeHookTypes.POST_TOOL_USE;
|
|
76
|
+
/** Hook event fired when a Claude Code session is about to stop */
|
|
77
|
+
const STOP = ClaudeCodeHookTypes.STOP;
|
|
78
|
+
/** Hook event fired when the user submits a new prompt in Claude Code */
|
|
79
|
+
const USER_PROMPT_SUBMIT = ClaudeCodeHookTypes.USER_PROMPT_SUBMIT;
|
|
80
|
+
/** Hook event fired when an agentic task completes in Claude Code */
|
|
81
|
+
const TASK_COMPLETED = ClaudeCodeHookTypes.TASK_COMPLETED;
|
|
82
|
+
/** Hook event fired before a tool is executed in Gemini CLI */
|
|
83
|
+
const BEFORE_TOOL_USE = GeminiCliHookTypes.BEFORE_TOOL_USE;
|
|
84
|
+
/** Hook event fired after a tool is executed in Gemini CLI */
|
|
85
|
+
const AFTER_TOOL_USE = GeminiCliHookTypes.AFTER_TOOL_USE;
|
|
64
86
|
|
|
65
87
|
//#endregion
|
|
66
88
|
//#region src/constants/decisions.ts
|
|
@@ -91,12 +113,13 @@ var BaseAdapter = class {
|
|
|
91
113
|
const stdin = await this.readStdin();
|
|
92
114
|
const response = await callback(this.parseInput(stdin));
|
|
93
115
|
if (response.decision === "skip") {
|
|
94
|
-
process.exit(0);
|
|
116
|
+
process.exit(response.exitCode ?? 0);
|
|
95
117
|
return;
|
|
96
118
|
}
|
|
119
|
+
if (response.userMessage) process.stderr.write(`${response.userMessage}\n`);
|
|
97
120
|
const output = this.formatOutput(response);
|
|
98
121
|
console.log(output);
|
|
99
|
-
process.exit(0);
|
|
122
|
+
process.exit(response.exitCode ?? 0);
|
|
100
123
|
} catch (error) {
|
|
101
124
|
this.handleError(error);
|
|
102
125
|
}
|
|
@@ -125,9 +148,10 @@ var BaseAdapter = class {
|
|
|
125
148
|
process.exit(0);
|
|
126
149
|
return;
|
|
127
150
|
}
|
|
151
|
+
if (finalResponse.userMessage) process.stderr.write(`${finalResponse.userMessage}\n`);
|
|
128
152
|
const output = this.formatOutput(finalResponse);
|
|
129
153
|
console.log(output);
|
|
130
|
-
process.exit(0);
|
|
154
|
+
process.exit(finalResponse.exitCode ?? 0);
|
|
131
155
|
} catch (error) {
|
|
132
156
|
this.handleError(error);
|
|
133
157
|
}
|
|
@@ -170,18 +194,18 @@ var BaseAdapter = class {
|
|
|
170
194
|
//#endregion
|
|
171
195
|
//#region src/adapters/ClaudeCodeAdapter.ts
|
|
172
196
|
/**
|
|
173
|
-
* ClaudeCodeAdapter - Unified adapter for Claude Code hook format
|
|
197
|
+
* ClaudeCodeAdapter - Unified adapter for Claude Code hook format
|
|
174
198
|
*
|
|
175
199
|
* DESIGN PATTERNS:
|
|
176
200
|
* - Adapter pattern: Converts Claude Code format to normalized format
|
|
177
201
|
* - Parser pattern: Extracts file paths and operations from tool inputs
|
|
178
|
-
* - State pattern: Stores hook event type to morph behavior between
|
|
202
|
+
* - State pattern: Stores hook event type to morph behavior between hook events
|
|
179
203
|
*
|
|
180
204
|
* CODING STANDARDS:
|
|
181
205
|
* - Parse Claude Code JSON stdin format exactly as specified
|
|
182
206
|
* - Format output to match Claude Code hook response schema
|
|
183
207
|
* - Handle missing/optional fields gracefully
|
|
184
|
-
* - Support
|
|
208
|
+
* - Support PreToolUse, PostToolUse, Stop, UserPromptSubmit, TaskCompleted events
|
|
185
209
|
*
|
|
186
210
|
* AVOID:
|
|
187
211
|
* - Assuming all fields are present
|
|
@@ -189,7 +213,15 @@ var BaseAdapter = class {
|
|
|
189
213
|
* - Mutating input objects
|
|
190
214
|
*/
|
|
191
215
|
/**
|
|
192
|
-
*
|
|
216
|
+
* Blockable hook event names
|
|
217
|
+
*/
|
|
218
|
+
const BLOCKABLE_EVENTS = new Set([
|
|
219
|
+
"Stop",
|
|
220
|
+
"UserPromptSubmit",
|
|
221
|
+
"TaskCompleted"
|
|
222
|
+
]);
|
|
223
|
+
/**
|
|
224
|
+
* Unified adapter for Claude Code hook format
|
|
193
225
|
*/
|
|
194
226
|
var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
195
227
|
hookEventName = "PreToolUse";
|
|
@@ -211,7 +243,7 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
211
243
|
}
|
|
212
244
|
/**
|
|
213
245
|
* Format normalized HookResponse into Claude Code output
|
|
214
|
-
* Morphs output based on hook event type
|
|
246
|
+
* Morphs output based on hook event type
|
|
215
247
|
*
|
|
216
248
|
* @param response - Normalized hook response
|
|
217
249
|
* @returns JSON string for Claude Code
|
|
@@ -226,6 +258,7 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
226
258
|
__agiflowai_aicode_utils.log.debug("ClaudeCodeAdapter: Skip decision, returning empty output");
|
|
227
259
|
return emptyOutput;
|
|
228
260
|
}
|
|
261
|
+
if (BLOCKABLE_EVENTS.has(this.hookEventName)) return this.formatBlockableOutput(response);
|
|
229
262
|
if (this.hookEventName === "PostToolUse") return this.formatPostToolUseOutput(response);
|
|
230
263
|
return this.formatPreToolUseOutput(response);
|
|
231
264
|
}
|
|
@@ -258,6 +291,20 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
258
291
|
__agiflowai_aicode_utils.log.debug("ClaudeCodeAdapter: Formatted PostToolUse output", { output: formattedOutput });
|
|
259
292
|
return formattedOutput;
|
|
260
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Format blockable output for Stop, UserPromptSubmit, and TaskCompleted hooks
|
|
296
|
+
* Maps 'deny' decision to 'block', otherwise returns empty object
|
|
297
|
+
*/
|
|
298
|
+
formatBlockableOutput(response) {
|
|
299
|
+
const output = {};
|
|
300
|
+
if (response.decision === "deny") {
|
|
301
|
+
output.decision = "block";
|
|
302
|
+
output.reason = response.message;
|
|
303
|
+
}
|
|
304
|
+
const formattedOutput = JSON.stringify(output, null, 2);
|
|
305
|
+
__agiflowai_aicode_utils.log.debug(`ClaudeCodeAdapter: Formatted ${this.hookEventName} output`, { output: formattedOutput });
|
|
306
|
+
return formattedOutput;
|
|
307
|
+
}
|
|
261
308
|
};
|
|
262
309
|
|
|
263
310
|
//#endregion
|
|
@@ -348,6 +395,12 @@ function isNodeError(error) {
|
|
|
348
395
|
return error instanceof Error && "code" in error;
|
|
349
396
|
}
|
|
350
397
|
/**
|
|
398
|
+
* Type guard for LogEntry — validates required fields at runtime
|
|
399
|
+
*/
|
|
400
|
+
function isLogEntry(value) {
|
|
401
|
+
return typeof value === "object" && value !== null && "filePath" in value && typeof value.filePath === "string" && "operation" in value && typeof value.operation === "string";
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
351
404
|
* Service for tracking hook executions using an append-only log
|
|
352
405
|
* Prevents duplicate hook actions (e.g., showing design patterns twice for same file)
|
|
353
406
|
* Each session has its own log file for isolation
|
|
@@ -444,8 +497,10 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
444
497
|
const lines = (await node_fs_promises.readFile(this.logFile, "utf-8")).trim().split("\n").filter(Boolean);
|
|
445
498
|
const entries = [];
|
|
446
499
|
for (const line of lines) try {
|
|
447
|
-
|
|
448
|
-
|
|
500
|
+
const parsed = JSON.parse(line);
|
|
501
|
+
if (isLogEntry(parsed)) entries.push(parsed);
|
|
502
|
+
else console.warn("Skipping malformed log entry:", line.substring(0, 100));
|
|
503
|
+
} catch (_parseError) {
|
|
449
504
|
console.warn("Skipping malformed log entry:", line.substring(0, 100));
|
|
450
505
|
}
|
|
451
506
|
this.cache = entries.slice(-ExecutionLogService.MAX_CACHE_SIZE);
|
|
@@ -508,10 +563,10 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
508
563
|
*/
|
|
509
564
|
async getFileMetadata(filePath) {
|
|
510
565
|
try {
|
|
511
|
-
const content = await node_fs_promises.readFile(filePath, "utf-8");
|
|
566
|
+
const [content, stats] = await Promise.all([node_fs_promises.readFile(filePath, "utf-8"), node_fs_promises.stat(filePath)]);
|
|
512
567
|
const checksum = node_crypto.createHash("md5").update(content).digest("hex");
|
|
513
568
|
return {
|
|
514
|
-
mtime:
|
|
569
|
+
mtime: stats.mtimeMs,
|
|
515
570
|
checksum
|
|
516
571
|
};
|
|
517
572
|
} catch (error) {
|
|
@@ -597,7 +652,7 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
597
652
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
598
653
|
const entry = entries[i];
|
|
599
654
|
if (entry.operation === "scaffold") {
|
|
600
|
-
if (entry.generatedFiles
|
|
655
|
+
if (entry.generatedFiles?.includes(filePath)) return true;
|
|
601
656
|
}
|
|
602
657
|
}
|
|
603
658
|
return false;
|
|
@@ -608,6 +663,50 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
608
663
|
}
|
|
609
664
|
};
|
|
610
665
|
|
|
666
|
+
//#endregion
|
|
667
|
+
//#region src/utils/guards.ts
|
|
668
|
+
/**
|
|
669
|
+
* Set of valid decision values for efficient runtime lookup.
|
|
670
|
+
* Mirrors the Decision type: 'allow' | 'deny' | 'ask' | 'skip'
|
|
671
|
+
*/
|
|
672
|
+
const VALID_DECISIONS = new Set([
|
|
673
|
+
"allow",
|
|
674
|
+
"deny",
|
|
675
|
+
"ask",
|
|
676
|
+
"skip"
|
|
677
|
+
]);
|
|
678
|
+
/**
|
|
679
|
+
* Type guard for HookResponse objects.
|
|
680
|
+
* Validates that a value conforms to the HookResponse interface at runtime.
|
|
681
|
+
*
|
|
682
|
+
* @param value - Unknown value to check
|
|
683
|
+
* @returns True if value is a valid HookResponse
|
|
684
|
+
*/
|
|
685
|
+
function isHookResponse(value) {
|
|
686
|
+
if (typeof value !== "object" || value === null) return false;
|
|
687
|
+
if (!("decision" in value) || typeof value.decision !== "string") return false;
|
|
688
|
+
if (!VALID_DECISIONS.has(value.decision)) return false;
|
|
689
|
+
if (!("message" in value) || typeof value.message !== "string") return false;
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Type guard for HookContext objects.
|
|
694
|
+
* Validates that a value conforms to the HookContext interface at runtime.
|
|
695
|
+
* toolInput is shallowly validated because its internal structure varies by tool
|
|
696
|
+
* and is not constrained by the interface.
|
|
697
|
+
*
|
|
698
|
+
* @param value - Unknown value to check
|
|
699
|
+
* @returns True if value is a valid HookContext
|
|
700
|
+
*/
|
|
701
|
+
function isHookContext(value) {
|
|
702
|
+
if (typeof value !== "object" || value === null) return false;
|
|
703
|
+
if (!("toolName" in value) || typeof value.toolName !== "string") return false;
|
|
704
|
+
if (!("toolInput" in value) || typeof value.toolInput !== "object" || value.toolInput === null) return false;
|
|
705
|
+
if (!("cwd" in value) || typeof value.cwd !== "string") return false;
|
|
706
|
+
if (!("sessionId" in value) || typeof value.sessionId !== "string") return false;
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
|
|
611
710
|
//#endregion
|
|
612
711
|
//#region src/utils/parseHookType.ts
|
|
613
712
|
/**
|
|
@@ -639,12 +738,19 @@ exports.AFTER_TOOL_USE = AFTER_TOOL_USE;
|
|
|
639
738
|
exports.BEFORE_TOOL_USE = BEFORE_TOOL_USE;
|
|
640
739
|
exports.BaseAdapter = BaseAdapter;
|
|
641
740
|
exports.ClaudeCodeAdapter = ClaudeCodeAdapter;
|
|
741
|
+
exports.ClaudeCodeHookTypes = ClaudeCodeHookTypes;
|
|
642
742
|
exports.DECISION_ALLOW = DECISION_ALLOW;
|
|
643
743
|
exports.DECISION_ASK = DECISION_ASK;
|
|
644
744
|
exports.DECISION_DENY = DECISION_DENY;
|
|
645
745
|
exports.DECISION_SKIP = DECISION_SKIP;
|
|
646
746
|
exports.ExecutionLogService = ExecutionLogService;
|
|
647
747
|
exports.GeminiCliAdapter = GeminiCliAdapter;
|
|
748
|
+
exports.GeminiCliHookTypes = GeminiCliHookTypes;
|
|
648
749
|
exports.POST_TOOL_USE = POST_TOOL_USE;
|
|
649
750
|
exports.PRE_TOOL_USE = PRE_TOOL_USE;
|
|
751
|
+
exports.STOP = STOP;
|
|
752
|
+
exports.TASK_COMPLETED = TASK_COMPLETED;
|
|
753
|
+
exports.USER_PROMPT_SUBMIT = USER_PROMPT_SUBMIT;
|
|
754
|
+
exports.isHookContext = isHookContext;
|
|
755
|
+
exports.isHookResponse = isHookResponse;
|
|
650
756
|
exports.parseHookType = parseHookType;
|
package/dist/index.d.cts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* DESIGN PATTERNS:
|
|
6
6
|
* - Strongly-typed constant exports for compile-time safety
|
|
7
7
|
* - Immutable by default (as const assertions)
|
|
8
|
+
* - Related constants grouped into const objects
|
|
8
9
|
*
|
|
9
10
|
* CODING STANDARDS:
|
|
10
11
|
* - Primitive constants: UPPER_SNAKE_CASE or PascalCase for event names
|
|
@@ -15,19 +16,55 @@
|
|
|
15
16
|
* - Magic strings without explanation
|
|
16
17
|
*/
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
+
* Grouped hook type identifiers for Claude Code hook events
|
|
19
20
|
*/
|
|
20
|
-
declare const
|
|
21
|
-
|
|
21
|
+
declare const ClaudeCodeHookTypes: {
|
|
22
|
+
/** Fires before a tool is executed, allowing interception or modification */
|
|
23
|
+
readonly PRE_TOOL_USE: "PreToolUse";
|
|
24
|
+
/** Fires after a tool has executed, allowing post-processing or blocking */
|
|
25
|
+
readonly POST_TOOL_USE: "PostToolUse";
|
|
26
|
+
/** Fires when a session is about to stop */
|
|
27
|
+
readonly STOP: "Stop";
|
|
28
|
+
/** Fires when the user submits a new prompt */
|
|
29
|
+
readonly USER_PROMPT_SUBMIT: "UserPromptSubmit";
|
|
30
|
+
/** Fires when an agentic task completes */
|
|
31
|
+
readonly TASK_COMPLETED: "TaskCompleted";
|
|
32
|
+
};
|
|
22
33
|
/**
|
|
23
|
-
*
|
|
34
|
+
* Grouped hook type identifiers for Gemini CLI hook events
|
|
24
35
|
*/
|
|
25
|
-
declare const
|
|
26
|
-
|
|
36
|
+
declare const GeminiCliHookTypes: {
|
|
37
|
+
/** Fires before a tool is executed in Gemini CLI */
|
|
38
|
+
readonly BEFORE_TOOL_USE: "BeforeTool";
|
|
39
|
+
/** Fires after a tool is executed in Gemini CLI */
|
|
40
|
+
readonly AFTER_TOOL_USE: "AfterTool";
|
|
41
|
+
};
|
|
42
|
+
/** Hook event fired before a tool is executed in Claude Code */
|
|
43
|
+
declare const PRE_TOOL_USE: "PreToolUse";
|
|
44
|
+
/** Hook event fired after a tool has executed in Claude Code */
|
|
45
|
+
declare const POST_TOOL_USE: "PostToolUse";
|
|
46
|
+
/** Hook event fired when a Claude Code session is about to stop */
|
|
47
|
+
declare const STOP: "Stop";
|
|
48
|
+
/** Hook event fired when the user submits a new prompt in Claude Code */
|
|
49
|
+
declare const USER_PROMPT_SUBMIT: "UserPromptSubmit";
|
|
50
|
+
/** Hook event fired when an agentic task completes in Claude Code */
|
|
51
|
+
declare const TASK_COMPLETED: "TaskCompleted";
|
|
52
|
+
/** Hook event fired before a tool is executed in Gemini CLI */
|
|
53
|
+
declare const BEFORE_TOOL_USE: "BeforeTool";
|
|
54
|
+
/** Hook event fired after a tool is executed in Gemini CLI */
|
|
55
|
+
declare const AFTER_TOOL_USE: "AfterTool";
|
|
27
56
|
/**
|
|
28
|
-
* Union type of all supported hook types
|
|
57
|
+
* Union type of all supported Claude Code hook types
|
|
29
58
|
*/
|
|
30
|
-
type
|
|
59
|
+
type ClaudeCodeHookType = (typeof ClaudeCodeHookTypes)[keyof typeof ClaudeCodeHookTypes];
|
|
60
|
+
/**
|
|
61
|
+
* Union type of all supported Gemini CLI hook types
|
|
62
|
+
*/
|
|
63
|
+
type GeminiCliHookType = (typeof GeminiCliHookTypes)[keyof typeof GeminiCliHookTypes];
|
|
64
|
+
/**
|
|
65
|
+
* Union type of all supported hook types across all agents
|
|
66
|
+
*/
|
|
67
|
+
type HookType = ClaudeCodeHookType | GeminiCliHookType;
|
|
31
68
|
//#endregion
|
|
32
69
|
//#region src/types/index.d.ts
|
|
33
70
|
/**
|
|
@@ -85,6 +122,8 @@ interface HookResponse {
|
|
|
85
122
|
userMessage?: string;
|
|
86
123
|
/** Optional updated input parameters for the tool */
|
|
87
124
|
updatedInput?: Record<string, unknown>;
|
|
125
|
+
/** Optional exit code for process termination (default: 0) */
|
|
126
|
+
exitCode?: number;
|
|
88
127
|
}
|
|
89
128
|
/**
|
|
90
129
|
* Content item in tool result from Claude Code
|
|
@@ -102,17 +141,6 @@ interface ToolResult {
|
|
|
102
141
|
/** Array of content items returned by the tool */
|
|
103
142
|
readonly content?: readonly ToolResultContentItem[];
|
|
104
143
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Scaffold execution data from execution log
|
|
107
|
-
*/
|
|
108
|
-
interface ScaffoldExecution {
|
|
109
|
-
/** Unique scaffold execution ID */
|
|
110
|
-
readonly scaffoldId: string;
|
|
111
|
-
/** List of files generated by the scaffold */
|
|
112
|
-
readonly generatedFiles: readonly string[];
|
|
113
|
-
/** Name of the scaffold feature/method */
|
|
114
|
-
readonly featureName?: string;
|
|
115
|
-
}
|
|
116
144
|
/**
|
|
117
145
|
* Log entry structure from ExecutionLogService
|
|
118
146
|
*/
|
|
@@ -132,7 +160,7 @@ interface LogEntry {
|
|
|
132
160
|
/** File path */
|
|
133
161
|
readonly filePath: string;
|
|
134
162
|
/** Decision made */
|
|
135
|
-
readonly decision?:
|
|
163
|
+
readonly decision?: Decision;
|
|
136
164
|
/** File pattern matched */
|
|
137
165
|
readonly filePattern?: string;
|
|
138
166
|
/** File modification timestamp */
|
|
@@ -143,15 +171,16 @@ interface LogEntry {
|
|
|
143
171
|
readonly projectPath?: string;
|
|
144
172
|
}
|
|
145
173
|
/**
|
|
146
|
-
*
|
|
174
|
+
* Scaffold execution data derived from LogEntry
|
|
147
175
|
*/
|
|
148
|
-
interface
|
|
149
|
-
/**
|
|
150
|
-
readonly
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
interface ScaffoldExecution extends Required<Pick<LogEntry, 'scaffoldId' | 'generatedFiles'>> {
|
|
177
|
+
/** Name of the scaffold feature/method */
|
|
178
|
+
readonly featureName?: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Pending scaffold log entry for temp file storage, derived from LogEntry
|
|
182
|
+
*/
|
|
183
|
+
interface PendingScaffoldLogEntry extends Required<Pick<LogEntry, 'scaffoldId' | 'generatedFiles' | 'projectPath'>> {
|
|
155
184
|
/** Scaffold feature name */
|
|
156
185
|
readonly featureName?: string;
|
|
157
186
|
}
|
|
@@ -214,42 +243,67 @@ declare abstract class BaseAdapter<TContext = any> {
|
|
|
214
243
|
//#endregion
|
|
215
244
|
//#region src/adapters/ClaudeCodeAdapter.d.ts
|
|
216
245
|
/**
|
|
217
|
-
* Claude Code hook
|
|
246
|
+
* Common fields shared by all Claude Code hook inputs
|
|
218
247
|
*/
|
|
219
|
-
interface
|
|
220
|
-
tool_name: string;
|
|
221
|
-
tool_input: Record<string, any>;
|
|
248
|
+
interface ClaudeCodeCommonFields {
|
|
222
249
|
cwd: string;
|
|
223
250
|
session_id: string;
|
|
224
|
-
hook_event_name: 'PreToolUse';
|
|
225
|
-
tool_use_id: string;
|
|
226
251
|
transcript_path: string;
|
|
227
252
|
permission_mode: string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Claude Code hook input format (PreToolUse)
|
|
256
|
+
*/
|
|
257
|
+
interface ClaudeCodePreToolUseInput extends ClaudeCodeCommonFields {
|
|
258
|
+
hook_event_name: 'PreToolUse';
|
|
259
|
+
tool_name: string;
|
|
260
|
+
tool_input: Record<string, any>;
|
|
261
|
+
tool_use_id: string;
|
|
228
262
|
llm_tool?: string;
|
|
229
263
|
tool_config?: Record<string, unknown>;
|
|
230
264
|
}
|
|
231
265
|
/**
|
|
232
266
|
* Claude Code hook input format (PostToolUse)
|
|
233
267
|
*/
|
|
234
|
-
interface ClaudeCodePostToolUseInput {
|
|
268
|
+
interface ClaudeCodePostToolUseInput extends ClaudeCodeCommonFields {
|
|
269
|
+
hook_event_name: 'PostToolUse';
|
|
235
270
|
tool_name: string;
|
|
236
271
|
tool_input: Record<string, any>;
|
|
237
272
|
tool_response: Record<string, any>;
|
|
238
|
-
cwd: string;
|
|
239
|
-
session_id: string;
|
|
240
|
-
hook_event_name: 'PostToolUse';
|
|
241
273
|
tool_use_id: string;
|
|
242
|
-
transcript_path: string;
|
|
243
|
-
permission_mode: string;
|
|
244
274
|
llm_tool?: string;
|
|
245
275
|
tool_config?: Record<string, unknown>;
|
|
246
276
|
}
|
|
247
277
|
/**
|
|
248
|
-
*
|
|
278
|
+
* Claude Code hook input format (Stop)
|
|
249
279
|
*/
|
|
250
|
-
|
|
280
|
+
interface ClaudeCodeStopInput extends ClaudeCodeCommonFields {
|
|
281
|
+
hook_event_name: 'Stop';
|
|
282
|
+
stop_hook_active: boolean;
|
|
283
|
+
last_assistant_message: string;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Claude Code hook input format (UserPromptSubmit)
|
|
287
|
+
*/
|
|
288
|
+
interface ClaudeCodeUserPromptSubmitInput extends ClaudeCodeCommonFields {
|
|
289
|
+
hook_event_name: 'UserPromptSubmit';
|
|
290
|
+
prompt: string;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Claude Code hook input format (TaskCompleted)
|
|
294
|
+
*/
|
|
295
|
+
interface ClaudeCodeTaskCompletedInput extends ClaudeCodeCommonFields {
|
|
296
|
+
hook_event_name: 'TaskCompleted';
|
|
297
|
+
task_id: string;
|
|
298
|
+
task_subject: string;
|
|
299
|
+
task_description: string;
|
|
300
|
+
}
|
|
251
301
|
/**
|
|
252
|
-
*
|
|
302
|
+
* Union type for all hook input formats
|
|
303
|
+
*/
|
|
304
|
+
type ClaudeCodeHookInput = ClaudeCodePreToolUseInput | ClaudeCodePostToolUseInput | ClaudeCodeStopInput | ClaudeCodeUserPromptSubmitInput | ClaudeCodeTaskCompletedInput;
|
|
305
|
+
/**
|
|
306
|
+
* Unified adapter for Claude Code hook format
|
|
253
307
|
*/
|
|
254
308
|
declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
255
309
|
private hookEventName;
|
|
@@ -262,7 +316,7 @@ declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
|
262
316
|
parseInput(stdin: string): ClaudeCodeHookInput;
|
|
263
317
|
/**
|
|
264
318
|
* Format normalized HookResponse into Claude Code output
|
|
265
|
-
* Morphs output based on hook event type
|
|
319
|
+
* Morphs output based on hook event type
|
|
266
320
|
*
|
|
267
321
|
* @param response - Normalized hook response
|
|
268
322
|
* @returns JSON string for Claude Code
|
|
@@ -276,6 +330,11 @@ declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
|
276
330
|
* Format PostToolUse output
|
|
277
331
|
*/
|
|
278
332
|
private formatPostToolUseOutput;
|
|
333
|
+
/**
|
|
334
|
+
* Format blockable output for Stop, UserPromptSubmit, and TaskCompleted hooks
|
|
335
|
+
* Maps 'deny' decision to 'block', otherwise returns empty object
|
|
336
|
+
*/
|
|
337
|
+
formatBlockableOutput(response: HookResponse): string;
|
|
279
338
|
}
|
|
280
339
|
//#endregion
|
|
281
340
|
//#region src/adapters/GeminiCliAdapter.d.ts
|
|
@@ -326,7 +385,7 @@ interface LogExecutionParams {
|
|
|
326
385
|
sessionId: string;
|
|
327
386
|
filePath: string;
|
|
328
387
|
operation: string;
|
|
329
|
-
decision:
|
|
388
|
+
decision: Decision;
|
|
330
389
|
filePattern?: string;
|
|
331
390
|
/** File modification timestamp (mtime) at time of execution */
|
|
332
391
|
fileMtime?: number;
|
|
@@ -348,7 +407,7 @@ interface HasExecutedParams {
|
|
|
348
407
|
/** File path to check */
|
|
349
408
|
filePath: string;
|
|
350
409
|
/** Decision to check for (e.g., 'deny' means we already showed patterns) */
|
|
351
|
-
decision:
|
|
410
|
+
decision: Decision;
|
|
352
411
|
/** Optional file pattern to match */
|
|
353
412
|
filePattern?: string;
|
|
354
413
|
/** Optional project path to distinguish same patterns in different projects */
|
|
@@ -439,7 +498,7 @@ declare class ExecutionLogService {
|
|
|
439
498
|
* @param decision - Decision type to check for
|
|
440
499
|
* @returns true if file has changed or no previous execution found, true on error (fail-open)
|
|
441
500
|
*/
|
|
442
|
-
hasFileChanged(filePath: string, decision:
|
|
501
|
+
hasFileChanged(filePath: string, decision: Decision): Promise<boolean>;
|
|
443
502
|
/**
|
|
444
503
|
* Check if file was recently reviewed (within debounce window)
|
|
445
504
|
* Prevents noisy feedback during rapid successive edits
|
|
@@ -465,6 +524,26 @@ declare class ExecutionLogService {
|
|
|
465
524
|
wasGeneratedByScaffold(filePath: string): Promise<boolean>;
|
|
466
525
|
}
|
|
467
526
|
//#endregion
|
|
527
|
+
//#region src/utils/guards.d.ts
|
|
528
|
+
/**
|
|
529
|
+
* Type guard for HookResponse objects.
|
|
530
|
+
* Validates that a value conforms to the HookResponse interface at runtime.
|
|
531
|
+
*
|
|
532
|
+
* @param value - Unknown value to check
|
|
533
|
+
* @returns True if value is a valid HookResponse
|
|
534
|
+
*/
|
|
535
|
+
declare function isHookResponse(value: unknown): value is HookResponse;
|
|
536
|
+
/**
|
|
537
|
+
* Type guard for HookContext objects.
|
|
538
|
+
* Validates that a value conforms to the HookContext interface at runtime.
|
|
539
|
+
* toolInput is shallowly validated because its internal structure varies by tool
|
|
540
|
+
* and is not constrained by the interface.
|
|
541
|
+
*
|
|
542
|
+
* @param value - Unknown value to check
|
|
543
|
+
* @returns True if value is a valid HookContext
|
|
544
|
+
*/
|
|
545
|
+
declare function isHookContext(value: unknown): value is HookContext;
|
|
546
|
+
//#endregion
|
|
468
547
|
//#region src/utils/parseHookType.d.ts
|
|
469
548
|
/**
|
|
470
549
|
* parseHookType Utilities
|
|
@@ -513,4 +592,4 @@ interface ParsedHookType {
|
|
|
513
592
|
*/
|
|
514
593
|
declare function parseHookType(hookType: string): ParsedHookType;
|
|
515
594
|
//#endregion
|
|
516
|
-
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, ClaudeCodeHookInput, ClaudeCodePostToolUseInput, ClaudeCodePreToolUseInput, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, Decision, ExecutionLogService, GeminiCliAdapter, GeminiCliHookInput, HasExecutedParams, HookContext, HookResponse, HookType, LogEntry, LogExecutionParams, LogStats, POST_TOOL_USE, PRE_TOOL_USE,
|
|
595
|
+
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, ClaudeCodeHookInput, ClaudeCodeHookType, ClaudeCodeHookTypes, ClaudeCodePostToolUseInput, ClaudeCodePreToolUseInput, ClaudeCodeStopInput, ClaudeCodeTaskCompletedInput, ClaudeCodeUserPromptSubmitInput, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, Decision, ExecutionLogService, GeminiCliAdapter, GeminiCliHookInput, GeminiCliHookType, GeminiCliHookTypes, HasExecutedParams, HookContext, HookResponse, HookType, LogEntry, LogExecutionParams, LogStats, POST_TOOL_USE, PRE_TOOL_USE, PendingScaffoldLogEntry, STOP, ScaffoldExecution, TASK_COMPLETED, ToolResult, ToolResultContentItem, USER_PROMPT_SUBMIT, isHookContext, isHookResponse, parseHookType };
|
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* DESIGN PATTERNS:
|
|
6
6
|
* - Strongly-typed constant exports for compile-time safety
|
|
7
7
|
* - Immutable by default (as const assertions)
|
|
8
|
+
* - Related constants grouped into const objects
|
|
8
9
|
*
|
|
9
10
|
* CODING STANDARDS:
|
|
10
11
|
* - Primitive constants: UPPER_SNAKE_CASE or PascalCase for event names
|
|
@@ -15,19 +16,55 @@
|
|
|
15
16
|
* - Magic strings without explanation
|
|
16
17
|
*/
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
+
* Grouped hook type identifiers for Claude Code hook events
|
|
19
20
|
*/
|
|
20
|
-
declare const
|
|
21
|
-
|
|
21
|
+
declare const ClaudeCodeHookTypes: {
|
|
22
|
+
/** Fires before a tool is executed, allowing interception or modification */
|
|
23
|
+
readonly PRE_TOOL_USE: "PreToolUse";
|
|
24
|
+
/** Fires after a tool has executed, allowing post-processing or blocking */
|
|
25
|
+
readonly POST_TOOL_USE: "PostToolUse";
|
|
26
|
+
/** Fires when a session is about to stop */
|
|
27
|
+
readonly STOP: "Stop";
|
|
28
|
+
/** Fires when the user submits a new prompt */
|
|
29
|
+
readonly USER_PROMPT_SUBMIT: "UserPromptSubmit";
|
|
30
|
+
/** Fires when an agentic task completes */
|
|
31
|
+
readonly TASK_COMPLETED: "TaskCompleted";
|
|
32
|
+
};
|
|
22
33
|
/**
|
|
23
|
-
*
|
|
34
|
+
* Grouped hook type identifiers for Gemini CLI hook events
|
|
24
35
|
*/
|
|
25
|
-
declare const
|
|
26
|
-
|
|
36
|
+
declare const GeminiCliHookTypes: {
|
|
37
|
+
/** Fires before a tool is executed in Gemini CLI */
|
|
38
|
+
readonly BEFORE_TOOL_USE: "BeforeTool";
|
|
39
|
+
/** Fires after a tool is executed in Gemini CLI */
|
|
40
|
+
readonly AFTER_TOOL_USE: "AfterTool";
|
|
41
|
+
};
|
|
42
|
+
/** Hook event fired before a tool is executed in Claude Code */
|
|
43
|
+
declare const PRE_TOOL_USE: "PreToolUse";
|
|
44
|
+
/** Hook event fired after a tool has executed in Claude Code */
|
|
45
|
+
declare const POST_TOOL_USE: "PostToolUse";
|
|
46
|
+
/** Hook event fired when a Claude Code session is about to stop */
|
|
47
|
+
declare const STOP: "Stop";
|
|
48
|
+
/** Hook event fired when the user submits a new prompt in Claude Code */
|
|
49
|
+
declare const USER_PROMPT_SUBMIT: "UserPromptSubmit";
|
|
50
|
+
/** Hook event fired when an agentic task completes in Claude Code */
|
|
51
|
+
declare const TASK_COMPLETED: "TaskCompleted";
|
|
52
|
+
/** Hook event fired before a tool is executed in Gemini CLI */
|
|
53
|
+
declare const BEFORE_TOOL_USE: "BeforeTool";
|
|
54
|
+
/** Hook event fired after a tool is executed in Gemini CLI */
|
|
55
|
+
declare const AFTER_TOOL_USE: "AfterTool";
|
|
27
56
|
/**
|
|
28
|
-
* Union type of all supported hook types
|
|
57
|
+
* Union type of all supported Claude Code hook types
|
|
29
58
|
*/
|
|
30
|
-
type
|
|
59
|
+
type ClaudeCodeHookType = (typeof ClaudeCodeHookTypes)[keyof typeof ClaudeCodeHookTypes];
|
|
60
|
+
/**
|
|
61
|
+
* Union type of all supported Gemini CLI hook types
|
|
62
|
+
*/
|
|
63
|
+
type GeminiCliHookType = (typeof GeminiCliHookTypes)[keyof typeof GeminiCliHookTypes];
|
|
64
|
+
/**
|
|
65
|
+
* Union type of all supported hook types across all agents
|
|
66
|
+
*/
|
|
67
|
+
type HookType = ClaudeCodeHookType | GeminiCliHookType;
|
|
31
68
|
//#endregion
|
|
32
69
|
//#region src/types/index.d.ts
|
|
33
70
|
/**
|
|
@@ -85,6 +122,8 @@ interface HookResponse {
|
|
|
85
122
|
userMessage?: string;
|
|
86
123
|
/** Optional updated input parameters for the tool */
|
|
87
124
|
updatedInput?: Record<string, unknown>;
|
|
125
|
+
/** Optional exit code for process termination (default: 0) */
|
|
126
|
+
exitCode?: number;
|
|
88
127
|
}
|
|
89
128
|
/**
|
|
90
129
|
* Content item in tool result from Claude Code
|
|
@@ -102,17 +141,6 @@ interface ToolResult {
|
|
|
102
141
|
/** Array of content items returned by the tool */
|
|
103
142
|
readonly content?: readonly ToolResultContentItem[];
|
|
104
143
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Scaffold execution data from execution log
|
|
107
|
-
*/
|
|
108
|
-
interface ScaffoldExecution {
|
|
109
|
-
/** Unique scaffold execution ID */
|
|
110
|
-
readonly scaffoldId: string;
|
|
111
|
-
/** List of files generated by the scaffold */
|
|
112
|
-
readonly generatedFiles: readonly string[];
|
|
113
|
-
/** Name of the scaffold feature/method */
|
|
114
|
-
readonly featureName?: string;
|
|
115
|
-
}
|
|
116
144
|
/**
|
|
117
145
|
* Log entry structure from ExecutionLogService
|
|
118
146
|
*/
|
|
@@ -132,7 +160,7 @@ interface LogEntry {
|
|
|
132
160
|
/** File path */
|
|
133
161
|
readonly filePath: string;
|
|
134
162
|
/** Decision made */
|
|
135
|
-
readonly decision?:
|
|
163
|
+
readonly decision?: Decision;
|
|
136
164
|
/** File pattern matched */
|
|
137
165
|
readonly filePattern?: string;
|
|
138
166
|
/** File modification timestamp */
|
|
@@ -143,15 +171,16 @@ interface LogEntry {
|
|
|
143
171
|
readonly projectPath?: string;
|
|
144
172
|
}
|
|
145
173
|
/**
|
|
146
|
-
*
|
|
174
|
+
* Scaffold execution data derived from LogEntry
|
|
147
175
|
*/
|
|
148
|
-
interface
|
|
149
|
-
/**
|
|
150
|
-
readonly
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
interface ScaffoldExecution extends Required<Pick<LogEntry, 'scaffoldId' | 'generatedFiles'>> {
|
|
177
|
+
/** Name of the scaffold feature/method */
|
|
178
|
+
readonly featureName?: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Pending scaffold log entry for temp file storage, derived from LogEntry
|
|
182
|
+
*/
|
|
183
|
+
interface PendingScaffoldLogEntry extends Required<Pick<LogEntry, 'scaffoldId' | 'generatedFiles' | 'projectPath'>> {
|
|
155
184
|
/** Scaffold feature name */
|
|
156
185
|
readonly featureName?: string;
|
|
157
186
|
}
|
|
@@ -214,42 +243,67 @@ declare abstract class BaseAdapter<TContext = any> {
|
|
|
214
243
|
//#endregion
|
|
215
244
|
//#region src/adapters/ClaudeCodeAdapter.d.ts
|
|
216
245
|
/**
|
|
217
|
-
* Claude Code hook
|
|
246
|
+
* Common fields shared by all Claude Code hook inputs
|
|
218
247
|
*/
|
|
219
|
-
interface
|
|
220
|
-
tool_name: string;
|
|
221
|
-
tool_input: Record<string, any>;
|
|
248
|
+
interface ClaudeCodeCommonFields {
|
|
222
249
|
cwd: string;
|
|
223
250
|
session_id: string;
|
|
224
|
-
hook_event_name: 'PreToolUse';
|
|
225
|
-
tool_use_id: string;
|
|
226
251
|
transcript_path: string;
|
|
227
252
|
permission_mode: string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Claude Code hook input format (PreToolUse)
|
|
256
|
+
*/
|
|
257
|
+
interface ClaudeCodePreToolUseInput extends ClaudeCodeCommonFields {
|
|
258
|
+
hook_event_name: 'PreToolUse';
|
|
259
|
+
tool_name: string;
|
|
260
|
+
tool_input: Record<string, any>;
|
|
261
|
+
tool_use_id: string;
|
|
228
262
|
llm_tool?: string;
|
|
229
263
|
tool_config?: Record<string, unknown>;
|
|
230
264
|
}
|
|
231
265
|
/**
|
|
232
266
|
* Claude Code hook input format (PostToolUse)
|
|
233
267
|
*/
|
|
234
|
-
interface ClaudeCodePostToolUseInput {
|
|
268
|
+
interface ClaudeCodePostToolUseInput extends ClaudeCodeCommonFields {
|
|
269
|
+
hook_event_name: 'PostToolUse';
|
|
235
270
|
tool_name: string;
|
|
236
271
|
tool_input: Record<string, any>;
|
|
237
272
|
tool_response: Record<string, any>;
|
|
238
|
-
cwd: string;
|
|
239
|
-
session_id: string;
|
|
240
|
-
hook_event_name: 'PostToolUse';
|
|
241
273
|
tool_use_id: string;
|
|
242
|
-
transcript_path: string;
|
|
243
|
-
permission_mode: string;
|
|
244
274
|
llm_tool?: string;
|
|
245
275
|
tool_config?: Record<string, unknown>;
|
|
246
276
|
}
|
|
247
277
|
/**
|
|
248
|
-
*
|
|
278
|
+
* Claude Code hook input format (Stop)
|
|
249
279
|
*/
|
|
250
|
-
|
|
280
|
+
interface ClaudeCodeStopInput extends ClaudeCodeCommonFields {
|
|
281
|
+
hook_event_name: 'Stop';
|
|
282
|
+
stop_hook_active: boolean;
|
|
283
|
+
last_assistant_message: string;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Claude Code hook input format (UserPromptSubmit)
|
|
287
|
+
*/
|
|
288
|
+
interface ClaudeCodeUserPromptSubmitInput extends ClaudeCodeCommonFields {
|
|
289
|
+
hook_event_name: 'UserPromptSubmit';
|
|
290
|
+
prompt: string;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Claude Code hook input format (TaskCompleted)
|
|
294
|
+
*/
|
|
295
|
+
interface ClaudeCodeTaskCompletedInput extends ClaudeCodeCommonFields {
|
|
296
|
+
hook_event_name: 'TaskCompleted';
|
|
297
|
+
task_id: string;
|
|
298
|
+
task_subject: string;
|
|
299
|
+
task_description: string;
|
|
300
|
+
}
|
|
251
301
|
/**
|
|
252
|
-
*
|
|
302
|
+
* Union type for all hook input formats
|
|
303
|
+
*/
|
|
304
|
+
type ClaudeCodeHookInput = ClaudeCodePreToolUseInput | ClaudeCodePostToolUseInput | ClaudeCodeStopInput | ClaudeCodeUserPromptSubmitInput | ClaudeCodeTaskCompletedInput;
|
|
305
|
+
/**
|
|
306
|
+
* Unified adapter for Claude Code hook format
|
|
253
307
|
*/
|
|
254
308
|
declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
255
309
|
private hookEventName;
|
|
@@ -262,7 +316,7 @@ declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
|
262
316
|
parseInput(stdin: string): ClaudeCodeHookInput;
|
|
263
317
|
/**
|
|
264
318
|
* Format normalized HookResponse into Claude Code output
|
|
265
|
-
* Morphs output based on hook event type
|
|
319
|
+
* Morphs output based on hook event type
|
|
266
320
|
*
|
|
267
321
|
* @param response - Normalized hook response
|
|
268
322
|
* @returns JSON string for Claude Code
|
|
@@ -276,6 +330,11 @@ declare class ClaudeCodeAdapter extends BaseAdapter<ClaudeCodeHookInput> {
|
|
|
276
330
|
* Format PostToolUse output
|
|
277
331
|
*/
|
|
278
332
|
private formatPostToolUseOutput;
|
|
333
|
+
/**
|
|
334
|
+
* Format blockable output for Stop, UserPromptSubmit, and TaskCompleted hooks
|
|
335
|
+
* Maps 'deny' decision to 'block', otherwise returns empty object
|
|
336
|
+
*/
|
|
337
|
+
formatBlockableOutput(response: HookResponse): string;
|
|
279
338
|
}
|
|
280
339
|
//#endregion
|
|
281
340
|
//#region src/adapters/GeminiCliAdapter.d.ts
|
|
@@ -326,7 +385,7 @@ interface LogExecutionParams {
|
|
|
326
385
|
sessionId: string;
|
|
327
386
|
filePath: string;
|
|
328
387
|
operation: string;
|
|
329
|
-
decision:
|
|
388
|
+
decision: Decision;
|
|
330
389
|
filePattern?: string;
|
|
331
390
|
/** File modification timestamp (mtime) at time of execution */
|
|
332
391
|
fileMtime?: number;
|
|
@@ -348,7 +407,7 @@ interface HasExecutedParams {
|
|
|
348
407
|
/** File path to check */
|
|
349
408
|
filePath: string;
|
|
350
409
|
/** Decision to check for (e.g., 'deny' means we already showed patterns) */
|
|
351
|
-
decision:
|
|
410
|
+
decision: Decision;
|
|
352
411
|
/** Optional file pattern to match */
|
|
353
412
|
filePattern?: string;
|
|
354
413
|
/** Optional project path to distinguish same patterns in different projects */
|
|
@@ -439,7 +498,7 @@ declare class ExecutionLogService {
|
|
|
439
498
|
* @param decision - Decision type to check for
|
|
440
499
|
* @returns true if file has changed or no previous execution found, true on error (fail-open)
|
|
441
500
|
*/
|
|
442
|
-
hasFileChanged(filePath: string, decision:
|
|
501
|
+
hasFileChanged(filePath: string, decision: Decision): Promise<boolean>;
|
|
443
502
|
/**
|
|
444
503
|
* Check if file was recently reviewed (within debounce window)
|
|
445
504
|
* Prevents noisy feedback during rapid successive edits
|
|
@@ -465,6 +524,26 @@ declare class ExecutionLogService {
|
|
|
465
524
|
wasGeneratedByScaffold(filePath: string): Promise<boolean>;
|
|
466
525
|
}
|
|
467
526
|
//#endregion
|
|
527
|
+
//#region src/utils/guards.d.ts
|
|
528
|
+
/**
|
|
529
|
+
* Type guard for HookResponse objects.
|
|
530
|
+
* Validates that a value conforms to the HookResponse interface at runtime.
|
|
531
|
+
*
|
|
532
|
+
* @param value - Unknown value to check
|
|
533
|
+
* @returns True if value is a valid HookResponse
|
|
534
|
+
*/
|
|
535
|
+
declare function isHookResponse(value: unknown): value is HookResponse;
|
|
536
|
+
/**
|
|
537
|
+
* Type guard for HookContext objects.
|
|
538
|
+
* Validates that a value conforms to the HookContext interface at runtime.
|
|
539
|
+
* toolInput is shallowly validated because its internal structure varies by tool
|
|
540
|
+
* and is not constrained by the interface.
|
|
541
|
+
*
|
|
542
|
+
* @param value - Unknown value to check
|
|
543
|
+
* @returns True if value is a valid HookContext
|
|
544
|
+
*/
|
|
545
|
+
declare function isHookContext(value: unknown): value is HookContext;
|
|
546
|
+
//#endregion
|
|
468
547
|
//#region src/utils/parseHookType.d.ts
|
|
469
548
|
/**
|
|
470
549
|
* parseHookType Utilities
|
|
@@ -513,4 +592,4 @@ interface ParsedHookType {
|
|
|
513
592
|
*/
|
|
514
593
|
declare function parseHookType(hookType: string): ParsedHookType;
|
|
515
594
|
//#endregion
|
|
516
|
-
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, ClaudeCodeHookInput, ClaudeCodePostToolUseInput, ClaudeCodePreToolUseInput, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, Decision, ExecutionLogService, GeminiCliAdapter, GeminiCliHookInput, HasExecutedParams, HookContext, HookResponse, HookType, LogEntry, LogExecutionParams, LogStats, POST_TOOL_USE, PRE_TOOL_USE,
|
|
595
|
+
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, ClaudeCodeHookInput, ClaudeCodeHookType, ClaudeCodeHookTypes, ClaudeCodePostToolUseInput, ClaudeCodePreToolUseInput, ClaudeCodeStopInput, ClaudeCodeTaskCompletedInput, ClaudeCodeUserPromptSubmitInput, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, Decision, ExecutionLogService, GeminiCliAdapter, GeminiCliHookInput, GeminiCliHookType, GeminiCliHookTypes, HasExecutedParams, HookContext, HookResponse, HookType, LogEntry, LogExecutionParams, LogStats, POST_TOOL_USE, PRE_TOOL_USE, PendingScaffoldLogEntry, STOP, ScaffoldExecution, TASK_COMPLETED, ToolResult, ToolResultContentItem, USER_PROMPT_SUBMIT, isHookContext, isHookResponse, parseHookType };
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import * as crypto from "node:crypto";
|
|
|
11
11
|
* DESIGN PATTERNS:
|
|
12
12
|
* - Strongly-typed constant exports for compile-time safety
|
|
13
13
|
* - Immutable by default (as const assertions)
|
|
14
|
+
* - Related constants grouped into const objects
|
|
14
15
|
*
|
|
15
16
|
* CODING STANDARDS:
|
|
16
17
|
* - Primitive constants: UPPER_SNAKE_CASE or PascalCase for event names
|
|
@@ -21,15 +22,36 @@ import * as crypto from "node:crypto";
|
|
|
21
22
|
* - Magic strings without explanation
|
|
22
23
|
*/
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Grouped hook type identifiers for Claude Code hook events
|
|
25
26
|
*/
|
|
26
|
-
const
|
|
27
|
-
|
|
27
|
+
const ClaudeCodeHookTypes = {
|
|
28
|
+
PRE_TOOL_USE: "PreToolUse",
|
|
29
|
+
POST_TOOL_USE: "PostToolUse",
|
|
30
|
+
STOP: "Stop",
|
|
31
|
+
USER_PROMPT_SUBMIT: "UserPromptSubmit",
|
|
32
|
+
TASK_COMPLETED: "TaskCompleted"
|
|
33
|
+
};
|
|
28
34
|
/**
|
|
29
|
-
*
|
|
35
|
+
* Grouped hook type identifiers for Gemini CLI hook events
|
|
30
36
|
*/
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
const GeminiCliHookTypes = {
|
|
38
|
+
BEFORE_TOOL_USE: "BeforeTool",
|
|
39
|
+
AFTER_TOOL_USE: "AfterTool"
|
|
40
|
+
};
|
|
41
|
+
/** Hook event fired before a tool is executed in Claude Code */
|
|
42
|
+
const PRE_TOOL_USE = ClaudeCodeHookTypes.PRE_TOOL_USE;
|
|
43
|
+
/** Hook event fired after a tool has executed in Claude Code */
|
|
44
|
+
const POST_TOOL_USE = ClaudeCodeHookTypes.POST_TOOL_USE;
|
|
45
|
+
/** Hook event fired when a Claude Code session is about to stop */
|
|
46
|
+
const STOP = ClaudeCodeHookTypes.STOP;
|
|
47
|
+
/** Hook event fired when the user submits a new prompt in Claude Code */
|
|
48
|
+
const USER_PROMPT_SUBMIT = ClaudeCodeHookTypes.USER_PROMPT_SUBMIT;
|
|
49
|
+
/** Hook event fired when an agentic task completes in Claude Code */
|
|
50
|
+
const TASK_COMPLETED = ClaudeCodeHookTypes.TASK_COMPLETED;
|
|
51
|
+
/** Hook event fired before a tool is executed in Gemini CLI */
|
|
52
|
+
const BEFORE_TOOL_USE = GeminiCliHookTypes.BEFORE_TOOL_USE;
|
|
53
|
+
/** Hook event fired after a tool is executed in Gemini CLI */
|
|
54
|
+
const AFTER_TOOL_USE = GeminiCliHookTypes.AFTER_TOOL_USE;
|
|
33
55
|
|
|
34
56
|
//#endregion
|
|
35
57
|
//#region src/constants/decisions.ts
|
|
@@ -60,12 +82,13 @@ var BaseAdapter = class {
|
|
|
60
82
|
const stdin = await this.readStdin();
|
|
61
83
|
const response = await callback(this.parseInput(stdin));
|
|
62
84
|
if (response.decision === "skip") {
|
|
63
|
-
process.exit(0);
|
|
85
|
+
process.exit(response.exitCode ?? 0);
|
|
64
86
|
return;
|
|
65
87
|
}
|
|
88
|
+
if (response.userMessage) process.stderr.write(`${response.userMessage}\n`);
|
|
66
89
|
const output = this.formatOutput(response);
|
|
67
90
|
console.log(output);
|
|
68
|
-
process.exit(0);
|
|
91
|
+
process.exit(response.exitCode ?? 0);
|
|
69
92
|
} catch (error) {
|
|
70
93
|
this.handleError(error);
|
|
71
94
|
}
|
|
@@ -94,9 +117,10 @@ var BaseAdapter = class {
|
|
|
94
117
|
process.exit(0);
|
|
95
118
|
return;
|
|
96
119
|
}
|
|
120
|
+
if (finalResponse.userMessage) process.stderr.write(`${finalResponse.userMessage}\n`);
|
|
97
121
|
const output = this.formatOutput(finalResponse);
|
|
98
122
|
console.log(output);
|
|
99
|
-
process.exit(0);
|
|
123
|
+
process.exit(finalResponse.exitCode ?? 0);
|
|
100
124
|
} catch (error) {
|
|
101
125
|
this.handleError(error);
|
|
102
126
|
}
|
|
@@ -139,18 +163,18 @@ var BaseAdapter = class {
|
|
|
139
163
|
//#endregion
|
|
140
164
|
//#region src/adapters/ClaudeCodeAdapter.ts
|
|
141
165
|
/**
|
|
142
|
-
* ClaudeCodeAdapter - Unified adapter for Claude Code hook format
|
|
166
|
+
* ClaudeCodeAdapter - Unified adapter for Claude Code hook format
|
|
143
167
|
*
|
|
144
168
|
* DESIGN PATTERNS:
|
|
145
169
|
* - Adapter pattern: Converts Claude Code format to normalized format
|
|
146
170
|
* - Parser pattern: Extracts file paths and operations from tool inputs
|
|
147
|
-
* - State pattern: Stores hook event type to morph behavior between
|
|
171
|
+
* - State pattern: Stores hook event type to morph behavior between hook events
|
|
148
172
|
*
|
|
149
173
|
* CODING STANDARDS:
|
|
150
174
|
* - Parse Claude Code JSON stdin format exactly as specified
|
|
151
175
|
* - Format output to match Claude Code hook response schema
|
|
152
176
|
* - Handle missing/optional fields gracefully
|
|
153
|
-
* - Support
|
|
177
|
+
* - Support PreToolUse, PostToolUse, Stop, UserPromptSubmit, TaskCompleted events
|
|
154
178
|
*
|
|
155
179
|
* AVOID:
|
|
156
180
|
* - Assuming all fields are present
|
|
@@ -158,7 +182,15 @@ var BaseAdapter = class {
|
|
|
158
182
|
* - Mutating input objects
|
|
159
183
|
*/
|
|
160
184
|
/**
|
|
161
|
-
*
|
|
185
|
+
* Blockable hook event names
|
|
186
|
+
*/
|
|
187
|
+
const BLOCKABLE_EVENTS = new Set([
|
|
188
|
+
"Stop",
|
|
189
|
+
"UserPromptSubmit",
|
|
190
|
+
"TaskCompleted"
|
|
191
|
+
]);
|
|
192
|
+
/**
|
|
193
|
+
* Unified adapter for Claude Code hook format
|
|
162
194
|
*/
|
|
163
195
|
var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
164
196
|
hookEventName = "PreToolUse";
|
|
@@ -180,7 +212,7 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
180
212
|
}
|
|
181
213
|
/**
|
|
182
214
|
* Format normalized HookResponse into Claude Code output
|
|
183
|
-
* Morphs output based on hook event type
|
|
215
|
+
* Morphs output based on hook event type
|
|
184
216
|
*
|
|
185
217
|
* @param response - Normalized hook response
|
|
186
218
|
* @returns JSON string for Claude Code
|
|
@@ -195,6 +227,7 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
195
227
|
log.debug("ClaudeCodeAdapter: Skip decision, returning empty output");
|
|
196
228
|
return emptyOutput;
|
|
197
229
|
}
|
|
230
|
+
if (BLOCKABLE_EVENTS.has(this.hookEventName)) return this.formatBlockableOutput(response);
|
|
198
231
|
if (this.hookEventName === "PostToolUse") return this.formatPostToolUseOutput(response);
|
|
199
232
|
return this.formatPreToolUseOutput(response);
|
|
200
233
|
}
|
|
@@ -227,6 +260,20 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
227
260
|
log.debug("ClaudeCodeAdapter: Formatted PostToolUse output", { output: formattedOutput });
|
|
228
261
|
return formattedOutput;
|
|
229
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Format blockable output for Stop, UserPromptSubmit, and TaskCompleted hooks
|
|
265
|
+
* Maps 'deny' decision to 'block', otherwise returns empty object
|
|
266
|
+
*/
|
|
267
|
+
formatBlockableOutput(response) {
|
|
268
|
+
const output = {};
|
|
269
|
+
if (response.decision === "deny") {
|
|
270
|
+
output.decision = "block";
|
|
271
|
+
output.reason = response.message;
|
|
272
|
+
}
|
|
273
|
+
const formattedOutput = JSON.stringify(output, null, 2);
|
|
274
|
+
log.debug(`ClaudeCodeAdapter: Formatted ${this.hookEventName} output`, { output: formattedOutput });
|
|
275
|
+
return formattedOutput;
|
|
276
|
+
}
|
|
230
277
|
};
|
|
231
278
|
|
|
232
279
|
//#endregion
|
|
@@ -317,6 +364,12 @@ function isNodeError(error) {
|
|
|
317
364
|
return error instanceof Error && "code" in error;
|
|
318
365
|
}
|
|
319
366
|
/**
|
|
367
|
+
* Type guard for LogEntry — validates required fields at runtime
|
|
368
|
+
*/
|
|
369
|
+
function isLogEntry(value) {
|
|
370
|
+
return typeof value === "object" && value !== null && "filePath" in value && typeof value.filePath === "string" && "operation" in value && typeof value.operation === "string";
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
320
373
|
* Service for tracking hook executions using an append-only log
|
|
321
374
|
* Prevents duplicate hook actions (e.g., showing design patterns twice for same file)
|
|
322
375
|
* Each session has its own log file for isolation
|
|
@@ -413,8 +466,10 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
413
466
|
const lines = (await fs.readFile(this.logFile, "utf-8")).trim().split("\n").filter(Boolean);
|
|
414
467
|
const entries = [];
|
|
415
468
|
for (const line of lines) try {
|
|
416
|
-
|
|
417
|
-
|
|
469
|
+
const parsed = JSON.parse(line);
|
|
470
|
+
if (isLogEntry(parsed)) entries.push(parsed);
|
|
471
|
+
else console.warn("Skipping malformed log entry:", line.substring(0, 100));
|
|
472
|
+
} catch (_parseError) {
|
|
418
473
|
console.warn("Skipping malformed log entry:", line.substring(0, 100));
|
|
419
474
|
}
|
|
420
475
|
this.cache = entries.slice(-ExecutionLogService.MAX_CACHE_SIZE);
|
|
@@ -477,10 +532,10 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
477
532
|
*/
|
|
478
533
|
async getFileMetadata(filePath) {
|
|
479
534
|
try {
|
|
480
|
-
const content = await fs.readFile(filePath, "utf-8");
|
|
535
|
+
const [content, stats] = await Promise.all([fs.readFile(filePath, "utf-8"), fs.stat(filePath)]);
|
|
481
536
|
const checksum = crypto.createHash("md5").update(content).digest("hex");
|
|
482
537
|
return {
|
|
483
|
-
mtime:
|
|
538
|
+
mtime: stats.mtimeMs,
|
|
484
539
|
checksum
|
|
485
540
|
};
|
|
486
541
|
} catch (error) {
|
|
@@ -566,7 +621,7 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
566
621
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
567
622
|
const entry = entries[i];
|
|
568
623
|
if (entry.operation === "scaffold") {
|
|
569
|
-
if (entry.generatedFiles
|
|
624
|
+
if (entry.generatedFiles?.includes(filePath)) return true;
|
|
570
625
|
}
|
|
571
626
|
}
|
|
572
627
|
return false;
|
|
@@ -577,6 +632,50 @@ var ExecutionLogService = class ExecutionLogService {
|
|
|
577
632
|
}
|
|
578
633
|
};
|
|
579
634
|
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/utils/guards.ts
|
|
637
|
+
/**
|
|
638
|
+
* Set of valid decision values for efficient runtime lookup.
|
|
639
|
+
* Mirrors the Decision type: 'allow' | 'deny' | 'ask' | 'skip'
|
|
640
|
+
*/
|
|
641
|
+
const VALID_DECISIONS = new Set([
|
|
642
|
+
"allow",
|
|
643
|
+
"deny",
|
|
644
|
+
"ask",
|
|
645
|
+
"skip"
|
|
646
|
+
]);
|
|
647
|
+
/**
|
|
648
|
+
* Type guard for HookResponse objects.
|
|
649
|
+
* Validates that a value conforms to the HookResponse interface at runtime.
|
|
650
|
+
*
|
|
651
|
+
* @param value - Unknown value to check
|
|
652
|
+
* @returns True if value is a valid HookResponse
|
|
653
|
+
*/
|
|
654
|
+
function isHookResponse(value) {
|
|
655
|
+
if (typeof value !== "object" || value === null) return false;
|
|
656
|
+
if (!("decision" in value) || typeof value.decision !== "string") return false;
|
|
657
|
+
if (!VALID_DECISIONS.has(value.decision)) return false;
|
|
658
|
+
if (!("message" in value) || typeof value.message !== "string") return false;
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Type guard for HookContext objects.
|
|
663
|
+
* Validates that a value conforms to the HookContext interface at runtime.
|
|
664
|
+
* toolInput is shallowly validated because its internal structure varies by tool
|
|
665
|
+
* and is not constrained by the interface.
|
|
666
|
+
*
|
|
667
|
+
* @param value - Unknown value to check
|
|
668
|
+
* @returns True if value is a valid HookContext
|
|
669
|
+
*/
|
|
670
|
+
function isHookContext(value) {
|
|
671
|
+
if (typeof value !== "object" || value === null) return false;
|
|
672
|
+
if (!("toolName" in value) || typeof value.toolName !== "string") return false;
|
|
673
|
+
if (!("toolInput" in value) || typeof value.toolInput !== "object" || value.toolInput === null) return false;
|
|
674
|
+
if (!("cwd" in value) || typeof value.cwd !== "string") return false;
|
|
675
|
+
if (!("sessionId" in value) || typeof value.sessionId !== "string") return false;
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
580
679
|
//#endregion
|
|
581
680
|
//#region src/utils/parseHookType.ts
|
|
582
681
|
/**
|
|
@@ -604,4 +703,4 @@ function parseHookType(hookType) {
|
|
|
604
703
|
}
|
|
605
704
|
|
|
606
705
|
//#endregion
|
|
607
|
-
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, ExecutionLogService, GeminiCliAdapter, POST_TOOL_USE, PRE_TOOL_USE, parseHookType };
|
|
706
|
+
export { AFTER_TOOL_USE, BEFORE_TOOL_USE, BaseAdapter, ClaudeCodeAdapter, ClaudeCodeHookTypes, DECISION_ALLOW, DECISION_ASK, DECISION_DENY, DECISION_SKIP, ExecutionLogService, GeminiCliAdapter, GeminiCliHookTypes, POST_TOOL_USE, PRE_TOOL_USE, STOP, TASK_COMPLETED, USER_PROMPT_SUBMIT, isHookContext, isHookResponse, parseHookType };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agiflowai/hooks-adapter",
|
|
3
3
|
"description": "Hook adapters for normalizing AI agent hook formats (Claude Code, Gemini, etc.)",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.16",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"author": "AgiflowIO",
|
|
7
7
|
"repository": {
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@agiflowai/aicode-utils": "1.0.
|
|
29
|
-
"@agiflowai/coding-agent-bridge": "1.0.
|
|
28
|
+
"@agiflowai/aicode-utils": "1.0.15",
|
|
29
|
+
"@agiflowai/coding-agent-bridge": "1.0.18"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|