feat: enhance search, ranking, reranker and CLI tooling across ccw and codex-lens

Major improvements to smart-search, chain-search cascade, ranking pipeline,
reranker factory, CLI history store, codex-lens integration, and uv-manager.
Simplify command-generator skill by inlining phases. Add comprehensive tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
catlog22
2026-03-16 20:35:08 +08:00
parent 1cd96b90e8
commit 5a4b18d9b1
73 changed files with 14684 additions and 2442 deletions

View File

@@ -0,0 +1,118 @@
/**
* Cross-project regression coverage for `ccw cli history` and `ccw cli detail`.
*/
import { after, afterEach, before, describe, it, mock } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const TEST_CCW_HOME = mkdtempSync(join(tmpdir(), 'ccw-cli-history-cross-home-'));
process.env.CCW_DATA_DIR = TEST_CCW_HOME;
const cliCommandPath = new URL('../dist/commands/cli.js', import.meta.url).href;
const cliExecutorPath = new URL('../dist/tools/cli-executor.js', import.meta.url).href;
const historyStorePath = new URL('../dist/tools/cli-history-store.js', import.meta.url).href;
function createConversation({ id, prompt, updatedAt }) {
return {
id,
created_at: updatedAt,
updated_at: updatedAt,
tool: 'gemini',
model: 'default',
mode: 'analysis',
category: 'user',
total_duration_ms: 456,
turn_count: 1,
latest_status: 'success',
turns: [
{
turn: 1,
timestamp: updatedAt,
prompt,
duration_ms: 456,
status: 'success',
exit_code: 0,
output: {
stdout: 'CROSS PROJECT OK',
stderr: '',
truncated: false,
cached: false,
},
},
],
};
}
describe('ccw cli history/detail cross-project', async () => {
let cliModule;
let cliExecutorModule;
let historyStoreModule;
before(async () => {
cliModule = await import(cliCommandPath);
cliExecutorModule = await import(cliExecutorPath);
historyStoreModule = await import(historyStorePath);
});
afterEach(() => {
mock.restoreAll();
try {
historyStoreModule?.closeAllStores?.();
} catch {
// ignore
}
});
after(() => {
try {
historyStoreModule?.closeAllStores?.();
} catch {
// ignore
}
rmSync(TEST_CCW_HOME, { recursive: true, force: true });
});
it('finds history and detail for executions stored in another registered project', async () => {
const projectRoot = mkdtempSync(join(tmpdir(), 'ccw-cli-cross-project-history-'));
const unrelatedCwd = mkdtempSync(join(tmpdir(), 'ccw-cli-cross-project-cwd-'));
const previousCwd = process.cwd();
try {
const store = new historyStoreModule.CliHistoryStore(projectRoot);
store.saveConversation(createConversation({
id: 'CONV-CROSS-PROJECT-1',
prompt: 'Cross project prompt',
updatedAt: new Date('2025-02-01T00:00:01.000Z').toISOString(),
}));
store.close();
const logs = [];
mock.method(console, 'log', (...args) => {
logs.push(args.map(String).join(' '));
});
mock.method(console, 'error', (...args) => {
logs.push(args.map(String).join(' '));
});
process.chdir(unrelatedCwd);
await cliModule.cliCommand('history', [], { limit: '20' });
assert.ok(logs.some((line) => line.includes('CONV-CROSS-PROJECT-1')));
await cliExecutorModule.getExecutionHistoryAsync(projectRoot, { limit: 1 });
logs.length = 0;
await cliModule.cliCommand('detail', ['CONV-CROSS-PROJECT-1'], {});
assert.ok(logs.some((line) => line.includes('Conversation Detail')));
assert.ok(logs.some((line) => line.includes('CONV-CROSS-PROJECT-1')));
assert.ok(logs.some((line) => line.includes('Cross project prompt')));
} finally {
process.chdir(previousCwd);
rmSync(projectRoot, { recursive: true, force: true });
rmSync(unrelatedCwd, { recursive: true, force: true });
}
});
});

View File

