claude-code-workflow 6.2.7 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +16 -1
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
- package/.claude/workflows/cli-tools-usage.md +14 -24
- package/.codex/AGENTS.md +51 -1
- package/.codex/prompts/compact.md +378 -0
- package/.gemini/GEMINI.md +57 -20
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +21 -8
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts +2 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +129 -8
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/hook.d.ts.map +1 -1
- package/ccw/dist/commands/hook.js +3 -2
- package/ccw/dist/commands/hook.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
- package/ccw/dist/config/litellm-api-config-manager.js +770 -0
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
- package/ccw/dist/config/provider-models.d.ts +73 -0
- package/ccw/dist/config/provider-models.d.ts.map +1 -0
- package/ccw/dist/config/provider-models.js +172 -0
- package/ccw/dist/config/provider-models.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts.map +1 -1
- package/ccw/dist/core/cache-manager.js +3 -5
- package/ccw/dist/core/cache-manager.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +3 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +169 -0
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +234 -18
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.js +30 -32
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.js +85 -0
- package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.js +2 -2
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +39 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +1 -1
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +15 -1
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/mcp-server/index.js +1 -1
- package/ccw/dist/mcp-server/index.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +82 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
- package/ccw/dist/tools/claude-cli-tools.js +216 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +76 -14
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +9 -2
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +114 -9
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/context-cache-store.d.ts +136 -0
- package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache-store.js +256 -0
- package/ccw/dist/tools/context-cache-store.js.map +1 -0
- package/ccw/dist/tools/context-cache.d.ts +56 -0
- package/ccw/dist/tools/context-cache.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache.js +294 -0
- package/ccw/dist/tools/context-cache.js.map +1 -0
- package/ccw/dist/tools/core-memory.d.ts.map +1 -1
- package/ccw/dist/tools/core-memory.js +33 -19
- package/ccw/dist/tools/core-memory.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +85 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-client.js +188 -0
- package/ccw/dist/tools/litellm-client.js.map +1 -0
- package/ccw/dist/tools/litellm-executor.d.ts +34 -0
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-executor.js +192 -0
- package/ccw/dist/tools/litellm-executor.js.map +1 -0
- package/ccw/dist/tools/pattern-parser.d.ts +55 -0
- package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
- package/ccw/dist/tools/pattern-parser.js +237 -0
- package/ccw/dist/tools/pattern-parser.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +1 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +117 -41
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/dist/types/litellm-api-config.d.ts +294 -0
- package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
- package/ccw/dist/types/litellm-api-config.js +8 -0
- package/ccw/dist/types/litellm-api-config.js.map +1 -0
- package/ccw/src/cli.ts +258 -244
- package/ccw/src/commands/cli.ts +153 -9
- package/ccw/src/commands/hook.ts +3 -2
- package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
- package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
- package/ccw/src/config/provider-models.ts +222 -0
- package/ccw/src/core/cache-manager.ts +292 -294
- package/ccw/src/core/dashboard-generator.ts +3 -1
- package/ccw/src/core/routes/cli-routes.ts +192 -0
- package/ccw/src/core/routes/codexlens-routes.ts +241 -19
- package/ccw/src/core/routes/hooks-routes.ts +399 -405
- package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
- package/ccw/src/core/routes/litellm-routes.ts +107 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
- package/ccw/src/core/routes/status-routes.ts +51 -0
- package/ccw/src/core/routes/system-routes.ts +1 -1
- package/ccw/src/core/server.ts +15 -1
- package/ccw/src/mcp-server/index.ts +1 -1
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
- package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
- package/ccw/src/templates/dashboard-js/components/cli-status.js +323 -9
- package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
- package/ccw/src/templates/dashboard-js/i18n.js +583 -1
- package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +199 -24
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
- package/ccw/src/templates/dashboard.html +840 -831
- package/ccw/src/tools/claude-cli-tools.ts +300 -0
- package/ccw/src/tools/cli-executor.ts +83 -14
- package/ccw/src/tools/codex-lens.ts +146 -9
- package/ccw/src/tools/context-cache-store.ts +368 -0
- package/ccw/src/tools/context-cache.ts +393 -0
- package/ccw/src/tools/core-memory.ts +33 -19
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +246 -0
- package/ccw/src/tools/litellm-executor.ts +241 -0
- package/ccw/src/tools/pattern-parser.ts +329 -0
- package/ccw/src/tools/smart-search.ts +142 -41
- package/ccw/src/types/litellm-api-config.ts +402 -0
- package/ccw-litellm/README.md +180 -0
- package/ccw-litellm/pyproject.toml +35 -0
- package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
- package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
- package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
- package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +378 -23
- package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
- package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
- package/codex-lens/src/codexlens/cli/output.py +12 -1
- package/codex-lens/src/codexlens/config.py +93 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +6 -2
- package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
- package/codex-lens/src/codexlens/search/ranking.py +1 -1
- package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/base.py +61 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
- package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
- package/codex-lens/src/codexlens/semantic/factory.py +98 -0
- package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
- package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
- package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
- package/package.json +15 -5
- package/.codex/prompts.zip +0 -0
- package/ccw/package.json +0 -65
|
@@ -36,10 +36,12 @@ const ParamsSchema = z.object({
|
|
|
36
36
|
path: z.string().optional(),
|
|
37
37
|
paths: z.array(z.string()).default([]),
|
|
38
38
|
contextLines: z.number().default(0),
|
|
39
|
-
maxResults: z.number().default(
|
|
39
|
+
maxResults: z.number().default(5), // Default 5 with full content
|
|
40
40
|
includeHidden: z.boolean().default(false),
|
|
41
41
|
languages: z.array(z.string()).optional(),
|
|
42
|
-
limit: z.number().default(
|
|
42
|
+
limit: z.number().default(5), // Default 5 with full content
|
|
43
|
+
extraFilesCount: z.number().default(10), // Additional file-only results
|
|
44
|
+
maxContentLength: z.number().default(200), // Max content length for truncation (50-2000)
|
|
43
45
|
offset: z.number().default(0), // NEW: Pagination offset (start_index)
|
|
44
46
|
enrich: z.boolean().default(false),
|
|
45
47
|
// Search modifiers for ripgrep mode
|
|
@@ -268,6 +270,7 @@ interface SearchMetadata {
|
|
|
268
270
|
interface SearchResult {
|
|
269
271
|
success: boolean;
|
|
270
272
|
results?: ExactMatch[] | SemanticMatch[] | GraphMatch[] | FileMatch[] | unknown;
|
|
273
|
+
extra_files?: string[]; // Additional file paths without content
|
|
271
274
|
output?: string;
|
|
272
275
|
metadata?: SearchMetadata;
|
|
273
276
|
error?: string;
|
|
@@ -275,11 +278,22 @@ interface SearchResult {
|
|
|
275
278
|
message?: string;
|
|
276
279
|
}
|
|
277
280
|
|
|
281
|
+
interface ModelInfo {
|
|
282
|
+
model_profile?: string;
|
|
283
|
+
model_name?: string;
|
|
284
|
+
embedding_dim?: number;
|
|
285
|
+
backend?: string;
|
|
286
|
+
created_at?: string;
|
|
287
|
+
updated_at?: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
278
290
|
interface IndexStatus {
|
|
279
291
|
indexed: boolean;
|
|
280
292
|
has_embeddings: boolean;
|
|
281
293
|
file_count?: number;
|
|
282
294
|
embeddings_coverage_percent?: number;
|
|
295
|
+
total_chunks?: number;
|
|
296
|
+
model_info?: ModelInfo | null;
|
|
283
297
|
warning?: string;
|
|
284
298
|
}
|
|
285
299
|
|
|
@@ -290,6 +304,42 @@ function stripAnsi(str: string): string {
|
|
|
290
304
|
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
291
305
|
}
|
|
292
306
|
|
|
307
|
+
/** Default maximum content length to return (avoid excessive output) */
|
|
308
|
+
const DEFAULT_MAX_CONTENT_LENGTH = 200;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Truncate content to specified length with ellipsis
|
|
312
|
+
* @param content - The content to truncate
|
|
313
|
+
* @param maxLength - Maximum length (default: 200)
|
|
314
|
+
*/
|
|
315
|
+
function truncateContent(content: string | null | undefined, maxLength: number = DEFAULT_MAX_CONTENT_LENGTH): string {
|
|
316
|
+
if (!content) return '';
|
|
317
|
+
if (content.length <= maxLength) return content;
|
|
318
|
+
return content.slice(0, maxLength) + '...';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Split results into full content results and extra file-only results
|
|
323
|
+
* Generic function supporting both SemanticMatch and ExactMatch types
|
|
324
|
+
* @param allResults - All search results (must have 'file' property)
|
|
325
|
+
* @param fullContentLimit - Number of results with full content (default: 5)
|
|
326
|
+
* @param extraFilesCount - Number of additional file-only results (default: 10)
|
|
327
|
+
*/
|
|
328
|
+
function splitResultsWithExtraFiles<T extends { file: string }>(
|
|
329
|
+
allResults: T[],
|
|
330
|
+
fullContentLimit: number = 5,
|
|
331
|
+
extraFilesCount: number = 10
|
|
332
|
+
): { results: T[]; extra_files: string[] } {
|
|
333
|
+
// First N results with full content
|
|
334
|
+
const results = allResults.slice(0, fullContentLimit);
|
|
335
|
+
|
|
336
|
+
// Next M results as file paths only (deduplicated)
|
|
337
|
+
const extraResults = allResults.slice(fullContentLimit, fullContentLimit + extraFilesCount);
|
|
338
|
+
const extra_files = [...new Set(extraResults.map(r => r.file))];
|
|
339
|
+
|
|
340
|
+
return { results, extra_files };
|
|
341
|
+
}
|
|
342
|
+
|
|
293
343
|
/**
|
|
294
344
|
* Check if CodexLens index exists for current directory
|
|
295
345
|
* @param path - Directory path to check
|
|
@@ -320,6 +370,18 @@ async function checkIndexStatus(path: string = '.'): Promise<IndexStatus> {
|
|
|
320
370
|
const embeddingsData = status.embeddings || {};
|
|
321
371
|
const embeddingsCoverage = embeddingsData.coverage_percent || 0;
|
|
322
372
|
const has_embeddings = embeddingsCoverage >= 50; // Threshold: 50%
|
|
373
|
+
const totalChunks = embeddingsData.total_chunks || 0;
|
|
374
|
+
|
|
375
|
+
// Extract model info if available
|
|
376
|
+
const modelInfoData = embeddingsData.model_info;
|
|
377
|
+
const modelInfo: ModelInfo | undefined = modelInfoData ? {
|
|
378
|
+
model_profile: modelInfoData.model_profile,
|
|
379
|
+
model_name: modelInfoData.model_name,
|
|
380
|
+
embedding_dim: modelInfoData.embedding_dim,
|
|
381
|
+
backend: modelInfoData.backend,
|
|
382
|
+
created_at: modelInfoData.created_at,
|
|
383
|
+
updated_at: modelInfoData.updated_at,
|
|
384
|
+
} : undefined;
|
|
323
385
|
|
|
324
386
|
let warning: string | undefined;
|
|
325
387
|
if (!indexed) {
|
|
@@ -335,6 +397,9 @@ async function checkIndexStatus(path: string = '.'): Promise<IndexStatus> {
|
|
|
335
397
|
has_embeddings,
|
|
336
398
|
file_count: status.total_files,
|
|
337
399
|
embeddings_coverage_percent: embeddingsCoverage,
|
|
400
|
+
total_chunks: totalChunks,
|
|
401
|
+
// Ensure model_info is null instead of undefined so it's included in JSON
|
|
402
|
+
model_info: modelInfo ?? null,
|
|
338
403
|
warning,
|
|
339
404
|
};
|
|
340
405
|
} catch {
|
|
@@ -688,7 +753,7 @@ async function executeAutoMode(params: Params): Promise<SearchResult> {
|
|
|
688
753
|
* Supports tokenized multi-word queries with OR matching and result ranking
|
|
689
754
|
*/
|
|
690
755
|
async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
691
|
-
const { query, paths = [], contextLines = 0, maxResults = 10, includeHidden = false, path = '.', regex = true, caseSensitive = true, tokenize = true } = params;
|
|
756
|
+
const { query, paths = [], contextLines = 0, maxResults = 5, extraFilesCount = 10, maxContentLength = 200, includeHidden = false, path = '.', regex = true, caseSensitive = true, tokenize = true } = params;
|
|
692
757
|
|
|
693
758
|
if (!query) {
|
|
694
759
|
return {
|
|
@@ -700,6 +765,9 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
700
765
|
// Check if ripgrep is available
|
|
701
766
|
const hasRipgrep = checkToolAvailability('rg');
|
|
702
767
|
|
|
768
|
+
// Calculate total to fetch for split (full content + extra files)
|
|
769
|
+
const totalToFetch = maxResults + extraFilesCount;
|
|
770
|
+
|
|
703
771
|
// If ripgrep not available, fall back to CodexLens exact mode
|
|
704
772
|
if (!hasRipgrep) {
|
|
705
773
|
const readyStatus = await ensureCodexLensReady();
|
|
@@ -711,7 +779,7 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
711
779
|
}
|
|
712
780
|
|
|
713
781
|
// Use CodexLens exact mode as fallback
|
|
714
|
-
const args = ['search', query, '--limit',
|
|
782
|
+
const args = ['search', query, '--limit', totalToFetch.toString(), '--mode', 'exact', '--json'];
|
|
715
783
|
const result = await executeCodexLens(args, { cwd: path });
|
|
716
784
|
|
|
717
785
|
if (!result.success) {
|
|
@@ -728,23 +796,27 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
728
796
|
}
|
|
729
797
|
|
|
730
798
|
// Parse results
|
|
731
|
-
let
|
|
799
|
+
let allResults: SemanticMatch[] = [];
|
|
732
800
|
try {
|
|
733
801
|
const parsed = JSON.parse(stripAnsi(result.output || '{}'));
|
|
734
802
|
const data = parsed.result?.results || parsed.results || parsed;
|
|
735
|
-
|
|
803
|
+
allResults = (Array.isArray(data) ? data : []).map((item: any) => ({
|
|
736
804
|
file: item.path || item.file,
|
|
737
805
|
score: item.score || 0,
|
|
738
|
-
content: item.
|
|
806
|
+
content: truncateContent(item.content || item.excerpt, maxContentLength),
|
|
739
807
|
symbol: item.symbol || null,
|
|
740
808
|
}));
|
|
741
809
|
} catch {
|
|
742
810
|
// Keep empty results
|
|
743
811
|
}
|
|
744
812
|
|
|
813
|
+
// Split results: first N with full content, rest as file paths only
|
|
814
|
+
const { results, extra_files } = splitResultsWithExtraFiles(allResults, maxResults, extraFilesCount);
|
|
815
|
+
|
|
745
816
|
return {
|
|
746
817
|
success: true,
|
|
747
818
|
results,
|
|
819
|
+
extra_files: extra_files.length > 0 ? extra_files : undefined,
|
|
748
820
|
metadata: {
|
|
749
821
|
mode: 'ripgrep',
|
|
750
822
|
backend: 'codexlens-fallback',
|
|
@@ -755,12 +827,12 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
755
827
|
};
|
|
756
828
|
}
|
|
757
829
|
|
|
758
|
-
// Use ripgrep
|
|
830
|
+
// Use ripgrep - request more results to support split
|
|
759
831
|
const { command, args, tokens } = buildRipgrepCommand({
|
|
760
832
|
query,
|
|
761
833
|
paths: paths.length > 0 ? paths : [path],
|
|
762
834
|
contextLines,
|
|
763
|
-
maxResults,
|
|
835
|
+
maxResults: totalToFetch, // Fetch more to support split
|
|
764
836
|
includeHidden,
|
|
765
837
|
regex,
|
|
766
838
|
caseSensitive,
|
|
@@ -786,14 +858,14 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
786
858
|
});
|
|
787
859
|
|
|
788
860
|
child.on('close', (code) => {
|
|
789
|
-
const
|
|
861
|
+
const allResults: ExactMatch[] = [];
|
|
790
862
|
const lines = stdout.split('\n').filter((line) => line.trim());
|
|
791
863
|
// Limit total results to prevent memory overflow (--max-count only limits per-file)
|
|
792
|
-
const effectiveLimit =
|
|
864
|
+
const effectiveLimit = totalToFetch > 0 ? totalToFetch : 500;
|
|
793
865
|
|
|
794
866
|
for (const line of lines) {
|
|
795
867
|
// Stop collecting if we've reached the limit
|
|
796
|
-
if (
|
|
868
|
+
if (allResults.length >= effectiveLimit) {
|
|
797
869
|
resultLimitReached = true;
|
|
798
870
|
break;
|
|
799
871
|
}
|
|
@@ -811,7 +883,7 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
811
883
|
: 1,
|
|
812
884
|
content: item.data.lines.text.trim(),
|
|
813
885
|
};
|
|
814
|
-
|
|
886
|
+
allResults.push(match);
|
|
815
887
|
}
|
|
816
888
|
} catch {
|
|
817
889
|
continue;
|
|
@@ -824,9 +896,12 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
824
896
|
|
|
825
897
|
// Apply token-based scoring and sorting for multi-word queries
|
|
826
898
|
// Results matching more tokens are ranked higher (exact matches first)
|
|
827
|
-
const scoredResults = tokens.length > 1 ? scoreByTokenMatch(
|
|
899
|
+
const scoredResults = tokens.length > 1 ? scoreByTokenMatch(allResults, tokens) : allResults;
|
|
828
900
|
|
|
829
901
|
if (code === 0 || code === 1 || (isWindowsDeviceError && scoredResults.length > 0)) {
|
|
902
|
+
// Split results: first N with full content, rest as file paths only
|
|
903
|
+
const { results, extra_files } = splitResultsWithExtraFiles(scoredResults, maxResults, extraFilesCount);
|
|
904
|
+
|
|
830
905
|
// Build warning message for various conditions
|
|
831
906
|
const warnings: string[] = [];
|
|
832
907
|
if (resultLimitReached) {
|
|
@@ -838,18 +913,19 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
838
913
|
|
|
839
914
|
resolve({
|
|
840
915
|
success: true,
|
|
841
|
-
results
|
|
916
|
+
results,
|
|
917
|
+
extra_files: extra_files.length > 0 ? extra_files : undefined,
|
|
842
918
|
metadata: {
|
|
843
919
|
mode: 'ripgrep',
|
|
844
920
|
backend: 'ripgrep',
|
|
845
|
-
count:
|
|
921
|
+
count: results.length,
|
|
846
922
|
query,
|
|
847
923
|
tokens: tokens.length > 1 ? tokens : undefined, // Include tokens in metadata for debugging
|
|
848
924
|
tokenized: tokens.length > 1,
|
|
849
925
|
...(warnings.length > 0 && { warning: warnings.join('; ') }),
|
|
850
926
|
},
|
|
851
927
|
});
|
|
852
|
-
} else if (isWindowsDeviceError &&
|
|
928
|
+
} else if (isWindowsDeviceError && allResults.length === 0) {
|
|
853
929
|
// Windows device error but no results - might be the only issue
|
|
854
930
|
resolve({
|
|
855
931
|
success: true,
|
|
@@ -886,7 +962,7 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
|
|
886
962
|
* Requires index
|
|
887
963
|
*/
|
|
888
964
|
async function executeCodexLensExactMode(params: Params): Promise<SearchResult> {
|
|
889
|
-
const { query, path = '.', maxResults = 10, enrich = false } = params;
|
|
965
|
+
const { query, path = '.', maxResults = 5, extraFilesCount = 10, maxContentLength = 200, enrich = false } = params;
|
|
890
966
|
|
|
891
967
|
if (!query) {
|
|
892
968
|
return {
|
|
@@ -907,7 +983,9 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
907
983
|
// Check index status
|
|
908
984
|
const indexStatus = await checkIndexStatus(path);
|
|
909
985
|
|
|
910
|
-
|
|
986
|
+
// Request more results to support split (full content + extra files)
|
|
987
|
+
const totalToFetch = maxResults + extraFilesCount;
|
|
988
|
+
const args = ['search', query, '--limit', totalToFetch.toString(), '--mode', 'exact', '--json'];
|
|
911
989
|
if (enrich) {
|
|
912
990
|
args.push('--enrich');
|
|
913
991
|
}
|
|
@@ -928,14 +1006,14 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
928
1006
|
}
|
|
929
1007
|
|
|
930
1008
|
// Parse results
|
|
931
|
-
let
|
|
1009
|
+
let allResults: SemanticMatch[] = [];
|
|
932
1010
|
try {
|
|
933
1011
|
const parsed = JSON.parse(stripAnsi(result.output || '{}'));
|
|
934
1012
|
const data = parsed.result?.results || parsed.results || parsed;
|
|
935
|
-
|
|
1013
|
+
allResults = (Array.isArray(data) ? data : []).map((item: any) => ({
|
|
936
1014
|
file: item.path || item.file,
|
|
937
1015
|
score: item.score || 0,
|
|
938
|
-
content: item.
|
|
1016
|
+
content: truncateContent(item.content || item.excerpt, maxContentLength),
|
|
939
1017
|
symbol: item.symbol || null,
|
|
940
1018
|
}));
|
|
941
1019
|
} catch {
|
|
@@ -943,8 +1021,8 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
943
1021
|
}
|
|
944
1022
|
|
|
945
1023
|
// Fallback to fuzzy mode if exact returns no results
|
|
946
|
-
if (
|
|
947
|
-
const fuzzyArgs = ['search', query, '--limit',
|
|
1024
|
+
if (allResults.length === 0) {
|
|
1025
|
+
const fuzzyArgs = ['search', query, '--limit', totalToFetch.toString(), '--mode', 'fuzzy', '--json'];
|
|
948
1026
|
if (enrich) {
|
|
949
1027
|
fuzzyArgs.push('--enrich');
|
|
950
1028
|
}
|
|
@@ -954,20 +1032,23 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
954
1032
|
try {
|
|
955
1033
|
const parsed = JSON.parse(stripAnsi(fuzzyResult.output || '{}'));
|
|
956
1034
|
const data = parsed.result?.results || parsed.results || parsed;
|
|
957
|
-
|
|
1035
|
+
allResults = (Array.isArray(data) ? data : []).map((item: any) => ({
|
|
958
1036
|
file: item.path || item.file,
|
|
959
1037
|
score: item.score || 0,
|
|
960
|
-
content: item.
|
|
1038
|
+
content: truncateContent(item.content || item.excerpt, maxContentLength),
|
|
961
1039
|
symbol: item.symbol || null,
|
|
962
1040
|
}));
|
|
963
1041
|
} catch {
|
|
964
1042
|
// Keep empty results
|
|
965
1043
|
}
|
|
966
1044
|
|
|
967
|
-
if (
|
|
1045
|
+
if (allResults.length > 0) {
|
|
1046
|
+
// Split results: first N with full content, rest as file paths only
|
|
1047
|
+
const { results, extra_files } = splitResultsWithExtraFiles(allResults, maxResults, extraFilesCount);
|
|
968
1048
|
return {
|
|
969
1049
|
success: true,
|
|
970
1050
|
results,
|
|
1051
|
+
extra_files: extra_files.length > 0 ? extra_files : undefined,
|
|
971
1052
|
metadata: {
|
|
972
1053
|
mode: 'exact',
|
|
973
1054
|
backend: 'codexlens',
|
|
@@ -982,9 +1063,13 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
982
1063
|
}
|
|
983
1064
|
}
|
|
984
1065
|
|
|
1066
|
+
// Split results: first N with full content, rest as file paths only
|
|
1067
|
+
const { results, extra_files } = splitResultsWithExtraFiles(allResults, maxResults, extraFilesCount);
|
|
1068
|
+
|
|
985
1069
|
return {
|
|
986
1070
|
success: true,
|
|
987
1071
|
results,
|
|
1072
|
+
extra_files: extra_files.length > 0 ? extra_files : undefined,
|
|
988
1073
|
metadata: {
|
|
989
1074
|
mode: 'exact',
|
|
990
1075
|
backend: 'codexlens',
|
|
@@ -1001,7 +1086,7 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
|
|
|
1001
1086
|
* Requires index with embeddings
|
|
1002
1087
|
*/
|
|
1003
1088
|
async function executeHybridMode(params: Params): Promise<SearchResult> {
|
|
1004
|
-
const { query, path = '.', maxResults = 10, enrich = false } = params;
|
|
1089
|
+
const { query, path = '.', maxResults = 5, extraFilesCount = 10, maxContentLength = 200, enrich = false } = params;
|
|
1005
1090
|
|
|
1006
1091
|
if (!query) {
|
|
1007
1092
|
return {
|
|
@@ -1022,7 +1107,9 @@ async function executeHybridMode(params: Params): Promise<SearchResult> {
|
|
|
1022
1107
|
// Check index status
|
|
1023
1108
|
const indexStatus = await checkIndexStatus(path);
|
|
1024
1109
|
|
|
1025
|
-
|
|
1110
|
+
// Request more results to support split (full content + extra files)
|
|
1111
|
+
const totalToFetch = maxResults + extraFilesCount;
|
|
1112
|
+
const args = ['search', query, '--limit', totalToFetch.toString(), '--mode', 'hybrid', '--json'];
|
|
1026
1113
|
if (enrich) {
|
|
1027
1114
|
args.push('--enrich');
|
|
1028
1115
|
}
|
|
@@ -1043,14 +1130,14 @@ async function executeHybridMode(params: Params): Promise<SearchResult> {
|
|
|
1043
1130
|
}
|
|
1044
1131
|
|
|
1045
1132
|
// Parse results
|
|
1046
|
-
let
|
|
1133
|
+
let allResults: SemanticMatch[] = [];
|
|
1047
1134
|
let baselineInfo: { score: number; count: number } | null = null;
|
|
1048
1135
|
let initialCount = 0;
|
|
1049
1136
|
|
|
1050
1137
|
try {
|
|
1051
1138
|
const parsed = JSON.parse(stripAnsi(result.output || '{}'));
|
|
1052
1139
|
const data = parsed.result?.results || parsed.results || parsed;
|
|
1053
|
-
|
|
1140
|
+
allResults = (Array.isArray(data) ? data : []).map((item: any) => {
|
|
1054
1141
|
const rawScore = item.score || 0;
|
|
1055
1142
|
// Hybrid mode returns distance scores (lower is better).
|
|
1056
1143
|
// Convert to similarity scores (higher is better) for consistency.
|
|
@@ -1059,27 +1146,27 @@ async function executeHybridMode(params: Params): Promise<SearchResult> {
|
|
|
1059
1146
|
return {
|
|
1060
1147
|
file: item.path || item.file,
|
|
1061
1148
|
score: similarityScore,
|
|
1062
|
-
content: item.
|
|
1149
|
+
content: truncateContent(item.content || item.excerpt, maxContentLength),
|
|
1063
1150
|
symbol: item.symbol || null,
|
|
1064
1151
|
};
|
|
1065
1152
|
});
|
|
1066
1153
|
|
|
1067
|
-
initialCount =
|
|
1154
|
+
initialCount = allResults.length;
|
|
1068
1155
|
|
|
1069
1156
|
// Post-processing pipeline to improve semantic search quality
|
|
1070
1157
|
// 0. Filter dominant baseline scores (hot spot detection)
|
|
1071
|
-
const baselineResult = filterDominantBaselineScores(
|
|
1072
|
-
|
|
1158
|
+
const baselineResult = filterDominantBaselineScores(allResults);
|
|
1159
|
+
allResults = baselineResult.filteredResults;
|
|
1073
1160
|
baselineInfo = baselineResult.baselineInfo;
|
|
1074
1161
|
|
|
1075
1162
|
// 1. Filter noisy files (coverage, node_modules, etc.)
|
|
1076
|
-
|
|
1163
|
+
allResults = filterNoisyFiles(allResults);
|
|
1077
1164
|
// 2. Boost results containing query keywords
|
|
1078
|
-
|
|
1165
|
+
allResults = applyKeywordBoosting(allResults, query);
|
|
1079
1166
|
// 3. Enforce score diversity (penalize identical scores)
|
|
1080
|
-
|
|
1167
|
+
allResults = enforceScoreDiversity(allResults);
|
|
1081
1168
|
// 4. Re-sort by adjusted scores
|
|
1082
|
-
|
|
1169
|
+
allResults.sort((a, b) => b.score - a.score);
|
|
1083
1170
|
} catch {
|
|
1084
1171
|
return {
|
|
1085
1172
|
success: true,
|
|
@@ -1095,15 +1182,19 @@ async function executeHybridMode(params: Params): Promise<SearchResult> {
|
|
|
1095
1182
|
};
|
|
1096
1183
|
}
|
|
1097
1184
|
|
|
1185
|
+
// Split results: first N with full content, rest as file paths only
|
|
1186
|
+
const { results, extra_files } = splitResultsWithExtraFiles(allResults, maxResults, extraFilesCount);
|
|
1187
|
+
|
|
1098
1188
|
// Build metadata with baseline info if detected
|
|
1099
1189
|
let note = 'Hybrid mode uses RRF fusion (exact + fuzzy + vector) for best results';
|
|
1100
1190
|
if (baselineInfo) {
|
|
1101
|
-
note += ` | Filtered ${initialCount -
|
|
1191
|
+
note += ` | Filtered ${initialCount - allResults.length} hot-spot results with baseline score ~${baselineInfo.score.toFixed(4)}`;
|
|
1102
1192
|
}
|
|
1103
1193
|
|
|
1104
1194
|
return {
|
|
1105
1195
|
success: true,
|
|
1106
1196
|
results,
|
|
1197
|
+
extra_files: extra_files.length > 0 ? extra_files : undefined,
|
|
1107
1198
|
metadata: {
|
|
1108
1199
|
mode: 'hybrid',
|
|
1109
1200
|
backend: 'codexlens',
|
|
@@ -1514,7 +1605,7 @@ export const schema: ToolSchema = {
|
|
|
1514
1605
|
mode: {
|
|
1515
1606
|
type: 'string',
|
|
1516
1607
|
enum: SEARCH_MODES,
|
|
1517
|
-
description: 'Search mode: auto
|
|
1608
|
+
description: 'Search mode: auto, hybrid (best quality), exact (CodexLens FTS), ripgrep (fast, no index), priority (fallback chain)',
|
|
1518
1609
|
default: 'auto',
|
|
1519
1610
|
},
|
|
1520
1611
|
output_mode: {
|
|
@@ -1550,6 +1641,16 @@ export const schema: ToolSchema = {
|
|
|
1550
1641
|
description: 'Alias for maxResults (default: 20)',
|
|
1551
1642
|
default: 20,
|
|
1552
1643
|
},
|
|
1644
|
+
extraFilesCount: {
|
|
1645
|
+
type: 'number',
|
|
1646
|
+
description: 'Number of additional file-only results (paths without content)',
|
|
1647
|
+
default: 10,
|
|
1648
|
+
},
|
|
1649
|
+
maxContentLength: {
|
|
1650
|
+
type: 'number',
|
|
1651
|
+
description: 'Maximum content length for truncation (50-2000)',
|
|
1652
|
+
default: 200,
|
|
1653
|
+
},
|
|
1553
1654
|
offset: {
|
|
1554
1655
|
type: 'number',
|
|
1555
1656
|
description: 'Pagination offset - skip first N results (default: 0)',
|