gitnexus 1.4.7 → 1.4.9

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 (242) hide show
  1. package/README.md +29 -1
  2. package/dist/cli/ai-context.d.ts +1 -1
  3. package/dist/cli/ai-context.js +1 -1
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +54 -21
  6. package/dist/cli/index-repo.d.ts +15 -0
  7. package/dist/cli/index-repo.js +115 -0
  8. package/dist/cli/index.js +13 -3
  9. package/dist/cli/setup.js +90 -10
  10. package/dist/cli/wiki.d.ts +4 -0
  11. package/dist/cli/wiki.js +174 -53
  12. package/dist/config/supported-languages.d.ts +33 -1
  13. package/dist/config/supported-languages.js +32 -0
  14. package/dist/core/embeddings/embedder.d.ts +6 -1
  15. package/dist/core/embeddings/embedder.js +65 -5
  16. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  17. package/dist/core/embeddings/http-client.d.ts +31 -0
  18. package/dist/core/embeddings/http-client.js +179 -0
  19. package/dist/core/embeddings/index.d.ts +1 -0
  20. package/dist/core/embeddings/index.js +1 -0
  21. package/dist/core/embeddings/types.d.ts +1 -1
  22. package/dist/core/graph/graph.js +9 -1
  23. package/dist/core/graph/types.d.ts +11 -2
  24. package/dist/core/ingestion/call-processor.d.ts +66 -2
  25. package/dist/core/ingestion/call-processor.js +650 -30
  26. package/dist/core/ingestion/call-routing.d.ts +9 -18
  27. package/dist/core/ingestion/call-routing.js +0 -19
  28. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  29. package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
  30. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  31. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
  32. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  33. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  34. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  35. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  36. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  37. package/dist/core/ingestion/cobol-processor.js +1186 -0
  38. package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
  39. package/dist/core/ingestion/entry-point-scoring.js +52 -28
  40. package/dist/core/ingestion/export-detection.d.ts +47 -8
  41. package/dist/core/ingestion/export-detection.js +29 -50
  42. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  43. package/dist/core/ingestion/field-extractor.js +25 -0
  44. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  45. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
  46. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
  48. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  49. package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
  50. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  51. package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
  52. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
  53. package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
  54. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  55. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  56. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  57. package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
  58. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  59. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  60. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  61. package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
  62. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  63. package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
  64. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  65. package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
  66. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  67. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
  68. package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
  69. package/dist/core/ingestion/field-extractors/generic.js +111 -0
  70. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  71. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  72. package/dist/core/ingestion/field-types.d.ts +59 -0
  73. package/dist/core/ingestion/field-types.js +2 -0
  74. package/dist/core/ingestion/framework-detection.d.ts +97 -2
  75. package/dist/core/ingestion/framework-detection.js +114 -14
  76. package/dist/core/ingestion/heritage-processor.js +62 -66
  77. package/dist/core/ingestion/import-processor.d.ts +9 -10
  78. package/dist/core/ingestion/import-processor.js +150 -196
  79. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
  80. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
  81. package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
  82. package/dist/core/ingestion/import-resolvers/dart.js +44 -0
  83. package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
  84. package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
  85. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
  86. package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
  87. package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
  88. package/dist/core/ingestion/import-resolvers/php.js +80 -0
  89. package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
  90. package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
  91. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
  92. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
  93. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
  94. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
  95. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
  96. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
  97. package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
  98. package/dist/core/ingestion/import-resolvers/swift.js +23 -0
  99. package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
  100. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  101. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
  102. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
  103. package/dist/core/ingestion/language-config.d.ts +6 -0
  104. package/dist/core/ingestion/language-config.js +13 -0
  105. package/dist/core/ingestion/language-provider.d.ts +121 -0
  106. package/dist/core/ingestion/language-provider.js +24 -0
  107. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  108. package/dist/core/ingestion/languages/c-cpp.js +71 -0
  109. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  110. package/dist/core/ingestion/languages/cobol.js +26 -0
  111. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  112. package/dist/core/ingestion/languages/csharp.js +49 -0
  113. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  114. package/dist/core/ingestion/languages/dart.js +58 -0
  115. package/dist/core/ingestion/languages/go.d.ts +11 -0
  116. package/dist/core/ingestion/languages/go.js +28 -0
  117. package/dist/core/ingestion/languages/index.d.ts +38 -0
  118. package/dist/core/ingestion/languages/index.js +63 -0
  119. package/dist/core/ingestion/languages/java.d.ts +9 -0
  120. package/dist/core/ingestion/languages/java.js +29 -0
  121. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  122. package/dist/core/ingestion/languages/kotlin.js +53 -0
  123. package/dist/core/ingestion/languages/php.d.ts +8 -0
  124. package/dist/core/ingestion/languages/php.js +145 -0
  125. package/dist/core/ingestion/languages/python.d.ts +12 -0
  126. package/dist/core/ingestion/languages/python.js +39 -0
  127. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  128. package/dist/core/ingestion/languages/ruby.js +44 -0
  129. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  130. package/dist/core/ingestion/languages/rust.js +44 -0
  131. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  132. package/dist/core/ingestion/languages/swift.js +133 -0
  133. package/dist/core/ingestion/languages/typescript.d.ts +10 -0
  134. package/dist/core/ingestion/languages/typescript.js +60 -0
  135. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  136. package/dist/core/ingestion/markdown-processor.js +124 -0
  137. package/dist/core/ingestion/mro-processor.js +22 -18
  138. package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
  139. package/dist/core/ingestion/named-binding-processor.js +42 -0
  140. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  141. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  142. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  143. package/dist/core/ingestion/named-bindings/java.js +29 -0
  144. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  145. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  146. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  147. package/dist/core/ingestion/named-bindings/php.js +61 -0
  148. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  149. package/dist/core/ingestion/named-bindings/python.js +49 -0
  150. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  151. package/dist/core/ingestion/named-bindings/rust.js +64 -0
  152. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  153. package/dist/core/ingestion/named-bindings/types.js +6 -0
  154. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  155. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  156. package/dist/core/ingestion/parsing-processor.d.ts +6 -2
  157. package/dist/core/ingestion/parsing-processor.js +125 -85
  158. package/dist/core/ingestion/pipeline.d.ts +10 -0
  159. package/dist/core/ingestion/pipeline.js +1235 -317
  160. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  161. package/dist/core/ingestion/resolution-context.js +8 -5
  162. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  163. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  164. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  165. package/dist/core/ingestion/route-extractors/middleware.js +143 -0
  166. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  167. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  168. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  169. package/dist/core/ingestion/route-extractors/php.js +21 -0
  170. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  171. package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
  172. package/dist/core/ingestion/symbol-table.d.ts +16 -0
  173. package/dist/core/ingestion/symbol-table.js +20 -6
  174. package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
  175. package/dist/core/ingestion/tree-sitter-queries.js +274 -11
  176. package/dist/core/ingestion/type-env.d.ts +42 -18
  177. package/dist/core/ingestion/type-env.js +481 -106
  178. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  179. package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
  180. package/dist/core/ingestion/type-extractors/csharp.js +149 -16
  181. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  182. package/dist/core/ingestion/type-extractors/dart.js +371 -0
  183. package/dist/core/ingestion/type-extractors/jvm.js +169 -66
  184. package/dist/core/ingestion/type-extractors/rust.js +35 -1
  185. package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
  186. package/dist/core/ingestion/type-extractors/shared.js +14 -112
  187. package/dist/core/ingestion/type-extractors/swift.js +338 -7
  188. package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
  189. package/dist/core/ingestion/type-extractors/typescript.js +141 -9
  190. package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
  191. package/dist/core/ingestion/utils/ast-helpers.js +817 -0
  192. package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
  193. package/dist/core/ingestion/utils/call-analysis.js +527 -0
  194. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  195. package/dist/core/ingestion/utils/event-loop.js +5 -0
  196. package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
  197. package/dist/core/ingestion/utils/language-detection.js +70 -0
  198. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  199. package/dist/core/ingestion/utils/verbose.js +7 -0
  200. package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
  201. package/dist/core/ingestion/workers/parse-worker.js +415 -225
  202. package/dist/core/lbug/csv-generator.js +51 -1
  203. package/dist/core/lbug/lbug-adapter.d.ts +10 -0
  204. package/dist/core/lbug/lbug-adapter.js +75 -4
  205. package/dist/core/lbug/schema.d.ts +8 -4
  206. package/dist/core/lbug/schema.js +65 -4
  207. package/dist/core/tree-sitter/parser-loader.js +7 -1
  208. package/dist/core/wiki/cursor-client.d.ts +31 -0
  209. package/dist/core/wiki/cursor-client.js +127 -0
  210. package/dist/core/wiki/generator.d.ts +28 -9
  211. package/dist/core/wiki/generator.js +115 -18
  212. package/dist/core/wiki/graph-queries.d.ts +4 -0
  213. package/dist/core/wiki/graph-queries.js +7 -1
  214. package/dist/core/wiki/llm-client.d.ts +2 -0
  215. package/dist/core/wiki/llm-client.js +8 -4
  216. package/dist/core/wiki/prompts.d.ts +3 -3
  217. package/dist/core/wiki/prompts.js +6 -0
  218. package/dist/mcp/core/embedder.js +11 -3
  219. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  220. package/dist/mcp/core/lbug-adapter.js +23 -2
  221. package/dist/mcp/local/local-backend.d.ts +38 -5
  222. package/dist/mcp/local/local-backend.js +804 -63
  223. package/dist/mcp/resources.js +2 -0
  224. package/dist/mcp/tools.js +73 -4
  225. package/dist/server/api.d.ts +19 -1
  226. package/dist/server/api.js +66 -6
  227. package/dist/storage/git.d.ts +12 -0
  228. package/dist/storage/git.js +21 -0
  229. package/dist/storage/repo-manager.d.ts +3 -0
  230. package/package.json +25 -16
  231. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  232. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  233. package/dist/core/ingestion/resolvers/index.d.ts +0 -18
  234. package/dist/core/ingestion/resolvers/index.js +0 -13
  235. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  236. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  237. package/dist/core/ingestion/resolvers/php.js +0 -35
  238. package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
  239. package/dist/core/ingestion/type-extractors/index.js +0 -31
  240. package/dist/core/ingestion/utils.d.ts +0 -138
  241. package/dist/core/ingestion/utils.js +0 -1290
  242. package/scripts/patch-tree-sitter-swift.cjs +0 -74
