@zuvia-software-solutions/code-mapper 2.5.2 → 2.6.1

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.
@@ -2,7 +2,6 @@
2
2
  export interface AnalyzeOptions {
3
3
  force?: boolean;
4
4
  embeddings?: boolean;
5
- tsgo?: boolean;
6
5
  verbose?: boolean;
7
6
  }
8
7
  export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -248,7 +248,7 @@ export const analyzeCommand = async (inputPath, options) => {
248
248
  }
249
249
  const scaled = Math.round(progress.percent * 0.6);
250
250
  updateBar(scaled, phaseLabel, baseLabel);
251
- }, options?.tsgo === false ? { tsgo: false } : {});
251
+ });
252
252
  // Phase 2: SQLite (60-85%)
253
253
  recordPhase('sqlite');
254
254
  updateBar(60, 'Loading into database...');
@@ -431,7 +431,6 @@ export const analyzeCommand = async (inputPath, options) => {
431
431
  const wallMs = Date.now() - t0Global || 1;
432
432
  const cpuPct = Math.round(((cpuEnd.user + cpuEnd.system) / 1e3) / wallMs * 100);
433
433
  console.log(` Memory: peak ${peakRssMB}MB RSS | CPU: ${cpuPct}% (${cpuCount} cores)`);
434
- console.log(` tsgo: ${pipelineResult.tsgoEnabled ? 'enabled (compiler-verified call resolution)' : 'disabled — install @typescript/native-preview for higher accuracy'}`);
435
434
  console.log(` ${repoPath}`);
436
435
  if (aiContext.files.length > 0) {
437
436
  console.log(` Context: ${aiContext.files.join(', ')}`);
package/dist/cli/index.js CHANGED
@@ -24,7 +24,6 @@ program
24
24
  .option('-f, --force', 'Force full re-index even if up to date')
25
25
  .option('--embeddings', 'Generate semantic embeddings (bge-small, CPU, fast)')
26
26
  .option('--no-embeddings', 'Skip embedding generation')
27
- .option('--no-tsgo', 'Skip tsgo LSP for call resolution (faster, less accurate)')
28
27
  .option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
29
28
  .addHelpText('after', '\nEnvironment variables:\n CODE_MAPPER_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .code-mapperignore)')
30
29
  .action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
@@ -72,7 +71,6 @@ program
72
71
  .option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
73
72
  .option('-f, --file <path>', 'File path to disambiguate common names')
74
73
  .option('--content', 'Include full symbol source code')
75
- .option('--no-tsgo', 'Disable tsgo LSP enrichment (faster, less accurate)')
76
74
  .action(createLazyAction(() => import('./tool.js'), 'contextCommand'));
77
75
  program
78
76
  .command('impact <target>')
@@ -81,7 +79,6 @@ program
81
79
  .option('-r, --repo <name>', 'Target repository')
82
80
  .option('--depth <n>', 'Max relationship depth (default: 3)')
83
81
  .option('--include-tests', 'Include test files in results')
84
- .option('--no-tsgo', 'Disable tsgo LSP enrichment (faster, less accurate)')
85
82
  .action(createLazyAction(() => import('./tool.js'), 'impactCommand'));
86
83
  program
87
84
  .command('sql <query>')
@@ -119,9 +119,6 @@ export const refreshCommand = async (options) => {
119
119
  const result = await refreshFiles(db, repoRoot, entries);
120
120
  const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
121
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
122
  // Refresh embeddings for dirty files (same quality as full analyze — graph context enriched)
126
123
  await refreshEmbeddings(db, entries);
127
124
  // Update metadata with current commit
@@ -15,14 +15,12 @@ export declare function contextCommand(name: string, options?: {
15
15
  file?: string;
16
16
  uid?: string;
17
17
  content?: boolean;
18
- tsgo?: boolean;
19
18
  }): Promise<void>;
20
19
  export declare function impactCommand(target: string, options?: {
21
20
  direction?: string;
22
21
  repo?: string;
23
22
  depth?: string;
24
23
  includeTests?: boolean;
25
- tsgo?: boolean;
26
24
  }): Promise<void>;
27
25
  export declare function sqlCommand(query: string, options?: {
28
26
  repo?: string;
package/dist/cli/tool.js CHANGED
@@ -50,7 +50,6 @@ export async function contextCommand(name, options) {
50
50
  uid: options?.uid,
51
51
  file_path: options?.file,
52
52
  include_content: options?.content ?? false,
53
- tsgo: options?.tsgo,
54
53
  repo: options?.repo,
55
54
  });
56
55
  output(result);
@@ -66,7 +65,6 @@ export async function impactCommand(target, options) {
66
65
  direction: options?.direction || 'upstream',
67
66
  maxDepth: options?.depth ? parseInt(options.depth) : undefined,
68
67
  includeTests: options?.includeTests ?? false,
69
- tsgo: options?.tsgo,
70
68
  repo: options?.repo,
71
69
  });
72
70
  output(result);
@@ -3,6 +3,6 @@
3
3
  * Spawned by buildNlEmbeddings — loads bge-small independently,
4
4
  * embeds texts received via IPC, sends vectors back.
5
5
  *
6
- * Same architecture as parallel tsgo: N processes, each with own model.
6
+ * Spawned by buildNlEmbeddings loads bge-small independently,
7
7
  */
8
8
  export {};
@@ -4,7 +4,7 @@
4
4
  * Spawned by buildNlEmbeddings — loads bge-small independently,
5
5
  * embeds texts received via IPC, sends vectors back.
6
6
  *
7
- * Same architecture as parallel tsgo: N processes, each with own model.
7
+ * Spawned by buildNlEmbeddings loads bge-small independently,
8
8
  */
9
9
  import { pipeline } from '@huggingface/transformers';
10
10
  const MODEL_ID = 'Xenova/bge-small-en-v1.5';
@@ -258,7 +258,7 @@ export async function buildNlEmbeddings(db, onProgress) {
258
258
  db.prepare('DELETE FROM nl_embeddings').run();
259
259
  }
260
260
  catch { /* table may not exist */ }
261
- // Parallel multi-process embedding — same architecture as tsgo
261
+ // Parallel multi-process embedding — N workers, each with own model
262
262
  // Each worker loads its own bge-small model, embeds independently.
263
263
  const os = await import('os');
264
264
  const { fork } = await import('child_process');
@@ -9,8 +9,7 @@
9
9
  * 1. Delete old nodes for dirty files
10
10
  * 2. Parse modified/created files with tree-sitter
11
11
  * 3. Insert File nodes + symbol nodes + DEFINES edges
12
- * 4. Resolve call edges using tsgo LSP (TS/JS files only)
13
- * 5. Repair cross-file edges (callers from unchanged files → changed symbols)
12
+ * 4. Resolve call edges (heuristic name-based matching)
14
13
  */
15
14
  import type Database from 'better-sqlite3';
16
15
  import { type DirtyFileEntry, type RefreshResult } from './types.js';
@@ -20,8 +19,7 @@ import { type DirtyFileEntry, type RefreshResult } from './types.js';
20
19
  * Phase 1: Delete old nodes for all dirty files
21
20
  * Phase 2: Parse modified/created files with tree-sitter
22
21
  * Phase 3: Insert File nodes + symbol nodes + DEFINES edges
23
- * Phase 4: Resolve call edges using tsgo LSP (TS/JS only, optional)
24
- * Phase 5: Repair cross-file edges from unchanged files (tsgo, optional)
22
+ * Phase 4: Resolve call edges (heuristic name-based matching)
25
23
  *
26
24
  * @param db - Open better-sqlite3 database instance
27
25
  * @param repoPath - Absolute path to the repository root
@@ -10,8 +10,7 @@
10
10
  * 1. Delete old nodes for dirty files
11
11
  * 2. Parse modified/created files with tree-sitter
12
12
  * 3. Insert File nodes + symbol nodes + DEFINES edges
13
- * 4. Resolve call edges using tsgo LSP (TS/JS files only)
14
- * 5. Repair cross-file edges (callers from unchanged files → changed symbols)
13
+ * 4. Resolve call edges (heuristic name-based matching)
15
14
  */
16
15
  import path from 'path';
17
16
  import fsSync from 'fs';
@@ -24,17 +23,10 @@ import { generateId } from '../../lib/utils.js';
24
23
  import { deleteNodesByFile, insertNode, insertEdge, findNodeAtLine, findNodesByFile, deleteEmbeddingsByFile, insertEmbeddingsBatch, countEmbeddings, deleteRefsByFile, insertRefsBatch, deleteFileWordsByFile, upsertFileWords } from '../db/adapter.js';
25
24
  import { assertNodeLabel, toNodeId, toEdgeId } from '../db/schema.js';
26
25
  import {} from './types.js';
27
- import { getTsgoService } from '../semantic/tsgo-service.js';
28
26
  import { EMBEDDABLE_LABELS } from '../embeddings/types.js';
29
27
  // ---------------------------------------------------------------------------
30
28
  // Helpers
31
29
  // ---------------------------------------------------------------------------
32
- /** File extensions that tsgo can resolve */
33
- const TS_JS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs']);
34
- function isTypeScriptOrJavaScript(filePath) {
35
- const ext = path.extname(filePath).toLowerCase();
36
- return TS_JS_EXTENSIONS.has(ext);
37
- }
38
30
  /**
39
31
  * Find the innermost symbol node enclosing a given 0-based line in a file.
40
32
  * Excludes structural nodes (File, Folder, Community, Process).
@@ -42,31 +34,13 @@ function isTypeScriptOrJavaScript(filePath) {
42
34
  function findEnclosingNode(db, filePath, line) {
43
35
  return findNodeAtLine(db, filePath, line, 'File');
44
36
  }
45
- /**
46
- * Find a node whose definition starts at a given 0-based line in a file.
47
- * Falls back to the enclosing node if no exact match.
48
- */
49
- function findNodeByFileAndLine(db, filePath, line) {
50
- // Try exact startLine match first (exclude structural labels)
51
- const exact = db.prepare(`SELECT * FROM nodes
52
- WHERE filePath = ? AND startLine = ?
53
- AND label NOT IN ('File', 'Folder', 'Community', 'Process')
54
- LIMIT 1`
55
- // Safe: schema and NodeRow defined together in schema.ts
56
- ).get(filePath, line);
57
- if (exact)
58
- return exact;
59
- // Fall back to enclosing node
60
- return findEnclosingNode(db, filePath, line);
61
- }
62
37
  /**
63
38
  * Parse dirty files with tree-sitter and write nodes/edges to the database.
64
39
  *
65
40
  * Phase 1: Delete old nodes for all dirty files
66
41
  * Phase 2: Parse modified/created files with tree-sitter
67
42
  * Phase 3: Insert File nodes + symbol nodes + DEFINES edges
68
- * Phase 4: Resolve call edges using tsgo LSP (TS/JS only, optional)
69
- * Phase 5: Repair cross-file edges from unchanged files (tsgo, optional)
43
+ * Phase 4: Resolve call edges (heuristic name-based matching)
70
44
  *
71
45
  * @param db - Open better-sqlite3 database instance
72
46
  * @param repoPath - Absolute path to the repository root
@@ -158,10 +132,9 @@ export async function refreshFiles(db, repoPath, dirtyFiles) {
158
132
  const callNameNode = captureMap['call.name'];
159
133
  if (callNameNode) {
160
134
  callSites.push({
161
- filePath: relPath, absPath,
135
+ filePath: relPath,
162
136
  name: callNameNode.text,
163
137
  line: callNameNode.startPosition.row,
164
- character: callNameNode.startPosition.column,
165
138
  });
166
139
  }
167
140
  continue;
@@ -283,33 +256,11 @@ export async function refreshFiles(db, repoPath, dirtyFiles) {
283
256
  if (wordSet.size > 0)
284
257
  upsertFileWords(db, relPath, [...wordSet].join(' '));
285
258
  }
286
- // Phase 4 + 5: Resolve call edges and cross-file edges using tsgo LSP
287
- // (TS/JS files only — tsgo is optional, skip if unavailable)
288
- console.error(`Code Mapper: refresh tsgo init with repoPath=${repoPath}`);
289
- const tsgoService = getTsgoService(repoPath);
290
- let tsgoReady = false;
291
- try {
292
- tsgoReady = await tsgoService.start();
293
- console.error(`Code Mapper: refresh tsgo ready=${tsgoReady}`);
294
- }
295
- catch (err) {
296
- console.error(`Code Mapper: refresh tsgo failed: ${err instanceof Error ? err.message : err}`);
297
- }
298
- // Phase 4: Resolve call edges from dirty files
299
- // Always runs — tsgo provides 0.99 confidence, heuristic fallback provides 0.5-0.95
259
+ // Phase 4: Resolve call edges from dirty files (heuristic name-based matching)
300
260
  if (callSites.length > 0) {
301
261
  db.exec('BEGIN');
302
262
  try {
303
263
  const { findNodesByName } = await import('../db/adapter.js');
304
- // Notify tsgo about changed files if available
305
- if (tsgoReady) {
306
- for (const entry of filesToProcess) {
307
- const absPath = path.resolve(repoPath, entry.relativePath);
308
- if (isTypeScriptOrJavaScript(entry.relativePath)) {
309
- await tsgoService.notifyFileChanged(absPath);
310
- }
311
- }
312
- }
313
264
  console.error(`Code Mapper: refresh Phase 4 — ${callSites.length} call sites to resolve`);
314
265
  for (const callSite of callSites) {
315
266
  const sourceNode = findEnclosingNode(db, callSite.filePath, callSite.line);
@@ -318,39 +269,23 @@ export async function refreshFiles(db, repoPath, dirtyFiles) {
318
269
  let targetNode;
319
270
  let confidence = 0.5;
320
271
  let reason = 'global';
321
- // Try tsgo first for TS/JS files
322
- if (tsgoReady && isTypeScriptOrJavaScript(callSite.filePath)) {
323
- try {
324
- const def = await tsgoService.resolveDefinition(callSite.absPath, callSite.line, callSite.character);
325
- if (def) {
326
- targetNode = findNodeByFileAndLine(db, def.filePath, def.line);
327
- if (targetNode) {
328
- confidence = 0.99;
329
- reason = 'tsgo-lsp';
330
- }
331
- }
332
- }
333
- catch { }
272
+ // Heuristic: name-based lookup in DB
273
+ const candidates = findNodesByName(db, callSite.name, undefined, 5);
274
+ const sameFile = candidates.find(c => c.filePath === callSite.filePath);
275
+ if (sameFile) {
276
+ targetNode = sameFile;
277
+ confidence = 0.95;
278
+ reason = 'same-file';
279
+ }
280
+ else if (candidates.length === 1) {
281
+ targetNode = candidates[0];
282
+ confidence = 0.9;
283
+ reason = 'import-resolved';
334
284
  }
335
- // Heuristic fallback: name-based lookup in DB
336
- if (!targetNode) {
337
- const candidates = findNodesByName(db, callSite.name, undefined, 5);
338
- const sameFile = candidates.find(c => c.filePath === callSite.filePath);
339
- if (sameFile) {
340
- targetNode = sameFile;
341
- confidence = 0.95;
342
- reason = 'same-file';
343
- }
344
- else if (candidates.length === 1) {
345
- targetNode = candidates[0];
346
- confidence = 0.9;
347
- reason = 'import-resolved';
348
- }
349
- else if (candidates.length > 0) {
350
- targetNode = candidates[0];
351
- confidence = 0.5;
352
- reason = 'global';
353
- }
285
+ else if (candidates.length > 0) {
286
+ targetNode = candidates[0];
287
+ confidence = 0.5;
288
+ reason = 'global';
354
289
  }
355
290
  if (!targetNode)
356
291
  continue;
@@ -375,61 +310,11 @@ export async function refreshFiles(db, repoPath, dirtyFiles) {
375
310
  console.error(`Code Mapper: Phase 4 call resolution failed: ${err instanceof Error ? err.message : err}`);
376
311
  }
377
312
  }
378
- // Phase 5: Repair cross-file edges (tsgo only — needs findReferences)
379
- if (tsgoReady) {
380
- const dirtyFilePaths = new Set(dirtyFiles.map(f => f.relativePath));
381
- db.exec('BEGIN');
382
- try {
383
- // Phase 5: Repair cross-file edges
384
- // For each newly inserted definition, find references from UNCHANGED files
385
- // and create CALLS edges so the graph stays consistent.
386
- for (const def of allDefinitions) {
387
- if (!isTypeScriptOrJavaScript(def.filePath))
388
- continue;
389
- const absPath = path.resolve(repoPath, def.filePath);
390
- const refs = await tsgoService.findReferences(absPath, def.startLine, 0);
391
- for (const ref of refs) {
392
- // Skip refs from dirty files — already handled in Phase 4
393
- if (dirtyFilePaths.has(ref.filePath))
394
- continue;
395
- // Skip refs from non-TS/JS files
396
- if (!isTypeScriptOrJavaScript(ref.filePath))
397
- continue;
398
- // This reference is from an UNCHANGED file → create/update the CALLS edge
399
- const sourceNode = findEnclosingNode(db, ref.filePath, ref.line);
400
- if (!sourceNode)
401
- continue;
402
- const targetId = toNodeId(def.nodeId);
403
- // Skip self-edges
404
- if (sourceNode.id === targetId)
405
- continue;
406
- const edgeId = toEdgeId(`${sourceNode.id}_CALLS_${targetId}`);
407
- insertEdge(db, {
408
- id: edgeId,
409
- sourceId: sourceNode.id,
410
- targetId,
411
- type: 'CALLS',
412
- confidence: 0.99,
413
- reason: 'tsgo-cross-file',
414
- callLine: ref.line,
415
- });
416
- edgesInserted++;
417
- }
418
- }
419
- db.exec('COMMIT');
420
- }
421
- catch (err) {
422
- db.exec('ROLLBACK');
423
- // Log but don't fail the entire refresh — Phases 1-3 already committed
424
- console.error(`Code Mapper: tsgo call resolution failed: ${err instanceof Error ? err.message : err}`);
425
- }
426
- }
427
313
  // FTS5 auto-updates via triggers — no manual rebuild needed
428
314
  return {
429
315
  filesProcessed: filesToProcess.length, filesSkipped,
430
316
  nodesDeleted, nodesInserted, edgesInserted,
431
317
  durationMs: Date.now() - t0,
432
- tsgoEnabled: tsgoReady,
433
318
  };
434
319
  }
435
320
  // ---------------------------------------------------------------------------
@@ -55,7 +55,6 @@ export interface RefreshResult {
55
55
  readonly nodesInserted: number;
56
56
  readonly edgesInserted: number;
57
57
  readonly durationMs: number;
58
- readonly tsgoEnabled: boolean;
59
58
  }
60
59
  /** Messages from child to parent */
61
60
  export type ChildToParentMessage = {
@@ -131,7 +131,6 @@ export function parseChildMessage(raw) {
131
131
  nodesInserted: Number(p['nodesInserted']) || 0,
132
132
  edgesInserted: Number(p['edgesInserted']) || 0,
133
133
  durationMs: Number(p['durationMs']) || 0,
134
- tsgoEnabled: Boolean(p['tsgoEnabled']),
135
134
  },
136
135
  };
137
136
  }
@@ -3,14 +3,13 @@ import type { KnowledgeGraph } from '../graph/types.js';
3
3
  import type { ASTCache } from './ast-cache.js';
4
4
  import type { ResolutionContext } from './resolution-context.js';
5
5
  import type { ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
6
- import { TsgoService } from '../semantic/tsgo-service.js';
7
6
  export declare const processCalls: (graph: KnowledgeGraph, files: {
8
7
  path: string;
9
8
  content: string;
10
9
  }[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<ExtractedHeritage[]>;
11
10
  export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
12
11
  /** Resolve pre-extracted call sites from workers (no AST parsing needed) */
13
- export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number, workerCount?: number) => void, constructorBindings?: FileConstructorBindings[], tsgoService?: TsgoService | null, repoPath?: string) => Promise<void>;
12
+ export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number, workerCount?: number) => void, constructorBindings?: FileConstructorBindings[]) => Promise<void>;
14
13
  /** Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods */
15
14
  export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extractedRoutes: ExtractedRoute[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;
16
15
  /**
@@ -41,6 +40,6 @@ export declare const createProvidesEdges: (graph: KnowledgeGraph, ctx: Resolutio
41
40
  * Creates CALLS edges: InterfaceMethod → ImplementationFunction.
42
41
  *
43
42
  * This makes the call chain traversable:
44
- * register() →[tsgo]→ EventBus.emit (Method) →[dispatch]→ emit (Function in event-bus.ts)
43
+ * register() → EventBus.emit (Method) →[dispatch]→ emit (Function in event-bus.ts)
45
44
  */
46
45
  export declare const resolveInterfaceDispatches: (graph: KnowledgeGraph, ctx: ResolutionContext) => Promise<number>;