@zuvia-software-solutions/code-mapper 1.4.0 → 2.0.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.
Files changed (137) hide show
  1. package/dist/cli/ai-context.js +1 -1
  2. package/dist/cli/analyze.d.ts +1 -0
  3. package/dist/cli/analyze.js +73 -82
  4. package/dist/cli/augment.js +0 -2
  5. package/dist/cli/eval-server.d.ts +2 -2
  6. package/dist/cli/eval-server.js +6 -6
  7. package/dist/cli/index.js +6 -10
  8. package/dist/cli/mcp.d.ts +1 -3
  9. package/dist/cli/mcp.js +3 -3
  10. package/dist/cli/refresh.d.ts +2 -2
  11. package/dist/cli/refresh.js +24 -29
  12. package/dist/cli/status.js +4 -13
  13. package/dist/cli/tool.d.ts +5 -4
  14. package/dist/cli/tool.js +8 -10
  15. package/dist/config/ignore-service.js +14 -34
  16. package/dist/core/augmentation/engine.js +53 -83
  17. package/dist/core/db/adapter.d.ts +99 -0
  18. package/dist/core/db/adapter.js +402 -0
  19. package/dist/core/db/graph-loader.d.ts +27 -0
  20. package/dist/core/db/graph-loader.js +148 -0
  21. package/dist/core/db/queries.d.ts +160 -0
  22. package/dist/core/db/queries.js +441 -0
  23. package/dist/core/db/schema.d.ts +108 -0
  24. package/dist/core/db/schema.js +136 -0
  25. package/dist/core/embeddings/embedder.d.ts +21 -12
  26. package/dist/core/embeddings/embedder.js +104 -50
  27. package/dist/core/embeddings/embedding-pipeline.d.ts +48 -22
  28. package/dist/core/embeddings/embedding-pipeline.js +220 -262
  29. package/dist/core/embeddings/text-generator.js +4 -19
  30. package/dist/core/embeddings/types.d.ts +1 -1
  31. package/dist/core/graph/graph.d.ts +1 -1
  32. package/dist/core/graph/graph.js +1 -0
  33. package/dist/core/graph/types.d.ts +11 -9
  34. package/dist/core/graph/types.js +4 -1
  35. package/dist/core/incremental/refresh.d.ts +46 -0
  36. package/dist/core/incremental/refresh.js +503 -0
  37. package/dist/core/incremental/types.d.ts +2 -1
  38. package/dist/core/incremental/types.js +42 -44
  39. package/dist/core/ingestion/ast-cache.js +1 -0
  40. package/dist/core/ingestion/call-processor.d.ts +15 -3
  41. package/dist/core/ingestion/call-processor.js +448 -60
  42. package/dist/core/ingestion/cluster-enricher.d.ts +1 -1
  43. package/dist/core/ingestion/cluster-enricher.js +2 -0
  44. package/dist/core/ingestion/community-processor.d.ts +1 -1
  45. package/dist/core/ingestion/community-processor.js +8 -3
  46. package/dist/core/ingestion/export-detection.d.ts +1 -1
  47. package/dist/core/ingestion/export-detection.js +1 -1
  48. package/dist/core/ingestion/filesystem-walker.js +1 -1
  49. package/dist/core/ingestion/heritage-processor.d.ts +2 -2
  50. package/dist/core/ingestion/heritage-processor.js +22 -11
  51. package/dist/core/ingestion/import-processor.d.ts +2 -2
  52. package/dist/core/ingestion/import-processor.js +24 -9
  53. package/dist/core/ingestion/language-config.js +7 -4
  54. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  55. package/dist/core/ingestion/mro-processor.js +23 -11
  56. package/dist/core/ingestion/named-binding-extraction.js +5 -5
  57. package/dist/core/ingestion/parsing-processor.d.ts +4 -4
  58. package/dist/core/ingestion/parsing-processor.js +26 -18
  59. package/dist/core/ingestion/pipeline.d.ts +4 -2
  60. package/dist/core/ingestion/pipeline.js +50 -20
  61. package/dist/core/ingestion/process-processor.d.ts +2 -2
  62. package/dist/core/ingestion/process-processor.js +28 -14
  63. package/dist/core/ingestion/resolution-context.d.ts +1 -1
  64. package/dist/core/ingestion/resolution-context.js +14 -4
  65. package/dist/core/ingestion/resolvers/csharp.js +4 -3
  66. package/dist/core/ingestion/resolvers/go.js +3 -1
  67. package/dist/core/ingestion/resolvers/jvm.js +13 -4
  68. package/dist/core/ingestion/resolvers/standard.js +2 -2
  69. package/dist/core/ingestion/resolvers/utils.js +6 -2
  70. package/dist/core/ingestion/route-stitcher.d.ts +15 -0
  71. package/dist/core/ingestion/route-stitcher.js +92 -0
  72. package/dist/core/ingestion/structure-processor.d.ts +1 -1
  73. package/dist/core/ingestion/structure-processor.js +3 -2
  74. package/dist/core/ingestion/symbol-table.d.ts +2 -0
  75. package/dist/core/ingestion/symbol-table.js +5 -1
  76. package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
  77. package/dist/core/ingestion/tree-sitter-queries.js +177 -0
  78. package/dist/core/ingestion/type-env.js +20 -0
  79. package/dist/core/ingestion/type-extractors/csharp.js +4 -3
  80. package/dist/core/ingestion/type-extractors/go.js +23 -12
  81. package/dist/core/ingestion/type-extractors/php.js +18 -10
  82. package/dist/core/ingestion/type-extractors/ruby.js +15 -3
  83. package/dist/core/ingestion/type-extractors/rust.js +3 -2
  84. package/dist/core/ingestion/type-extractors/shared.js +3 -2
  85. package/dist/core/ingestion/type-extractors/typescript.js +11 -5
  86. package/dist/core/ingestion/utils.d.ts +27 -4
  87. package/dist/core/ingestion/utils.js +145 -100
  88. package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
  89. package/dist/core/ingestion/workers/parse-worker.js +97 -29
  90. package/dist/core/ingestion/workers/worker-pool.js +3 -0
  91. package/dist/core/search/bm25-index.d.ts +15 -8
  92. package/dist/core/search/bm25-index.js +48 -98
  93. package/dist/core/search/hybrid-search.d.ts +9 -3
  94. package/dist/core/search/hybrid-search.js +30 -25
  95. package/dist/core/search/reranker.js +9 -7
  96. package/dist/core/search/types.d.ts +0 -4
  97. package/dist/core/semantic/tsgo-service.d.ts +7 -1
  98. package/dist/core/semantic/tsgo-service.js +165 -66
  99. package/dist/lib/tsgo-test.d.ts +2 -0
  100. package/dist/lib/tsgo-test.js +6 -0
  101. package/dist/lib/type-utils.d.ts +25 -0
  102. package/dist/lib/type-utils.js +22 -0
  103. package/dist/lib/utils.d.ts +3 -2
  104. package/dist/lib/utils.js +3 -2
  105. package/dist/mcp/compatible-stdio-transport.js +1 -1
  106. package/dist/mcp/local/local-backend.d.ts +29 -56
  107. package/dist/mcp/local/local-backend.js +808 -1118
  108. package/dist/mcp/resources.js +35 -25
  109. package/dist/mcp/server.d.ts +1 -1
  110. package/dist/mcp/server.js +5 -5
  111. package/dist/mcp/tools.js +24 -25
  112. package/dist/storage/repo-manager.d.ts +2 -12
  113. package/dist/storage/repo-manager.js +1 -47
  114. package/dist/types/pipeline.d.ts +8 -5
  115. package/dist/types/pipeline.js +5 -0
  116. package/package.json +18 -11
  117. package/dist/cli/serve.d.ts +0 -5
  118. package/dist/cli/serve.js +0 -8
  119. package/dist/core/incremental/child-process.d.ts +0 -8
  120. package/dist/core/incremental/child-process.js +0 -649
  121. package/dist/core/incremental/refresh-coordinator.d.ts +0 -32
  122. package/dist/core/incremental/refresh-coordinator.js +0 -147
  123. package/dist/core/lbug/csv-generator.d.ts +0 -28
  124. package/dist/core/lbug/csv-generator.js +0 -355
  125. package/dist/core/lbug/lbug-adapter.d.ts +0 -96
  126. package/dist/core/lbug/lbug-adapter.js +0 -753
  127. package/dist/core/lbug/schema.d.ts +0 -46
  128. package/dist/core/lbug/schema.js +0 -402
  129. package/dist/mcp/core/embedder.d.ts +0 -24
  130. package/dist/mcp/core/embedder.js +0 -168
  131. package/dist/mcp/core/lbug-adapter.d.ts +0 -29
  132. package/dist/mcp/core/lbug-adapter.js +0 -330
  133. package/dist/server/api.d.ts +0 -5
  134. package/dist/server/api.js +0 -340
  135. package/dist/server/mcp-http.d.ts +0 -7
  136. package/dist/server/mcp-http.js +0 -95
  137. package/models/mlx-embedder.py +0 -185
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * @file tool.ts
3
- * @description Direct CLI tool commands (query, context, impact, cypher). Bypasses MCP
4
- * entirely and invokes LocalBackend directly for minimal overhead. Output goes to stderr
5
- * because LadybugDB's native module captures stdout at the OS level during init
3
+ * @description Direct CLI tool commands (query, context, impact, sql). Bypasses MCP
4
+ * entirely and invokes LocalBackend directly for minimal overhead.
6
5
  */
