From d1f22fd75ed49ef6c107b576cbbda53a13efdab1 Mon Sep 17 00:00:00 2001 From: jmdri Date: Mon, 2 Mar 2026 17:15:55 -0300 Subject: [PATCH] fix(installer): use relative paths for hook commands in settings.local.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The installer generated platform-specific hook commands that caused issues: - Windows: absolute paths with escaped backslashes (machine-dependent, fragile) - Unix: $CLAUDE_PROJECT_DIR variable (known bugs GH #6023/#5814) Both approaches led to UserPromptSubmit hook errors in installed projects. Changes: - Use relative path `node .claude/hooks/` on all platforms - Remove hardcoded `timeout: 10` from HOOK_EVENT_MAP and generated settings (Claude Code manages hook timeouts natively, each hook has internal safety) - Remove unused `isWindows` and `hookFilePath` variables - Update tests to reflect timeout removal The HOOK_EVENT_MAP event routing (MIS-3.1) remains unchanged — precompact is correctly mapped to PreCompact, not UserPromptSubmit. Co-Authored-By: Claude Opus 4.6 --- .../src/wizard/ide-config-generator.js | 23 ++++++++----------- .../artifact-copy-pipeline.test.js | 8 +++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/installer/src/wizard/ide-config-generator.js b/packages/installer/src/wizard/ide-config-generator.js index da0c4fdcba..ba54ad389c 100644 --- a/packages/installer/src/wizard/ide-config-generator.js +++ b/packages/installer/src/wizard/ide-config-generator.js @@ -703,10 +703,15 @@ async function copyClaudeHooksFolder(projectRoot) { } /** - * Hook event mapping: fileName → { event, matcher, timeout } + * Hook event mapping: fileName → { event, matcher } * Maps each .cjs hook file to its correct Claude Code event. * Extensible: add new hooks here as they are created. * + * NOTE: timeout is intentionally omitted — Claude Code manages hook timeouts + * natively (default 60s). Overriding with low values (e.g. 10) caused + * premature kills on Windows. Each hook has its own internal safety timeout + * (e.g. synapse-engine.cjs uses 5s via setTimeout + unref). + * * @see Story MIS-3.1 - Fix Session-Digest Hook Registration * @see https://code.claude.com/docs/en/hooks (Claude Code Hooks Documentation) */ @@ -714,17 +719,14 @@ const HOOK_EVENT_MAP = { 'synapse-engine.cjs': { event: 'UserPromptSubmit', matcher: null, - timeout: 10, }, 'code-intel-pretool.cjs': { event: 'PreToolUse', matcher: 'Write|Edit', - timeout: 10, }, 'precompact-session-digest.cjs': { event: 'PreCompact', matcher: null, - timeout: 10, }, }; @@ -732,7 +734,6 @@ const HOOK_EVENT_MAP = { const DEFAULT_HOOK_CONFIG = { event: 'UserPromptSubmit', matcher: null, - timeout: 10, }; /** @@ -759,8 +760,6 @@ async function createClaudeSettingsLocal(projectRoot) { return null; } - const isWindows = process.platform === 'win32'; - let settings = {}; // Merge with existing settings if present @@ -781,7 +780,6 @@ async function createClaudeSettingsLocal(projectRoot) { // Register each .cjs hook file under its correct event for (const hookFileName of hookFiles) { - const hookFilePath = path.join(hooksDir, hookFileName); const hookConfig = HOOK_EVENT_MAP[hookFileName] || DEFAULT_HOOK_CONFIG; const eventName = hookConfig.event; @@ -790,10 +788,10 @@ async function createClaudeSettingsLocal(projectRoot) { settings.hooks[eventName] = []; } - // Windows workaround: $CLAUDE_PROJECT_DIR has known bug on Windows (GH #6023/#5814) - const hookCommand = isWindows - ? `node "${hookFilePath.replace(/\\/g, '\\\\')}"` // Absolute path with escaped backslashes - : `node "$CLAUDE_PROJECT_DIR/.claude/hooks/${hookFileName}"`; + // Use relative path — works on all platforms, no $CLAUDE_PROJECT_DIR bugs + // (GH #6023/#5814), no fragile absolute Windows paths with escaped backslashes. + // Claude Code resolves relative paths from the project root (cwd). + const hookCommand = `node .claude/hooks/${hookFileName}`; // Check if this hook is already registered under this event const hookBaseName = hookFileName.replace('.cjs', ''); @@ -810,7 +808,6 @@ async function createClaudeSettingsLocal(projectRoot) { { type: 'command', command: hookCommand, - timeout: hookConfig.timeout, }, ], }; diff --git a/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js b/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js index cb440cd09a..0e87dedfed 100644 --- a/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +++ b/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js @@ -174,7 +174,7 @@ describe('artifact-copy-pipeline (Story INS-4.3)', () => { expect(config).toBeDefined(); expect(config.event).toBe('UserPromptSubmit'); expect(config.matcher).toBeNull(); - expect(config.timeout).toBe(10); + expect(config.timeout).toBeUndefined(); // Claude Code manages timeouts }); test('maps code-intel-pretool.cjs to PreToolUse with Write|Edit matcher', () => { @@ -182,7 +182,7 @@ describe('artifact-copy-pipeline (Story INS-4.3)', () => { expect(config).toBeDefined(); expect(config.event).toBe('PreToolUse'); expect(config.matcher).toBe('Write|Edit'); - expect(config.timeout).toBe(10); + expect(config.timeout).toBeUndefined(); // Claude Code manages timeouts }); test('maps precompact-session-digest.cjs to PreCompact', () => { @@ -190,7 +190,7 @@ describe('artifact-copy-pipeline (Story INS-4.3)', () => { expect(config).toBeDefined(); expect(config.event).toBe('PreCompact'); expect(config.matcher).toBeNull(); - expect(config.timeout).toBe(10); + expect(config.timeout).toBeUndefined(); // Claude Code manages timeouts }); test('covers all 3 known hooks', () => { @@ -204,7 +204,7 @@ describe('artifact-copy-pipeline (Story INS-4.3)', () => { test('DEFAULT_HOOK_CONFIG falls back to UserPromptSubmit', () => { expect(DEFAULT_HOOK_CONFIG.event).toBe('UserPromptSubmit'); expect(DEFAULT_HOOK_CONFIG.matcher).toBeNull(); - expect(DEFAULT_HOOK_CONFIG.timeout).toBe(10); + expect(DEFAULT_HOOK_CONFIG.timeout).toBeUndefined(); // Claude Code manages timeouts }); });