@goodfoot/claude-code-hooks 1.0.17 → 1.0.19
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 +51 -31
- package/dist/scaffold.js +2 -2
- package/package.json +3 -3
- package/types/cli.d.ts +20 -4
- package/types/types.d.ts +1 -7
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import * as crypto from "node:crypto";
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
|
-
import * as os from "node:os";
|
|
20
19
|
import * as path from "node:path";
|
|
21
20
|
import * as esbuild from "esbuild";
|
|
22
21
|
import { glob } from "glob";
|
|
@@ -374,46 +373,53 @@ async function discoverHookFiles(pattern, cwd) {
|
|
|
374
373
|
/**
|
|
375
374
|
* Compiles a TypeScript hook file to a self-contained ESM executable.
|
|
376
375
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
376
|
+
* Uses esbuild's stdin option to avoid writing temporary wrapper files to disk.
|
|
377
|
+
* This produces stable, reproducible builds by:
|
|
378
|
+
* - Using a distinct sourcefile name to avoid import resolution conflicts
|
|
379
|
+
* - Eliminating environment-specific temp paths from source comments
|
|
380
|
+
*
|
|
381
|
+
* Uses a two-step process to generate stable content hashes:
|
|
382
|
+
* 1. Compile WITHOUT sourcemaps → generate stable content hash
|
|
383
|
+
* 2. Compile WITH sourcemaps → final output content
|
|
384
|
+
*
|
|
379
385
|
* @param options - Compilation options
|
|
380
|
-
* @returns Compiled
|
|
386
|
+
* @returns Compiled content and stable content hash
|
|
381
387
|
*/
|
|
382
388
|
async function compileHook(options) {
|
|
383
389
|
const { sourcePath, logFilePath } = options;
|
|
384
|
-
// Create a temporary wrapper file that imports the hook and executes it
|
|
385
|
-
// Use system temp directory with deterministic name based on hook basename (not full path)
|
|
386
|
-
// This ensures builds are reproducible across different environments/machines
|
|
387
|
-
const hashInputs = [path.basename(sourcePath), logFilePath ? "log" : ""].join(":");
|
|
388
|
-
const buildHash = crypto.createHash("sha256").update(hashInputs).digest("hex").substring(0, 16);
|
|
389
|
-
const tempDir = path.join(os.tmpdir(), "claude-code-hooks-build", buildHash);
|
|
390
|
-
const wrapperPath = path.join(tempDir, "wrapper.ts");
|
|
391
|
-
const tempOutput = path.join(tempDir, "output.mjs");
|
|
392
390
|
// Get the path to the runtime module (relative to this CLI)
|
|
393
391
|
const runtimePath = path.resolve(path.dirname(new URL(import.meta.url).pathname), "./runtime.js");
|
|
394
|
-
// Ensure temp directory exists (don't delete - concurrent builds may be using it)
|
|
395
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
396
392
|
// Build log file injection code if specified
|
|
397
393
|
const logFileInjection = logFilePath !== undefined
|
|
398
394
|
? `process.env['CLAUDE_CODE_HOOKS_CLI_LOG_FILE'] = ${JSON.stringify(logFilePath)};\n`
|
|
399
395
|
: "";
|
|
400
|
-
// Create wrapper that imports the hook and calls execute
|
|
396
|
+
// Create wrapper content that imports the hook and calls execute
|
|
397
|
+
// Uses absolute paths to avoid resolution issues
|
|
401
398
|
const wrapperContent = `${logFileInjection}
|
|
402
399
|
import hook from '${sourcePath.replace(/\\/g, "/")}';
|
|
403
400
|
import { execute } from '${runtimePath.replace(/\\/g, "/")}';
|
|
404
401
|
|
|
405
402
|
execute(hook);
|
|
406
403
|
`;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
404
|
+
// Use stdin instead of a temp file - sourcefile becomes the stable reference
|
|
405
|
+
// The sourcefile name must be distinct from the actual source file to avoid
|
|
406
|
+
// esbuild resolving the import to the stdin content instead of the real file
|
|
407
|
+
const baseName = path.basename(sourcePath, path.extname(sourcePath));
|
|
408
|
+
const stdinOptions = {
|
|
409
|
+
contents: wrapperContent,
|
|
410
|
+
resolveDir: path.dirname(sourcePath),
|
|
411
|
+
sourcefile: `${baseName}-entry.ts`,
|
|
412
|
+
loader: "ts",
|
|
413
|
+
};
|
|
414
|
+
// Common esbuild options
|
|
415
|
+
const commonOptions = {
|
|
416
|
+
stdin: stdinOptions,
|
|
411
417
|
format: "esm",
|
|
412
418
|
platform: "node",
|
|
413
419
|
target: "node20",
|
|
414
420
|
bundle: true,
|
|
415
|
-
sourcemap: "inline",
|
|
416
421
|
minify: false,
|
|
422
|
+
write: false, // Return content directly via outputFiles
|
|
417
423
|
// Keep node built-ins external
|
|
418
424
|
external: [
|
|
419
425
|
"node:*",
|
|
@@ -437,11 +443,27 @@ execute(hook);
|
|
|
437
443
|
// Ensure we get clean ESM output
|
|
438
444
|
mainFields: ["module", "main"],
|
|
439
445
|
conditions: ["import", "node"],
|
|
446
|
+
};
|
|
447
|
+
// Step 1: Compile WITHOUT sourcemaps to generate stable content hash
|
|
448
|
+
const resultNoSourcemap = await esbuild.build({
|
|
449
|
+
...commonOptions,
|
|
450
|
+
sourcemap: false,
|
|
451
|
+
});
|
|
452
|
+
const contentForHash = resultNoSourcemap.outputFiles?.[0]?.text;
|
|
453
|
+
if (contentForHash === undefined) {
|
|
454
|
+
throw new Error(`esbuild produced no output for ${sourcePath}`);
|
|
455
|
+
}
|
|
456
|
+
const contentHash = generateContentHash(contentForHash);
|
|
457
|
+
// Step 2: Compile WITH sourcemaps for final output
|
|
458
|
+
const resultWithSourcemap = await esbuild.build({
|
|
459
|
+
...commonOptions,
|
|
460
|
+
sourcemap: "inline",
|
|
440
461
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
462
|
+
const content = resultWithSourcemap.outputFiles?.[0]?.text;
|
|
463
|
+
if (content === undefined) {
|
|
464
|
+
throw new Error(`esbuild produced no output for ${sourcePath}`);
|
|
465
|
+
}
|
|
466
|
+
return { content, contentHash };
|
|
445
467
|
}
|
|
446
468
|
/**
|
|
447
469
|
* Generates a content hash (SHA-256, 8-char prefix) for a compiled hook.
|
|
@@ -476,19 +498,17 @@ async function compileAllHooks(options) {
|
|
|
476
498
|
matcher: metadata.matcher,
|
|
477
499
|
timeout: metadata.timeout,
|
|
478
500
|
});
|
|
479
|
-
// Compile the hook
|
|
501
|
+
// Compile the hook (two-step process for stable content hash)
|
|
480
502
|
log("info", `Compiling: ${sourcePath}`);
|
|
481
|
-
const
|
|
482
|
-
//
|
|
483
|
-
const hash = generateContentHash(compiledContent);
|
|
484
|
-
// Determine output filename
|
|
503
|
+
const { content, contentHash } = await compileHook({ sourcePath, outputDir, logFilePath });
|
|
504
|
+
// Determine output filename using the stable content hash
|
|
485
505
|
const baseName = path.basename(sourcePath, path.extname(sourcePath));
|
|
486
|
-
const outputFilename = `${baseName}.${
|
|
506
|
+
const outputFilename = `${baseName}.${contentHash}.mjs`;
|
|
487
507
|
const outputPath = path.join(outputDir, outputFilename);
|
|
488
508
|
// Write compiled output with shebang for direct execution
|
|
489
509
|
// --enable-source-maps enables stack traces with original source locations
|
|
490
510
|
const shebang = "#!/usr/bin/env -S node --enable-source-maps\n";
|
|
491
|
-
fs.writeFileSync(outputPath, shebang +
|
|
511
|
+
fs.writeFileSync(outputPath, shebang + content, { encoding: "utf-8", mode: 0o755 });
|
|
492
512
|
log("info", `Wrote: ${outputPath}`);
|
|
493
513
|
compiledHooks.push({
|
|
494
514
|
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.19",
|
|
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,31 @@ 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 esbuild's stdin option to avoid writing temporary wrapper files to disk.
|
|
167
|
+
* This produces stable, reproducible builds by:
|
|
168
|
+
* - Using a distinct sourcefile name to avoid import resolution conflicts
|
|
169
|
+
* - Eliminating environment-specific temp paths from source comments
|
|
170
|
+
*
|
|
171
|
+
* Uses a two-step process to generate stable content hashes:
|
|
172
|
+
* 1. Compile WITHOUT sourcemaps → generate stable content hash
|
|
173
|
+
* 2. Compile WITH sourcemaps → final output content
|
|
174
|
+
*
|
|
159
175
|
* @param options - Compilation options
|
|
160
|
-
* @returns Compiled
|
|
176
|
+
* @returns Compiled content and stable content hash
|
|
161
177
|
*/
|
|
162
|
-
declare function compileHook(options: CompileHookOptions): Promise<
|
|
178
|
+
declare function compileHook(options: CompileHookOptions): Promise<CompileHookResult>;
|
|
163
179
|
/**
|
|
164
180
|
* Generates a content hash (SHA-256, 8-char prefix) for a compiled hook.
|
|
165
181
|
* @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
|
*
|