7
6
  export declare function queryCommand(queryText: string, options?: {
8
7
  repo?: string;
@@ -16,13 +15,15 @@ export declare function contextCommand(name: string, options?: {
16
15
  file?: string;
17
16
  uid?: string;
18
17
  content?: boolean;
18
+ tsgo?: boolean;
19
19
  }): Promise<void>;
20
20
  export declare function impactCommand(target: string, options?: {
21
21
  direction?: string;
22
22
  repo?: string;
23
23
  depth?: string;
24
24
  includeTests?: boolean;
25
+ tsgo?: boolean;
25
26
  }): Promise<void>;
26
- export declare function cypherCommand(query: string, options?: {
27
+ export declare function sqlCommand(query: string, options?: {
27
28
  repo?: string;
28
29
  }): Promise<void>;
package/dist/cli/tool.js CHANGED
@@ -1,9 +1,8 @@
1
1
  // code-mapper/src/cli/tool.ts
2
2
  /**
3
3
  * @file tool.ts
4
- * @description Direct CLI tool commands (query, context, impact, cypher). Bypasses MCP
5
- * entirely and invokes LocalBackend directly for minimal overhead. Output goes to stderr
6
- * because LadybugDB's native module captures stdout at the OS level during init
4
+ * @description Direct CLI tool commands (query, context, impact, sql). Bypasses MCP
5
+ * entirely and invokes LocalBackend directly for minimal overhead.
7
6
  */
8
7
  import { LocalBackend } from '../mcp/local/local-backend.js';
9
8
  let _backend = null;
@@ -20,11 +19,8 @@ async function getBackend() {
20
19
  }
21
20
  function output(data) {
22
21
  const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
23
- // stderr because LadybugDB captures stdout at OS level
24
22
  process.stderr.write(text + '\n');
25
- // Force exit LadybugDB native module crashes on cleanup (mutex error)
26
- // and prevents the process from exiting. This is safe because CLI is
27
- // single-shot: once output is written, we're done.
23
+ // Force exit to ensure clean termination CLI is single-shot.
28
24
  setTimeout(() => process.exit(0), 50);
29
25
  }
30
26
  export async function queryCommand(queryText, options) {
@@ -54,6 +50,7 @@ export async function contextCommand(name, options) {
54
50
  uid: options?.uid,
55
51
  file_path: options?.file,
56
52
  include_content: options?.content ?? false,
53
+ tsgo: options?.tsgo,
57
54
  repo: options?.repo,
58
55
  });
59
56
  output(result);
@@ -69,17 +66,18 @@ export async function impactCommand(target, options) {
69
66
  direction: options?.direction || 'upstream',
70
67
  maxDepth: options?.depth ? parseInt(options.depth) : undefined,
71
68
  includeTests: options?.includeTests ?? false,
69
+ tsgo: options?.tsgo,
72
70
  repo: options?.repo,
73
71
  });
74
72
  output(result);
75
73
  }
76
- export async function cypherCommand(query, options) {
74
+ export async function sqlCommand(query, options) {
77
75
  if (!query?.trim()) {
78
- console.error('Usage: code-mapper cypher <cypher_query>');
76
+ console.error('Usage: code-mapper sql <sql_query>');
79
77
  process.exit(1);
80
78
  }
81
79
  const backend = await getBackend();
82
- const result = await backend.callTool('cypher', {
80
+ const result = await backend.callTool('sql', {
83
81
  query,
84
82
  repo: options?.repo,
85
83
  });
@@ -1,15 +1,15 @@
1
1
  // code-mapper/src/config/ignore-service.ts
2
2
  /** @file ignore-service.ts @description Hardcoded and user-configurable ignore rules for file/directory filtering during repo traversal */
3
- import ignore from 'ignore';
3
+ import ignore, {} from 'ignore';
4
4
  import fs from 'fs/promises';
5
5
  import nodePath from 'path';
6
6
  const DEFAULT_IGNORE_LIST = new Set([
7
- // Version control
7
+ // Version control internals
8
8
  '.git',
9
9
  '.svn',
10
10
  '.hg',
11
11
  '.bzr',
12
- // IDEs and editors
12
+ // IDE/editor config (not source code)
13
13
  '.idea',
14
14
  '.vscode',
15
15
  '.vs',
@@ -17,16 +17,12 @@ const DEFAULT_IGNORE_LIST = new Set([
17
17
  '.settings',
18
18
  '.DS_Store',
19
19
  'Thumbs.db',
20
- // Dependencies
20
+ // Downloaded dependencies (not your code)
21
21
  'node_modules',
22
22
  'bower_components',
23
23
  'jspm_packages',
24
- 'vendor', // PHP/Go
25
- // 'packages' not ignored — commonly used for monorepo source code
26
24
  'venv',
27
25
  '.venv',
28
- 'env',
29
- '.env',
30
26
  '__pycache__',
31
27
  '.pytest_cache',
32
28
  '.mypy_cache',
@@ -35,17 +31,14 @@ const DEFAULT_IGNORE_LIST = new Set([
35
31
  'eggs',
36
32
  '.eggs',
37
33
  'lib64',
38
- 'parts',
39
34
  'sdist',
40
35
  'wheels',
41
- // Build outputs
36
+ // Compiled/bundled output (derived from source)
42
37
  'dist',
43
38
  'build',
44
39
  'out',
45
- 'output',
46
- 'bin',
47
40
  'obj',
48
- 'target', // Java, Rust
41
+ 'target', // Java/Rust compiled output
49
42
  '.next',
50
43
  '.nuxt',
51
44
  '.output',
@@ -57,37 +50,26 @@ const DEFAULT_IGNORE_LIST = new Set([
57
50
  '.parcel-cache',
58
51
  '.turbo',
59
52
  '.svelte-kit',
60
- // Test and coverage
53
+ // Coverage reports (not source code)
61
54
  'coverage',
62
55
  '.nyc_output',
63
56
  'htmlcov',
64
57
  '.coverage',
65
- '__tests__',
66
- '__mocks__',
67
58
  '.jest',
68
- // Logs and temp
69
- 'logs',
70
- 'log',
59
+ // Temp/cache (ephemeral)
71
60
  'tmp',
72
61
  'temp',
73
62
  'cache',
74
63
  '.cache',
75
64
  '.tmp',
76
65
  '.temp',
77
- // Generated and compiled
78
- '.generated',
79
- 'generated',
80
- 'auto-generated',
66
+ // Infrastructure config (not source code)
81
67
  '.terraform',
82
- '.serverless',
83
- // Misc
68
+ // CI config (not source code)
84
69
  '.husky',
85
70
  '.github',
86
71
  '.circleci',
87
72
  '.gitlab',
88
- 'fixtures',
89
- 'snapshots',
90
- '__snapshots__',
91
73
  ]);
92
74
  const IGNORED_EXTENSIONS = new Set([
93
75
  // Images
@@ -174,7 +156,7 @@ const IGNORED_FILES = new Set([
174
156
  export const shouldIgnorePath = (filePath) => {
175
157
  const normalizedPath = filePath.replace(/\\/g, '/');
176
158
  const parts = normalizedPath.split('/');
177
- const fileName = parts[parts.length - 1];
159
+ const fileName = parts.at(-1) ?? '';
178
160
  const fileNameLower = fileName.toLowerCase();
179
161
  // Check if any path segment matches the ignore list
180
162
  for (const part of parts) {
@@ -202,11 +184,9 @@ export const shouldIgnorePath = (filePath) => {
202
184
  }
203
185
  // TODO: dot-file filtering intentionally skipped — many are important configs
204
186
  // Relies on explicit IGNORED_FILES list above instead
205
- // Ignore generated/bundled code and TypeScript declaration files
187
+ // Ignore bundled/minified code (derived output, not source)
206
188
  if (fileNameLower.includes('.bundle.') ||
207
- fileNameLower.includes('.chunk.') ||
208
- fileNameLower.includes('.generated.') ||
209
- fileNameLower.endsWith('.d.ts')) {
189
+ fileNameLower.includes('.chunk.')) {
210
190
  return true;
211
191
  }
212
192
  return false;
@@ -224,7 +204,7 @@ export const loadIgnoreRules = async (repoPath, options) => {
224
204
  const ig = ignore();
225
205
  let hasRules = false;
226
206
  // Allow users to bypass .gitignore parsing (e.g. when .gitignore accidentally excludes source files)
227
- const skipGitignore = options?.noGitignore ?? !!process.env.CODE_MAPPER_NO_GITIGNORE;
207
+ const skipGitignore = options?.noGitignore ?? !!process.env['CODE_MAPPER_NO_GITIGNORE'];
228
208
  const filenames = skipGitignore
229
209
  ? ['.code-mapperignore']
230
210
  : ['.gitignore', '.code-mapperignore'];
@@ -49,7 +49,6 @@ async function findRepoForCwd(cwd) {
49
49
  return {
50
50
  name: bestMatch.name,
51
51
  storagePath: bestMatch.storagePath,
52
- lbugPath: path.join(bestMatch.storagePath, 'lbug'),
53
52
  };
54
53
  }
55
54
  catch {
@@ -74,64 +73,40 @@ export async function augment(pattern, cwd) {
74
73
  const repo = await findRepoForCwd(workDir);
75
74
  if (!repo)
76
75
  return '';
77
- // Lazy-load lbug adapter
78
- const { initLbug, executeQuery, isLbugReady } = await import('../../mcp/core/lbug-adapter.js');
79
- const { searchFTSFromLbug } = await import('../search/bm25-index.js');
80
- const repoId = repo.name.toLowerCase();
81
- // Init LadybugDB if needed
82
- if (!isLbugReady(repoId)) {
83
- await initLbug(repoId, repo.lbugPath);
84
- }
76
+ // Lazy-load SQLite adapter and search
77
+ const { openDb } = await import('../db/adapter.js');
78
+ const { toNodeId } = await import('../db/schema.js');
79
+ const { searchFTSSymbols } = await import('../search/bm25-index.js');
80
+ const { rawQuery } = await import('../db/adapter.js');
81
+ const dbPath = path.join(repo.storagePath, 'index.db');
82
+ const db = openDb(dbPath);
85
83
  // Step 1: BM25 search (fast, no embeddings needed)
86
- const bm25Results = await searchFTSFromLbug(pattern, 10, repoId);
84
+ const bm25Results = searchFTSSymbols(pattern, 10, db);
87
85
  if (bm25Results.length === 0)
88
86
  return '';
89
- // Step 2: Map BM25 file results to code symbols
90
- const symbolMatches = [];
91
- for (const result of bm25Results.slice(0, 5)) {
92
- const escaped = result.filePath.replace(/'/g, "''");
93
- try {
94
- const symbols = await executeQuery(repoId, `
95
- MATCH (n) WHERE n.filePath = '${escaped}'
96
- AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}'
97
- RETURN n.id AS id, n.name AS name, labels(n) AS type, n.filePath AS filePath
98
- LIMIT 3
99
- `);
100
- for (const sym of symbols) {
101
- symbolMatches.push({
102
- nodeId: sym.id || sym[0],
103
- name: sym.name || sym[1],
104
- type: sym.type || sym[2],
105
- filePath: sym.filePath || sym[3],
106
- score: result.score,
107
- });
108
- }
109
- }
110
- catch { /* skip */ }
111
- }
112
- if (symbolMatches.length === 0)
113
- return '';
114
- // Step 3: Batch-fetch callers/callees/processes/cohesion (WHERE n.id IN [...])
115
- const uniqueSymbols = symbolMatches.slice(0, 5).filter((sym, i, arr) => arr.findIndex(s => s.nodeId === sym.nodeId) === i);
87
+ // BM25 already returns symbol-level results (nodeId, name, type, filePath)
88
+ // so we skip the old "map file results to symbols" step.
89
+ const uniqueSymbols = bm25Results.slice(0, 5).filter((sym, i, arr) => arr.findIndex(s => s.nodeId === sym.nodeId) === i);
116
90
  if (uniqueSymbols.length === 0)
117
91
  return '';
118
- const idList = uniqueSymbols.map(s => `'${s.nodeId.replace(/'/g, "''")}'`).join(', ');
92
+ const nodeIds = uniqueSymbols.map(s => toNodeId(s.nodeId));
93
+ const ph = nodeIds.map(() => '?').join(',');
94
+ // Step 2: Batch-fetch callers/callees/processes/cohesion via SQL
119
95
  // Callers
120
96
  const callersMap = new Map();
121
97
  try {
122
- const rows = await executeQuery(repoId, `
123
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n)
124
- WHERE n.id IN [${idList}]
125
- RETURN n.id AS targetId, caller.name AS name
98
+ const rows = rawQuery(db, `
99
+ SELECT e.targetId AS targetId, n.name AS name
100
+ FROM edges e
101
+ JOIN nodes n ON n.id = e.sourceId
102
+ WHERE e.targetId IN (${ph}) AND e.type = 'CALLS'
126
103
  LIMIT 15
127
- `);
104
+ `, [...nodeIds]);
128
105
  for (const r of rows) {
129
- const tid = r.targetId || r[0];
130
- const name = r.name || r[1];
131
- if (tid && name) {
132
- if (!callersMap.has(tid))
133
- callersMap.set(tid, []);
134
- callersMap.get(tid).push(name);
106
+ if (r.targetId && r.name) {
107
+ if (!callersMap.has(r.targetId))
108
+ callersMap.set(r.targetId, []);
109
+ callersMap.get(r.targetId)?.push(r.name);
135
110
  }
136
111
  }
137
112
  }
@@ -139,19 +114,18 @@ export async function augment(pattern, cwd) {
139
114
  // Callees
140
115
  const calleesMap = new Map();
141
116
  try {
142
- const rows = await executeQuery(repoId, `
143
- MATCH (n)-[:CodeRelation {type: 'CALLS'}]->(callee)
144
- WHERE n.id IN [${idList}]
145
- RETURN n.id AS sourceId, callee.name AS name
117
+ const rows = rawQuery(db, `
118
+ SELECT e.sourceId AS sourceId, n.name AS name
119
+ FROM edges e
120
+ JOIN nodes n ON n.id = e.targetId
121
+ WHERE e.sourceId IN (${ph}) AND e.type = 'CALLS'
146
122
  LIMIT 15
147
- `);
123
+ `, [...nodeIds]);
148
124
  for (const r of rows) {
149
- const sid = r.sourceId || r[0];
150
- const name = r.name || r[1];
151
- if (sid && name) {
152
- if (!calleesMap.has(sid))
153
- calleesMap.set(sid, []);
154
- calleesMap.get(sid).push(name);
125
+ if (r.sourceId && r.name) {
126
+ if (!calleesMap.has(r.sourceId))
127
+ calleesMap.set(r.sourceId, []);
128
+ calleesMap.get(r.sourceId)?.push(r.name);
155
129
  }
156
130
  }
157
131
  }
@@ -159,20 +133,17 @@ export async function augment(pattern, cwd) {
159
133
  // Processes
160
134
  const processesMap = new Map();
161
135
  try {
162
- const rows = await executeQuery(repoId, `
163
- MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
164
- WHERE n.id IN [${idList}]
165
- RETURN n.id AS nodeId, p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
166
- `);
136
+ const rows = rawQuery(db, `
137
+ SELECT e.sourceId AS nodeId, p.heuristicLabel AS label, e.step AS step, p.stepCount AS stepCount
138
+ FROM edges e
139
+ JOIN nodes p ON p.id = e.targetId
140
+ WHERE e.sourceId IN (${ph}) AND e.type = 'STEP_IN_PROCESS' AND p.label = 'Process'
141
+ `, [...nodeIds]);
167
142
  for (const r of rows) {
168
- const nid = r.nodeId || r[0];
169
- const label = r.label || r[1];
170
- const step = r.step || r[2];
171
- const stepCount = r.stepCount || r[3];
172
- if (nid && label) {
173
- if (!processesMap.has(nid))
174
- processesMap.set(nid, []);
175
- processesMap.get(nid).push(`${label} (step ${step}/${stepCount})`);
143
+ if (r.nodeId && r.label) {
144
+ if (!processesMap.has(r.nodeId))
145
+ processesMap.set(r.nodeId, []);
146
+ processesMap.get(r.nodeId)?.push(`${r.label} (step ${r.step}/${r.stepCount})`);
176
147
  }
177
148
  }
178
149
  }
@@ -180,16 +151,15 @@ export async function augment(pattern, cwd) {
180
151
  // Cohesion (internal ranking signal)
181
152
  const cohesionMap = new Map();
182
153
  try {
183
- const rows = await executeQuery(repoId, `
184
- MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
185
- WHERE n.id IN [${idList}]
186
- RETURN n.id AS nodeId, c.cohesion AS cohesion
187
- `);
154
+ const rows = rawQuery(db, `
155
+ SELECT e.sourceId AS nodeId, c.cohesion AS cohesion
156
+ FROM edges e
157
+ JOIN nodes c ON c.id = e.targetId
158
+ WHERE e.sourceId IN (${ph}) AND e.type = 'MEMBER_OF' AND c.label = 'Community'
159
+ `, [...nodeIds]);
188
160
  for (const r of rows) {
189
- const nid = r.nodeId || r[0];
190
- const coh = r.cohesion ?? r[1] ?? 0;
191
- if (nid)
192
- cohesionMap.set(nid, coh);
161
+ if (r.nodeId)
162
+ cohesionMap.set(r.nodeId, r.cohesion ?? 0);
193
163
  }
194
164
  }
195
165
  catch { /* skip */ }
@@ -207,7 +177,7 @@ export async function augment(pattern, cwd) {
207
177
  }
208
178
  if (enriched.length === 0)
209
179
  return '';
210
- // Step 4: Rank by cohesion and format output
180
+ // Step 3: Rank by cohesion and format output
211
181
  enriched.sort((a, b) => b.cohesion - a.cohesion);
212
182
  const lines = [`[Code Mapper] ${enriched.length} related symbols found:`, ''];
213
183
  for (const item of enriched) {
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @file Unified SQLite database adapter for code-mapper.
3
+ *
4
+ * Single module for both CLI and MCP database access.
5
+ * WAL mode: concurrent readers + single writer, zero lock conflicts.
6
+ * Synchronous better-sqlite3 API: no connection pools needed.
7
+ *
8
+ * All public functions are typed with branded IDs and schema types
9
+ * from schema.ts — the compiler prevents passing wrong ID types
10
+ * or invalid labels/edge types.
11
+ */
12
+ import Database from 'better-sqlite3';
13
+ import { type NodeId, type NodeLabel, type EdgeType, type NodeRow, type EdgeRow, type NodeInsert, type EdgeInsert } from './schema.js';
14
+ /** Open (or reuse) a SQLite database. Creates schema if new. */
15
+ export declare function openDb(dbPath: string): Database.Database;
16
+ /** Close one or all databases. */
17
+ export declare function closeDb(dbPath?: string): void;
18
+ /** Delete and recreate a database (full re-analyze). */
19
+ export declare function resetDb(dbPath: string): Database.Database;
20
+ /** Check if a database is open. */
21
+ export declare function isDbOpen(dbPath: string): boolean;
22
+ /**
23
+ * Expand an identifier into natural language words for FTS matching.
24
+ * camelCase, PascalCase, snake_case, UPPER_CASE → space-separated words.
25
+ *
26
+ * Examples:
27
+ * buildTestGraph → "build test graph"
28
+ * processCallsFromExtracted → "process calls from extracted"
29
+ * MAX_CHAIN_DEPTH → "max chain depth"
30
+ * deleteNodesByFile → "delete nodes by file"
31
+ */
32
+ export declare function expandIdentifier(name: string): string;
33
+ /** Insert or replace a node. Automatically expands name for FTS natural language matching. */
34
+ export declare function insertNode(db: Database.Database, node: NodeInsert): void;
35
+ /** Get a node by ID. Returns undefined if not found. */
36
+ export declare function getNode(db: Database.Database, id: NodeId): NodeRow | undefined;
37
+ /** Find nodes by name (optionally filtered by label). */
38
+ export declare function findNodesByName(db: Database.Database, name: string, label?: NodeLabel, limit?: number): NodeRow[];
39
+ /** Find nodes by file path. */
40
+ export declare function findNodesByFile(db: Database.Database, filePath: string): NodeRow[];
41
+ /** Find the node containing a given line in a file (best match). */
42
+ export declare function findNodeAtLine(db: Database.Database, filePath: string, line: number, excludeLabel?: NodeLabel): NodeRow | undefined;
43
+ /** Count nodes, optionally by label. */
44
+ export declare function countNodes(db: Database.Database, label?: NodeLabel): number;
45
+ /** Delete all nodes (and related edges/embeddings) for a file path. Returns count deleted. */
46
+ export declare function deleteNodesByFile(db: Database.Database, filePath: string): number;
47
+ /** Insert an edge (ignores duplicates). */
48
+ export declare function insertEdge(db: Database.Database, edge: EdgeInsert): void;
49
+ /** Find edges from a source node, optionally filtered by type. */
50
+ export declare function findEdgesFrom(db: Database.Database, sourceId: NodeId, type?: EdgeType, minConfidence?: number): EdgeRow[];
51
+ /** Find edges to a target node, optionally filtered by type. */
52
+ export declare function findEdgesTo(db: Database.Database, targetId: NodeId, type?: EdgeType, minConfidence?: number): EdgeRow[];
53
+ /** Count edges. */
54
+ export declare function countEdges(db: Database.Database): number;
55
+ /** Insert or replace an embedding (stores as binary Float32Array). */
56
+ export declare function insertEmbedding(db: Database.Database, nodeId: NodeId, embedding: number[]): void;
57
+ /** Delete embeddings for nodes in a given file. */
58
+ export declare function deleteEmbeddingsByFile(db: Database.Database, filePath: string): void;
59
+ /** Count embeddings. */
60
+ export declare function countEmbeddings(db: Database.Database): number;
61
+ /** Vector similarity search (brute-force cosine). Fast enough for <200K vectors at 256 dims. */
62
+ export declare function searchVector(db: Database.Database, queryVec: number[], limit?: number, maxDistance?: number): Array<{
63
+ nodeId: NodeId;
64
+ distance: number;
65
+ }>;
66
+ /** Full-text search across nodes. FTS5 auto-maintains the index. */
67
+ export declare function searchFTS(db: Database.Database, query: string, limit?: number): Array<{
68
+ id: NodeId;
69
+ name: string;
70
+ label: NodeLabel;
71
+ filePath: string;
72
+ score: number;
73
+ }>;
74
+ /** Get node count, edge count, and embedding count. */
75
+ export declare function getStats(db: Database.Database): {
76
+ nodes: number;
77
+ edges: number;
78
+ embeddings: number;
79
+ };
80
+ /** Batch insert nodes in a single transaction. */
81
+ export declare function insertNodesBatch(db: Database.Database, nodes: readonly NodeInsert[]): void;
82
+ /** Batch insert edges in a single transaction. */
83
+ export declare function insertEdgesBatch(db: Database.Database, edges: readonly EdgeInsert[]): void;
84
+ /** Batch insert embeddings in a single transaction. */
85
+ export declare function insertEmbeddingsBatch(db: Database.Database, items: readonly {
86
+ nodeId: NodeId;
87
+ embedding: number[];
88
+ textHash?: string;
89
+ }[]): void;
90
+ /** Get all textHashes from the embeddings table for hash-based skip on re-index */
91
+ export declare function getEmbeddingHashes(db: Database.Database): Map<string, string>;
92
+ /** Escape a string for use in SQL single-quoted literals. */
93
+ export declare function escapeSql(value: string): string;
94
+ /** Execute a raw SQL query and return rows. */
95
+ export declare function rawQuery(db: Database.Database, sql: string, params?: unknown[]): unknown[];
96
+ /** Execute a raw SQL statement (no return). */
97
+ export declare function rawRun(db: Database.Database, sql: string, params?: unknown[]): {
98
+ changes: number;
99
+ };