@@ -123,6 +123,39 @@ describe('ccw cli output --final', async () => {
}
});
it('loads cached output from another registered project without --project', async () => {
const projectRoot = createTestProjectRoot();
const unrelatedCwd = createTestProjectRoot();
const previousCwd = process.cwd();
const store = new historyStoreModule.CliHistoryStore(projectRoot);
try {
store.saveConversation(createConversation({
id: 'EXEC-CROSS-PROJECT-OUTPUT',
stdoutFull: 'cross project raw output',
parsedOutput: 'cross project parsed output',
finalOutput: 'cross project final output',
}));
process.chdir(unrelatedCwd);
const logs = [];
mock.method(console, 'log', (...args) => {
logs.push(args.map(String).join(' '));
});
mock.method(console, 'error', () => {});
await cliModule.cliCommand('output', ['EXEC-CROSS-PROJECT-OUTPUT'], {});
assert.equal(logs.at(-1), 'cross project final output');
} finally {
process.chdir(previousCwd);
store.close();
rmSync(projectRoot, { recursive: true, force: true });
rmSync(unrelatedCwd, { recursive: true, force: true });
}
});
it('fails fast for explicit --final when no final agent result can be recovered', async () => {
const projectRoot = createTestProjectRoot();
const store = new historyStoreModule.CliHistoryStore(projectRoot);
@@ -159,4 +192,34 @@ describe('ccw cli output --final', async () => {
rmSync(projectRoot, { recursive: true, force: true });
}
});
it('prints CCW execution ID guidance when output cannot find the requested execution', async () => {
const projectRoot = createTestProjectRoot();
const previousCwd = process.cwd();
try {
process.chdir(projectRoot);
const errors = [];
const exitCodes = [];
mock.method(console, 'log', () => {});
mock.method(console, 'error', (...args) => {
errors.push(args.map(String).join(' '));
});
mock.method(process, 'exit', (code) => {
exitCodes.push(code);
});
await cliModule.cliCommand('output', ['rebuttal-structure-analysis'], {});
assert.deepEqual(exitCodes, [1]);
assert.ok(errors.some((line) => line.includes('real CCW execution ID')));
assert.ok(errors.some((line) => line.includes('CCW_EXEC_ID')));
assert.ok(errors.some((line) => line.includes('ccw cli show or ccw cli history')));
} finally {
process.chdir(previousCwd);
rmSync(projectRoot, { recursive: true, force: true });
}
});
});

View File

