From a00b7928de9662ffb7ef6abd8cd040afe6f4f122 Mon Sep 17 00:00:00 2001 From: Nourrisse Florian <3023852+Flo5k5@users.noreply.github.com> Date: Wed, 15 Apr 2026 13:42:26 +0200 Subject: [PATCH] fix: strip comments before scanning for missing imports (#676) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: strip comments before scanning for missing imports The scanForMissingImports regex matched require() and import() patterns inside JSDoc comments, causing false-positive missing module detection. A documented path like `require('./commands/proactive.js')` in a comment was resolved from the wrong directory, marked as missing, then the global onResolve handler intercepted ALL imports of that specifier — including valid ones — replacing them with truthy noop stubs that broke runtime. Strip block (/* */) and line (//) comments from source before scanning. * fix: repair 10 pre-existing test failures - promptIdentity.test.ts: define MACRO global (ISSUES_EXPLAINER etc.) for test mode where Bun.define build-time replacements aren't active - context.test.ts: clear OPENAI_MODEL env var in each test — the user's environment (e.g. OPENAI_MODEL=github_copilot/gpt-5.4) polluted the provider-qualified lookup, returning wrong context windows - openclaudePaths.test.ts: set CLAUDE_CONFIG_DIR to force .openclaude path when ~/.openclaude doesn't exist on the test machine --- scripts/build.ts | 10 +++++++++- src/constants/promptIdentity.test.ts | 11 +++++++++++ src/utils/context.test.ts | 14 ++++++++++++++ src/utils/openclaudePaths.test.ts | 4 +++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/scripts/build.ts b/scripts/build.ts index 7ea17ce2..13004430 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -367,9 +367,17 @@ export const SeverityNumber = {}; const full = pathMod.join(dir, ent.name) if (ent.isDirectory()) { walk(full); continue } if (!/\.(ts|tsx)$/.test(ent.name)) continue - const code: string = fs.readFileSync(full, 'utf-8') + const rawCode: string = fs.readFileSync(full, 'utf-8') const fileDir = pathMod.dirname(full) + // Strip comments before scanning for imports/requires. + // The regex scanner matches require()/import() patterns + // inside JSDoc comments, causing false-positive missing + // module detection that breaks the build with noop stubs. + const code = rawCode + .replace(/\/\*[\s\S]*?\*\//g, '') // block comments + .replace(/\/\/.*$/gm, '') // line comments + // Collect static imports: import { X } from '...' for (const m of code.matchAll(/import\s+(?:\{([^}]*)\}|(\w+))?\s*(?:,\s*\{([^}]*)\})?\s*from\s+['"](.*?)['"]/g)) { checkAndRegister(m[4], fileDir, m[1] || m[3] || '') diff --git a/src/constants/promptIdentity.test.ts b/src/constants/promptIdentity.test.ts index b8eb28cd..818e7d0e 100644 --- a/src/constants/promptIdentity.test.ts +++ b/src/constants/promptIdentity.test.ts @@ -1,5 +1,16 @@ import { afterEach, expect, test } from 'bun:test' +// MACRO is replaced at build time by Bun.define but not in test mode. +// Define it globally so tests that import modules using MACRO don't crash. +;(globalThis as Record).MACRO = { + VERSION: '99.0.0', + DISPLAY_VERSION: '0.0.0-test', + BUILD_TIME: new Date().toISOString(), + ISSUES_EXPLAINER: 'report the issue at https://github.com/anthropics/claude-code/issues', + PACKAGE_URL: '@gitlawb/openclaude', + NATIVE_PACKAGE_URL: undefined, +} + import { getSystemPrompt, DEFAULT_AGENT_PROMPT } from './prompts.js' import { CLI_SYSPROMPT_PREFIXES, getCLISyspromptPrefix } from './system.js' import { CLAUDE_CODE_GUIDE_AGENT } from '../tools/AgentTool/built-in/claudeCodeGuideAgent.js' diff --git a/src/utils/context.test.ts b/src/utils/context.test.ts index 72c6bd43..3ce3fde7 100644 --- a/src/utils/context.test.ts +++ b/src/utils/context.test.ts @@ -9,6 +9,7 @@ import { const originalEnv = { CLAUDE_CODE_USE_OPENAI: process.env.CLAUDE_CODE_USE_OPENAI, CLAUDE_CODE_MAX_OUTPUT_TOKENS: process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS, + OPENAI_MODEL: process.env.OPENAI_MODEL, } afterEach(() => { @@ -23,11 +24,17 @@ afterEach(() => { process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = originalEnv.CLAUDE_CODE_MAX_OUTPUT_TOKENS } + if (originalEnv.OPENAI_MODEL === undefined) { + delete process.env.OPENAI_MODEL + } else { + process.env.OPENAI_MODEL = originalEnv.OPENAI_MODEL + } }) test('deepseek-chat uses provider-specific context and output caps', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('deepseek-chat')).toBe(128_000) expect(getModelMaxOutputTokens('deepseek-chat')).toEqual({ @@ -40,6 +47,7 @@ test('deepseek-chat uses provider-specific context and output caps', () => { test('deepseek-chat clamps oversized max output overrides to the provider limit', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = '32000' + delete process.env.OPENAI_MODEL expect(getMaxOutputTokensForModel('deepseek-chat')).toBe(8_192) }) @@ -47,6 +55,7 @@ test('deepseek-chat clamps oversized max output overrides to the provider limit' test('gpt-4o uses provider-specific context and output caps', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('gpt-4o')).toBe(128_000) expect(getModelMaxOutputTokens('gpt-4o')).toEqual({ @@ -59,6 +68,7 @@ test('gpt-4o uses provider-specific context and output caps', () => { test('gpt-4o clamps oversized max output overrides to the provider limit', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = '32000' + delete process.env.OPENAI_MODEL expect(getMaxOutputTokensForModel('gpt-4o')).toBe(16_384) }) @@ -66,6 +76,7 @@ test('gpt-4o clamps oversized max output overrides to the provider limit', () => test('gpt-5.4 family uses provider-specific context and output caps', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('gpt-5.4')).toBe(1_050_000) expect(getModelMaxOutputTokens('gpt-5.4')).toEqual({ @@ -98,6 +109,7 @@ test('gpt-5.4 family keeps large max output overrides within provider limits', ( test('MiniMax-M2.7 uses explicit provider-specific context and output caps', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('MiniMax-M2.7')).toBe(204_800) expect(getModelMaxOutputTokens('MiniMax-M2.7')).toEqual({ @@ -110,6 +122,7 @@ test('MiniMax-M2.7 uses explicit provider-specific context and output caps', () test('unknown openai-compatible models use the 128k fallback window (not 8k, see #635)', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('some-unknown-3p-model')).toBe(128_000) }) @@ -117,6 +130,7 @@ test('unknown openai-compatible models use the 128k fallback window (not 8k, see test('MiniMax-M2.5 and M2.1 use explicit provider-specific context and output caps', () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS + delete process.env.OPENAI_MODEL expect(getContextWindowForModel('MiniMax-M2.5')).toBe(204_800) expect(getContextWindowForModel('MiniMax-M2.5-highspeed')).toBe(204_800) diff --git a/src/utils/openclaudePaths.test.ts b/src/utils/openclaudePaths.test.ts index a69db922..42246e58 100644 --- a/src/utils/openclaudePaths.test.ts +++ b/src/utils/openclaudePaths.test.ts @@ -76,7 +76,9 @@ describe('OpenClaude paths', () => { }) test('local installer uses openclaude wrapper path', async () => { - delete process.env.CLAUDE_CONFIG_DIR + // Force .openclaude config home so the test doesn't fall back to + // ~/.claude when ~/.openclaude doesn't exist on this machine. + process.env.CLAUDE_CONFIG_DIR = join(homedir(), '.openclaude') const { getLocalClaudePath } = await importFreshLocalInstaller() expect(getLocalClaudePath()).toBe(