@@ -208,6 +208,12 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
208
208
  const codeElemWriter = new BufferedCSVWriter(path.join(csvDir, 'codeelement.csv'), codeElementHeader);
209
209
  const communityWriter = new BufferedCSVWriter(path.join(csvDir, 'community.csv'), 'id,label,heuristicLabel,keywords,description,enrichedBy,cohesion,symbolCount');
210
210
  const processWriter = new BufferedCSVWriter(path.join(csvDir, 'process.csv'), 'id,label,heuristicLabel,processType,stepCount,communities,entryPointId,terminalId');
211
+ // Section nodes have an extra 'level' column
212
+ const sectionWriter = new BufferedCSVWriter(path.join(csvDir, 'section.csv'), 'id,name,filePath,startLine,endLine,level,content,description');
213
+ // Route nodes for API endpoint mapping
214
+ const routeWriter = new BufferedCSVWriter(path.join(csvDir, 'route.csv'), 'id,name,filePath,responseKeys,errorKeys,middleware');
215
+ // Tool nodes for MCP tool definitions
216
+ const toolWriter = new BufferedCSVWriter(path.join(csvDir, 'tool.csv'), 'id,name,filePath,description');
211
217
  // Multi-language node types share the same CSV shape (no isExported column)
212
218
  const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
