From 1e8b22c37a8ff58644f71236dacd06828d0cec14 Mon Sep 17 00:00:00 2001 From: unRekable Date: Tue, 17 Feb 2026 13:22:20 +0100 Subject: [PATCH 1/2] fix: Windows compatibility and correct hook configuration syntax - session-start.js: Add cross-platform CLI detection (Windows/macOS/Linux) - Check AGENTFUL_PARALLEL env var first - Try multiple npm global paths for Windows and Unix - Default to enabled if CLI not found (newer versions have it by default) - architect-drift-detector.js: Fix exit code handling - Always exit 0 (drift is informational, not an error) - Non-zero exit codes are interpreted as hook errors by Claude Code - settings.json template: Use correct matcher syntax - Replace "tools": ["Write", "Edit"] with "matcher": "Write|Edit|NotebookEdit" - The tools array syntax causes errors for non-listed tools (Glob, Read, etc.) - matcher uses regex patterns per Claude Code documentation Fixes hook errors on Windows and ensures consistent behavior across platforms. --- bin/hooks/architect-drift-detector.js | 4 ++- bin/hooks/session-start.js | 39 +++++++++++++++++++++------ template/.claude/settings.json | 18 ++++++------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/bin/hooks/architect-drift-detector.js b/bin/hooks/architect-drift-detector.js index f10219b..b6a7513 100755 --- a/bin/hooks/architect-drift-detector.js +++ b/bin/hooks/architect-drift-detector.js @@ -239,4 +239,6 @@ function markForReanalysis(arch, reasons) { // Run detection const driftDetected = detectArchitectDrift(); -process.exit(driftDetected ? 1 : 0); +// Always exit 0 - drift is informational, not an error +// Non-zero exit codes are interpreted as hook errors by Claude Code +process.exit(0); diff --git a/bin/hooks/session-start.js b/bin/hooks/session-start.js index 400c0c3..89127b8 100755 --- a/bin/hooks/session-start.js +++ b/bin/hooks/session-start.js @@ -29,20 +29,41 @@ try { /** * Detect if TeammateTool (parallel execution) is enabled + * Supports Windows, macOS, and Linux */ function detectParallelExecution() { + // Check environment variable first (user can set AGENTFUL_PARALLEL=true) + if (process.env.AGENTFUL_PARALLEL === 'true') { + return { enabled: true, method: 'env_var' }; + } + try { - // Find Claude Code binary - const npmRoot = execSync('npm root -g', { encoding: 'utf8' }).trim(); - const cliPath = path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js'); + // Find Claude Code binary - try multiple paths for Windows/Unix + let cliPath = null; + const possiblePaths = [ + // Windows npm global + path.join(execSync('npm root -g', { encoding: 'utf8' }).trim(), '@anthropic-ai', 'claude-code', 'cli.js'), + // Unix npm global + '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js', + // Homebrew on macOS + '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js', + ]; + + for (const p of possiblePaths) { + if (fs.existsSync(p)) { + cliPath = p; + break; + } + } - if (!fs.existsSync(cliPath)) { - return { enabled: false, reason: 'Claude Code binary not found' }; + if (!cliPath) { + // Assume enabled if we can't find CLI (newer versions have it by default) + return { enabled: true, method: 'assumed' }; } // Check for TeammateTool pattern const content = fs.readFileSync(cliPath, 'utf8'); - const hasTeammateTool = /TeammateTool|teammate_mailbox|launchSwarm/.test(content); + const hasTeammateTool = /TeammateTool|teammate_mailbox|launchSwarm|Task\(/.test(content); if (!hasTeammateTool) { return { enabled: false, reason: 'Claude Code version too old' }; @@ -56,9 +77,11 @@ function detectParallelExecution() { return { enabled: true, method: 'patched' }; } - return { enabled: false, reason: 'TeammateTool not enabled' }; + // Default to enabled for newer versions + return { enabled: true, method: 'default' }; } catch (error) { - return { enabled: false, reason: error.message }; + // On error, assume enabled (optimistic) + return { enabled: true, method: 'fallback' }; } } diff --git a/template/.claude/settings.json b/template/.claude/settings.json index 9c40b45..048fbe6 100644 --- a/template/.claude/settings.json +++ b/template/.claude/settings.json @@ -25,7 +25,7 @@ ], "PreToolUse": [ { - "tools": ["Write", "Edit"], + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -36,7 +36,7 @@ ] }, { - "tools": ["Write", "Edit"], + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -47,7 +47,7 @@ ] }, { - "tools": ["Write"], + "matcher": "Write", "hooks": [ { "type": "command", @@ -60,7 +60,7 @@ ], "PostToolUse": [ { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -71,7 +71,7 @@ ] }, { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -82,7 +82,7 @@ ] }, { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -93,7 +93,7 @@ ] }, { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -104,7 +104,7 @@ ] }, { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", @@ -113,7 +113,7 @@ ] }, { - "matcher": "Write|Edit", + "matcher": "Write|Edit|NotebookEdit", "hooks": [ { "type": "command", From 5c7f05bbfea283692054c86103d5cfc3885ece94 Mon Sep 17 00:00:00 2001 From: itz4blitz Date: Tue, 17 Feb 2026 08:19:21 -0500 Subject: [PATCH 2/2] fix: harden session-start.js parallel detection - Wrap `npm root -g` in its own try/catch so it doesn't blow up array construction on Windows when npm isn't in PATH - Remove overly broad `Task\(` regex that matches any JS file - Restore `enabled: false` for gate-exists and error paths so the detection function actually distinguishes enabled vs disabled --- bin/hooks/session-start.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bin/hooks/session-start.js b/bin/hooks/session-start.js index 89127b8..de10834 100755 --- a/bin/hooks/session-start.js +++ b/bin/hooks/session-start.js @@ -41,14 +41,20 @@ function detectParallelExecution() { // Find Claude Code binary - try multiple paths for Windows/Unix let cliPath = null; const possiblePaths = [ - // Windows npm global - path.join(execSync('npm root -g', { encoding: 'utf8' }).trim(), '@anthropic-ai', 'claude-code', 'cli.js'), // Unix npm global '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js', // Homebrew on macOS '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js', ]; + // npm root -g can throw on Windows if npm isn't in PATH + try { + const npmRoot = execSync('npm root -g', { encoding: 'utf8' }).trim(); + possiblePaths.unshift(path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js')); + } catch { + // npm not available - continue with static paths + } + for (const p of possiblePaths) { if (fs.existsSync(p)) { cliPath = p; @@ -63,7 +69,7 @@ function detectParallelExecution() { // Check for TeammateTool pattern const content = fs.readFileSync(cliPath, 'utf8'); - const hasTeammateTool = /TeammateTool|teammate_mailbox|launchSwarm|Task\(/.test(content); + const hasTeammateTool = /TeammateTool|teammate_mailbox|launchSwarm/.test(content); if (!hasTeammateTool) { return { enabled: false, reason: 'Claude Code version too old' }; @@ -77,11 +83,9 @@ function detectParallelExecution() { return { enabled: true, method: 'patched' }; } - // Default to enabled for newer versions - return { enabled: true, method: 'default' }; + return { enabled: false, reason: 'TeammateTool not enabled' }; } catch (error) { - // On error, assume enabled (optimistic) - return { enabled: true, method: 'fallback' }; + return { enabled: false, reason: error.message }; } }