@zuvia-software-solutions/code-mapper 1.4.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.
Files changed (213) hide show
  1. package/README.md +215 -0
  2. package/dist/cli/ai-context.d.ts +19 -0
  3. package/dist/cli/ai-context.js +168 -0
  4. package/dist/cli/analyze.d.ts +7 -0
  5. package/dist/cli/analyze.js +325 -0
  6. package/dist/cli/augment.d.ts +7 -0
  7. package/dist/cli/augment.js +27 -0
  8. package/dist/cli/clean.d.ts +5 -0
  9. package/dist/cli/clean.js +56 -0
  10. package/dist/cli/eval-server.d.ts +25 -0
  11. package/dist/cli/eval-server.js +365 -0
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +102 -0
  14. package/dist/cli/lazy-action.d.ts +6 -0
  15. package/dist/cli/lazy-action.js +19 -0
  16. package/dist/cli/list.d.ts +2 -0
  17. package/dist/cli/list.js +27 -0
  18. package/dist/cli/mcp.d.ts +8 -0
  19. package/dist/cli/mcp.js +35 -0
  20. package/dist/cli/refresh.d.ts +12 -0
  21. package/dist/cli/refresh.js +165 -0
  22. package/dist/cli/serve.d.ts +5 -0
  23. package/dist/cli/serve.js +8 -0
  24. package/dist/cli/setup.d.ts +6 -0
  25. package/dist/cli/setup.js +218 -0
  26. package/dist/cli/status.d.ts +2 -0
  27. package/dist/cli/status.js +33 -0
  28. package/dist/cli/tool.d.ts +28 -0
  29. package/dist/cli/tool.js +87 -0
  30. package/dist/config/ignore-service.d.ts +32 -0
  31. package/dist/config/ignore-service.js +282 -0
  32. package/dist/config/supported-languages.d.ts +23 -0
  33. package/dist/config/supported-languages.js +52 -0
  34. package/dist/core/augmentation/engine.d.ts +22 -0
  35. package/dist/core/augmentation/engine.js +232 -0
  36. package/dist/core/embeddings/embedder.d.ts +35 -0
  37. package/dist/core/embeddings/embedder.js +171 -0
  38. package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
  39. package/dist/core/embeddings/embedding-pipeline.js +402 -0
  40. package/dist/core/embeddings/index.d.ts +5 -0
  41. package/dist/core/embeddings/index.js +6 -0
  42. package/dist/core/embeddings/text-generator.d.ts +20 -0
  43. package/dist/core/embeddings/text-generator.js +159 -0
  44. package/dist/core/embeddings/types.d.ts +60 -0
  45. package/dist/core/embeddings/types.js +23 -0
  46. package/dist/core/graph/graph.d.ts +4 -0
  47. package/dist/core/graph/graph.js +65 -0
  48. package/dist/core/graph/types.d.ts +69 -0
  49. package/dist/core/graph/types.js +3 -0
  50. package/dist/core/incremental/child-process.d.ts +8 -0
  51. package/dist/core/incremental/child-process.js +649 -0
  52. package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
  53. package/dist/core/incremental/refresh-coordinator.js +147 -0
  54. package/dist/core/incremental/types.d.ts +78 -0
  55. package/dist/core/incremental/types.js +153 -0
  56. package/dist/core/incremental/watcher.d.ts +63 -0
  57. package/dist/core/incremental/watcher.js +338 -0
  58. package/dist/core/ingestion/ast-cache.d.ts +12 -0
  59. package/dist/core/ingestion/ast-cache.js +34 -0
  60. package/dist/core/ingestion/call-processor.d.ts +34 -0
  61. package/dist/core/ingestion/call-processor.js +937 -0
  62. package/dist/core/ingestion/call-routing.d.ts +40 -0
  63. package/dist/core/ingestion/call-routing.js +97 -0
  64. package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
  65. package/dist/core/ingestion/cluster-enricher.js +151 -0
  66. package/dist/core/ingestion/community-processor.d.ts +26 -0
  67. package/dist/core/ingestion/community-processor.js +272 -0
  68. package/dist/core/ingestion/constants.d.ts +5 -0
  69. package/dist/core/ingestion/constants.js +8 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +317 -0
  72. package/dist/core/ingestion/export-detection.d.ts +11 -0
  73. package/dist/core/ingestion/export-detection.js +203 -0
  74. package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
  75. package/dist/core/ingestion/filesystem-walker.js +64 -0
  76. package/dist/core/ingestion/framework-detection.d.ts +42 -0
  77. package/dist/core/ingestion/framework-detection.js +405 -0
  78. package/dist/core/ingestion/heritage-processor.d.ts +15 -0
  79. package/dist/core/ingestion/heritage-processor.js +237 -0
  80. package/dist/core/ingestion/import-processor.d.ts +31 -0
  81. package/dist/core/ingestion/import-processor.js +416 -0
  82. package/dist/core/ingestion/language-config.d.ts +32 -0
  83. package/dist/core/ingestion/language-config.js +161 -0
  84. package/dist/core/ingestion/mro-processor.d.ts +32 -0
  85. package/dist/core/ingestion/mro-processor.js +343 -0
  86. package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
  87. package/dist/core/ingestion/named-binding-extraction.js +343 -0
  88. package/dist/core/ingestion/parsing-processor.d.ts +20 -0
  89. package/dist/core/ingestion/parsing-processor.js +282 -0
  90. package/dist/core/ingestion/pipeline.d.ts +3 -0
  91. package/dist/core/ingestion/pipeline.js +416 -0
  92. package/dist/core/ingestion/process-processor.d.ts +42 -0
  93. package/dist/core/ingestion/process-processor.js +357 -0
  94. package/dist/core/ingestion/resolution-context.d.ts +40 -0
  95. package/dist/core/ingestion/resolution-context.js +171 -0
  96. package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
  97. package/dist/core/ingestion/resolvers/csharp.js +101 -0
  98. package/dist/core/ingestion/resolvers/go.d.ts +8 -0
  99. package/dist/core/ingestion/resolvers/go.js +33 -0
  100. package/dist/core/ingestion/resolvers/index.d.ts +14 -0
  101. package/dist/core/ingestion/resolvers/index.js +10 -0
  102. package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
  103. package/dist/core/ingestion/resolvers/jvm.js +74 -0
  104. package/dist/core/ingestion/resolvers/php.d.ts +7 -0
  105. package/dist/core/ingestion/resolvers/php.js +30 -0
  106. package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
  107. package/dist/core/ingestion/resolvers/ruby.js +13 -0
  108. package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
  109. package/dist/core/ingestion/resolvers/rust.js +62 -0
  110. package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
  111. package/dist/core/ingestion/resolvers/standard.js +144 -0
  112. package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
  113. package/dist/core/ingestion/resolvers/utils.js +113 -0
  114. package/dist/core/ingestion/structure-processor.d.ts +4 -0
  115. package/dist/core/ingestion/structure-processor.js +39 -0
  116. package/dist/core/ingestion/symbol-table.d.ts +34 -0
  117. package/dist/core/ingestion/symbol-table.js +48 -0
  118. package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
  119. package/dist/core/ingestion/tree-sitter-queries.js +691 -0
  120. package/dist/core/ingestion/type-env.d.ts +52 -0
  121. package/dist/core/ingestion/type-env.js +349 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
  123. package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
  124. package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
  125. package/dist/core/ingestion/type-extractors/csharp.js +224 -0
  126. package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
  127. package/dist/core/ingestion/type-extractors/go.js +261 -0
  128. package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
  129. package/dist/core/ingestion/type-extractors/index.js +30 -0
  130. package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
  131. package/dist/core/ingestion/type-extractors/jvm.js +386 -0
  132. package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
  133. package/dist/core/ingestion/type-extractors/php.js +280 -0
  134. package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
  135. package/dist/core/ingestion/type-extractors/python.js +175 -0
  136. package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
  137. package/dist/core/ingestion/type-extractors/ruby.js +218 -0
  138. package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
  139. package/dist/core/ingestion/type-extractors/rust.js +290 -0
  140. package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
  141. package/dist/core/ingestion/type-extractors/shared.js +322 -0
  142. package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
  143. package/dist/core/ingestion/type-extractors/swift.js +140 -0
  144. package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
  145. package/dist/core/ingestion/type-extractors/types.js +4 -0
  146. package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
  147. package/dist/core/ingestion/type-extractors/typescript.js +227 -0
  148. package/dist/core/ingestion/utils.d.ts +73 -0
  149. package/dist/core/ingestion/utils.js +992 -0
  150. package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
  151. package/dist/core/ingestion/workers/parse-worker.js +1055 -0
  152. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  153. package/dist/core/ingestion/workers/worker-pool.js +123 -0
  154. package/dist/core/lbug/csv-generator.d.ts +28 -0
  155. package/dist/core/lbug/csv-generator.js +355 -0
  156. package/dist/core/lbug/lbug-adapter.d.ts +96 -0
  157. package/dist/core/lbug/lbug-adapter.js +753 -0
  158. package/dist/core/lbug/schema.d.ts +46 -0
  159. package/dist/core/lbug/schema.js +402 -0
  160. package/dist/core/search/bm25-index.d.ts +20 -0
  161. package/dist/core/search/bm25-index.js +123 -0
  162. package/dist/core/search/hybrid-search.d.ts +32 -0
  163. package/dist/core/search/hybrid-search.js +131 -0
  164. package/dist/core/search/query-cache.d.ts +18 -0
  165. package/dist/core/search/query-cache.js +47 -0
  166. package/dist/core/search/query-expansion.d.ts +19 -0
  167. package/dist/core/search/query-expansion.js +75 -0
  168. package/dist/core/search/reranker.d.ts +29 -0
  169. package/dist/core/search/reranker.js +122 -0
  170. package/dist/core/search/types.d.ts +154 -0
  171. package/dist/core/search/types.js +51 -0
  172. package/dist/core/semantic/tsgo-service.d.ts +67 -0
  173. package/dist/core/semantic/tsgo-service.js +355 -0
  174. package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
  175. package/dist/core/tree-sitter/parser-loader.js +71 -0
  176. package/dist/lib/memory-guard.d.ts +35 -0
  177. package/dist/lib/memory-guard.js +70 -0
  178. package/dist/lib/utils.d.ts +3 -0
  179. package/dist/lib/utils.js +6 -0
  180. package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
  181. package/dist/mcp/compatible-stdio-transport.js +209 -0
  182. package/dist/mcp/core/embedder.d.ts +24 -0
  183. package/dist/mcp/core/embedder.js +168 -0
  184. package/dist/mcp/core/lbug-adapter.d.ts +29 -0
  185. package/dist/mcp/core/lbug-adapter.js +330 -0
  186. package/dist/mcp/local/local-backend.d.ts +188 -0
  187. package/dist/mcp/local/local-backend.js +2759 -0
  188. package/dist/mcp/resources.d.ts +22 -0
  189. package/dist/mcp/resources.js +379 -0
  190. package/dist/mcp/server.d.ts +10 -0
  191. package/dist/mcp/server.js +217 -0
  192. package/dist/mcp/staleness.d.ts +10 -0
  193. package/dist/mcp/staleness.js +25 -0
  194. package/dist/mcp/tools.d.ts +21 -0
  195. package/dist/mcp/tools.js +202 -0
  196. package/dist/server/api.d.ts +5 -0
  197. package/dist/server/api.js +340 -0
  198. package/dist/server/mcp-http.d.ts +7 -0
  199. package/dist/server/mcp-http.js +95 -0
  200. package/dist/storage/git.d.ts +6 -0
  201. package/dist/storage/git.js +35 -0
  202. package/dist/storage/repo-manager.d.ts +87 -0
  203. package/dist/storage/repo-manager.js +249 -0
  204. package/dist/types/pipeline.d.ts +35 -0
  205. package/dist/types/pipeline.js +20 -0
  206. package/hooks/claude/code-mapper-hook.cjs +238 -0
  207. package/hooks/claude/pre-tool-use.sh +79 -0
  208. package/hooks/claude/session-start.sh +42 -0
  209. package/models/mlx-embedder.py +185 -0
  210. package/package.json +100 -0
  211. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  212. package/vendor/leiden/index.cjs +355 -0
  213. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,325 @@