213
219
  const MULTI_LANG_TYPES = ['Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
@@ -292,6 +298,47 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
292
298
  ].join(','));
293
299
  break;
294
300
  }
301
+ case 'Section': {
302
+ const content = await extractContent(node, contentCache);
303
+ await sectionWriter.addRow([
304
+ escapeCSVField(node.id),
305
+ escapeCSVField(node.properties.name || ''),
306
+ escapeCSVField(node.properties.filePath || ''),
307
+ escapeCSVNumber(node.properties.startLine, -1),
308
+ escapeCSVNumber(node.properties.endLine, -1),
309
+ escapeCSVNumber(node.properties.level, 1),
310
+ escapeCSVField(content),
311
+ escapeCSVField(node.properties.description || ''),
312
+ ].join(','));
313
+ break;
314
+ }
315
+ case 'Route': {
316
+ const responseKeys = node.properties.responseKeys || [];
317
+ // LadybugDB array literal inside a quoted CSV field: escapeCSVField wraps in "..."
318
+ // and the array uses single-quoted elements
319
+ const keysStr = `[${responseKeys.map((k) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
320
+ const errorKeys = node.properties.errorKeys || [];
321
+ const errorKeysStr = `[${errorKeys.map((k) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
322
+ const middleware = node.properties.middleware || [];
323
+ const middlewareStr = `[${middleware.map((m) => `'${m.replace(/'/g, "''")}'`).join(',')}]`;
324
+ await routeWriter.addRow([
325
+ escapeCSVField(node.id),
326
+ escapeCSVField(node.properties.name || ''),
327
+ escapeCSVField(node.properties.filePath || ''),
328
+ escapeCSVField(keysStr),
329
+ escapeCSVField(errorKeysStr),
330
+ escapeCSVField(middlewareStr),
331
+ ].join(','));
332
+ break;
333
+ }
334
+ case 'Tool':
335
+ await toolWriter.addRow([
336
+ escapeCSVField(node.id),
337
+ escapeCSVField(node.properties.name || ''),
338
+ escapeCSVField(node.properties.filePath || ''),
339
+ escapeCSVField(node.properties.description || ''),
340
+ ].join(','));
341
+ break;
295
342
  default: {
296
343
  // Code element nodes (Function, Class, Interface, CodeElement)
297
344
  const writer = codeWriterMap[node.label];
@@ -329,7 +376,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
329
376
  }
330
377
  }
331
378
  // Finish all node writers
332
- const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, ...multiLangWriters.values()];
379
+ const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, sectionWriter, routeWriter, toolWriter, ...multiLangWriters.values()];
333
380
  await Promise.all(allWriters.map(w => w.finish()));
334
381
  // --- Stream relationship CSV ---
335
382
  const relCsvPath = path.join(csvDir, 'relations.csv');
@@ -353,6 +400,9 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
353
400
  ['Interface', interfaceWriter], ['Method', methodWriter],
354
401
  ['CodeElement', codeElemWriter],
355
402
  ['Community', communityWriter], ['Process', processWriter],
