@zuvia-software-solutions/code-mapper 1.4.0 → 2.0.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/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +1 -0
- package/dist/cli/analyze.js +73 -82
- package/dist/cli/augment.js +0 -2
- package/dist/cli/eval-server.d.ts +2 -2
- package/dist/cli/eval-server.js +6 -6
- package/dist/cli/index.js +6 -10
- package/dist/cli/mcp.d.ts +1 -3
- package/dist/cli/mcp.js +3 -3
- package/dist/cli/refresh.d.ts +2 -2
- package/dist/cli/refresh.js +24 -29
- package/dist/cli/status.js +4 -13
- package/dist/cli/tool.d.ts +5 -4
- package/dist/cli/tool.js +8 -10
- package/dist/config/ignore-service.js +14 -34
- package/dist/core/augmentation/engine.js +53 -83
- package/dist/core/db/adapter.d.ts +99 -0
- package/dist/core/db/adapter.js +402 -0
- package/dist/core/db/graph-loader.d.ts +27 -0
- package/dist/core/db/graph-loader.js +148 -0
- package/dist/core/db/queries.d.ts +160 -0
- package/dist/core/db/queries.js +441 -0
- package/dist/core/db/schema.d.ts +108 -0
- package/dist/core/db/schema.js +136 -0
- package/dist/core/embeddings/embedder.d.ts +21 -12
- package/dist/core/embeddings/embedder.js +104 -50
- package/dist/core/embeddings/embedding-pipeline.d.ts +48 -22
- package/dist/core/embeddings/embedding-pipeline.js +220 -262
- package/dist/core/embeddings/text-generator.js +4 -19
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.d.ts +1 -1
- package/dist/core/graph/graph.js +1 -0
- package/dist/core/graph/types.d.ts +11 -9
- package/dist/core/graph/types.js +4 -1
- package/dist/core/incremental/refresh.d.ts +46 -0
- package/dist/core/incremental/refresh.js +464 -0
- package/dist/core/incremental/types.d.ts +2 -1
- package/dist/core/incremental/types.js +42 -44
- package/dist/core/ingestion/ast-cache.js +1 -0
- package/dist/core/ingestion/call-processor.d.ts +15 -3
- package/dist/core/ingestion/call-processor.js +448 -60
- package/dist/core/ingestion/cluster-enricher.d.ts +1 -1
- package/dist/core/ingestion/cluster-enricher.js +2 -0
- package/dist/core/ingestion/community-processor.d.ts +1 -1
- package/dist/core/ingestion/community-processor.js +8 -3
- package/dist/core/ingestion/export-detection.d.ts +1 -1
- package/dist/core/ingestion/export-detection.js +1 -1
- package/dist/core/ingestion/filesystem-walker.js +1 -1
- package/dist/core/ingestion/heritage-processor.d.ts +2 -2
- package/dist/core/ingestion/heritage-processor.js +22 -11
- package/dist/core/ingestion/import-processor.d.ts +2 -2
- package/dist/core/ingestion/import-processor.js +24 -9
- package/dist/core/ingestion/language-config.js +7 -4
- package/dist/core/ingestion/mro-processor.d.ts +1 -1
- package/dist/core/ingestion/mro-processor.js +23 -11
- package/dist/core/ingestion/named-binding-extraction.js +5 -5
- package/dist/core/ingestion/parsing-processor.d.ts +4 -4
- package/dist/core/ingestion/parsing-processor.js +26 -18
- package/dist/core/ingestion/pipeline.d.ts +4 -2
- package/dist/core/ingestion/pipeline.js +50 -20
- package/dist/core/ingestion/process-processor.d.ts +2 -2
- package/dist/core/ingestion/process-processor.js +28 -14
- package/dist/core/ingestion/resolution-context.d.ts +1 -1
- package/dist/core/ingestion/resolution-context.js +14 -4
- package/dist/core/ingestion/resolvers/csharp.js +4 -3
- package/dist/core/ingestion/resolvers/go.js +3 -1
- package/dist/core/ingestion/resolvers/jvm.js +13 -4
- package/dist/core/ingestion/resolvers/standard.js +2 -2
- package/dist/core/ingestion/resolvers/utils.js +6 -2
- package/dist/core/ingestion/route-stitcher.d.ts +15 -0
- package/dist/core/ingestion/route-stitcher.js +92 -0
- package/dist/core/ingestion/structure-processor.d.ts +1 -1
- package/dist/core/ingestion/structure-processor.js +3 -2
- package/dist/core/ingestion/symbol-table.d.ts +2 -0
- package/dist/core/ingestion/symbol-table.js +5 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
- package/dist/core/ingestion/tree-sitter-queries.js +177 -0
- package/dist/core/ingestion/type-env.js +20 -0
- package/dist/core/ingestion/type-extractors/csharp.js +4 -3
- package/dist/core/ingestion/type-extractors/go.js +23 -12
- package/dist/core/ingestion/type-extractors/php.js +18 -10
- package/dist/core/ingestion/type-extractors/ruby.js +15 -3
- package/dist/core/ingestion/type-extractors/rust.js +3 -2
- package/dist/core/ingestion/type-extractors/shared.js +3 -2
- package/dist/core/ingestion/type-extractors/typescript.js +11 -5
- package/dist/core/ingestion/utils.d.ts +27 -4
- package/dist/core/ingestion/utils.js +145 -100
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +97 -29
- package/dist/core/ingestion/workers/worker-pool.js +3 -0
- package/dist/core/search/bm25-index.d.ts +15 -8
- package/dist/core/search/bm25-index.js +48 -98
- package/dist/core/search/hybrid-search.d.ts +9 -3
- package/dist/core/search/hybrid-search.js +30 -25
- package/dist/core/search/reranker.js +9 -7
- package/dist/core/search/types.d.ts +0 -4
- package/dist/core/semantic/tsgo-service.d.ts +5 -1
- package/dist/core/semantic/tsgo-service.js +161 -66
- package/dist/lib/tsgo-test.d.ts +2 -0
- package/dist/lib/tsgo-test.js +6 -0
- package/dist/lib/type-utils.d.ts +25 -0
- package/dist/lib/type-utils.js +22 -0
- package/dist/lib/utils.d.ts +3 -2
- package/dist/lib/utils.js +3 -2
- package/dist/mcp/compatible-stdio-transport.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +29 -56
- package/dist/mcp/local/local-backend.js +808 -1118
- package/dist/mcp/resources.js +35 -25
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +5 -5
- package/dist/mcp/tools.js +24 -25
- package/dist/storage/repo-manager.d.ts +2 -12
- package/dist/storage/repo-manager.js +1 -47
- package/dist/types/pipeline.d.ts +8 -5
- package/dist/types/pipeline.js +5 -0
- package/package.json +18 -11
- package/dist/cli/serve.d.ts +0 -5
- package/dist/cli/serve.js +0 -8
- package/dist/core/incremental/child-process.d.ts +0 -8
- package/dist/core/incremental/child-process.js +0 -649
- package/dist/core/incremental/refresh-coordinator.d.ts +0 -32
- package/dist/core/incremental/refresh-coordinator.js +0 -147
- package/dist/core/lbug/csv-generator.d.ts +0 -28
- package/dist/core/lbug/csv-generator.js +0 -355
- package/dist/core/lbug/lbug-adapter.d.ts +0 -96
- package/dist/core/lbug/lbug-adapter.js +0 -753
- package/dist/core/lbug/schema.d.ts +0 -46
- package/dist/core/lbug/schema.js +0 -402
- package/dist/mcp/core/embedder.d.ts +0 -24
- package/dist/mcp/core/embedder.js +0 -168
- package/dist/mcp/core/lbug-adapter.d.ts +0 -29
- package/dist/mcp/core/lbug-adapter.js +0 -330
- package/dist/server/api.d.ts +0 -5
- package/dist/server/api.js +0 -340
- package/dist/server/mcp-http.d.ts +0 -7
- package/dist/server/mcp-http.js +0 -95
- package/models/mlx-embedder.py +0 -185
package/dist/cli/ai-context.js
CHANGED
|
@@ -62,7 +62,7 @@ This project is indexed by Code Mapper as **${projectName}** (${stats.nodes || 0
|
|
|
62
62
|
| \`impact\` | Blast radius before editing | \`code-mapper_impact({target: "X", direction: "upstream"})\` |
|
|
63
63
|
| \`detect_changes\` | Pre-commit scope check | \`code-mapper_detect_changes({scope: "staged"})\` |
|
|
64
64
|
| \`rename\` | Safe multi-file rename | \`code-mapper_rename({symbol_name: "old", new_name: "new", dry_run: true})\` |
|
|
65
|
-
| \`
|
|
65
|
+
| \`sql\` | Custom SQL queries | \`code-mapper_sql({query: "SELECT ..."})\` |
|
|
66
66
|
|
|
67
67
|
## Impact Risk Levels
|
|
68
68
|
|
package/dist/cli/analyze.d.ts
CHANGED
package/dist/cli/analyze.js
CHANGED
|
@@ -5,11 +5,14 @@ import { execFileSync } from 'child_process';
|
|
|
5
5
|
import v8 from 'v8';
|
|
6
6
|
import cliProgress from 'cli-progress';
|
|
7
7
|
import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
|
|
8
|
-
import {
|
|
8
|
+
import { openDb, closeDb, resetDb, getStats, insertEmbeddingsBatch, countEmbeddings } from '../core/db/adapter.js';
|
|
9
|
+
import { loadGraphToDb } from '../core/db/graph-loader.js';
|
|
10
|
+
import { stitchRoutes } from '../core/ingestion/route-stitcher.js';
|
|
11
|
+
import { toNodeId } from '../core/db/schema.js';
|
|
9
12
|
// Embedding imports are lazy (dynamic import) so onnxruntime-node is never loaded when
|
|
10
13
|
// embeddings are not requested, avoiding crashes on unsupported Node ABIs (#89)
|
|
11
14
|
// disposeEmbedder intentionally not called — ONNX Runtime segfaults on cleanup (#38)
|
|
12
|
-
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath
|
|
15
|
+
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
|
|
13
16
|
import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
|
|
14
17
|
import { generateAIContextFiles } from './ai-context.js';
|
|
15
18
|
import fs from 'fs/promises';
|
|
@@ -17,7 +20,7 @@ const HEAP_MB = 8192;
|
|
|
17
20
|
const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
|
|
18
21
|
/** Re-exec the process with an 8 GB heap if currently below that */
|
|
19
22
|
function ensureHeap() {
|
|
20
|
-
const nodeOpts = process.env
|
|
23
|
+
const nodeOpts = process.env['NODE_OPTIONS'] || '';
|
|
21
24
|
if (nodeOpts.includes('--max-old-space-size'))
|
|
22
25
|
return false;
|
|
23
26
|
const v8Heap = v8.getHeapStatistics().heap_size_limit;
|
|
@@ -34,7 +37,9 @@ function ensureHeap() {
|
|
|
34
37
|
}
|
|
35
38
|
return true;
|
|
36
39
|
}
|
|
40
|
+
const ANALYZE_PHASES = ['db', 'fts', 'embeddings', 'done'];
|
|
37
41
|
const PHASE_LABELS = {
|
|
42
|
+
idle: 'Initializing...',
|
|
38
43
|
extracting: 'Scanning files',
|
|
39
44
|
structure: 'Building structure',
|
|
40
45
|
parsing: 'Parsing code',
|
|
@@ -43,8 +48,10 @@ const PHASE_LABELS = {
|
|
|
43
48
|
heritage: 'Extracting inheritance',
|
|
44
49
|
communities: 'Detecting communities',
|
|
45
50
|
processes: 'Detecting processes',
|
|
51
|
+
enriching: 'Enriching clusters',
|
|
46
52
|
complete: 'Pipeline complete',
|
|
47
|
-
|
|
53
|
+
error: 'Error',
|
|
54
|
+
db: 'Loading into database',
|
|
48
55
|
fts: 'Creating search indexes',
|
|
49
56
|
embeddings: 'Generating embeddings',
|
|
50
57
|
done: 'Done',
|
|
@@ -53,7 +60,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
53
60
|
if (ensureHeap())
|
|
54
61
|
return;
|
|
55
62
|
if (options?.verbose) {
|
|
56
|
-
process.env
|
|
63
|
+
process.env['CODE_MAPPER_VERBOSE'] = '1';
|
|
57
64
|
}
|
|
58
65
|
console.log('\n Code Mapper Analyzer\n');
|
|
59
66
|
let repoPath;
|
|
@@ -74,20 +81,14 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
74
81
|
process.exitCode = 1;
|
|
75
82
|
return;
|
|
76
83
|
}
|
|
77
|
-
const { storagePath,
|
|
78
|
-
// Clean up stale KuzuDB files from before the LadybugDB migration
|
|
79
|
-
// If kuzu existed but lbug doesn't, we're doing a migration re-index
|
|
80
|
-
const kuzuResult = await cleanupOldKuzuFiles(storagePath);
|
|
81
|
-
if (kuzuResult.found && kuzuResult.needsReindex) {
|
|
82
|
-
console.log(' Migrating from KuzuDB to LadybugDB — rebuilding index...\n');
|
|
83
|
-
}
|
|
84
|
+
const { storagePath, dbPath } = getStoragePaths(repoPath);
|
|
84
85
|
const currentCommit = getCurrentCommit(repoPath);
|
|
85
86
|
const existingMeta = await loadMeta(storagePath);
|
|
86
87
|
if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
|
|
87
88
|
console.log(' Already up to date\n');
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
90
|
-
if (process.env
|
|
91
|
+
if (process.env['CODE_MAPPER_NO_GITIGNORE']) {
|
|
91
92
|
console.log(' CODE_MAPPER_NO_GITIGNORE is set — skipping .gitignore (still reading .code-mapperignore)\n');
|
|
92
93
|
}
|
|
93
94
|
// Single progress bar for the entire pipeline
|
|
@@ -110,7 +111,11 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
110
111
|
aborted = true;
|
|
111
112
|
bar.stop();
|
|
112
113
|
console.log('\n Interrupted — cleaning up...');
|
|
113
|
-
|
|
114
|
+
try {
|
|
115
|
+
closeDb(dbPath);
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
process.exit(130);
|
|
114
119
|
};
|
|
115
120
|
process.on('SIGINT', sigintHandler);
|
|
116
121
|
// Route all console output through bar.log() so the bar doesn't redraw
|
|
@@ -155,58 +160,50 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
155
160
|
if (options?.embeddings && existingMeta && !options?.force) {
|
|
156
161
|
try {
|
|
157
162
|
updateBar(0, 'Caching embeddings...');
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
const oldDb = openDb(dbPath);
|
|
164
|
+
const rows = oldDb.prepare('SELECT nodeId, embedding FROM embeddings').all();
|
|
165
|
+
for (const row of rows) {
|
|
166
|
+
const nodeId = row.nodeId;
|
|
167
|
+
if (!nodeId)
|
|
168
|
+
continue;
|
|
169
|
+
cachedEmbeddingNodeIds.add(nodeId);
|
|
170
|
+
const vec = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
|
|
171
|
+
cachedEmbeddings.push({ nodeId, embedding: Array.from(vec) });
|
|
172
|
+
}
|
|
173
|
+
closeDb(dbPath);
|
|
163
174
|
}
|
|
164
175
|
catch {
|
|
165
176
|
try {
|
|
166
|
-
|
|
177
|
+
closeDb(dbPath);
|
|
167
178
|
}
|
|
168
179
|
catch { }
|
|
169
180
|
}
|
|
170
181
|
}
|
|
171
|
-
// Phase 1: Full Pipeline (0
|
|
182
|
+
// Phase 1: Full Pipeline (0-60%)
|
|
172
183
|
const pipelineResult = await runPipelineFromRepo(repoPath, (progress) => {
|
|
173
184
|
const phaseLabel = PHASE_LABELS[progress.phase] || progress.phase;
|
|
174
185
|
const scaled = Math.round(progress.percent * 0.6);
|
|
175
186
|
updateBar(scaled, phaseLabel);
|
|
176
|
-
});
|
|
177
|
-
// Phase 2:
|
|
178
|
-
updateBar(60, 'Loading into
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
const t0Lbug = Date.now();
|
|
188
|
-
await initLbug(lbugPath);
|
|
189
|
-
let lbugMsgCount = 0;
|
|
190
|
-
const lbugResult = await loadGraphToLbug(pipelineResult.graph, pipelineResult.repoPath, storagePath, (msg) => {
|
|
191
|
-
lbugMsgCount++;
|
|
192
|
-
const progress = Math.min(84, 60 + Math.round((lbugMsgCount / (lbugMsgCount + 10)) * 24));
|
|
187
|
+
}, options?.tsgo === false ? { tsgo: false } : {});
|
|
188
|
+
// Phase 2: SQLite (60-85%)
|
|
189
|
+
updateBar(60, 'Loading into database...');
|
|
190
|
+
// Reset the database (delete and recreate)
|
|
191
|
+
const t0Db = Date.now();
|
|
192
|
+
const db = resetDb(dbPath);
|
|
193
|
+
let dbMsgCount = 0;
|
|
194
|
+
const dbResult = loadGraphToDb(db, pipelineResult.graph, pipelineResult.repoPath, (msg) => {
|
|
195
|
+
dbMsgCount++;
|
|
196
|
+
const progress = Math.min(84, 60 + Math.round((dbMsgCount / (dbMsgCount + 10)) * 24));
|
|
193
197
|
updateBar(progress, msg);
|
|
194
198
|
});
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
// Phase
|
|
198
|
-
|
|
199
|
+
const dbTime = ((Date.now() - t0Db) / 1000).toFixed(1);
|
|
200
|
+
const dbWarnings = dbResult.warnings;
|
|
201
|
+
// Phase 2.5: HTTP route stitching (post-DB-load, needs content field)
|
|
202
|
+
stitchRoutes(db);
|
|
203
|
+
// Phase 3: FTS (85-90%)
|
|
204
|
+
// FTS5 is auto-created by schema triggers — no manual index creation needed
|
|
205
|
+
updateBar(85, 'Search indexes ready');
|
|
199
206
|
const t0Fts = Date.now();
|
|
200
|
-
try {
|
|
201
|
-
await createFTSIndex('File', 'file_fts', ['name', 'content']);
|
|
202
|
-
await createFTSIndex('Function', 'function_fts', ['name', 'content']);
|
|
203
|
-
await createFTSIndex('Class', 'class_fts', ['name', 'content']);
|
|
204
|
-
await createFTSIndex('Method', 'method_fts', ['name', 'content']);
|
|
205
|
-
await createFTSIndex('Interface', 'interface_fts', ['name', 'content']);
|
|
206
|
-
}
|
|
207
|
-
catch (e) {
|
|
208
|
-
// Non-fatal — FTS is best-effort; silently continue
|
|
209
|
-
}
|
|
210
207
|
const ftsTime = ((Date.now() - t0Fts) / 1000).toFixed(1);
|
|
211
208
|
// Phase 3.5: Re-insert cached embeddings
|
|
212
209
|
if (cachedEmbeddings.length > 0) {
|
|
@@ -214,15 +211,17 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
214
211
|
const EMBED_BATCH = 200;
|
|
215
212
|
for (let i = 0; i < cachedEmbeddings.length; i += EMBED_BATCH) {
|
|
216
213
|
const batch = cachedEmbeddings.slice(i, i + EMBED_BATCH);
|
|
217
|
-
const paramsList = batch.map(e => ({ nodeId: e.nodeId, embedding: e.embedding }));
|
|
218
214
|
try {
|
|
219
|
-
|
|
215
|
+
insertEmbeddingsBatch(db, batch.map(e => ({
|
|
216
|
+
nodeId: toNodeId(e.nodeId),
|
|
217
|
+
embedding: e.embedding,
|
|
218
|
+
})));
|
|
220
219
|
}
|
|
221
220
|
catch { /* some may fail if node was removed, that's fine */ }
|
|
222
221
|
}
|
|
223
222
|
}
|
|
224
|
-
// Phase 4: Embeddings (90
|
|
225
|
-
const stats =
|
|
223
|
+
// Phase 4: Embeddings (90-98%)
|
|
224
|
+
const stats = getStats(db);
|
|
226
225
|
let embeddingTime = '0.0';
|
|
227
226
|
let embeddingSkipped = true;
|
|
228
227
|
let embeddingSkipReason = 'off (use --no-embeddings to skip)';
|
|
@@ -233,22 +232,19 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
233
232
|
updateBar(90, 'Loading embedding model...');
|
|
234
233
|
const t0Emb = Date.now();
|
|
235
234
|
const { runEmbeddingPipeline } = await import('../core/embeddings/embedding-pipeline.js');
|
|
236
|
-
await runEmbeddingPipeline(
|
|
235
|
+
await runEmbeddingPipeline(db, (progress) => {
|
|
237
236
|
const scaled = 90 + Math.round((progress.percent / 100) * 8);
|
|
238
237
|
const label = progress.phase === 'loading-model' ? 'Loading embedding model...' : `Embedding ${progress.nodesProcessed || 0}/${progress.totalNodes || '?'}`;
|
|
239
238
|
updateBar(scaled, label);
|
|
240
239
|
}, {}, cachedEmbeddingNodeIds.size > 0 ? cachedEmbeddingNodeIds : undefined);
|
|
241
240
|
embeddingTime = ((Date.now() - t0Emb) / 1000).toFixed(1);
|
|
242
241
|
}
|
|
243
|
-
// Phase 5: Finalize (98
|
|
242
|
+
// Phase 5: Finalize (98-100%)
|
|
244
243
|
updateBar(98, 'Saving metadata...');
|
|
245
244
|
// Count embeddings in the index (cached + newly generated) for metadata
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
embeddingCount = embResult?.[0]?.cnt ?? 0;
|
|
250
|
-
}
|
|
251
|
-
catch { /* table may not exist if embeddings never ran */ }
|
|
245
|
+
const embeddingCount = countEmbeddings(db);
|
|
246
|
+
const communityCount = pipelineResult.communityResult?.stats.totalCommunities;
|
|
247
|
+
const processCount = pipelineResult.processResult?.stats.totalProcesses;
|
|
252
248
|
const meta = {
|
|
253
249
|
repoPath,
|
|
254
250
|
lastCommit: currentCommit,
|
|
@@ -257,8 +253,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
257
253
|
files: pipelineResult.totalFileCount,
|
|
258
254
|
nodes: stats.nodes,
|
|
259
255
|
edges: stats.edges,
|
|
260
|
-
communities:
|
|
261
|
-
processes:
|
|
256
|
+
...(communityCount !== undefined ? { communities: communityCount } : {}),
|
|
257
|
+
...(processCount !== undefined ? { processes: processCount } : {}),
|
|
262
258
|
embeddings: embeddingCount,
|
|
263
259
|
},
|
|
264
260
|
};
|
|
@@ -279,13 +275,11 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
279
275
|
files: pipelineResult.totalFileCount,
|
|
280
276
|
nodes: stats.nodes,
|
|
281
277
|
edges: stats.edges,
|
|
282
|
-
communities:
|
|
278
|
+
...(communityCount !== undefined ? { communities: communityCount } : {}),
|
|
283
279
|
clusters: aggregatedClusterCount,
|
|
284
|
-
processes:
|
|
280
|
+
...(processCount !== undefined ? { processes: processCount } : {}),
|
|
285
281
|
});
|
|
286
|
-
|
|
287
|
-
// NOTE: disposeEmbedder() is intentionally skipped — ONNX Runtime's native cleanup
|
|
288
|
-
// segfaults on macOS and some Linux configs; the process exits immediately after
|
|
282
|
+
closeDb(dbPath);
|
|
289
283
|
const totalTime = ((Date.now() - t0Global) / 1000).toFixed(1);
|
|
290
284
|
clearInterval(elapsedTimer);
|
|
291
285
|
process.removeListener('SIGINT', sigintHandler);
|
|
@@ -298,18 +292,15 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
298
292
|
const embeddingsCached = cachedEmbeddings.length > 0;
|
|
299
293
|
console.log(`\n Repository indexed successfully (${totalTime}s)${embeddingsCached ? ` [${cachedEmbeddings.length} embeddings cached]` : ''}\n`);
|
|
300
294
|
console.log(` ${stats.nodes.toLocaleString()} nodes | ${stats.edges.toLocaleString()} edges | ${pipelineResult.communityResult?.stats.totalCommunities || 0} clusters | ${pipelineResult.processResult?.stats.totalProcesses || 0} flows`);
|
|
301
|
-
console.log(`
|
|
295
|
+
console.log(` SQLite ${dbTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`);
|
|
296
|
+
console.log(` tsgo: ${pipelineResult.tsgoEnabled ? 'enabled (compiler-verified call resolution)' : 'disabled — install @typescript/native-preview for higher accuracy'}`);
|
|
302
297
|
console.log(` ${repoPath}`);
|
|
303
298
|
if (aiContext.files.length > 0) {
|
|
304
299
|
console.log(` Context: ${aiContext.files.join(', ')}`);
|
|
305
300
|
}
|
|
306
|
-
// Show a quiet summary if some
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
const m = w.match(/\((\d+) edges\)/);
|
|
310
|
-
return sum + (m ? parseInt(m[1]) : 0);
|
|
311
|
-
}, 0);
|
|
312
|
-
console.log(` Note: ${totalFallback} edges across ${lbugWarnings.length} types inserted via fallback (schema will be updated in next release)`);
|
|
301
|
+
// Show a quiet summary if some node types had warnings during load
|
|
302
|
+
if (dbWarnings.length > 0) {
|
|
303
|
+
console.log(` Note: ${dbWarnings.length} warnings during graph load`);
|
|
313
304
|
}
|
|
314
305
|
try {
|
|
315
306
|
await fs.access(getGlobalRegistryPath());
|
|
@@ -318,8 +309,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
318
309
|
console.log('\n Tip: Run `code-mapper setup` to configure MCP for your editor.');
|
|
319
310
|
}
|
|
320
311
|
console.log('');
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
312
|
+
// better-sqlite3 is synchronous and doesn't hold native handles that prevent exit.
|
|
313
|
+
// However, the MLX embedder (Python subprocess) or ONNX Runtime may still hold
|
|
314
|
+
// native handles, so force-exit to ensure clean termination.
|
|
324
315
|
process.exit(0);
|
|
325
316
|
};
|
package/dist/cli/augment.js
CHANGED
|
@@ -14,8 +14,6 @@ export async function augmentCommand(pattern) {
|
|
|
14
14
|
const result = await augment(pattern, process.cwd());
|
|
15
15
|
if (result) {
|
|
16
16
|
// IMPORTANT: Write to stderr, NOT stdout
|
|
17
|
-
// LadybugDB's native module captures stdout fd at OS level during init,
|
|
18
|
-
// making stdout permanently broken in subprocess contexts
|
|
19
17
|
// The hook reads from the subprocess's stderr
|
|
20
18
|
process.stderr.write(result + '\n');
|
|
21
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file eval-server.ts
|
|
3
|
-
* @description Lightweight HTTP server for SWE-bench evaluation. Keeps
|
|
3
|
+
* @description Lightweight HTTP server for SWE-bench evaluation. Keeps the database warm in
|
|
4
4
|
* memory so tool calls from the agent are near-instant. Returns LLM-friendly text (not raw
|
|
5
5
|
* JSON) and appends next-step hints for productive tool chaining (query -> context -> impact)
|
|
6
6
|
*
|
|
@@ -18,7 +18,7 @@ export interface EvalServerOptions {
|
|
|
18
18
|
export declare function formatQueryResult(result: any): string;
|
|
19
19
|
export declare function formatContextResult(result: any): string;
|
|
20
20
|
export declare function formatImpactResult(result: any): string;
|
|
21
|
-
export declare function
|
|
21
|
+
export declare function formatSqlResult(result: any): string;
|
|
22
22
|
export declare function formatDetectChangesResult(result: any): string;
|
|
23
23
|
export declare function formatListReposResult(result: any): string;
|
|
24
24
|
export declare function evalServerCommand(options?: EvalServerOptions): Promise<void>;
|
package/dist/cli/eval-server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// code-mapper/src/cli/eval-server.ts
|
|
2
2
|
/**
|
|
3
3
|
* @file eval-server.ts
|
|
4
|
-
* @description Lightweight HTTP server for SWE-bench evaluation. Keeps
|
|
4
|
+
* @description Lightweight HTTP server for SWE-bench evaluation. Keeps the database warm in
|
|
5
5
|
* memory so tool calls from the agent are near-instant. Returns LLM-friendly text (not raw
|
|
6
6
|
* JSON) and appends next-step hints for productive tool chaining (query -> context -> impact)
|
|
7
7
|
*
|
|
@@ -141,7 +141,7 @@ export function formatImpactResult(result) {
|
|
|
141
141
|
}
|
|
142
142
|
return lines.join('\n').trim();
|
|
143
143
|
}
|
|
144
|
-
export function
|
|
144
|
+
export function formatSqlResult(result) {
|
|
145
145
|
if (result.error)
|
|
146
146
|
return `Error: ${result.error}`;
|
|
147
147
|
if (Array.isArray(result)) {
|
|
@@ -211,7 +211,7 @@ function formatToolResult(toolName, result) {
|
|
|
211
211
|
case 'query': return formatQueryResult(result);
|
|
212
212
|
case 'context': return formatContextResult(result);
|
|
213
213
|
case 'impact': return formatImpactResult(result);
|
|
214
|
-
case '
|
|
214
|
+
case 'sql': return formatSqlResult(result);
|
|
215
215
|
case 'detect_changes': return formatDetectChangesResult(result);
|
|
216
216
|
case 'list_repos': return formatListReposResult(result);
|
|
217
217
|
default: return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
@@ -227,7 +227,7 @@ function getNextStepHint(toolName) {
|
|
|
227
227
|
return '\n---\nNext: To check what breaks if you change this, run code-mapper-impact "<name>" upstream';
|
|
228
228
|
case 'impact':
|
|
229
229
|
return '\n---\nNext: Review d=1 items first (WILL BREAK). Read the source with cat to understand the code, then make your fix.';
|
|
230
|
-
case '
|
|
230
|
+
case 'sql':
|
|
231
231
|
return '\n---\nNext: To explore a result symbol in depth, run code-mapper-context "<name>"';
|
|
232
232
|
case 'detect_changes':
|
|
233
233
|
return '\n---\nNext: Run code-mapper-context "<symbol>" on high-risk changed symbols to check their callers.';
|
|
@@ -284,7 +284,7 @@ export async function evalServerCommand(options) {
|
|
|
284
284
|
// POST /tool/:name
|
|
285
285
|
const toolMatch = req.url?.match(/^\/tool\/(\w+)$/);
|
|
286
286
|
if (req.method === 'POST' && toolMatch) {
|
|
287
|
-
const toolName = toolMatch[1];
|
|
287
|
+
const toolName = toolMatch[1] ?? '';
|
|
288
288
|
const body = await readBody(req);
|
|
289
289
|
let args = {};
|
|
290
290
|
if (body.trim()) {
|
|
@@ -323,7 +323,7 @@ export async function evalServerCommand(options) {
|
|
|
323
323
|
console.error(` POST /tool/query — search execution flows`);
|
|
324
324
|
console.error(` POST /tool/context — 360-degree symbol view`);
|
|
325
325
|
console.error(` POST /tool/impact — blast radius analysis`);
|
|
326
|
-
console.error(` POST /tool/
|
|
326
|
+
console.error(` POST /tool/sql — raw SQL query`);
|
|
327
327
|
console.error(` GET /health — health check`);
|
|
328
328
|
console.error(` POST /shutdown — graceful shutdown`);
|
|
329
329
|
if (idleTimeoutSec > 0) {
|
package/dist/cli/index.js
CHANGED
|
@@ -24,19 +24,13 @@ program
|
|
|
24
24
|
.option('-f, --force', 'Force full re-index even if up to date')
|
|
25
25
|
.option('--embeddings', 'Enable embedding generation for semantic search (on by default)', true)
|
|
26
26
|
.option('--no-embeddings', 'Skip embedding generation')
|
|
27
|
+
.option('--no-tsgo', 'Skip tsgo LSP for call resolution (faster, less accurate)')
|
|
27
28
|
.option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
|
|
28
29
|
.addHelpText('after', '\nEnvironment variables:\n CODE_MAPPER_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .code-mapperignore)')
|
|
29
30
|
.action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
|
|
30
|
-
program
|
|
31
|
-
.command('serve')
|
|
32
|
-
.description('Start local HTTP server for web UI connection')
|
|
33
|
-
.option('-p, --port <port>', 'Port number', '4747')
|
|
34
|
-
.option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
|
|
35
|
-
.action(createLazyAction(() => import('./serve.js'), 'serveCommand'));
|
|
36
31
|
program
|
|
37
32
|
.command('mcp')
|
|
38
33
|
.description('Start MCP server (stdio) — serves all indexed repos')
|
|
39
|
-
.option('--tsgo', 'Enable tsgo semantic resolution for confidence-1.0 call edges')
|
|
40
34
|
.action(createLazyAction(() => import('./mcp.js'), 'mcpCommand'));
|
|
41
35
|
program
|
|
42
36
|
.command('list')
|
|
@@ -78,6 +72,7 @@ program
|
|
|
78
72
|
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
|
79
73
|
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
80
74
|
.option('--content', 'Include full symbol source code')
|
|
75
|
+
.option('--no-tsgo', 'Disable tsgo LSP enrichment (faster, less accurate)')
|
|
81
76
|
.action(createLazyAction(() => import('./tool.js'), 'contextCommand'));
|
|
82
77
|
program
|
|
83
78
|
.command('impact <target>')
|
|
@@ -86,12 +81,13 @@ program
|
|
|
86
81
|
.option('-r, --repo <name>', 'Target repository')
|
|
87
82
|
.option('--depth <n>', 'Max relationship depth (default: 3)')
|
|
88
83
|
.option('--include-tests', 'Include test files in results')
|
|
84
|
+
.option('--no-tsgo', 'Disable tsgo LSP enrichment (faster, less accurate)')
|
|
89
85
|
.action(createLazyAction(() => import('./tool.js'), 'impactCommand'));
|
|
90
86
|
program
|
|
91
|
-
.command('
|
|
92
|
-
.description('Execute raw
|
|
87
|
+
.command('sql <query>')
|
|
88
|
+
.description('Execute raw SQL query against the knowledge graph')
|
|
93
89
|
.option('-r, --repo <name>', 'Target repository')
|
|
94
|
-
.action(createLazyAction(() => import('./tool.js'), '
|
|
90
|
+
.action(createLazyAction(() => import('./tool.js'), 'sqlCommand'));
|
|
95
91
|
// Eval server — persistent daemon for SWE-bench evaluation
|
|
96
92
|
program
|
|
97
93
|
.command('eval-server')
|
package/dist/cli/mcp.d.ts
CHANGED
|
@@ -3,6 +3,4 @@
|
|
|
3
3
|
* @description Starts the MCP server in standalone mode. Loads all indexed repos from the
|
|
4
4
|
* global registry — works from any directory
|
|
5
5
|
*/
|
|
6
|
-
export declare const mcpCommand: (
|
|
7
|
-
tsgo?: boolean;
|
|
8
|
-
}) => Promise<void>;
|
|
6
|
+
export declare const mcpCommand: () => Promise<void>;
|
package/dist/cli/mcp.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { startMCPServer } from '../mcp/server.js';
|
|
8
8
|
import { LocalBackend } from '../mcp/local/local-backend.js';
|
|
9
|
-
export const mcpCommand = async (
|
|
9
|
+
export const mcpCommand = async () => {
|
|
10
10
|
// Prevent unhandled errors from crashing the MCP server process
|
|
11
|
-
//
|
|
11
|
+
// Transient errors should degrade gracefully
|
|
12
12
|
process.on('uncaughtException', (err) => {
|
|
13
13
|
console.error(`Code Mapper MCP: uncaught exception — ${err.message}`);
|
|
14
14
|
// Process is in undefined state after uncaughtException — exit after flushing
|
|
@@ -22,7 +22,7 @@ export const mcpCommand = async (opts) => {
|
|
|
22
22
|
// Starts even with 0 repos — tools call refreshRepos() lazily,
|
|
23
23
|
// so repos indexed after server start are discovered automatically
|
|
24
24
|
const backend = new LocalBackend();
|
|
25
|
-
await backend.init(
|
|
25
|
+
await backend.init();
|
|
26
26
|
const repos = await backend.listRepos();
|
|
27
27
|
if (repos.length === 0) {
|
|
28
28
|
console.error('Code Mapper: No indexed repos yet. Run `code-mapper analyze` in a git repo — the server will pick it up automatically.');
|
package/dist/cli/refresh.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file refresh.ts
|
|
3
3
|
* @description CLI command for incremental refresh — re-indexes only files
|
|
4
|
-
* changed since the last commit.
|
|
5
|
-
*
|
|
4
|
+
* changed since the last commit. Calls refreshFiles directly for in-process
|
|
5
|
+
* tree-sitter parsing and SQLite updates.
|
|
6
6
|
*
|
|
7
7
|
* Usage: code-mapper refresh [--scope unstaged|staged|committed]
|
|
8
8
|
*/
|
package/dist/cli/refresh.js
CHANGED
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @file refresh.ts
|
|
4
4
|
* @description CLI command for incremental refresh — re-indexes only files
|
|
5
|
-
* changed since the last commit.
|
|
6
|
-
*
|
|
5
|
+
* changed since the last commit. Calls refreshFiles directly for in-process
|
|
6
|
+
* tree-sitter parsing and SQLite updates.
|
|
7
7
|
*
|
|
8
8
|
* Usage: code-mapper refresh [--scope unstaged|staged|committed]
|
|
9
9
|
*/
|
|
10
|
-
import path from 'path';
|
|
11
10
|
import { execFileSync } from 'child_process';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { refreshFiles, refreshEmbeddings } from '../core/incremental/refresh.js';
|
|
12
|
+
import { toRelativeFilePath, } from '../core/incremental/types.js';
|
|
14
13
|
import { getStoragePaths, loadMeta, saveMeta, } from '../storage/repo-manager.js';
|
|
15
14
|
import { getCurrentCommit, getGitRoot, isGitRepo } from '../storage/git.js';
|
|
16
|
-
import {
|
|
15
|
+
import { openDb, closeDb, getStats } from '../core/db/adapter.js';
|
|
17
16
|
export const refreshCommand = async (options) => {
|
|
18
17
|
const cwd = process.cwd();
|
|
19
18
|
// Find repo root
|
|
@@ -26,13 +25,12 @@ export const refreshCommand = async (options) => {
|
|
|
26
25
|
console.error('Could not determine git root.');
|
|
27
26
|
process.exit(1);
|
|
28
27
|
}
|
|
29
|
-
const { storagePath
|
|
28
|
+
const { storagePath } = getStoragePaths(repoRoot);
|
|
30
29
|
const meta = await loadMeta(storagePath);
|
|
31
30
|
if (!meta) {
|
|
32
31
|
console.error('No Code Mapper index found. Run: code-mapper analyze');
|
|
33
32
|
process.exit(1);
|
|
34
33
|
}
|
|
35
|
-
const repoName = path.basename(repoRoot);
|
|
36
34
|
const scope = options?.scope || 'committed';
|
|
37
35
|
// Get changed files based on scope
|
|
38
36
|
let diffArgs;
|
|
@@ -104,37 +102,33 @@ export const refreshCommand = async (options) => {
|
|
|
104
102
|
return;
|
|
105
103
|
}
|
|
106
104
|
console.log(`Refreshing ${entries.length} file(s) (${description})...`);
|
|
107
|
-
//
|
|
108
|
-
const
|
|
109
|
-
let
|
|
105
|
+
// Open SQLite database
|
|
106
|
+
const { dbPath } = getStoragePaths(repoRoot);
|
|
107
|
+
let db;
|
|
110
108
|
try {
|
|
111
|
-
|
|
112
|
-
const rows = await executeQuery('MATCH (f:File) RETURN f.filePath AS filePath');
|
|
113
|
-
existingPaths = rows.map((r) => String(r.filePath ?? ''));
|
|
114
|
-
await closeLbug();
|
|
109
|
+
db = openDb(dbPath);
|
|
115
110
|
}
|
|
116
111
|
catch (err) {
|
|
117
|
-
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
console.error(`Failed to open index: ${message}`);
|
|
118
114
|
process.exit(1);
|
|
119
115
|
}
|
|
120
|
-
// Run incremental refresh via
|
|
121
|
-
const coordinator = new RefreshCoordinator();
|
|
116
|
+
// Run incremental refresh directly via refreshFiles
|
|
122
117
|
const t0 = Date.now();
|
|
123
118
|
try {
|
|
124
|
-
const result = await
|
|
125
|
-
id: toRepoId(repoId),
|
|
126
|
-
repoRoot: toRepoRoot(repoRoot),
|
|
127
|
-
dbPath: toDbPath(lbugPath),
|
|
128
|
-
storagePath,
|
|
129
|
-
}, entries, existingPaths, 60_000);
|
|
119
|
+
const result = await refreshFiles(db, repoRoot, entries);
|
|
130
120
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
131
121
|
console.log(`Refreshed ${result.filesProcessed} file(s) in ${elapsed}s`);
|
|
122
|
+
if (!result.tsgoEnabled) {
|
|
123
|
+
console.log('Note: tsgo unavailable — call resolution used heuristic matching. Install @typescript/native-preview for higher accuracy.');
|
|
124
|
+
}
|
|
125
|
+
// Refresh embeddings for dirty files (same quality as full analyze — graph context enriched)
|
|
126
|
+
await refreshEmbeddings(db, entries);
|
|
132
127
|
// Update metadata with current commit
|
|
133
128
|
if (scope === 'committed' || scope === 'staged') {
|
|
134
129
|
try {
|
|
135
|
-
await initLbug(lbugPath);
|
|
136
130
|
const currentCommitHash = getCurrentCommit(repoRoot);
|
|
137
|
-
const stats =
|
|
131
|
+
const stats = getStats(db);
|
|
138
132
|
await saveMeta(storagePath, {
|
|
139
133
|
repoPath: repoRoot,
|
|
140
134
|
lastCommit: currentCommitHash,
|
|
@@ -145,7 +139,7 @@ export const refreshCommand = async (options) => {
|
|
|
145
139
|
edges: stats.edges ?? meta.stats?.edges ?? 0,
|
|
146
140
|
communities: meta.stats?.communities ?? 0,
|
|
147
141
|
processes: meta.stats?.processes ?? 0,
|
|
148
|
-
embeddings: meta.stats?.embeddings ?? 0,
|
|
142
|
+
embeddings: stats.embeddings ?? meta.stats?.embeddings ?? 0,
|
|
149
143
|
},
|
|
150
144
|
});
|
|
151
145
|
}
|
|
@@ -153,12 +147,13 @@ export const refreshCommand = async (options) => {
|
|
|
153
147
|
}
|
|
154
148
|
}
|
|
155
149
|
catch (err) {
|
|
156
|
-
|
|
150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
151
|
+
console.error(`Refresh failed: ${message}`);
|
|
157
152
|
process.exit(1);
|
|
158
153
|
}
|
|
159
154
|
finally {
|
|
160
155
|
try {
|
|
161
|
-
|
|
156
|
+
closeDb(dbPath);
|
|
162
157
|
}
|
|
163
158
|
catch { }
|
|
164
159
|
}
|
package/dist/cli/status.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// code-mapper/src/cli/status.ts
|
|
2
2
|
/** @file status.ts @description Shows the indexing status of the current repository */
|
|
3
|
-
import { findRepo
|
|
4
|
-
import { getCurrentCommit, isGitRepo
|
|
3
|
+
import { findRepo } from '../storage/repo-manager.js';
|
|
4
|
+
import { getCurrentCommit, isGitRepo } from '../storage/git.js';
|
|
5
5
|
export const statusCommand = async () => {
|
|
6
6
|
const cwd = process.cwd();
|
|
7
7
|
if (!isGitRepo(cwd)) {
|
|
@@ -10,17 +10,8 @@ export const statusCommand = async () => {
|
|
|
10
10
|
}
|
|
11
11
|
const repo = await findRepo(cwd);
|
|
12
12
|
if (!repo) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const { storagePath } = getStoragePaths(repoRoot);
|
|
16
|
-
if (await hasKuzuIndex(storagePath)) {
|
|
17
|
-
console.log('Repository has a stale KuzuDB index from a previous version.');
|
|
18
|
-
console.log('Run: code-mapper analyze (rebuilds the index with LadybugDB)');
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
console.log('Repository not indexed.');
|
|
22
|
-
console.log('Run: code-mapper analyze');
|
|
23
|
-
}
|
|
13
|
+
console.log('Repository not indexed.');
|
|
14
|
+
console.log('Run: code-mapper analyze');
|
|
24
15
|
return;
|
|
25
16
|
}
|
|
26
17
|
const currentCommit = getCurrentCommit(repo.repoPath);
|