@goodfoot/claude-code-hooks 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/dist/cli.js +54 -58
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -16,6 +16,7 @@
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';
19
20
  import * as path from 'node:path';
20
21
  import * as esbuild from 'esbuild';
21
22
  import { glob } from 'glob';
@@ -374,74 +375,69 @@ async function discoverHookFiles(pattern, cwd) {
374
375
  * @returns Compiled output content as a string
375
376
  */
376
377
  async function compileHook(options) {
377
- const { sourcePath, outputDir, logFilePath } = options;
378
+ const { sourcePath, logFilePath } = options;
378
379
  // Create a temporary wrapper file that imports the hook and executes it
379
- const tempDir = path.join(outputDir, '_temp_' + Date.now().toString());
380
+ // Use system temp directory with deterministic name based on all inputs that affect output
381
+ // This ensures the same inputs always produce the same temp path, making builds deterministic
382
+ const hashInputs = [sourcePath, logFilePath ?? ''].join(':');
383
+ const buildHash = crypto.createHash('sha256').update(hashInputs).digest('hex').substring(0, 16);
384
+ const tempDir = path.join(os.tmpdir(), 'claude-code-hooks-build', buildHash);
380
385
  const wrapperPath = path.join(tempDir, 'wrapper.ts');
381
386
  const tempOutput = path.join(tempDir, 'output.mjs');
382
387
  // Get the path to the runtime module (relative to this CLI)
383
388
  const runtimePath = path.resolve(path.dirname(new URL(import.meta.url).pathname), './runtime.js');
384
- try {
385
- // Ensure temp directory exists
386
- fs.mkdirSync(tempDir, { recursive: true });
387
- // Build log file injection code if specified
388
- const logFileInjection =
389
- logFilePath !== undefined
390
- ? `process.env['CLAUDE_CODE_HOOKS_CLI_LOG_FILE'] = ${JSON.stringify(logFilePath)};\n`
391
- : '';
392
- // Create wrapper that imports the hook and calls execute
393
- const wrapperContent = `${logFileInjection}
389
+ // Ensure temp directory exists (don't delete - concurrent builds may be using it)
390
+ fs.mkdirSync(tempDir, { recursive: true });
391
+ // Build log file injection code if specified
392
+ const logFileInjection =
393
+ logFilePath !== undefined
394
+ ? `process.env['CLAUDE_CODE_HOOKS_CLI_LOG_FILE'] = ${JSON.stringify(logFilePath)};\n`
395
+ : '';
396
+ // Create wrapper that imports the hook and calls execute
397
+ const wrapperContent = `${logFileInjection}
394
398
  import hook from '${sourcePath.replace(/\\/g, '/')}';
395
399
  import { execute } from '${runtimePath.replace(/\\/g, '/')}';
396
400
 
397
401
  execute(hook);
398
402
  `;
399
- fs.writeFileSync(wrapperPath, wrapperContent, 'utf-8');
400
- await esbuild.build({
401
- entryPoints: [wrapperPath],
402
- outfile: tempOutput,
403
- format: 'esm',
404
- platform: 'node',
405
- target: 'node20',
406
- bundle: true,
407
- sourcemap: 'inline',
408
- minify: false,
409
- // Keep node built-ins external
410
- external: [
411
- 'node:*',
412
- 'http',
413
- 'https',
414
- 'url',
415
- 'stream',
416
- 'zlib',
417
- 'events',
418
- 'buffer',
419
- 'util',
420
- 'path',
421
- 'fs',
422
- 'os',
423
- 'crypto',
424
- 'child_process',
425
- 'perf_hooks',
426
- 'async_hooks',
427
- 'diagnostics_channel'
428
- ],
429
- // Ensure we get clean ESM output
430
- mainFields: ['module', 'main'],
431
- conditions: ['import', 'node']
432
- });
433
- // Read the compiled content
434
- const content = fs.readFileSync(tempOutput, 'utf-8');
435
- // Clean up temp directory
436
- fs.rmSync(tempDir, { recursive: true });
437
- return content;
438
- } catch (error) {
439
- // Clean up temp directory on error
440
- if (fs.existsSync(tempDir)) {
441
- fs.rmSync(tempDir, { recursive: true });
442
- }
443
- throw error;
444
- }
403
+ fs.writeFileSync(wrapperPath, wrapperContent, 'utf-8');
404
+ await esbuild.build({
405
+ entryPoints: [wrapperPath],
406
+ outfile: tempOutput,
407
+ format: 'esm',
408
+ platform: 'node',
409
+ target: 'node20',
410
+ bundle: true,
411
+ sourcemap: 'inline',
412
+ minify: false,
413
+ // Keep node built-ins external
414
+ external: [
415
+ 'node:*',
416
+ 'http',
417
+ 'https',
418
+ 'url',
419
+ 'stream',
420
+ 'zlib',
421
+ 'events',
422
+ 'buffer',
423
+ 'util',
424
+ 'path',
425
+ 'fs',
426
+ 'os',
427
+ 'crypto',
428
+ 'child_process',
429
+ 'perf_hooks',
430
+ 'async_hooks',
431
+ 'diagnostics_channel'
432
+ ],
433
+ // Ensure we get clean ESM output
434
+ mainFields: ['module', 'main'],
435
+ conditions: ['import', 'node']
436
+ });
437
+ // Read and return the compiled content
438
+ // Don't delete temp directory - allows concurrent builds of same source
439
+ // and the OS will clean up /tmp periodically
440
+ return fs.readFileSync(tempOutput, 'utf-8');
445
441
  }
446
442
  /**
447
443
  * Generates a content hash (SHA-256, 8-char prefix) for a compiled hook.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodfoot/claude-code-hooks",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Type-safe Claude Code hooks library with camelCase types and output builders",
5
5
  "type": "module",
6
6
  "sideEffects": false,