1
+ // code-mapper/src/cli/analyze.ts
2
+ /** @file analyze.ts @description Indexes a repository, builds the knowledge graph, and stores it in .code-mapper/ */
3
+ import path from 'path';
4
+ import { execFileSync } from 'child_process';
5
+ import v8 from 'v8';
6
+ import cliProgress from 'cli-progress';
7
+ import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
8
+ import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, createFTSIndex, loadCachedEmbeddings } from '../core/lbug/lbug-adapter.js';
9
+ // Embedding imports are lazy (dynamic import) so onnxruntime-node is never loaded when
10
+ // embeddings are not requested, avoiding crashes on unsupported Node ABIs (#89)
11
+ // disposeEmbedder intentionally not called — ONNX Runtime segfaults on cleanup (#38)
12
+ import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath, cleanupOldKuzuFiles } from '../storage/repo-manager.js';
13
+ import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
14
+ import { generateAIContextFiles } from './ai-context.js';
15
+ import fs from 'fs/promises';
16
+ const HEAP_MB = 8192;
17
+ const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
18
+ /** Re-exec the process with an 8 GB heap if currently below that */
19
+ function ensureHeap() {
20
+ const nodeOpts = process.env.NODE_OPTIONS || '';
21
+ if (nodeOpts.includes('--max-old-space-size'))
22
+ return false;
23
+ const v8Heap = v8.getHeapStatistics().heap_size_limit;
24
+ if (v8Heap >= HEAP_MB * 1024 * 1024 * 0.9)
25
+ return false;
26
+ try {
27
+ execFileSync(process.execPath, [HEAP_FLAG, ...process.argv.slice(1)], {
28
+ stdio: 'inherit',
29
+ env: { ...process.env, NODE_OPTIONS: `${nodeOpts} ${HEAP_FLAG}`.trim() },
30
+ });
31
+ }
32
+ catch (e) {
33
+ process.exitCode = e.status ?? 1;
34
+ }
35
+ return true;
36
+ }
37
+ const PHASE_LABELS = {
38
+ extracting: 'Scanning files',
39
+ structure: 'Building structure',
40
+ parsing: 'Parsing code',
41
+ imports: 'Resolving imports',
42
+ calls: 'Tracing calls',
43
+ heritage: 'Extracting inheritance',
44
+ communities: 'Detecting communities',
45
+ processes: 'Detecting processes',
46
+ complete: 'Pipeline complete',
47
+ lbug: 'Loading into LadybugDB',
48
+ fts: 'Creating search indexes',
49
+ embeddings: 'Generating embeddings',
50
+ done: 'Done',
51
+ };
52
+ export const analyzeCommand = async (inputPath, options) => {
53
+ if (ensureHeap())
54
+ return;
55
+ if (options?.verbose) {
56
+ process.env.CODE_MAPPER_VERBOSE = '1';
57
+ }
58
+ console.log('\n Code Mapper Analyzer\n');
59
+ let repoPath;
60
+ if (inputPath) {
61
+ repoPath = path.resolve(inputPath);
62
+ }
63
+ else {
64
+ const gitRoot = getGitRoot(process.cwd());
65
+ if (!gitRoot) {
66
+ console.log(' Not inside a git repository\n');
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ repoPath = gitRoot;
71
+ }
72
+ if (!isGitRepo(repoPath)) {
73
+ console.log(' Not a git repository\n');
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+ const { storagePath, lbugPath } = getStoragePaths(repoPath);
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 currentCommit = getCurrentCommit(repoPath);
85
+ const existingMeta = await loadMeta(storagePath);
86
+ if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
87
+ console.log(' Already up to date\n');
88
+ return;
89
+ }
90
+ if (process.env.CODE_MAPPER_NO_GITIGNORE) {
91
+ console.log(' CODE_MAPPER_NO_GITIGNORE is set — skipping .gitignore (still reading .code-mapperignore)\n');
92
+ }
93
+ // Single progress bar for the entire pipeline
94
+ const bar = new cliProgress.SingleBar({
95
+ format: ' {bar} {percentage}% | {phase}',
96
+ barCompleteChar: '\u2588',
97
+ barIncompleteChar: '\u2591',
98
+ hideCursor: true,
99
+ barGlue: '',
100
+ autopadding: true,
101
+ clearOnComplete: false,
102
+ stopOnComplete: false,
103
+ }, cliProgress.Presets.shades_grey);
104
+ bar.start(100, 0, { phase: 'Initializing...' });
105
+ // Graceful SIGINT handling — clean up resources and exit cleanly
106
+ let aborted = false;
107
+ const sigintHandler = () => {
108
+ if (aborted)
109
+ process.exit(1); // Second Ctrl-C: force exit
110
+ aborted = true;
111
+ bar.stop();
112
+ console.log('\n Interrupted — cleaning up...');
113
+ closeLbug().catch(() => { }).finally(() => process.exit(130));
114
+ };
115
+ process.on('SIGINT', sigintHandler);
116
+ // Route all console output through bar.log() so the bar doesn't redraw
117
+ // multiple times when other code writes to stdout/stderr mid-render
118
+ const origLog = console.log.bind(console);
119
+ const origWarn = console.warn.bind(console);
120
+ const origError = console.error.bind(console);
121
+ const barLog = (...args) => {
122
+ // Clear the bar line, print the message, then let the next bar.update() redraw
123
+ process.stdout.write('\x1b[2K\r');
124
+ origLog(args.map(a => (typeof a === 'string' ? a : String(a))).join(' '));
125
+ };
126
+ console.log = barLog;
127
+ console.warn = barLog;
128
+ console.error = barLog;
129
+ // Track elapsed time per phase — both updateBar and the interval use
130
+ // the same format so they don't flicker against each other
131
+ let lastPhaseLabel = 'Initializing...';
132
+ let phaseStart = Date.now();
133
+ // Update bar with phase label + elapsed seconds (shown after 3s)
134
+ const updateBar = (value, phaseLabel) => {
135
+ if (phaseLabel !== lastPhaseLabel) {
136
+ lastPhaseLabel = phaseLabel;
137
+ phaseStart = Date.now();
138
+ }
139
+ const elapsed = Math.round((Date.now() - phaseStart) / 1000);
140
+ const display = elapsed >= 3 ? `${phaseLabel} (${elapsed}s)` : phaseLabel;
141
+ bar.update(value, { phase: display });
142
+ };
143
+ // Tick elapsed seconds for phases with infrequent progress callbacks
144
+ // (e.g. CSV streaming, FTS indexing) — uses the same display format as updateBar
145
+ const elapsedTimer = setInterval(() => {
146
+ const elapsed = Math.round((Date.now() - phaseStart) / 1000);
147
+ if (elapsed >= 3) {
148
+ bar.update({ phase: `${lastPhaseLabel} (${elapsed}s)` });
149
+ }
150
+ }, 1000);
151
+ const t0Global = Date.now();
152
+ // Cache embeddings from existing index before rebuild
153
+ let cachedEmbeddingNodeIds = new Set();
154
+ let cachedEmbeddings = [];
155
+ if (options?.embeddings && existingMeta && !options?.force) {
156
+ try {
157
+ updateBar(0, 'Caching embeddings...');
158
+ await initLbug(lbugPath);
159
+ const cached = await loadCachedEmbeddings();
160
+ cachedEmbeddingNodeIds = cached.embeddingNodeIds;
161
+ cachedEmbeddings = cached.embeddings;
162
+ await closeLbug();
163
+ }
164
+ catch {
165
+ try {
166
+ await closeLbug();
167
+ }
168
+ catch { }
169
+ }
170
+ }
171
+ // Phase 1: Full Pipeline (0–60%)
172
+ const pipelineResult = await runPipelineFromRepo(repoPath, (progress) => {
173
+ const phaseLabel = PHASE_LABELS[progress.phase] || progress.phase;
174
+ const scaled = Math.round(progress.percent * 0.6);
175
+ updateBar(scaled, phaseLabel);
176
+ });
177
+ // Phase 2: LadybugDB (60–85%)
178
+ updateBar(60, 'Loading into LadybugDB...');
179
+ await closeLbug();
180
+ const lbugFiles = [lbugPath, `${lbugPath}.wal`, `${lbugPath}.lock`];
181
+ for (const f of lbugFiles) {
182
+ try {
183
+ await fs.rm(f, { recursive: true, force: true });
184
+ }
185
+ catch { }
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));
193
+ updateBar(progress, msg);
194
+ });
195
+ const lbugTime = ((Date.now() - t0Lbug) / 1000).toFixed(1);
196
+ const lbugWarnings = lbugResult.warnings;
197
+ // Phase 3: FTS (85–90%)
198
+ updateBar(85, 'Creating search indexes...');
199
+ 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
+ const ftsTime = ((Date.now() - t0Fts) / 1000).toFixed(1);
211
+ // Phase 3.5: Re-insert cached embeddings
212
+ if (cachedEmbeddings.length > 0) {
213
+ updateBar(88, `Restoring ${cachedEmbeddings.length} cached embeddings...`);
214
+ const EMBED_BATCH = 200;
215
+ for (let i = 0; i < cachedEmbeddings.length; i += EMBED_BATCH) {
216
+ const batch = cachedEmbeddings.slice(i, i + EMBED_BATCH);
217
+ const paramsList = batch.map(e => ({ nodeId: e.nodeId, embedding: e.embedding }));
218
+ try {
219
+ await executeWithReusedStatement(`CREATE (e:CodeEmbedding {nodeId: $nodeId, embedding: $embedding})`, paramsList);
220
+ }
221
+ catch { /* some may fail if node was removed, that's fine */ }
222
+ }
223
+ }
224
+ // Phase 4: Embeddings (90–98%)
225
+ const stats = await getLbugStats();
226
+ let embeddingTime = '0.0';
227
+ let embeddingSkipped = true;
228
+ let embeddingSkipReason = 'off (use --no-embeddings to skip)';
229
+ if (options?.embeddings) {
230
+ embeddingSkipped = false;
231
+ }
232
+ if (!embeddingSkipped) {
233
+ updateBar(90, 'Loading embedding model...');
234
+ const t0Emb = Date.now();
235
+ const { runEmbeddingPipeline } = await import('../core/embeddings/embedding-pipeline.js');
236
+ await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, (progress) => {
237
+ const scaled = 90 + Math.round((progress.percent / 100) * 8);
238
+ const label = progress.phase === 'loading-model' ? 'Loading embedding model...' : `Embedding ${progress.nodesProcessed || 0}/${progress.totalNodes || '?'}`;
239
+ updateBar(scaled, label);
240
+ }, {}, cachedEmbeddingNodeIds.size > 0 ? cachedEmbeddingNodeIds : undefined);
241
+ embeddingTime = ((Date.now() - t0Emb) / 1000).toFixed(1);
242
+ }
243
+ // Phase 5: Finalize (98–100%)
244
+ updateBar(98, 'Saving metadata...');
245
+ // Count embeddings in the index (cached + newly generated) for metadata
246
+ let embeddingCount = 0;
247
+ try {
248
+ const embResult = await executeQuery(`MATCH (e:CodeEmbedding) RETURN count(e) AS cnt`);
249
+ embeddingCount = embResult?.[0]?.cnt ?? 0;
250
+ }
251
+ catch { /* table may not exist if embeddings never ran */ }
252
+ const meta = {
253
+ repoPath,
254
+ lastCommit: currentCommit,
255
+ indexedAt: new Date().toISOString(),
256
+ stats: {
257
+ files: pipelineResult.totalFileCount,
258
+ nodes: stats.nodes,
259
+ edges: stats.edges,
260
+ communities: pipelineResult.communityResult?.stats.totalCommunities,
261
+ processes: pipelineResult.processResult?.stats.totalProcesses,
262
+ embeddings: embeddingCount,
263
+ },
264
+ };
265
+ await saveMeta(storagePath, meta);
266
+ await registerRepo(repoPath, meta);
267
+ await addToGitignore(repoPath);
268
+ const projectName = path.basename(repoPath);
269
+ let aggregatedClusterCount = 0;
270
+ if (pipelineResult.communityResult?.communities) {
271
+ const groups = new Map();
272
+ for (const c of pipelineResult.communityResult.communities) {
273
+ const label = c.heuristicLabel || c.label || 'Unknown';
274
+ groups.set(label, (groups.get(label) || 0) + c.symbolCount);
275
+ }
276
+ aggregatedClusterCount = Array.from(groups.values()).filter(count => count >= 5).length;
277
+ }
278
+ const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
279
+ files: pipelineResult.totalFileCount,
280
+ nodes: stats.nodes,
281
+ edges: stats.edges,
282
+ communities: pipelineResult.communityResult?.stats.totalCommunities,
283
+ clusters: aggregatedClusterCount,
284
+ processes: pipelineResult.processResult?.stats.totalProcesses,
285
+ });
286
+ await closeLbug();
287
+ // NOTE: disposeEmbedder() is intentionally skipped — ONNX Runtime's native cleanup
288
+ // segfaults on macOS and some Linux configs; the process exits immediately after
289
+ const totalTime = ((Date.now() - t0Global) / 1000).toFixed(1);
290
+ clearInterval(elapsedTimer);
291
+ process.removeListener('SIGINT', sigintHandler);
292
+ console.log = origLog;
293
+ console.warn = origWarn;
294
+ console.error = origError;
295
+ bar.update(100, { phase: 'Done' });
296
+ bar.stop();
297
+ // Summary
298
+ const embeddingsCached = cachedEmbeddings.length > 0;
299
+ console.log(`\n Repository indexed successfully (${totalTime}s)${embeddingsCached ? ` [${cachedEmbeddings.length} embeddings cached]` : ''}\n`);
300
+ 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(` LadybugDB ${lbugTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`);
302
+ console.log(` ${repoPath}`);
303
+ if (aiContext.files.length > 0) {
304
+ console.log(` Context: ${aiContext.files.join(', ')}`);
305
+ }
306
+ // Show a quiet summary if some edge types needed fallback insertion
307
+ if (lbugWarnings.length > 0) {
308
+ const totalFallback = lbugWarnings.reduce((sum, w) => {
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)`);
313
+ }
314
+ try {
315
+ await fs.access(getGlobalRegistryPath());
316
+ }
317
+ catch {
318
+ console.log('\n Tip: Run `code-mapper setup` to configure MCP for your editor.');
319
+ }
320
+ console.log('');
321
+ // LadybugDB's native module holds open handles that prevent Node from exiting
322
+ // ONNX Runtime also registers native atexit hooks that segfault (#38, #40)
323
+ // Force-exit to ensure clean termination
324
+ process.exit(0);
325
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file augment.ts
3
+ * @description Fast-path CLI command for platform hooks (Claude Code PreToolUse, Cursor
4
+ * beforeShellExecution). Must cold-start in <500ms — skips web server and full DB warmup
5
+ * Usage: code-mapper augment <pattern>
6
+ */
7
+ export declare function augmentCommand(pattern: string): Promise<void>;
@@ -0,0 +1,27 @@
1
+ // code-mapper/src/cli/augment.ts
2
+ /**
3
+ * @file augment.ts
4
+ * @description Fast-path CLI command for platform hooks (Claude Code PreToolUse, Cursor
5
+ * beforeShellExecution). Must cold-start in <500ms — skips web server and full DB warmup
6
+ * Usage: code-mapper augment <pattern>
7
+ */
8
+ import { augment } from '../core/augmentation/engine.js';
9
+ export async function augmentCommand(pattern) {
10
+ if (!pattern || pattern.length < 3) {
11
+ process.exit(0);
12
+ }
13
+ try {
14
+ const result = await augment(pattern, process.cwd());
15
+ if (result) {
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
+ // The hook reads from the subprocess's stderr
20
+ process.stderr.write(result + '\n');
21
+ }
22
+ }
23
+ catch {
24
+ // Graceful failure — never break the calling hook
25
+ process.exit(0);
26
+ }
27
+ }
@@ -0,0 +1,5 @@
1
+ /** @file clean.ts @description Removes the .code-mapper index from a repository and unregisters it from the global registry */
2
+ export declare const cleanCommand: (options?: {
3
+ force?: boolean;
4
+ all?: boolean;
5
+ }) => Promise<void>;
@@ -0,0 +1,56 @@
1
+ // code-mapper/src/cli/clean.ts
2
+ /** @file clean.ts @description Removes the .code-mapper index from a repository and unregisters it from the global registry */
3
+ import fs from 'fs/promises';
4
+ import { findRepo, unregisterRepo, listRegisteredRepos } from '../storage/repo-manager.js';
5
+ export const cleanCommand = async (options) => {
6
+ // Clean all indexed repos when --all is passed
7
+ if (options?.all) {
8
+ if (!options?.force) {
9
+ const entries = await listRegisteredRepos();
10
+ if (entries.length === 0) {
11
+ console.log('No indexed repositories found.');
12
+ return;
13
+ }
14
+ console.log(`This will delete Code Mapper indexes for ${entries.length} repo(s):`);
15
+ for (const entry of entries) {
16
+ console.log(` - ${entry.name} (${entry.path})`);
17
+ }
18
+ console.log('\nRun with --force to confirm deletion.');
19
+ return;
20
+ }
21
+ const entries = await listRegisteredRepos();
22
+ for (const entry of entries) {
23
+ try {
24
+ await fs.rm(entry.storagePath, { recursive: true, force: true });
25
+ await unregisterRepo(entry.path);
26
+ console.log(`Deleted: ${entry.name} (${entry.storagePath})`);
27
+ }
28
+ catch (err) {
29
+ console.error(`Failed to delete ${entry.name}:`, err);
30
+ }
31
+ }
32
+ return;
33
+ }
34
+ // Default: clean the current repo
35
+ const cwd = process.cwd();
36
+ const repo = await findRepo(cwd);
37
+ if (!repo) {
38
+ console.log('No indexed repository found in this directory.');
39
+ return;
40
+ }
41
+ const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath;
42
+ if (!options?.force) {
43
+ console.log(`This will delete the Code Mapper index for: ${repoName}`);
44
+ console.log(` Path: ${repo.storagePath}`);
45
+ console.log('\nRun with --force to confirm deletion.');
46
+ return;
47
+ }
48
+ try {
49
+ await fs.rm(repo.storagePath, { recursive: true, force: true });
50
+ await unregisterRepo(repo.repoPath);
51
+ console.log(`Deleted: ${repo.storagePath}`);
52
+ }
53
+ catch (err) {
54
+ console.error('Failed to delete:', err);
55
+ }
56
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @file eval-server.ts
3
+ * @description Lightweight HTTP server for SWE-bench evaluation. Keeps LadybugDB warm in
4
+ * memory so tool calls from the agent are near-instant. Returns LLM-friendly text (not raw
5
+ * JSON) and appends next-step hints for productive tool chaining (query -> context -> impact)
6
+ *
7
+ * Architecture: Agent bash cmd -> curl localhost:PORT/tool/query -> eval-server -> LocalBackend -> format -> text
8
+ *
9
+ * API:
10
+ * POST /tool/:name - Call a tool; body is JSON arguments, returns formatted text
11
+ * GET /health - Health check; returns {"status":"ok","repos":[...]}
12
+ * POST /shutdown - Graceful shutdown
13
+ */
14
+ export interface EvalServerOptions {
15
+ port?: string;
16
+ idleTimeout?: string;
17
+ }
18
+ export declare function formatQueryResult(result: any): string;
19
+ export declare function formatContextResult(result: any): string;
20
+ export declare function formatImpactResult(result: any): string;
21
+ export declare function formatCypherResult(result: any): string;
22
+ export declare function formatDetectChangesResult(result: any): string;
23
+ export declare function formatListReposResult(result: any): string;
24
+ export declare function evalServerCommand(options?: EvalServerOptions): Promise<void>;
25
+ export declare const MAX_BODY_SIZE: number;