@@ -163,6 +163,42 @@ describe('ccw cli show running time formatting', async () => {
assert.match(rendered, /1h\.\.\./);
});
it('lists executions from other registered projects in show output', async () => {
const projectRoot = mkdtempSync(join(tmpdir(), 'ccw-cli-show-cross-project-'));
const unrelatedCwd = mkdtempSync(join(tmpdir(), 'ccw-cli-show-cross-cwd-'));
const previousCwd = process.cwd();
try {
process.chdir(unrelatedCwd);
const store = new historyStoreModule.CliHistoryStore(projectRoot);
store.saveConversation(createConversationRecord({
id: 'EXEC-CROSS-PROJECT-SHOW',
prompt: 'cross project show prompt',
updatedAt: new Date('2025-02-02T00:00:00.000Z').toISOString(),
durationMs: 1800,
}));
store.close();
stubActiveExecutionsResponse([]);
const logs = [];
mock.method(console, 'log', (...args) => {
logs.push(args.map(String).join(' '));
});
mock.method(console, 'error', () => {});
await cliModule.cliCommand('show', [], {});
const rendered = logs.join('\n');
assert.match(rendered, /EXEC-CROSS-PROJECT-SHOW/);
assert.match(rendered, /cross project show prompt/);
} finally {
process.chdir(previousCwd);
rmSync(projectRoot, { recursive: true, force: true });
rmSync(unrelatedCwd, { recursive: true, force: true });
}
});
it('suppresses stale running rows when saved history is newer than the active start time', async () => {
const projectRoot = mkdtempSync(join(tmpdir(), 'ccw-cli-show-stale-project-'));
const previousCwd = process.cwd();

View File

@@ -13,6 +13,38 @@ after(() => {
});
describe('CodexLens CLI compatibility retries', () => {
it('builds hidden Python spawn options for CLI invocations', async () => {
const moduleUrl = new URL(`../dist/tools/codex-lens.js?spawn-opts=${Date.now()}`, import.meta.url).href;
const { __testables } = await import(moduleUrl);
const options = __testables.buildCodexLensSpawnOptions(tmpdir(), 12345);
assert.equal(options.cwd, tmpdir());
assert.equal(options.shell, false);
assert.equal(options.timeout, 12345);
assert.equal(options.windowsHide, true);
assert.equal(options.env.PYTHONIOENCODING, 'utf-8');
});
it('probes Python version without a shell-backed console window', async () => {
const moduleUrl = new URL(`../dist/tools/codex-lens.js?python-probe=${Date.now()}`, import.meta.url).href;
const { __testables } = await import(moduleUrl);
const probeCalls = [];
const version = __testables.probePythonVersion({ command: 'python', args: [], display: 'python' }, (command, args, options) => {
probeCalls.push({ command, args, options });
return { status: 0, stdout: '', stderr: 'Python 3.11.9\n' };
});
assert.equal(version, 'Python 3.11.9');
assert.equal(probeCalls.length, 1);
assert.equal(probeCalls[0].command, 'python');
assert.deepEqual(probeCalls[0].args, ['--version']);
assert.equal(probeCalls[0].options.shell, false);
assert.equal(probeCalls[0].options.windowsHide, true);
assert.equal(probeCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('initializes a tiny index even when CLI emits compatibility conflicts first', async () => {
const moduleUrl = new URL(`../dist/tools/codex-lens.js?compat=${Date.now()}`, import.meta.url).href;
const { checkVenvStatus, executeCodexLens } = await import(moduleUrl);
@@ -32,4 +64,76 @@ describe('CodexLens CLI compatibility retries', () => {
assert.equal(result.success, true, result.error ?? 'Expected init to succeed');
assert.ok((result.output ?? '').length > 0 || (result.warning ?? '').length > 0, 'Expected init output or compatibility warning');
});
it('synthesizes a machine-readable fallback when JSON search output is empty', async () => {
const moduleUrl = new URL(`../dist/tools/codex-lens.js?compat-empty=${Date.now()}`, import.meta.url).href;
const { __testables } = await import(moduleUrl);
const normalized = __testables.normalizeSearchCommandResult(
{ success: true },
{ query: 'missing symbol', cwd: tmpdir(), limit: 5, filesOnly: false },
);
assert.equal(normalized.success, true);
assert.match(normalized.warning ?? '', /empty stdout/i);
assert.deepEqual(normalized.results, {
success: true,
result: {
query: 'missing symbol',
count: 0,
results: [],
},
});
});
it('returns structured semantic search results for a local embedded workspace', async () => {
const codexLensUrl = new URL(`../dist/tools/codex-lens.js?compat-search=${Date.now()}`, import.meta.url).href;
const smartSearchUrl = new URL(`../dist/tools/smart-search.js?compat-search=${Date.now()}`, import.meta.url).href;
const codexLensModule = await import(codexLensUrl);
const smartSearchModule = await import(smartSearchUrl);
const ready = await codexLensModule.checkVenvStatus(true);
if (!ready.ready) {
console.log('Skipping: CodexLens not ready');
return;
}
const semantic = await codexLensModule.checkSemanticStatus();
if (!semantic.available) {
console.log('Skipping: semantic dependencies not ready');
return;
}
const projectDir = mkdtempSync(join(tmpdir(), 'codexlens-search-'));
tempDirs.push(projectDir);
writeFileSync(
join(projectDir, 'sample.ts'),
'export function greet(name) { return `hello ${name}`; }\nexport const sum = (a, b) => a + b;\n',
);
const init = await smartSearchModule.handler({ action: 'init', path: projectDir });
assert.equal(init.success, true, init.error ?? 'Expected smart-search init to succeed');
const embed = await smartSearchModule.handler({
action: 'embed',
path: projectDir,
embeddingBackend: 'local',
force: true,
});
assert.equal(embed.success, true, embed.error ?? 'Expected smart-search embed to succeed');
const result = await codexLensModule.codexLensTool.execute({
action: 'search',
path: projectDir,
query: 'greet function',
mode: 'semantic',
format: 'json',
});
assert.equal(result.success, true, result.error ?? 'Expected semantic search compatibility fallback to succeed');
const payload = result.results?.result ?? result.results;
assert.ok(Array.isArray(payload?.results), 'Expected structured search results payload');
assert.ok(payload.results.length > 0, 'Expected at least one structured semantic search result');
assert.doesNotMatch(result.error ?? '', /unexpected extra arguments/i);
});
});

View File

@@ -0,0 +1,66 @@
import { after, afterEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { createRequire, syncBuiltinESMExports } from 'node:module';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const require = createRequire(import.meta.url);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('node:fs');
const originalExistsSync = fs.existsSync;
const originalCodexLensDataDir = process.env.CODEXLENS_DATA_DIR;
const tempDirs = [];
afterEach(() => {
fs.existsSync = originalExistsSync;
syncBuiltinESMExports();
if (originalCodexLensDataDir === undefined) {
delete process.env.CODEXLENS_DATA_DIR;
} else {
process.env.CODEXLENS_DATA_DIR = originalCodexLensDataDir;
}
});
after(() => {
while (tempDirs.length > 0) {
rmSync(tempDirs.pop(), { recursive: true, force: true });
}
});
describe('codexlens-path hidden python selection', () => {
it('prefers pythonw.exe for hidden Windows subprocesses when available', async () => {
if (process.platform !== 'win32') {
return;
}
const dataDir = mkdtempSync(join(tmpdir(), 'ccw-codexlens-hidden-python-'));
tempDirs.push(dataDir);
process.env.CODEXLENS_DATA_DIR = dataDir;
const expectedPythonw = join(dataDir, 'venv', 'Scripts', 'pythonw.exe');
fs.existsSync = (path) => String(path) === expectedPythonw;
syncBuiltinESMExports();
const moduleUrl = new URL(`../dist/utils/codexlens-path.js?t=${Date.now()}`, import.meta.url);
const mod = await import(moduleUrl.href);
assert.equal(mod.getCodexLensHiddenPython(), expectedPythonw);
});
it('falls back to python.exe when pythonw.exe is unavailable', async () => {
const dataDir = mkdtempSync(join(tmpdir(), 'ccw-codexlens-hidden-fallback-'));
tempDirs.push(dataDir);
process.env.CODEXLENS_DATA_DIR = dataDir;
fs.existsSync = () => false;
syncBuiltinESMExports();
const moduleUrl = new URL(`../dist/utils/codexlens-path.js?t=${Date.now()}`, import.meta.url);
const mod = await import(moduleUrl.href);
assert.equal(mod.getCodexLensHiddenPython(), mod.getCodexLensPython());
});
});

View File

@@ -105,7 +105,10 @@ describe('memory-embedder-bridge', () => {
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].args.at(-2), 'status');
assert.equal(spawnCalls[0].args.at(-1), 'C:\\tmp\\db.sqlite');
assert.equal(spawnCalls[0].options.shell, false);
assert.equal(spawnCalls[0].options.timeout, 30000);
assert.equal(spawnCalls[0].options.windowsHide, true);
assert.equal(spawnCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('generateEmbeddings builds args for sourceId, batchSize, and force', async () => {
@@ -138,7 +141,10 @@ describe('memory-embedder-bridge', () => {
assert.equal(args[batchSizeIndex + 1], '4');
assert.ok(args.includes('--force'));
assert.equal(spawnCalls[0].options.shell, false);
assert.equal(spawnCalls[0].options.timeout, 300000);
assert.equal(spawnCalls[0].options.windowsHide, true);
assert.equal(spawnCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
spawnCalls.length = 0;
spawnPlan.push({

View File

@@ -103,7 +103,7 @@ describe('LiteLLM client bridge', () => {
assert.equal(available, true);
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].command, 'python');
assert.equal(spawnCalls[0].command, mod.getCodexLensVenvPython());
assert.deepEqual(spawnCalls[0].args, ['-m', 'ccw_litellm.cli', 'version']);
});
@@ -117,6 +117,19 @@ describe('LiteLLM client bridge', () => {
assert.equal(spawnCalls[0].command, 'python3');
});
it('spawns LiteLLM Python with hidden window options', async () => {
spawnPlan.push({ type: 'close', code: 0, stdout: '1.2.3\n' });
const client = new mod.LiteLLMClient({ timeout: 10 });
const available = await client.isAvailable();
assert.equal(available, true);
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].options.shell, false);
assert.equal(spawnCalls[0].options.windowsHide, true);
assert.equal(spawnCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('isAvailable returns false on spawn error', async () => {
spawnPlan.push({ type: 'error', error: new Error('ENOENT') });
@@ -154,7 +167,7 @@ describe('LiteLLM client bridge', () => {
assert.deepEqual(cfg, { ok: true });
assert.equal(spawnCalls.length, 1);
assert.deepEqual(spawnCalls[0].args, ['-m', 'ccw_litellm.cli', 'config', '--json']);
assert.deepEqual(spawnCalls[0].args, ['-m', 'ccw_litellm.cli', 'config']);
});
it('getConfig throws on malformed JSON', async () => {

View File

@@ -76,6 +76,26 @@ describe('Smart Search - Query Intent + RRF Weights', async () => {
});
});
describe('classifyIntent lexical routing', () => {
it('routes config/backend queries to exact when index and embeddings are available', () => {
if (!smartSearchModule) return;
const classification = smartSearchModule.__testables.classifyIntent(
'embedding backend fastembed local litellm api config',
true,
true,
);
assert.strictEqual(classification.mode, 'exact');
assert.match(classification.reasoning, /lexical priority/i);
});
it('routes generated artifact queries to exact when index and embeddings are available', () => {
if (!smartSearchModule) return;
const classification = smartSearchModule.__testables.classifyIntent('dist bundle output', true, true);
assert.strictEqual(classification.mode, 'exact');
assert.match(classification.reasoning, /generated artifact/i);
});
});
describe('adjustWeightsByIntent', () => {
it('maps keyword intent to exact-heavy weights', () => {
if (!smartSearchModule) return;
@@ -119,4 +139,3 @@ describe('Smart Search - Query Intent + RRF Weights', async () => {
});
});
});

View File

@@ -1,16 +1,19 @@
import { afterEach, before, describe, it } from 'node:test';
import { after, afterEach, before, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const smartSearchPath = new URL('../dist/tools/smart-search.js', import.meta.url).href;
const originalAutoInitMissing = process.env.CODEXLENS_AUTO_INIT_MISSING;
const originalAutoEmbedMissing = process.env.CODEXLENS_AUTO_EMBED_MISSING;
describe('Smart Search MCP usage defaults and path handling', async () => {
let smartSearchModule;
const tempDirs = [];
before(async () => {
process.env.CODEXLENS_AUTO_INIT_MISSING = 'false';
try {
smartSearchModule = await import(smartSearchPath);
} catch (err) {
@@ -18,10 +21,30 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
}
});
after(() => {
if (originalAutoInitMissing === undefined) {
delete process.env.CODEXLENS_AUTO_INIT_MISSING;
} else {
process.env.CODEXLENS_AUTO_INIT_MISSING = originalAutoInitMissing;
}
if (originalAutoEmbedMissing === undefined) {
delete process.env.CODEXLENS_AUTO_EMBED_MISSING;
return;
}
process.env.CODEXLENS_AUTO_EMBED_MISSING = originalAutoEmbedMissing;
});
afterEach(() => {
while (tempDirs.length > 0) {
rmSync(tempDirs.pop(), { recursive: true, force: true });
}
if (smartSearchModule?.__testables) {
smartSearchModule.__testables.__resetRuntimeOverrides();
smartSearchModule.__testables.__resetBackgroundJobs();
}
process.env.CODEXLENS_AUTO_INIT_MISSING = 'false';
delete process.env.CODEXLENS_AUTO_EMBED_MISSING;
});
function createWorkspace() {
@@ -30,6 +53,15 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
return dir;
}
function createDetachedChild() {
return {
on() {
return this;
},
unref() {},
};
}
it('keeps schema defaults aligned with runtime docs', () => {
if (!smartSearchModule) return;
@@ -50,14 +82,202 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
assert.equal(props.output_mode.default, 'ace');
});
it('defaults auto embedding warmup to enabled unless explicitly disabled', () => {
it('defaults auto embedding warmup off on Windows unless explicitly enabled', () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
assert.equal(__testables.isAutoEmbedMissingEnabled(undefined), true);
assert.equal(__testables.isAutoEmbedMissingEnabled({}), true);
assert.equal(__testables.isAutoEmbedMissingEnabled({ embedding_auto_embed_missing: true }), true);
delete process.env.CODEXLENS_AUTO_EMBED_MISSING;
assert.equal(__testables.isAutoEmbedMissingEnabled(undefined), process.platform !== 'win32');
assert.equal(__testables.isAutoEmbedMissingEnabled({}), process.platform !== 'win32');
assert.equal(
__testables.isAutoEmbedMissingEnabled({ embedding_auto_embed_missing: true }),
process.platform === 'win32' ? false : true,
);
assert.equal(__testables.isAutoEmbedMissingEnabled({ embedding_auto_embed_missing: false }), false);
process.env.CODEXLENS_AUTO_EMBED_MISSING = 'true';
assert.equal(__testables.isAutoEmbedMissingEnabled({ embedding_auto_embed_missing: false }), true);
process.env.CODEXLENS_AUTO_EMBED_MISSING = 'off';
assert.equal(__testables.isAutoEmbedMissingEnabled({ embedding_auto_embed_missing: true }), false);
});
it('defaults auto index warmup off on Windows unless explicitly enabled', () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
delete process.env.CODEXLENS_AUTO_INIT_MISSING;
assert.equal(__testables.isAutoInitMissingEnabled(), process.platform !== 'win32');
process.env.CODEXLENS_AUTO_INIT_MISSING = 'off';
assert.equal(__testables.isAutoInitMissingEnabled(), false);
process.env.CODEXLENS_AUTO_INIT_MISSING = '1';
assert.equal(__testables.isAutoInitMissingEnabled(), true);
});
it('explains when Windows disables background warmup by default', () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
delete process.env.CODEXLENS_AUTO_INIT_MISSING;
delete process.env.CODEXLENS_AUTO_EMBED_MISSING;
const initReason = __testables.getAutoInitMissingDisabledReason();
const embedReason = __testables.getAutoEmbedMissingDisabledReason({});
if (process.platform === 'win32') {
assert.match(initReason, /disabled by default on Windows/i);
assert.match(embedReason, /disabled by default on Windows/i);
assert.match(embedReason, /auto_embed_missing=true/i);
} else {
assert.match(initReason, /disabled/i);
assert.match(embedReason, /disabled/i);
}
});
it('builds hidden subprocess options for Smart Search child processes', () => {
if (!smartSearchModule) return;
const options = smartSearchModule.__testables.buildSmartSearchSpawnOptions(tmpdir(), {
detached: true,
stdio: 'ignore',
timeout: 12345,
});
assert.equal(options.cwd, tmpdir());
assert.equal(options.shell, false);
assert.equal(options.windowsHide, true);
assert.equal(options.detached, true);
assert.equal(options.timeout, 12345);
assert.equal(options.env.PYTHONIOENCODING, 'utf-8');
});
it('avoids detached background warmup children on Windows consoles', () => {
if (!smartSearchModule) return;
assert.equal(
smartSearchModule.__testables.shouldDetachBackgroundSmartSearchProcess(),
process.platform !== 'win32',
);
});
it('checks tool availability without shell-based lookup popups', () => {
if (!smartSearchModule) return;
const lookupCalls = [];
const available = smartSearchModule.__testables.checkToolAvailability(
'rg',
(command, args, options) => {
lookupCalls.push({ command, args, options });
return { status: 0, stdout: '', stderr: '' };
},
);
assert.equal(available, true);
assert.equal(lookupCalls.length, 1);
assert.equal(lookupCalls[0].command, process.platform === 'win32' ? 'where' : 'which');
assert.deepEqual(lookupCalls[0].args, ['rg']);
assert.equal(lookupCalls[0].options.shell, false);
assert.equal(lookupCalls[0].options.windowsHide, true);
assert.equal(lookupCalls[0].options.stdio, 'ignore');
assert.equal(lookupCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('starts background static index build once for unindexed paths', async () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
const dir = createWorkspace();
const fakePython = join(dir, 'python.exe');
writeFileSync(fakePython, '');
process.env.CODEXLENS_AUTO_INIT_MISSING = 'true';
const spawnCalls = [];
__testables.__setRuntimeOverrides({
getVenvPythonPath: () => fakePython,
now: () => 1234567890,
spawnProcess: (command, args, options) => {
spawnCalls.push({ command, args, options });
return createDetachedChild();
},
});
const scope = { workingDirectory: dir, searchPaths: ['.'] };
const indexStatus = { indexed: false, has_embeddings: false };
const first = await __testables.maybeStartBackgroundAutoInit(scope, indexStatus);
const second = await __testables.maybeStartBackgroundAutoInit(scope, indexStatus);
assert.match(first.note, /started/i);
assert.match(second.note, /already running/i);
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].command, fakePython);
assert.deepEqual(spawnCalls[0].args, ['-m', 'codexlens', 'index', 'init', dir, '--no-embeddings']);
assert.equal(spawnCalls[0].options.cwd, dir);
assert.equal(
spawnCalls[0].options.detached,
smartSearchModule.__testables.shouldDetachBackgroundSmartSearchProcess(),
);
assert.equal(spawnCalls[0].options.windowsHide, true);
});
it('starts background embedding build without detached Windows consoles', async () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
const dir = createWorkspace();
const fakePython = join(dir, 'python.exe');
writeFileSync(fakePython, '');
process.env.CODEXLENS_AUTO_EMBED_MISSING = 'true';
const spawnCalls = [];
__testables.__setRuntimeOverrides({
getVenvPythonPath: () => fakePython,
checkSemanticStatus: async () => ({ available: true, litellmAvailable: true }),
now: () => 1234567890,
spawnProcess: (command, args, options) => {
spawnCalls.push({ command, args, options });
return createDetachedChild();
},
});
const status = await __testables.maybeStartBackgroundAutoEmbed(
{ workingDirectory: dir, searchPaths: ['.'] },
{
indexed: true,
has_embeddings: false,
config: { embedding_backend: 'fastembed' },
},
);
assert.match(status.note, /started/i);
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].command, fakePython);
assert.deepEqual(spawnCalls[0].args.slice(0, 1), ['-c']);
assert.equal(spawnCalls[0].options.cwd, dir);
assert.equal(
spawnCalls[0].options.detached,
smartSearchModule.__testables.shouldDetachBackgroundSmartSearchProcess(),
);
assert.equal(spawnCalls[0].options.windowsHide, true);
assert.equal(spawnCalls[0].options.stdio, 'ignore');
});
it('surfaces warnings when background static index warmup cannot start', async () => {
if (!smartSearchModule) return;
const { __testables } = smartSearchModule;
const dir = createWorkspace();
process.env.CODEXLENS_AUTO_INIT_MISSING = 'true';
__testables.__setRuntimeOverrides({
getVenvPythonPath: () => join(dir, 'missing-python.exe'),
});
const status = await __testables.maybeStartBackgroundAutoInit(
{ workingDirectory: dir, searchPaths: ['.'] },
{ indexed: false, has_embeddings: false },
);
assert.match(status.warning, /Automatic static index warmup could not start/i);
assert.match(status.warning, /not ready yet/i);
});
it('honors explicit small limit values', async () => {
@@ -246,15 +466,98 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
assert.match(String(matches[0].file).replace(/\\/g, '/'), /target\.ts$/);
});
it('detects centralized vector artifacts as full embedding coverage evidence', () => {
it('uses root-scoped embedding status instead of subtree artifacts', () => {
if (!smartSearchModule) return;
const dir = createWorkspace();
writeFileSync(join(dir, '_vectors.hnsw'), 'hnsw');
writeFileSync(join(dir, '_vectors_meta.db'), 'meta');
writeFileSync(join(dir, '_binary_vectors.mmap'), 'mmap');
const summary = smartSearchModule.__testables.extractEmbeddingsStatusSummary({
total_indexes: 3,
indexes_with_embeddings: 2,
total_chunks: 24,
coverage_percent: 66.7,
root: {
total_files: 4,
files_with_embeddings: 0,
total_chunks: 0,
coverage_percent: 0,
has_embeddings: false,
},
subtree: {
total_indexes: 3,
indexes_with_embeddings: 2,
total_files: 12,
files_with_embeddings: 8,
total_chunks: 24,
coverage_percent: 66.7,
},
centralized: {
dense_index_exists: true,
binary_index_exists: true,
meta_db_exists: true,
usable: false,
},
});
assert.equal(smartSearchModule.__testables.hasCentralizedVectorArtifacts(dir), true);
assert.equal(summary.coveragePercent, 0);
assert.equal(summary.totalChunks, 0);
assert.equal(summary.hasEmbeddings, false);
});
it('accepts validated root centralized readiness from CLI status payloads', () => {
if (!smartSearchModule) return;
const summary = smartSearchModule.__testables.extractEmbeddingsStatusSummary({
total_indexes: 2,
indexes_with_embeddings: 1,
total_chunks: 10,
coverage_percent: 25,
root: {
total_files: 2,
files_with_embeddings: 1,
total_chunks: 3,
coverage_percent: 50,
has_embeddings: true,
},
centralized: {
usable: true,
dense_ready: true,
chunk_metadata_rows: 3,
},
});
assert.equal(summary.coveragePercent, 50);
assert.equal(summary.totalChunks, 3);
assert.equal(summary.hasEmbeddings, true);
});
it('prefers embeddings_status over legacy embeddings summary payloads', () => {
if (!smartSearchModule) return;
const payload = smartSearchModule.__testables.selectEmbeddingsStatusPayload({
embeddings: {
total_indexes: 7,
indexes_with_embeddings: 4,
total_chunks: 99,
},
embeddings_status: {
total_indexes: 7,
total_chunks: 3,
root: {
total_files: 2,
files_with_embeddings: 1,
total_chunks: 3,
coverage_percent: 50,
has_embeddings: true,
},
centralized: {
usable: true,
dense_ready: true,
chunk_metadata_rows: 3,
},
},
});
assert.equal(payload.root.total_chunks, 3);
assert.equal(payload.centralized.usable, true);
});
it('recognizes CodexLens CLI compatibility failures and invalid regex fallback', () => {
@@ -281,6 +584,37 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
assert.match(resolution.warning, /literal ripgrep matching/i);
});
it('suppresses compatibility-only fuzzy warnings when ripgrep already produced hits', () => {
if (!smartSearchModule) return;
assert.equal(
smartSearchModule.__testables.shouldSurfaceCodexLensFtsCompatibilityWarning({
compatibilityTriggeredThisQuery: true,
skipExactDueToCompatibility: false,
ripgrepResultCount: 2,
}),
false,
);
assert.equal(
smartSearchModule.__testables.shouldSurfaceCodexLensFtsCompatibilityWarning({
compatibilityTriggeredThisQuery: true,
skipExactDueToCompatibility: false,
ripgrepResultCount: 0,
}),
true,
);
assert.equal(
smartSearchModule.__testables.shouldSurfaceCodexLensFtsCompatibilityWarning({
compatibilityTriggeredThisQuery: false,
skipExactDueToCompatibility: true,
ripgrepResultCount: 0,
}),
true,
);
});
it('builds actionable index suggestions for unhealthy index states', () => {
if (!smartSearchModule) return;
@@ -318,4 +652,52 @@ describe('Smart Search MCP usage defaults and path handling', async () => {
assert.match(toolResult.error, /Both search backends failed:/);
assert.match(toolResult.error, /(FTS|Ripgrep)/);
});
it('returns structured semantic results after local init and embed without JSON parse warnings', async () => {
if (!smartSearchModule) return;
const codexLensModule = await import(new URL(`../dist/tools/codex-lens.js?smart-semantic=${Date.now()}`, import.meta.url).href);
const ready = await codexLensModule.checkVenvStatus(true);
if (!ready.ready) {
console.log('Skipping: CodexLens not ready');
return;
}
const semantic = await codexLensModule.checkSemanticStatus();
if (!semantic.available) {
console.log('Skipping: semantic dependencies not ready');
return;
}
const dir = createWorkspace();
writeFileSync(
join(dir, 'sample.ts'),
'export function parseCodexLensOutput() { return stripAnsiOutput(); }\nexport const sum = (a, b) => a + b;\n',
);
const init = await smartSearchModule.handler({ action: 'init', path: dir });
assert.equal(init.success, true, init.error ?? 'Expected init to succeed');
const embed = await smartSearchModule.handler({
action: 'embed',
path: dir,
embeddingBackend: 'local',
force: true,
});
assert.equal(embed.success, true, embed.error ?? 'Expected local embed to succeed');
const search = await smartSearchModule.handler({
action: 'search',
mode: 'semantic',
path: dir,
query: 'parse CodexLens output strip ANSI',
limit: 5,
});
assert.equal(search.success, true, search.error ?? 'Expected semantic search to succeed');
assert.equal(search.result.success, true);
assert.equal(search.result.results.format, 'ace');
assert.ok(search.result.results.total >= 1, 'Expected at least one structured semantic match');
assert.doesNotMatch(search.result.metadata?.warning ?? '', /Failed to parse JSON output/i);
});
});

View File

@@ -0,0 +1,97 @@
import { after, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { EventEmitter } from 'node:events';
import { createRequire } from 'node:module';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const require = createRequire(import.meta.url);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('node:fs') as typeof import('node:fs');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const childProcess = require('node:child_process') as typeof import('node:child_process');
class FakeChildProcess extends EventEmitter {
stdout = new EventEmitter();
stderr = new EventEmitter();
stdinChunks: string[] = [];
stdin = {
write: (chunk: string | Buffer) => {
this.stdinChunks.push(String(chunk));
return true;
},
end: () => undefined,
};
}
type SpawnCall = {
command: string;
args: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options: any;
child: FakeChildProcess;
};
const spawnCalls: SpawnCall[] = [];
const tempDirs: string[] = [];
let embedderAvailable = true;
const originalExistsSync = fs.existsSync;
const originalSpawn = childProcess.spawn;
fs.existsSync = ((..._args: unknown[]) => embedderAvailable) as typeof fs.existsSync;
childProcess.spawn = ((command: string, args: string[] = [], options: unknown = {}) => {
const child = new FakeChildProcess();
spawnCalls.push({ command: String(command), args: args.map(String), options, child });
queueMicrotask(() => {
child.stdout.emit('data', JSON.stringify({
success: true,
total_chunks: 4,
hnsw_available: true,
hnsw_count: 4,
dimension: 384,
}));
child.emit('close', 0);
});
return child as unknown as ReturnType<typeof childProcess.spawn>;
}) as typeof childProcess.spawn;
after(() => {
fs.existsSync = originalExistsSync;
childProcess.spawn = originalSpawn;
while (tempDirs.length > 0) {
rmSync(tempDirs.pop() as string, { recursive: true, force: true });
}
});
describe('unified-vector-index', () => {
beforeEach(() => {
embedderAvailable = true;
spawnCalls.length = 0;
});
it('spawns CodexLens venv python with hidden window options', async () => {
const projectDir = mkdtempSync(join(tmpdir(), 'ccw-unified-vector-index-'));
tempDirs.push(projectDir);
const moduleUrl = new URL('../dist/core/unified-vector-index.js', import.meta.url);
moduleUrl.searchParams.set('t', String(Date.now()));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mod: any = await import(moduleUrl.href);
const index = new mod.UnifiedVectorIndex(projectDir);
const status = await index.getStatus();
assert.equal(status.success, true);
assert.equal(spawnCalls.length, 1);
assert.equal(spawnCalls[0].options.shell, false);
assert.equal(spawnCalls[0].options.windowsHide, true);
assert.equal(spawnCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
assert.deepEqual(spawnCalls[0].options.stdio, ['pipe', 'pipe', 'pipe']);
assert.match(spawnCalls[0].child.stdinChunks.join(''), /"operation":"status"/);
});
});

View File

@@ -3,13 +3,16 @@ import assert from 'node:assert/strict';
import { execSync } from 'node:child_process';
const uvManagerPath = new URL('../dist/utils/uv-manager.js', import.meta.url).href;
const pythonUtilsPath = new URL('../dist/utils/python-utils.js', import.meta.url).href;
describe('CodexLens UV python preference', async () => {
let mod;
let pythonUtils;
const originalPython = process.env.CCW_PYTHON;
before(async () => {
mod = await import(uvManagerPath);
pythonUtils = await import(pythonUtilsPath);
});
afterEach(() => {
@@ -25,6 +28,73 @@ describe('CodexLens UV python preference', async () => {
assert.equal(mod.getPreferredCodexLensPythonSpec(), 'C:/Custom/Python/python.exe');
});
it('parses py launcher commands into spawn-safe command specs', () => {
const spec = pythonUtils.parsePythonCommandSpec('py -3.11');
assert.equal(spec.command, 'py');
assert.deepEqual(spec.args, ['-3.11']);
assert.equal(spec.display, 'py -3.11');
});
it('treats unquoted Windows-style executable paths as a single command', () => {
const spec = pythonUtils.parsePythonCommandSpec('C:/Program Files/Python311/python.exe');
assert.equal(spec.command, 'C:/Program Files/Python311/python.exe');
assert.deepEqual(spec.args, []);
assert.equal(spec.display, '"C:/Program Files/Python311/python.exe"');
});
it('probes Python launcher versions without opening a shell window', () => {
const probeCalls = [];
const version = pythonUtils.probePythonCommandVersion(
{ command: 'py', args: ['-3.11'], display: 'py -3.11' },
(command, args, options) => {
probeCalls.push({ command, args, options });
return { status: 0, stdout: '', stderr: 'Python 3.11.9\n' };
},
);
assert.equal(version, 'Python 3.11.9');
assert.equal(probeCalls.length, 1);
assert.equal(probeCalls[0].command, 'py');
assert.deepEqual(probeCalls[0].args, ['-3.11', '--version']);
assert.equal(probeCalls[0].options.shell, false);
assert.equal(probeCalls[0].options.windowsHide, true);
assert.equal(probeCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('looks up uv on PATH without spawning a visible shell window', () => {
const lookupCalls = [];
const found = mod.__testables.findExecutableOnPath('uv', (command, args, options) => {
lookupCalls.push({ command, args, options });
return { status: 0, stdout: 'C:/Tools/uv.exe\n', stderr: '' };
});
assert.equal(found, 'C:/Tools/uv.exe');
assert.equal(lookupCalls.length, 1);
assert.equal(lookupCalls[0].command, process.platform === 'win32' ? 'where' : 'which');
assert.deepEqual(lookupCalls[0].args, ['uv']);
assert.equal(lookupCalls[0].options.shell, false);
assert.equal(lookupCalls[0].options.windowsHide, true);
assert.equal(lookupCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('checks Windows launcher preferences with hidden subprocess options', () => {
const probeCalls = [];
const available = mod.__testables.hasWindowsPythonLauncherVersion('3.11', (command, args, options) => {
probeCalls.push({ command, args, options });
return { status: 0, stdout: '', stderr: 'Python 3.11.9\n' };
});
assert.equal(available, true);
assert.equal(probeCalls.length, 1);
assert.equal(probeCalls[0].command, 'py');
assert.deepEqual(probeCalls[0].args, ['-3.11', '--version']);
assert.equal(probeCalls[0].options.shell, false);
assert.equal(probeCalls[0].options.windowsHide, true);
assert.equal(probeCalls[0].options.env.PYTHONIOENCODING, 'utf-8');
});
it('prefers Python 3.11 or 3.10 on Windows when available', () => {
if (process.platform !== 'win32') return;
delete process.env.CCW_PYTHON;