@goodfoot/claude-code-hooks 1.0.1
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/LICENSE +21 -0
- package/README.md +317 -0
- package/dist/cli.js +914 -0
- package/dist/constants.js +21 -0
- package/dist/env.js +188 -0
- package/dist/hooks.js +391 -0
- package/dist/index.js +77 -0
- package/dist/inputs.js +35 -0
- package/dist/logger.js +494 -0
- package/dist/outputs.js +282 -0
- package/dist/runtime.js +222 -0
- package/dist/scaffold.js +466 -0
- package/dist/tool-helpers.js +366 -0
- package/dist/tool-inputs.js +21 -0
- package/package.json +68 -0
- package/types/cli.d.ts +281 -0
- package/types/constants.d.ts +9 -0
- package/types/env.d.ts +150 -0
- package/types/hooks.d.ts +851 -0
- package/types/index.d.ts +137 -0
- package/types/inputs.d.ts +601 -0
- package/types/logger.d.ts +471 -0
- package/types/outputs.d.ts +643 -0
- package/types/runtime.d.ts +75 -0
- package/types/scaffold.d.ts +46 -0
- package/types/tool-helpers.d.ts +336 -0
- package/types/tool-inputs.d.ts +228 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guards and helper functions for Claude Code tool inputs.
|
|
3
|
+
*
|
|
4
|
+
* Provides safe type narrowing for tool inputs and utility functions
|
|
5
|
+
* for common patterns like file path extraction and content inspection.
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import {
|
|
9
|
+
* preToolUseHook,
|
|
10
|
+
* preToolUseOutput,
|
|
11
|
+
* isWriteTool,
|
|
12
|
+
* getFilePath,
|
|
13
|
+
* isTsFile,
|
|
14
|
+
* checkContentForPattern
|
|
15
|
+
* } from '@goodfoot/claude-code-hooks';
|
|
16
|
+
*
|
|
17
|
+
* export default preToolUseHook({ matcher: 'Write|Edit|MultiEdit' }, (input) => {
|
|
18
|
+
* const filePath = getFilePath(input);
|
|
19
|
+
* if (!filePath || !isTsFile(filePath)) return preToolUseOutput({});
|
|
20
|
+
*
|
|
21
|
+
* const result = checkContentForPattern(input, /@ts-ignore/g);
|
|
22
|
+
* if (result?.isAddition) {
|
|
23
|
+
* return preToolUseOutput({
|
|
24
|
+
* hookSpecificOutput: {
|
|
25
|
+
* permissionDecision: 'deny',
|
|
26
|
+
* permissionDecisionReason: `Cannot add: ${result.matches.join(', ')}`
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* return preToolUseOutput({});
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
* @see https://code.claude.com/docs/en/hooks
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Type Guards
|
|
39
|
+
// ============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Type guard for Write tool inputs.
|
|
42
|
+
*
|
|
43
|
+
* Narrows the input type to include a typed WriteToolInput.
|
|
44
|
+
* @param input - The hook input to check
|
|
45
|
+
* @returns True if the input is for a Write tool
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* if (isWriteTool(input)) {
|
|
49
|
+
* // input.tool_input is now typed as WriteToolInput
|
|
50
|
+
* console.log(input.tool_input.file_path);
|
|
51
|
+
* console.log(input.tool_input.content);
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function isWriteTool(input) {
|
|
56
|
+
return input.tool_name === 'Write';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Type guard for Edit tool inputs.
|
|
60
|
+
*
|
|
61
|
+
* Narrows the input type to include a typed EditToolInput.
|
|
62
|
+
* @param input - The hook input to check
|
|
63
|
+
* @returns True if the input is for an Edit tool
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* if (isEditTool(input)) {
|
|
67
|
+
* console.log(input.tool_input.old_string);
|
|
68
|
+
* console.log(input.tool_input.new_string);
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function isEditTool(input) {
|
|
73
|
+
return input.tool_name === 'Edit';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Type guard for MultiEdit tool inputs.
|
|
77
|
+
*
|
|
78
|
+
* Narrows the input type to include a typed MultiEditToolInput.
|
|
79
|
+
* @param input - The hook input to check
|
|
80
|
+
* @returns True if the input is for a MultiEdit tool
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* if (isMultiEditTool(input)) {
|
|
84
|
+
* for (const edit of input.tool_input.edits) {
|
|
85
|
+
* console.log(`${edit.old_string} -> ${edit.new_string}`);
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function isMultiEditTool(input) {
|
|
91
|
+
return input.tool_name === 'MultiEdit';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Type guard for any file-modifying tool (Write, Edit, or MultiEdit).
|
|
95
|
+
*
|
|
96
|
+
* Use this when you need to handle all file modifications generically.
|
|
97
|
+
* @param input - The hook input to check
|
|
98
|
+
* @returns True if the input is for a Write, Edit, or MultiEdit tool
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* if (isFileModifyingTool(input)) {
|
|
102
|
+
* const filePath = getFilePath(input); // Works for all three types
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function isFileModifyingTool(input) {
|
|
107
|
+
return input.tool_name === 'Write' || input.tool_name === 'Edit' || input.tool_name === 'MultiEdit';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Type guard for Read tool inputs.
|
|
111
|
+
*
|
|
112
|
+
* Narrows the input type to include a typed ReadToolInput.
|
|
113
|
+
* @param input - The hook input to check
|
|
114
|
+
* @returns True if the input is for a Read tool
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* if (isReadTool(input)) {
|
|
118
|
+
* console.log(input.tool_input.file_path);
|
|
119
|
+
* console.log(input.tool_input.offset);
|
|
120
|
+
* }
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function isReadTool(input) {
|
|
124
|
+
return input.tool_name === 'Read';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Type guard for Bash tool inputs.
|
|
128
|
+
*
|
|
129
|
+
* Narrows the input type to include a typed BashToolInput.
|
|
130
|
+
* @param input - The hook input to check
|
|
131
|
+
* @returns True if the input is for a Bash tool
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* if (isBashTool(input)) {
|
|
135
|
+
* console.log(input.tool_input.command);
|
|
136
|
+
* console.log(input.tool_input.timeout);
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export function isBashTool(input) {
|
|
141
|
+
return input.tool_name === 'Bash';
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Type guard for Glob tool inputs.
|
|
145
|
+
*
|
|
146
|
+
* Narrows the input type to include a typed GlobToolInput.
|
|
147
|
+
* @param input - The hook input to check
|
|
148
|
+
* @returns True if the input is for a Glob tool
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* if (isGlobTool(input)) {
|
|
152
|
+
* console.log(input.tool_input.pattern);
|
|
153
|
+
* console.log(input.tool_input.path);
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function isGlobTool(input) {
|
|
158
|
+
return input.tool_name === 'Glob';
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Type guard for Grep tool inputs.
|
|
162
|
+
*
|
|
163
|
+
* Narrows the input type to include a typed GrepToolInput.
|
|
164
|
+
* @param input - The hook input to check
|
|
165
|
+
* @returns True if the input is for a Grep tool
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* if (isGrepTool(input)) {
|
|
169
|
+
* console.log(input.tool_input.pattern);
|
|
170
|
+
* console.log(input.tool_input.glob);
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function isGrepTool(input) {
|
|
175
|
+
return input.tool_name === 'Grep';
|
|
176
|
+
}
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// File Path Utilities
|
|
179
|
+
// ============================================================================
|
|
180
|
+
/**
|
|
181
|
+
* Extracts the file path from a tool input.
|
|
182
|
+
*
|
|
183
|
+
* Works with Write, Edit, MultiEdit, and Read tools.
|
|
184
|
+
* Returns null for other tools or if file_path is missing.
|
|
185
|
+
* @param input - The hook input to extract from
|
|
186
|
+
* @returns The file path, or null if not applicable
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const filePath = getFilePath(input);
|
|
190
|
+
* if (filePath && isTsFile(filePath)) {
|
|
191
|
+
* // Handle TypeScript file
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export function getFilePath(input) {
|
|
196
|
+
const toolInput = input.tool_input;
|
|
197
|
+
if (toolInput && typeof toolInput === 'object' && 'file_path' in toolInput) {
|
|
198
|
+
const filePath = toolInput.file_path;
|
|
199
|
+
return typeof filePath === 'string' ? filePath : null;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Checks if a file path is a JavaScript or TypeScript file.
|
|
205
|
+
*
|
|
206
|
+
* Matches .js, .jsx, .ts, .tsx, .mjs, .mts, .cjs, .cts extensions.
|
|
207
|
+
* @param filePath - The file path to check
|
|
208
|
+
* @returns True if the file is JavaScript or TypeScript
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* if (isJsTsFile(filePath)) {
|
|
212
|
+
* // Check for TypeScript-specific patterns
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export function isJsTsFile(filePath) {
|
|
217
|
+
return /\.[cm]?[jt]sx?$/.test(filePath);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Checks if a file path is a TypeScript file.
|
|
221
|
+
*
|
|
222
|
+
* Matches .ts, .tsx, .mts, .cts extensions.
|
|
223
|
+
* @param filePath - The file path to check
|
|
224
|
+
* @returns True if the file is TypeScript
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* if (isTsFile(filePath)) {
|
|
228
|
+
* // Enforce TypeScript-specific rules
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export function isTsFile(filePath) {
|
|
233
|
+
return /\.[cm]?tsx?$/.test(filePath);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Checks if a pattern exists in the content being written or edited.
|
|
237
|
+
*
|
|
238
|
+
* For Write: checks the content being written
|
|
239
|
+
* For Edit: checks new_string (and old_string to detect additions)
|
|
240
|
+
* For MultiEdit: checks all edits and aggregates results
|
|
241
|
+
* @param input - The PreToolUse hook input
|
|
242
|
+
* @param pattern - The regex pattern to search for (global flag will be used)
|
|
243
|
+
* @returns Result object, or null if not a file-modifying tool
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* // Block @ts-ignore being added
|
|
247
|
+
* const result = checkContentForPattern(input, /@ts-ignore/g);
|
|
248
|
+
* if (result?.isAddition) {
|
|
249
|
+
* return preToolUseOutput({
|
|
250
|
+
* hookSpecificOutput: {
|
|
251
|
+
* permissionDecision: 'deny',
|
|
252
|
+
* permissionDecisionReason: `Cannot add: ${result.matches.join(', ')}`
|
|
253
|
+
* }
|
|
254
|
+
* });
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export function checkContentForPattern(input, pattern) {
|
|
259
|
+
// Ensure pattern has global flag for matchAll
|
|
260
|
+
const globalPattern = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g');
|
|
261
|
+
if (isWriteTool(input)) {
|
|
262
|
+
const matches = [...input.tool_input.content.matchAll(globalPattern)].map((m) => m[0]);
|
|
263
|
+
const uniqueMatches = [...new Set(matches)];
|
|
264
|
+
return {
|
|
265
|
+
found: uniqueMatches.length > 0,
|
|
266
|
+
isAddition: uniqueMatches.length > 0, // For Write, any match is an addition
|
|
267
|
+
matches: uniqueMatches
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (isEditTool(input)) {
|
|
271
|
+
const newMatches = [...input.tool_input.new_string.matchAll(globalPattern)].map((m) => m[0]);
|
|
272
|
+
const oldMatches = [...input.tool_input.old_string.matchAll(globalPattern)].map((m) => m[0]);
|
|
273
|
+
const uniqueNewMatches = [...new Set(newMatches)];
|
|
274
|
+
const uniqueOldMatches = new Set(oldMatches);
|
|
275
|
+
// Addition = found in new but not in old
|
|
276
|
+
const additions = uniqueNewMatches.filter((m) => !uniqueOldMatches.has(m));
|
|
277
|
+
return {
|
|
278
|
+
found: uniqueNewMatches.length > 0,
|
|
279
|
+
isAddition: additions.length > 0,
|
|
280
|
+
matches: uniqueNewMatches
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (isMultiEditTool(input)) {
|
|
284
|
+
const details = [];
|
|
285
|
+
const allMatches = new Set();
|
|
286
|
+
let anyFound = false;
|
|
287
|
+
let anyAddition = false;
|
|
288
|
+
for (let i = 0; i < input.tool_input.edits.length; i++) {
|
|
289
|
+
const edit = input.tool_input.edits[i];
|
|
290
|
+
const newMatches = [...edit.new_string.matchAll(globalPattern)].map((m) => m[0]);
|
|
291
|
+
const oldMatches = [...edit.old_string.matchAll(globalPattern)].map((m) => m[0]);
|
|
292
|
+
const uniqueNewMatches = [...new Set(newMatches)];
|
|
293
|
+
const uniqueOldMatches = new Set(oldMatches);
|
|
294
|
+
const additions = uniqueNewMatches.filter((m) => !uniqueOldMatches.has(m));
|
|
295
|
+
const found = uniqueNewMatches.length > 0;
|
|
296
|
+
const isAddition = additions.length > 0;
|
|
297
|
+
if (found) anyFound = true;
|
|
298
|
+
if (isAddition) anyAddition = true;
|
|
299
|
+
uniqueNewMatches.forEach((m) => allMatches.add(m));
|
|
300
|
+
details.push({
|
|
301
|
+
index: i,
|
|
302
|
+
found,
|
|
303
|
+
isAddition,
|
|
304
|
+
matches: uniqueNewMatches
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
found: anyFound,
|
|
309
|
+
isAddition: anyAddition,
|
|
310
|
+
matches: [...allMatches],
|
|
311
|
+
details
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Iterates over content in Write/Edit/MultiEdit operations.
|
|
318
|
+
*
|
|
319
|
+
* Provides a unified way to inspect content regardless of operation type.
|
|
320
|
+
* Return false from the callback to stop iteration early.
|
|
321
|
+
* @param input - The PreToolUse hook input
|
|
322
|
+
* @param callback - Function called for each content piece, return false to stop
|
|
323
|
+
* @returns True if all callbacks returned true, false if stopped early or not applicable
|
|
324
|
+
* @example
|
|
325
|
+
* ```typescript
|
|
326
|
+
* // Check all content for sensitive data
|
|
327
|
+
* const hasSensitive = !forEachContent(input, ({ newContent }) => {
|
|
328
|
+
* if (/password|secret|api.?key/i.test(newContent)) {
|
|
329
|
+
* return false; // Stop - found sensitive data
|
|
330
|
+
* }
|
|
331
|
+
* return true; // Continue
|
|
332
|
+
* });
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
export function forEachContent(input, callback) {
|
|
336
|
+
if (isWriteTool(input)) {
|
|
337
|
+
return callback({
|
|
338
|
+
newContent: input.tool_input.content,
|
|
339
|
+
oldContent: null,
|
|
340
|
+
index: 0,
|
|
341
|
+
isWrite: true
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
if (isEditTool(input)) {
|
|
345
|
+
return callback({
|
|
346
|
+
newContent: input.tool_input.new_string,
|
|
347
|
+
oldContent: input.tool_input.old_string,
|
|
348
|
+
index: 0,
|
|
349
|
+
isWrite: false
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (isMultiEditTool(input)) {
|
|
353
|
+
for (let i = 0; i < input.tool_input.edits.length; i++) {
|
|
354
|
+
const edit = input.tool_input.edits[i];
|
|
355
|
+
const shouldContinue = callback({
|
|
356
|
+
newContent: edit.new_string,
|
|
357
|
+
oldContent: edit.old_string,
|
|
358
|
+
index: i,
|
|
359
|
+
isWrite: false
|
|
360
|
+
});
|
|
361
|
+
if (!shouldContinue) return false;
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for well-known Claude Code tool inputs.
|
|
3
|
+
*
|
|
4
|
+
* These types define the structure of `toolInput` for common Claude Code tools.
|
|
5
|
+
* Use them with type guards from `tool-helpers.ts` for safe type narrowing,
|
|
6
|
+
* or with typed hook factory overloads for automatic typing.
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { isWriteTool, getFilePath } from '@goodfoot/claude-code-hooks';
|
|
10
|
+
*
|
|
11
|
+
* preToolUseHook({ matcher: 'Write|Edit|MultiEdit' }, (input) => {
|
|
12
|
+
* if (isWriteTool(input)) {
|
|
13
|
+
* // input.tool_input is now typed as WriteToolInput
|
|
14
|
+
* console.log(input.tool_input.file_path);
|
|
15
|
+
* }
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
* @see https://code.claude.com/docs/en/hooks
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodfoot/claude-code-hooks",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Type-safe Claude Code hooks library with camelCase types and output builders",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20.11.0"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"types"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"prettier": "prettier . --write --log-level warn",
|
|
16
|
+
"prettier:files": "prettier --write --log-level warn",
|
|
17
|
+
"eslint": "eslint --cache --fix ./src ./tests ./e2e",
|
|
18
|
+
"eslint:files": "eslint --cache --fix",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"build": "tsc --build tsconfig.build.json",
|
|
21
|
+
"lint": "bash -c 'if [ $# -eq 0 ]; then yarn typecheck && yarn eslint && yarn prettier; else yarn typecheck && yarn eslint:files \"$@\" && yarn prettier:files \"$@\"; fi' --",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:types": "vitest run --typecheck",
|
|
24
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
25
|
+
"release": "bash ../../scripts/release-package.sh claude-code-hooks",
|
|
26
|
+
"release:dry-run": "bash ../../scripts/release-package.sh claude-code-hooks --dry-run"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./src/index.ts",
|
|
31
|
+
"types": "./src/index.ts"
|
|
32
|
+
},
|
|
33
|
+
"./*": {
|
|
34
|
+
"import": "./src/*.ts",
|
|
35
|
+
"types": "./src/*.ts"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"bin": "./dist/cli.js",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"bin": {
|
|
41
|
+
"claude-code-hooks": "./dist/cli.js"
|
|
42
|
+
},
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"import": "./dist/index.js",
|
|
46
|
+
"types": "./types/index.d.ts"
|
|
47
|
+
},
|
|
48
|
+
"./*": {
|
|
49
|
+
"import": "./dist/*.js",
|
|
50
|
+
"types": "./types/*.d.ts"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"esbuild": "^0.24.0",
|
|
56
|
+
"glob": "^11.0.0",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
|
61
|
+
"@types/node": "^24",
|
|
62
|
+
"eslint": "^9.36.0",
|
|
63
|
+
"eslint-plugin-jsdoc": "^50.0.0",
|
|
64
|
+
"prettier": "^3.6.2",
|
|
65
|
+
"tsx": "^4.20.3",
|
|
66
|
+
"vitest": "4.0.13"
|
|
67
|
+
}
|
|
68
|
+
}
|