403
+ ['Section', sectionWriter],
404
+ ['Route', routeWriter],
405
+ ['Tool', toolWriter],
356
406
  ...Array.from(multiLangWriters.entries()).map(([name, w]) => [name, w]),
357
407
  ];
358
408
  for (const [name, writer] of tableMap) {
@@ -2,6 +2,12 @@ import lbug from '@ladybugdb/core';
2
2
  import { KnowledgeGraph } from '../graph/types.js';
3
3
  /** Expose the current Database for pool adapter reuse in tests. */
4
4
  export declare const getDatabase: () => lbug.Database | null;
5
+ /**
6
+ * Return true when the error message indicates that another process holds
7
+ * an exclusive lock on the LadybugDB file (e.g. `gitnexus analyze` or
8
+ * `gitnexus serve` running at the same time).
9
+ */
10
+ export declare const isDbBusyError: (err: unknown) => boolean;
5
11
  export declare const initLbug: (dbPath: string) => Promise<{
6
12
  db: lbug.Database;
7
13
  conn: lbug.Connection;
@@ -9,6 +15,10 @@ export declare const initLbug: (dbPath: string) => Promise<{
9
15
  /**
10
16
  * Execute multiple queries against one repo DB atomically.
11
17
  * While the callback runs, no other request can switch the active DB.
18
+ *
19
+ * Automatically retries up to DB_LOCK_RETRY_ATTEMPTS times when the
20
+ * database is busy (e.g. `gitnexus analyze` holds the write lock).
21
+ * Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds.
12
22
  */
13
23
  export declare const withLbugDb: <T>(dbPath: string, operation: () => Promise<T>) => Promise<T>;
14
24
  export type LbugProgressCallback = (message: string) => void;
@@ -14,6 +14,22 @@ export const getDatabase = () => db;
14
14
  // Global session lock for operations that touch module-level lbug globals.
15
15
  // This guarantees no DB switch can happen while an operation is running.
16
16
  let sessionLock = Promise.resolve();
17
+ /** Number of times to retry on a BUSY / lock-held error before giving up. */
18
+ const DB_LOCK_RETRY_ATTEMPTS = 3;
19
+ /** Base back-off in ms between BUSY retries (multiplied by attempt number). */
20
+ const DB_LOCK_RETRY_DELAY_MS = 500;
21
+ /**
22
+ * Return true when the error message indicates that another process holds
23
+ * an exclusive lock on the LadybugDB file (e.g. `gitnexus analyze` or
24
+ * `gitnexus serve` running at the same time).
25
+ */
26
+ export const isDbBusyError = (err) => {
27
+ const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
28
+ return (msg.includes('busy')
29
+ || msg.includes('lock')
30
+ || msg.includes('already in use')
31
+ || msg.includes('could not set lock'));
32
+ };
17
33
  const runWithSessionLock = async (operation) => {
18
34
  const previous = sessionLock;
19
35
  let release = null;
@@ -35,12 +51,50 @@ export const initLbug = async (dbPath) => {
35
51
  /**
36
52
  * Execute multiple queries against one repo DB atomically.
37
53
  * While the callback runs, no other request can switch the active DB.
54
+ *
55
+ * Automatically retries up to DB_LOCK_RETRY_ATTEMPTS times when the
56
+ * database is busy (e.g. `gitnexus analyze` holds the write lock).
57
+ * Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds.
38
58
  */
39
59
  export const withLbugDb = async (dbPath, operation) => {
40
- return runWithSessionLock(async () => {
41
- await ensureLbugInitialized(dbPath);
42
- return operation();
43
- });
60
+ let lastError;
61
+ for (let attempt = 1; attempt <= DB_LOCK_RETRY_ATTEMPTS; attempt++) {
62
+ try {
63
+ return await runWithSessionLock(async () => {
64
+ await ensureLbugInitialized(dbPath);
65
+ return operation();
66
+ });
67
+ }
68
+ catch (err) {
69
+ lastError = err;
70
+ if (!isDbBusyError(err) || attempt === DB_LOCK_RETRY_ATTEMPTS) {
71
+ throw err;
72
+ }
73
+ // Close stale connection inside the session lock to prevent race conditions
74
+ // with concurrent operations that might acquire the lock between cleanup steps
75
+ await runWithSessionLock(async () => {
76
+ try {
77
+ if (conn)
78
+ await conn.close();
79
+ }
80
+ catch { /* best-effort */ }
81
+ try {
82
+ if (db)
83
+ await db.close();
84
+ }
85
+ catch { /* best-effort */ }
86
+ conn = null;
87
+ db = null;
88
+ currentDbPath = null;
89
+ ftsLoaded = false;
90
+ });
91
+ // Sleep outside the lock — no need to block others while waiting
92
+ await new Promise(resolve => setTimeout(resolve, DB_LOCK_RETRY_DELAY_MS * attempt));
93
+ }
94
+ }
95
+ // This line is unreachable — the loop either returns or throws inside,
96
+ // but TypeScript needs an explicit throw to satisfy the return type.
97
+ throw lastError;
44
98
  };
45
99
  const ensureLbugInitialized = async (dbPath) => {
46
100
  if (conn && currentDbPath === dbPath) {
@@ -323,6 +377,15 @@ const getCopyQuery = (table, filePath) => {
323
377
  if (table === 'Process') {
324
378
  return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`;
325
379
  }
380
+ if (table === 'Section') {
381
+ return `COPY ${t}(id, name, filePath, startLine, endLine, level, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
382
+ }
383
+ if (table === 'Route') {
384
+ return `COPY ${t}(id, name, filePath, responseKeys, errorKeys, middleware) FROM "${filePath}" ${COPY_CSV_OPTS}`;
385
+ }
386
+ if (table === 'Tool') {
387
+ return `COPY ${t}(id, name, filePath, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
388
+ }
326
389
  if (table === 'Method') {
327
390
  return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
328
391
  }
@@ -363,6 +426,10 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
363
426
  else if (label === 'Folder') {
364
427
  query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`;
365
428
  }
429
+ else if (label === 'Section') {
430
+ const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
431
+ query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`;
432
+ }
366
433
  else if (TABLES_WITH_EXPORTED.has(label)) {
367
434
  const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
368
435
  query = `CREATE (n:${t} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${escapeValue(properties.content || '')}${descPart}})`;
@@ -438,6 +505,10 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
438
505
  else if (label === 'Folder') {
439
506
  query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`;
440
507
  }
508
+ else if (label === 'Section') {
509
+ const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
510
+ query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
511
+ }
441
512
  else if (TABLES_WITH_EXPORTED.has(label)) {
442
513
  const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
443
514
  query = `MERGE (n:${t} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
@@ -8,10 +8,10 @@
8
8
  * This allows LLMs to write natural Cypher queries like:
9
9
  * MATCH (f:Function)-[r:CodeRelation {type: 'CALLS'}]->(g:Function) RETURN f, g
10
10
  */
11
- export declare const NODE_TABLES: readonly ["File", "Folder", "Function", "Class", "Interface", "Method", "CodeElement", "Community", "Process", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module"];
11
+ export declare const NODE_TABLES: readonly ["File", "Folder", "Function", "Class", "Interface", "Method", "CodeElement", "Community", "Process", "Section", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module", "Route", "Tool"];
12
12
  export type NodeTableName = typeof NODE_TABLES[number];
13
13
  export declare const REL_TABLE_NAME = "CodeRelation";
14
- export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "OVERRIDES", "MEMBER_OF", "STEP_IN_PROCESS"];
14
+ export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "OVERRIDES", "MEMBER_OF", "STEP_IN_PROCESS", "HANDLES_ROUTE", "FETCHES", "HANDLES_TOOL", "ENTRY_POINT_OF", "WRAPS", "QUERIES"];
15
15
  export type RelType = typeof REL_TYPES[number];
16
16
  export declare const EMBEDDING_TABLE_NAME = "CodeEmbedding";
17
17
  export declare const FILE_SCHEMA = "\nCREATE NODE TABLE File (\n id STRING,\n name STRING,\n filePath STRING,\n content STRING,\n PRIMARY KEY (id)\n)";
@@ -41,8 +41,12 @@ export declare const ANNOTATION_SCHEMA: string;
41
41
  export declare const CONSTRUCTOR_SCHEMA: string;
42
42
  export declare const TEMPLATE_SCHEMA: string;
43
43
  export declare const MODULE_SCHEMA: string;
44
- export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
45
- export declare const EMBEDDING_SCHEMA = "\nCREATE NODE TABLE CodeEmbedding (\n nodeId STRING,\n embedding FLOAT[384],\n PRIMARY KEY (nodeId)\n)";
44
+ export declare const ROUTE_SCHEMA = "\nCREATE NODE TABLE Route (\n id STRING,\n name STRING,\n filePath STRING,\n responseKeys STRING[],\n errorKeys STRING[],\n middleware STRING[],\n PRIMARY KEY (id)\n)";
45
+ export declare const TOOL_SCHEMA = "\nCREATE NODE TABLE Tool (\n id STRING,\n name STRING,\n filePath STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
46
+ export declare const SECTION_SCHEMA = "\nCREATE NODE TABLE Section (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n level INT64,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
47
+ export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM File TO Section,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Function TO CodeElement,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM Method TO CodeElement,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM Section TO Section,\n FROM Section TO File,\n FROM File TO Route,\n FROM Function TO Route,\n FROM Method TO Route,\n FROM File TO Tool,\n FROM Function TO Tool,\n FROM Method TO Tool,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n FROM Route TO Process,\n FROM Tool TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
48
+ export declare const EMBEDDING_DIMS: number;
49
+ export declare const EMBEDDING_SCHEMA: string;
46
50
  /**
47
51
  * Create vector index for semantic search
48
52
  * Uses HNSW (Hierarchical Navigable Small World) algorithm with cosine similarity
@@ -12,17 +12,20 @@
12
12
  // NODE TABLE NAMES
13
13
  // ============================================================================
14
14
  export const NODE_TABLES = [
15
- 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process',
15
+ 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', 'Section',
16
16
  // Multi-language support
17
17
  'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
18
- 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'
18
+ 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module',
19
+ 'Route',
20
+ 'Tool'
19
21
  ];
20
22
  // ============================================================================
21
23
  // RELATION TABLE
22
24
  // ============================================================================
23
25
  export const REL_TABLE_NAME = 'CodeRelation';
24
26
  // Valid relation types
25
- export const REL_TYPES = ['CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'ACCESSES', 'OVERRIDES', 'MEMBER_OF', 'STEP_IN_PROCESS'];
27
+ // Note: WRAPS is reserved for future middleware graph traversal (not yet emitted)
28
+ export const REL_TYPES = ['CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'ACCESSES', 'OVERRIDES', 'MEMBER_OF', 'STEP_IN_PROCESS', 'HANDLES_ROUTE', 'FETCHES', 'HANDLES_TOOL', 'ENTRY_POINT_OF', 'WRAPS', 'QUERIES'];
26
29
  // ============================================================================
27
30
  // EMBEDDING TABLE
28
31
  // ============================================================================
@@ -171,6 +174,39 @@ export const ANNOTATION_SCHEMA = CODE_ELEMENT_BASE('Annotation');
171
174
  export const CONSTRUCTOR_SCHEMA = CODE_ELEMENT_BASE('Constructor');
172
175
  export const TEMPLATE_SCHEMA = CODE_ELEMENT_BASE('Template');
173
176
  export const MODULE_SCHEMA = CODE_ELEMENT_BASE('Module');
177
+ // API route endpoints (Next.js, Express, etc.)
178
+ export const ROUTE_SCHEMA = `
179
+ CREATE NODE TABLE Route (
180
+ id STRING,
181
+ name STRING,
182
+ filePath STRING,
183
+ responseKeys STRING[],
184
+ errorKeys STRING[],
185
+ middleware STRING[],
186
+ PRIMARY KEY (id)
187
+ )`;
188
+ // MCP tool definitions
189
+ export const TOOL_SCHEMA = `
190
+ CREATE NODE TABLE Tool (
191
+ id STRING,
192
+ name STRING,
193
+ filePath STRING,
194
+ description STRING,
195
+ PRIMARY KEY (id)
196
+ )`;
197
+ // Markdown heading sections
198
+ export const SECTION_SCHEMA = `
199
+ CREATE NODE TABLE Section (
200
+ id STRING,
201
+ name STRING,
202
+ filePath STRING,
203
+ startLine INT64,
204
+ endLine INT64,
205
+ level INT64,
206
+ content STRING,
207
+ description STRING,
208
+ PRIMARY KEY (id)
209
+ )`;
174
210
  // ============================================================================
175
211
  // RELATION TABLE SCHEMA
176
212
  // Single table with 'type' property - connects all node tables
@@ -202,6 +238,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
202
238
  FROM File TO \`Constructor\`,
203
239
  FROM File TO \`Template\`,
204
240
  FROM File TO \`Module\`,
241
+ FROM File TO Section,
205
242
  FROM Folder TO Folder,
206
243
  FROM Folder TO File,
207
244
  FROM Function TO Function,
@@ -222,6 +259,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
222
259
  FROM Function TO \`Typedef\`,
223
260
  FROM Function TO \`Union\`,
224
261
  FROM Function TO \`Property\`,
262
+ FROM Function TO CodeElement,
225
263
  FROM Class TO Method,
226
264
  FROM Class TO Function,
227
265
  FROM Class TO Class,
@@ -255,6 +293,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
255
293
  FROM Method TO Interface,
256
294
  FROM Method TO \`Constructor\`,
257
295
  FROM Method TO \`Property\`,
296
+ FROM Method TO CodeElement,
258
297
  FROM \`Template\` TO \`Template\`,
259
298
  FROM \`Template\` TO Function,
260
299
  FROM \`Template\` TO Method,
@@ -266,6 +305,14 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
266
305
  FROM \`Template\` TO Interface,
267
306
  FROM \`Template\` TO \`Constructor\`,
268
307
  FROM \`Module\` TO \`Module\`,
308
+ FROM Section TO Section,
309
+ FROM Section TO File,
310
+ FROM File TO Route,
311
+ FROM Function TO Route,
312
+ FROM Method TO Route,
313
+ FROM File TO Tool,
314
+ FROM Function TO Tool,
315
+ FROM Method TO Tool,
269
316
  FROM CodeElement TO Community,
270
317
  FROM Interface TO Community,
271
318
  FROM Interface TO Function,
@@ -364,6 +411,8 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
364
411
  FROM \`Annotation\` TO Process,
365
412
  FROM \`Template\` TO Process,
366
413
  FROM CodeElement TO Process,
414
+ FROM Route TO Process,
415
+ FROM Tool TO Process,
367
416
  type STRING,
368
417
  confidence DOUBLE,
369
418
  reason STRING,
@@ -373,10 +422,16 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
373
422
  // EMBEDDING TABLE SCHEMA
374
423
  // Separate table for vector storage to avoid copy-on-write overhead
375
424
  // ============================================================================
425
+ /** Embedding vector dimensions. Default 384 (snowflake-arctic-embed-xs). */
426
+ const _rawDims = parseInt(process.env.GITNEXUS_EMBEDDING_DIMS ?? '384', 10);
427
+ if (Number.isNaN(_rawDims) || _rawDims <= 0) {
428
+ throw new Error(`GITNEXUS_EMBEDDING_DIMS must be a positive integer, got "${process.env.GITNEXUS_EMBEDDING_DIMS}"`);
429
+ }
430
+ export const EMBEDDING_DIMS = _rawDims;
376
431
  export const EMBEDDING_SCHEMA = `
377
432
  CREATE NODE TABLE ${EMBEDDING_TABLE_NAME} (
378
433
  nodeId STRING,
379
- embedding FLOAT[384],
434
+ embedding FLOAT[${EMBEDDING_DIMS}],
380
435
  PRIMARY KEY (nodeId)
381
436
  )`;
382
437
  /**
@@ -419,6 +474,12 @@ export const NODE_SCHEMA_QUERIES = [
419
474
  CONSTRUCTOR_SCHEMA,
420
475
  TEMPLATE_SCHEMA,
421
476
  MODULE_SCHEMA,
477
+ // Markdown support
478
+ SECTION_SCHEMA,
479
+ // API routes
480
+ ROUTE_SCHEMA,
481
+ // MCP tools
482
+ TOOL_SCHEMA,
422
483
  ];
423
484
  export const REL_SCHEMA_QUERIES = [
424
485
  RELATION_SCHEMA,
@@ -12,13 +12,18 @@ import PHP from 'tree-sitter-php';
12
12
  import Ruby from 'tree-sitter-ruby';
13
13
  import { createRequire } from 'node:module';
14
14
  import { SupportedLanguages } from '../../config/supported-languages.js';
15
- // tree-sitter-swift is an optionalDependency — may not be installed
15
+ // tree-sitter-swift and tree-sitter-dart are optionalDependencies — may not be installed
16
16
  const _require = createRequire(import.meta.url);
17
17
  let Swift = null;
18
18
  try {
19
19
  Swift = _require('tree-sitter-swift');
20
20
  }
21
21
  catch { }
22
+ let Dart = null;
23
+ try {
24
+ Dart = _require('tree-sitter-dart');
25
+ }
26
+ catch { }
22
27
  // tree-sitter-kotlin is an optionalDependency — may not be installed
23
28
  let Kotlin = null;
24
29
  try {
@@ -40,6 +45,7 @@ const languageMap = {
40
45
  ...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
41
46
  [SupportedLanguages.PHP]: PHP.php_only,
42
47
  [SupportedLanguages.Ruby]: Ruby,
48
+ ...(Dart ? { [SupportedLanguages.Dart]: Dart } : {}),
43
49
  ...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
44
50
  };
45
51
  export const isLanguageAvailable = (language) => language in languageMap;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Cursor CLI Client for Wiki Generation
3
+ *
4
+ * Wrapper for the Cursor headless CLI (`agent` command).
5
+ * Uses print mode for non-interactive LLM calls.
6
+ *
7
+ * Docs: https://cursor.com/docs/cli/headless
8
+ */
9
+ import type { LLMResponse, CallLLMOptions } from './llm-client.js';
10
+ export interface CursorConfig {
11
+ model?: string;
12
+ workingDirectory?: string;
13
+ }
14
+ /**
15
+ * Detect if Cursor CLI is available in PATH.
16
+ * Returns the binary name if found ('agent'), null otherwise.
17
+ * Result is cached after the first call.
18
+ */
19
+ export declare function detectCursorCLI(): string | null;
20
+ /**
21
+ * Resolve Cursor CLI configuration.
22
+ * Model is optional - if not provided, Cursor CLI uses its default (auto).
23
+ */
24
+ export declare function resolveCursorConfig(overrides?: Partial<CursorConfig>): CursorConfig;
25
+ /**
26
+ * Call the Cursor CLI in print mode.
27
+ *
28
+ * Uses `agent -p --output-format text` for clean non-streaming output.
29
+ * The prompt is passed as the final CLI argument.
30
+ */
31
+ export declare function callCursorLLM(prompt: string, config: CursorConfig, systemPrompt?: string, options?: CallLLMOptions): Promise<LLMResponse>;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Cursor CLI Client for Wiki Generation
3
+ *
4
+ * Wrapper for the Cursor headless CLI (`agent` command).
5
+ * Uses print mode for non-interactive LLM calls.
6
+ *
7
+ * Docs: https://cursor.com/docs/cli/headless
8
+ */
9
+ import { spawn, execSync } from 'child_process';
10
+ function isVerbose() {
11
+ return process.env.GITNEXUS_VERBOSE === '1';
12
+ }
13
+ function verboseLog(...args) {
14
+ if (isVerbose()) {
15
+ console.log('[cursor-cli]', ...args);
16
+ }
17
+ }
18
+ let cachedCursorBin;
19
+ /**
20
+ * Detect if Cursor CLI is available in PATH.
21
+ * Returns the binary name if found ('agent'), null otherwise.
22
+ * Result is cached after the first call.
23
+ */
24
+ export function detectCursorCLI() {
25
+ if (cachedCursorBin !== undefined)
26
+ return cachedCursorBin;
27
+ try {
28
+ execSync('agent --version', { stdio: 'ignore' });
29
+ cachedCursorBin = 'agent';
30
+ }
31
+ catch {
32
+ cachedCursorBin = null;
33
+ }
34
+ return cachedCursorBin;
35
+ }
36
+ /**
37
+ * Resolve Cursor CLI configuration.
38
+ * Model is optional - if not provided, Cursor CLI uses its default (auto).
39
+ */
40
+ export function resolveCursorConfig(overrides) {
41
+ return {
42
+ model: overrides?.model,
43
+ workingDirectory: overrides?.workingDirectory,
44
+ };
45
+ }
46
+ /**
47
+ * Call the Cursor CLI in print mode.
48
+ *
49
+ * Uses `agent -p --output-format text` for clean non-streaming output.
50
+ * The prompt is passed as the final CLI argument.
51
+ */
52
+ export async function callCursorLLM(prompt, config, systemPrompt, options) {
53
+ const cursorBin = detectCursorCLI();
54
+ if (!cursorBin) {
55
+ throw new Error('Cursor CLI not found. Install it from https://cursor.com/docs/cli/installation');
56
+ }
57
+ // Always use text format to get clean output without agent narration/thinking.
58
+ // stream-json captures assistant messages which include "Let me explore..." narration
59
+ // that pollutes the actual content when using thinking models.
60
+ const fullPrompt = systemPrompt
61
+ ? `${systemPrompt}\n\n---\n\n${prompt}`
62
+ : prompt;
63
+ const args = [
64
+ '-p',
65
+ '--output-format', 'text',
66
+ ];
67
+ if (config.model) {
68
+ args.push('--model', config.model);
69
+ }
70
+ // Add the prompt as the final argument
71
+ args.push(fullPrompt);
72
+ verboseLog('Spawning:', cursorBin, args.slice(0, -1).join(' '), '[prompt length:', fullPrompt.length, 'chars]');
73
+ verboseLog('Working directory:', config.workingDirectory || process.cwd());
74
+ if (config.model) {
75
+ verboseLog('Model:', config.model);
76
+ }
77
+ else {
78
+ verboseLog('Model: auto (default)');
79
+ }
80
+ const startTime = Date.now();
81
+ return new Promise((resolve, reject) => {
82
+ const child = spawn(cursorBin, args, {
83
+ cwd: config.workingDirectory || process.cwd(),
84
+ stdio: ['pipe', 'pipe', 'pipe'],
85
+ env: {
86
+ ...process.env,
87
+ // Ensure non-interactive mode
88
+ CI: '1',
89
+ },
90
+ });
91
+ verboseLog('Process spawned with PID:', child.pid);
92
+ let stdout = '';
93
+ let stderr = '';
94
+ // Text mode - collect all output, report progress based on output size
95
+ child.stdout.on('data', (chunk) => {
96
+ const chunkStr = chunk.toString();
97
+ stdout += chunkStr;
98
+ verboseLog(`[stdout] received ${chunkStr.length} chars, total: ${stdout.length}`);
99
+ // Report progress if callback provided
100
+ if (options?.onChunk) {
101
+ options.onChunk(stdout.length);
102
+ }
103
+ });
104
+ child.on('close', (code) => {
105
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
106
+ verboseLog(`Process exited with code ${code} after ${elapsed}s`);
107
+ verboseLog(`stdout length: ${stdout.length} chars`);
108
+ if (code !== 0) {
109
+ verboseLog('stderr:', stderr);
110
+ reject(new Error(`Cursor CLI exited with code ${code}: ${stderr}`));
111
+ return;
112
+ }
113
+ resolve({ content: stdout.trim() });
114
+ });
115
+ child.stderr.on('data', (chunk) => {
116
+ const chunkStr = chunk.toString();
117
+ stderr += chunkStr;
118
+ verboseLog('[stderr]', chunkStr.trim());
119
+ });
120
+ child.on('error', (err) => {
121
+ verboseLog('Spawn error:', err.message);
122
+ reject(new Error(`Failed to spawn Cursor CLI: ${err.message}`));
123
+ });
124
+ // Close stdin immediately since we pass prompt as argument
125
+ child.stdin.end();
126
+ });
127
+ }