From b65276146f0516eb53e2405d7fa7226bcc995fa2 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Wed, 10 Jun 2026 06:56:15 +0000 Subject: [PATCH] fix(hooks): unify GITNEXUS_DEBUG gating in main() catch handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main() catch-handler in all three hook copies still gated its crash log on truthy `if (process.env.GITNEXUS_DEBUG)`, while the skip diagnostic the #1913 fix added is gated on the strict `isDebugEnabled()` helper (=== '1' || === 'true'). That split meant GITNEXUS_DEBUG=0 or =false suppressed the skip line yet still enabled crash logging — two conflicting contract signals in the same file. Switch the three catch handlers to isDebugEnabled() so GITNEXUS_DEBUG has one strict meaning everywhere: exactly '1' or 'true' enables all diagnostics; everything else (incl. '0', 'false', empty, unset) is silent. Add boundary tests asserting the MCP-owner skip stays silent with GITNEXUS_DEBUG='0' and 'false' (CJS + Plugin), pinning the strict contract. Refs #1913 Co-Authored-By: Claude Opus 4.8 (1M context) --- gitnexus-claude-plugin/hooks/gitnexus-hook.js | 2 +- .../antigravity/gitnexus-antigravity-hook.cjs | 2 +- gitnexus/hooks/claude/gitnexus-hook.cjs | 2 +- gitnexus/test/unit/hooks.test.ts | 47 +++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/gitnexus-claude-plugin/hooks/gitnexus-hook.js b/gitnexus-claude-plugin/hooks/gitnexus-hook.js index c1754e052..c3ec2ecf5 100644 --- a/gitnexus-claude-plugin/hooks/gitnexus-hook.js +++ b/gitnexus-claude-plugin/hooks/gitnexus-hook.js @@ -381,7 +381,7 @@ function main() { const handler = handlers[input.hook_event_name || '']; if (handler) handler(input); } catch (err) { - if (process.env.GITNEXUS_DEBUG) { + if (isDebugEnabled()) { console.error('GitNexus hook error:', (err.message || '').slice(0, 200)); } } diff --git a/gitnexus/hooks/antigravity/gitnexus-antigravity-hook.cjs b/gitnexus/hooks/antigravity/gitnexus-antigravity-hook.cjs index e363b15ef..5cff1f1e1 100755 --- a/gitnexus/hooks/antigravity/gitnexus-antigravity-hook.cjs +++ b/gitnexus/hooks/antigravity/gitnexus-antigravity-hook.cjs @@ -352,7 +352,7 @@ function main() { const handler = handlers[input.hook_event_name || '']; if (handler) handler(input); } catch (err) { - if (process.env.GITNEXUS_DEBUG) { + if (isDebugEnabled()) { console.error('GitNexus antigravity hook error:', (err.message || '').slice(0, 200)); } } diff --git a/gitnexus/hooks/claude/gitnexus-hook.cjs b/gitnexus/hooks/claude/gitnexus-hook.cjs index 1edf364c6..40d0b08df 100755 --- a/gitnexus/hooks/claude/gitnexus-hook.cjs +++ b/gitnexus/hooks/claude/gitnexus-hook.cjs @@ -376,7 +376,7 @@ function main() { const handler = handlers[input.hook_event_name || '']; if (handler) handler(input); } catch (err) { - if (process.env.GITNEXUS_DEBUG) { + if (isDebugEnabled()) { console.error('GitNexus hook error:', (err.message || '').slice(0, 200)); } } diff --git a/gitnexus/test/unit/hooks.test.ts b/gitnexus/test/unit/hooks.test.ts index 2b119f206..54193d837 100644 --- a/gitnexus/test/unit/hooks.test.ts +++ b/gitnexus/test/unit/hooks.test.ts @@ -1005,6 +1005,53 @@ describe('PreToolUse augmentation filtering (integration)', () => { } }, ); + + // #1913: the GITNEXUS_DEBUG contract is strict — ONLY '1' and 'true' enable + // diagnostics. Pin that non-canonical truthy-looking values ('0', 'false') + // are treated as OFF, so the skip stays silent. A truthy-gated reader would + // have emitted on these; this guards the unified strict gate (incl. the + // main() catch handler) across the claude/plugin copies. + for (const debugValue of ['0', 'false']) { + it.skipIf(process.platform === 'win32')( + `${label}: MCP-owner skip stays SILENT with GITNEXUS_DEBUG='${debugValue}' (strict contract)`, + () => { + const markerPath = path.join( + os.tmpdir(), + `gitnexus-hook-dbg-${debugValue}-${process.pid}-${label}`, + ); + const lbugPath = path.join(gitNexusDir, 'lbug'); + fs.writeFileSync(lbugPath, ''); + fs.rmSync(markerPath, { force: true }); + const binDir = createHookToolDir({ + gitnexusMarkerPath: markerPath, + lsofOutput: '12345\n', + psOutput: 'node /tmp/node_modules/.bin/gitnexus mcp\n', + }); + try { + const result = runHook( + hookPath, + { + hook_event_name: 'PreToolUse', + tool_name: 'Grep', + tool_input: { pattern: 'validateUser' }, + cwd: tmpDir, + }, + undefined, + { env: { ...hookEnv(binDir), GITNEXUS_DEBUG: debugValue } }, + ); + + expect(result.stdout.trim()).toBe(''); + expect(result.stderr.trim()).toBe(''); + expect(result.status).toBe(0); + expect(fs.existsSync(markerPath)).toBe(false); + } finally { + fs.rmSync(lbugPath, { force: true }); + fs.rmSync(markerPath, { force: true }); + fs.rmSync(binDir, { recursive: true, force: true }); + } + }, + ); + } } });