@goodfoot/claude-code-hooks 1.0.16 → 1.0.18
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/cli.js +36 -19
- package/dist/scaffold.js +2 -2
- package/package.json +3 -3
- package/types/cli.d.ts +18 -4
- package/types/types.d.ts +1 -7
package/dist/cli.js
CHANGED
|
@@ -374,21 +374,27 @@ async function discoverHookFiles(pattern, cwd) {
|
|
|
374
374
|
/**
|
|
375
375
|
* Compiles a TypeScript hook file to a self-contained ESM executable.
|
|
376
376
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
377
|
+
* Uses a two-step process to generate stable content hashes:
|
|
378
|
+
* 1. Compile WITHOUT sourcemaps → generate stable content hash
|
|
379
|
+
* 2. Compile WITH sourcemaps → final output content
|
|
380
|
+
*
|
|
381
|
+
* This ensures content hashes are reproducible across different build
|
|
382
|
+
* environments, since sourcemaps contain file paths that can vary.
|
|
383
|
+
*
|
|
379
384
|
* @param options - Compilation options
|
|
380
|
-
* @returns Compiled
|
|
385
|
+
* @returns Compiled content and stable content hash
|
|
381
386
|
*/
|
|
382
387
|
async function compileHook(options) {
|
|
383
388
|
const { sourcePath, logFilePath } = options;
|
|
384
389
|
// Create a temporary wrapper file that imports the hook and executes it
|
|
385
|
-
// Use system temp directory with deterministic name based on
|
|
386
|
-
// This ensures
|
|
387
|
-
const hashInputs = [sourcePath, logFilePath
|
|
390
|
+
// Use system temp directory with deterministic name based on hook basename (not full path)
|
|
391
|
+
// This ensures builds are reproducible across different environments/machines
|
|
392
|
+
const hashInputs = [path.basename(sourcePath), logFilePath ? "log" : ""].join(":");
|
|
388
393
|
const buildHash = crypto.createHash("sha256").update(hashInputs).digest("hex").substring(0, 16);
|
|
389
394
|
const tempDir = path.join(os.tmpdir(), "claude-code-hooks-build", buildHash);
|
|
390
395
|
const wrapperPath = path.join(tempDir, "wrapper.ts");
|
|
391
|
-
const
|
|
396
|
+
const tempOutputNoSourcemap = path.join(tempDir, "output-no-sourcemap.mjs");
|
|
397
|
+
const tempOutputWithSourcemap = path.join(tempDir, "output.mjs");
|
|
392
398
|
// Get the path to the runtime module (relative to this CLI)
|
|
393
399
|
const runtimePath = path.resolve(path.dirname(new URL(import.meta.url).pathname), "./runtime.js");
|
|
394
400
|
// Ensure temp directory exists (don't delete - concurrent builds may be using it)
|
|
@@ -405,14 +411,13 @@ import { execute } from '${runtimePath.replace(/\\/g, "/")}';
|
|
|
405
411
|
execute(hook);
|
|
406
412
|
`;
|
|
407
413
|
fs.writeFileSync(wrapperPath, wrapperContent, "utf-8");
|
|
408
|
-
|
|
414
|
+
// Common esbuild options
|
|
415
|
+
const commonOptions = {
|
|
409
416
|
entryPoints: [wrapperPath],
|
|
410
|
-
outfile: tempOutput,
|
|
411
417
|
format: "esm",
|
|
412
418
|
platform: "node",
|
|
413
419
|
target: "node20",
|
|
414
420
|
bundle: true,
|
|
415
|
-
sourcemap: "inline",
|
|
416
421
|
minify: false,
|
|
417
422
|
// Keep node built-ins external
|
|
418
423
|
external: [
|
|
@@ -437,11 +442,25 @@ execute(hook);
|
|
|
437
442
|
// Ensure we get clean ESM output
|
|
438
443
|
mainFields: ["module", "main"],
|
|
439
444
|
conditions: ["import", "node"],
|
|
445
|
+
};
|
|
446
|
+
// Step 1: Compile WITHOUT sourcemaps to generate stable content hash
|
|
447
|
+
await esbuild.build({
|
|
448
|
+
...commonOptions,
|
|
449
|
+
outfile: tempOutputNoSourcemap,
|
|
450
|
+
sourcemap: false,
|
|
451
|
+
});
|
|
452
|
+
const contentForHash = fs.readFileSync(tempOutputNoSourcemap, "utf-8");
|
|
453
|
+
const contentHash = generateContentHash(contentForHash);
|
|
454
|
+
// Step 2: Compile WITH sourcemaps for final output
|
|
455
|
+
await esbuild.build({
|
|
456
|
+
...commonOptions,
|
|
457
|
+
outfile: tempOutputWithSourcemap,
|
|
458
|
+
sourcemap: "inline",
|
|
440
459
|
});
|
|
441
|
-
|
|
460
|
+
const content = fs.readFileSync(tempOutputWithSourcemap, "utf-8");
|
|
442
461
|
// Don't delete temp directory - allows concurrent builds of same source
|
|
443
462
|
// and the OS will clean up /tmp periodically
|
|
444
|
-
return
|
|
463
|
+
return { content, contentHash };
|
|
445
464
|
}
|
|
446
465
|
/**
|
|
447
466
|
* Generates a content hash (SHA-256, 8-char prefix) for a compiled hook.
|
|
@@ -476,19 +495,17 @@ async function compileAllHooks(options) {
|
|
|
476
495
|
matcher: metadata.matcher,
|
|
477
496
|
timeout: metadata.timeout,
|
|
478
497
|
});
|
|
479
|
-
// Compile the hook
|
|
498
|
+
// Compile the hook (two-step process for stable content hash)
|
|
480
499
|
log("info", `Compiling: ${sourcePath}`);
|
|
481
|
-
const
|
|
482
|
-
//
|
|
483
|
-
const hash = generateContentHash(compiledContent);
|
|
484
|
-
// Determine output filename
|
|
500
|
+
const { content, contentHash } = await compileHook({ sourcePath, outputDir, logFilePath });
|
|
501
|
+
// Determine output filename using the stable content hash
|
|
485
502
|
const baseName = path.basename(sourcePath, path.extname(sourcePath));
|
|
486
|
-
const outputFilename = `${baseName}.${
|
|
503
|
+
const outputFilename = `${baseName}.${contentHash}.mjs`;
|
|
487
504
|
const outputPath = path.join(outputDir, outputFilename);
|
|
488
505
|
// Write compiled output with shebang for direct execution
|
|
489
506
|
// --enable-source-maps enables stack traces with original source locations
|
|
490
507
|
const shebang = "#!/usr/bin/env -S node --enable-source-maps\n";
|
|
491
|
-
fs.writeFileSync(outputPath, shebang +
|
|
508
|
+
fs.writeFileSync(outputPath, shebang + content, { encoding: "utf-8", mode: 0o755 });
|
|
492
509
|
log("info", `Wrote: ${outputPath}`);
|
|
493
510
|
compiledHooks.push({
|
|
494
511
|
sourcePath,
|
package/dist/scaffold.js
CHANGED
|
@@ -117,7 +117,7 @@ function generatePackageJson(projectName, outputPath) {
|
|
|
117
117
|
"@goodfoot/claude-code-hooks": "^1.0.9",
|
|
118
118
|
},
|
|
119
119
|
devDependencies: {
|
|
120
|
-
"@biomejs/biome": "2.3.
|
|
120
|
+
"@biomejs/biome": "2.3.14",
|
|
121
121
|
"@types/node": "^22.0.0",
|
|
122
122
|
typescript: "^5.9.3",
|
|
123
123
|
vitest: "^4.0.16",
|
|
@@ -160,7 +160,7 @@ function generateTsConfig() {
|
|
|
160
160
|
*/
|
|
161
161
|
function generateBiomeConfig() {
|
|
162
162
|
return `{
|
|
163
|
-
"$schema": "https://biomejs.dev/schemas/2.3.
|
|
163
|
+
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
|
|
164
164
|
"formatter": {
|
|
165
165
|
"enabled": true,
|
|
166
166
|
"indentStyle": "space",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodfoot/claude-code-hooks",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Type-safe Claude Code hooks library with camelCase types and output builders",
|
|
5
5
|
"homepage": "https://github.com/goodfoot-io/marketplace/tree/main/packages/claude-code-hooks",
|
|
6
6
|
"repository": {
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"typescript": "^5.9.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
57
|
-
"@biomejs/biome": "2.3.
|
|
56
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.31",
|
|
57
|
+
"@biomejs/biome": "2.3.14",
|
|
58
58
|
"@types/node": "^24",
|
|
59
59
|
"ts-morph": "^25.0.0",
|
|
60
60
|
"tsx": "^4.20.3",
|
package/types/cli.d.ts
CHANGED
|
@@ -151,15 +151,29 @@ interface CompileHookOptions {
|
|
|
151
151
|
/** Optional log file path to inject into compiled hook. */
|
|
152
152
|
logFilePath?: string;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Result of compiling a hook.
|
|
156
|
+
*/
|
|
157
|
+
interface CompileHookResult {
|
|
158
|
+
/** Compiled content WITH sourcemaps (for final output). */
|
|
159
|
+
content: string;
|
|
160
|
+
/** Stable content hash generated from sourcemap-free output. */
|
|
161
|
+
contentHash: string;
|
|
162
|
+
}
|
|
154
163
|
/**
|
|
155
164
|
* Compiles a TypeScript hook file to a self-contained ESM executable.
|
|
156
165
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
166
|
+
* Uses a two-step process to generate stable content hashes:
|
|
167
|
+
* 1. Compile WITHOUT sourcemaps → generate stable content hash
|
|
168
|
+
* 2. Compile WITH sourcemaps → final output content
|
|
169
|
+
*
|
|
170
|
+
* This ensures content hashes are reproducible across different build
|
|
171
|
+
* environments, since sourcemaps contain file paths that can vary.
|
|
172
|
+
*
|
|
159
173
|
* @param options - Compilation options
|
|
160
|
-
* @returns Compiled
|
|
174
|
+
* @returns Compiled content and stable content hash
|
|
161
175
|
*/
|
|
162
|
-
declare function compileHook(options: CompileHookOptions): Promise<
|
|
176
|
+
declare function compileHook(options: CompileHookOptions): Promise<CompileHookResult>;
|
|
163
177
|
/**
|
|
164
178
|
* Generates a content hash (SHA-256, 8-char prefix) for a compiled hook.
|
|
165
179
|
* @param content - Compiled hook content
|
package/types/types.d.ts
CHANGED
|
@@ -307,13 +307,7 @@ export type SubagentStartInput = SDKSubagentStartHookInput;
|
|
|
307
307
|
* ```
|
|
308
308
|
* @see https://code.claude.com/docs/en/hooks#subagentstop
|
|
309
309
|
*/
|
|
310
|
-
export
|
|
311
|
-
/**
|
|
312
|
-
* Type of subagent that is stopping.
|
|
313
|
-
* Examples: 'explore', 'codebase-analysis', custom agent types
|
|
314
|
-
*/
|
|
315
|
-
agent_type: string;
|
|
316
|
-
}
|
|
310
|
+
export type SubagentStopInput = SDKSubagentStopHookInput;
|
|
317
311
|
/**
|
|
318
312
|
* Input for PreCompact hooks.
|
|
319
313
|
*
|