gitnexus 1.4.9 → 1.5.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 (186) hide show
  1. package/README.md +6 -5
  2. package/dist/cli/ai-context.d.ts +4 -1
  3. package/dist/cli/ai-context.js +19 -11
  4. package/dist/cli/analyze.d.ts +6 -0
  5. package/dist/cli/analyze.js +105 -251
  6. package/dist/cli/eval-server.js +20 -11
  7. package/dist/cli/index-repo.js +20 -22
  8. package/dist/cli/index.js +8 -7
  9. package/dist/cli/mcp.js +1 -1
  10. package/dist/cli/serve.js +29 -1
  11. package/dist/cli/setup.js +9 -9
  12. package/dist/cli/skill-gen.js +15 -9
  13. package/dist/cli/wiki.d.ts +2 -0
  14. package/dist/cli/wiki.js +141 -26
  15. package/dist/config/ignore-service.js +102 -22
  16. package/dist/config/supported-languages.d.ts +8 -42
  17. package/dist/config/supported-languages.js +8 -43
  18. package/dist/core/augmentation/engine.js +19 -7
  19. package/dist/core/embeddings/embedder.js +19 -15
  20. package/dist/core/embeddings/embedding-pipeline.js +6 -6
  21. package/dist/core/embeddings/http-client.js +3 -3
  22. package/dist/core/embeddings/text-generator.js +9 -24
  23. package/dist/core/embeddings/types.d.ts +1 -1
  24. package/dist/core/embeddings/types.js +1 -7
  25. package/dist/core/graph/graph.js +6 -2
  26. package/dist/core/graph/types.d.ts +9 -59
  27. package/dist/core/ingestion/ast-cache.js +3 -3
  28. package/dist/core/ingestion/call-processor.d.ts +20 -2
  29. package/dist/core/ingestion/call-processor.js +347 -144
  30. package/dist/core/ingestion/call-routing.js +10 -4
  31. package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
  32. package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
  33. package/dist/core/ingestion/call-sites/java.d.ts +9 -0
  34. package/dist/core/ingestion/call-sites/java.js +30 -0
  35. package/dist/core/ingestion/cluster-enricher.js +6 -8
  36. package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
  37. package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
  38. package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
  39. package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
  40. package/dist/core/ingestion/cobol-processor.js +102 -56
  41. package/dist/core/ingestion/community-processor.js +21 -15
  42. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
  43. package/dist/core/ingestion/entry-point-scoring.js +5 -6
  44. package/dist/core/ingestion/export-detection.js +32 -9
  45. package/dist/core/ingestion/field-extractor.d.ts +1 -1
  46. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
  48. package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
  49. package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
  50. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
  51. package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
  52. package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
  53. package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
  54. package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
  55. package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
  56. package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
  57. package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
  58. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
  59. package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
  60. package/dist/core/ingestion/field-extractors/generic.js +6 -0
  61. package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
  62. package/dist/core/ingestion/field-extractors/typescript.js +1 -1
  63. package/dist/core/ingestion/field-types.d.ts +4 -2
  64. package/dist/core/ingestion/filesystem-walker.js +3 -3
  65. package/dist/core/ingestion/framework-detection.d.ts +1 -1
  66. package/dist/core/ingestion/framework-detection.js +355 -85
  67. package/dist/core/ingestion/heritage-processor.d.ts +24 -0
  68. package/dist/core/ingestion/heritage-processor.js +99 -8
  69. package/dist/core/ingestion/import-processor.js +44 -15
  70. package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
  71. package/dist/core/ingestion/import-resolvers/dart.js +1 -1
  72. package/dist/core/ingestion/import-resolvers/go.js +4 -2
  73. package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
  74. package/dist/core/ingestion/import-resolvers/php.js +4 -4
  75. package/dist/core/ingestion/import-resolvers/python.js +1 -1
  76. package/dist/core/ingestion/import-resolvers/rust.js +9 -3
  77. package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
  78. package/dist/core/ingestion/import-resolvers/standard.js +6 -5
  79. package/dist/core/ingestion/import-resolvers/swift.js +2 -1
  80. package/dist/core/ingestion/import-resolvers/utils.js +26 -7
  81. package/dist/core/ingestion/language-config.js +5 -4
  82. package/dist/core/ingestion/language-provider.d.ts +7 -2
  83. package/dist/core/ingestion/languages/c-cpp.js +106 -21
  84. package/dist/core/ingestion/languages/cobol.js +1 -1
  85. package/dist/core/ingestion/languages/csharp.js +96 -19
  86. package/dist/core/ingestion/languages/dart.js +23 -7
  87. package/dist/core/ingestion/languages/go.js +1 -1
  88. package/dist/core/ingestion/languages/index.d.ts +1 -1
  89. package/dist/core/ingestion/languages/index.js +2 -3
  90. package/dist/core/ingestion/languages/java.js +4 -1
  91. package/dist/core/ingestion/languages/kotlin.js +60 -13
  92. package/dist/core/ingestion/languages/php.js +102 -25
  93. package/dist/core/ingestion/languages/python.js +28 -5
  94. package/dist/core/ingestion/languages/ruby.js +56 -14
  95. package/dist/core/ingestion/languages/rust.js +55 -11
  96. package/dist/core/ingestion/languages/swift.js +112 -27
  97. package/dist/core/ingestion/languages/typescript.js +95 -19
  98. package/dist/core/ingestion/markdown-processor.js +5 -5
  99. package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
  100. package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
  101. package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
  102. package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
  103. package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
  104. package/dist/core/ingestion/method-extractors/generic.js +137 -0
  105. package/dist/core/ingestion/method-types.d.ts +61 -0
  106. package/dist/core/ingestion/method-types.js +2 -0
  107. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  108. package/dist/core/ingestion/mro-processor.js +12 -8
  109. package/dist/core/ingestion/named-binding-processor.js +2 -2
  110. package/dist/core/ingestion/named-bindings/rust.js +3 -1
  111. package/dist/core/ingestion/parsing-processor.js +74 -24
  112. package/dist/core/ingestion/pipeline.d.ts +2 -1
  113. package/dist/core/ingestion/pipeline.js +208 -102
  114. package/dist/core/ingestion/process-processor.js +12 -10
  115. package/dist/core/ingestion/resolution-context.js +3 -3
  116. package/dist/core/ingestion/route-extractors/middleware.js +31 -7
  117. package/dist/core/ingestion/route-extractors/php.js +2 -1
  118. package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
  119. package/dist/core/ingestion/structure-processor.d.ts +1 -1
  120. package/dist/core/ingestion/structure-processor.js +4 -4
  121. package/dist/core/ingestion/symbol-table.d.ts +1 -1
  122. package/dist/core/ingestion/symbol-table.js +22 -6
  123. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  124. package/dist/core/ingestion/tree-sitter-queries.js +1 -1
  125. package/dist/core/ingestion/type-env.d.ts +2 -2
  126. package/dist/core/ingestion/type-env.js +75 -50
  127. package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
  128. package/dist/core/ingestion/type-extractors/csharp.js +24 -14
  129. package/dist/core/ingestion/type-extractors/dart.js +6 -8
  130. package/dist/core/ingestion/type-extractors/go.js +7 -6
  131. package/dist/core/ingestion/type-extractors/jvm.js +10 -21
  132. package/dist/core/ingestion/type-extractors/php.js +26 -13
  133. package/dist/core/ingestion/type-extractors/python.js +11 -15
  134. package/dist/core/ingestion/type-extractors/ruby.js +8 -3
  135. package/dist/core/ingestion/type-extractors/rust.js +6 -8
  136. package/dist/core/ingestion/type-extractors/shared.js +134 -50
  137. package/dist/core/ingestion/type-extractors/swift.js +16 -13
  138. package/dist/core/ingestion/type-extractors/typescript.js +23 -15
  139. package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
  140. package/dist/core/ingestion/utils/ast-helpers.js +72 -35
  141. package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
  142. package/dist/core/ingestion/utils/call-analysis.js +96 -49
  143. package/dist/core/ingestion/utils/event-loop.js +1 -1
  144. package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
  145. package/dist/core/ingestion/workers/parse-worker.js +364 -84
  146. package/dist/core/ingestion/workers/worker-pool.js +5 -10
  147. package/dist/core/lbug/csv-generator.js +54 -15
  148. package/dist/core/lbug/lbug-adapter.d.ts +5 -0
  149. package/dist/core/lbug/lbug-adapter.js +86 -23
  150. package/dist/core/lbug/schema.d.ts +3 -6
  151. package/dist/core/lbug/schema.js +6 -30
  152. package/dist/core/run-analyze.d.ts +49 -0
  153. package/dist/core/run-analyze.js +257 -0
  154. package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
  155. package/dist/core/tree-sitter/parser-loader.js +1 -1
  156. package/dist/core/wiki/cursor-client.js +2 -7
  157. package/dist/core/wiki/generator.js +38 -23
  158. package/dist/core/wiki/graph-queries.js +10 -10
  159. package/dist/core/wiki/html-viewer.js +7 -3
  160. package/dist/core/wiki/llm-client.d.ts +23 -2
  161. package/dist/core/wiki/llm-client.js +96 -26
  162. package/dist/core/wiki/prompts.js +7 -6
  163. package/dist/mcp/core/embedder.js +1 -1
  164. package/dist/mcp/core/lbug-adapter.d.ts +4 -1
  165. package/dist/mcp/core/lbug-adapter.js +17 -7
  166. package/dist/mcp/local/local-backend.js +247 -95
  167. package/dist/mcp/resources.js +14 -6
  168. package/dist/mcp/server.js +13 -5
  169. package/dist/mcp/staleness.js +5 -1
  170. package/dist/mcp/tools.js +100 -23
  171. package/dist/server/analyze-job.d.ts +53 -0
  172. package/dist/server/analyze-job.js +146 -0
  173. package/dist/server/analyze-worker.d.ts +13 -0
  174. package/dist/server/analyze-worker.js +59 -0
  175. package/dist/server/api.js +795 -44
  176. package/dist/server/git-clone.d.ts +25 -0
  177. package/dist/server/git-clone.js +91 -0
  178. package/dist/storage/git.js +1 -3
  179. package/dist/storage/repo-manager.d.ts +5 -2
  180. package/dist/storage/repo-manager.js +4 -4
  181. package/dist/types/pipeline.d.ts +1 -21
  182. package/dist/types/pipeline.js +1 -18
  183. package/hooks/claude/gitnexus-hook.cjs +52 -22
  184. package/package.json +13 -13
  185. package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
  186. package/dist/core/ingestion/utils/language-detection.js +0 -70
@@ -72,31 +72,26 @@ export const createWorkerPool = (workerUrl, poolSize) => {
72
72
  const handler = (msg) => {
73
73
  if (settled)
74
74
  return;
75
- if (msg && msg.type === 'progress') {
75
+ if (msg.type === 'progress') {
76
76
  workerProgress[i] = msg.filesProcessed;
77
77
  if (onProgress) {
78
78
  const total = workerProgress.reduce((a, b) => a + b, 0);
79
79
  onProgress(total);
80
80
  }
81
81
  }
82
- else if (msg && msg.type === 'sub-batch-done') {
82
+ else if (msg.type === 'sub-batch-done') {
83
83
  sendNextSubBatch();
84
84
  }
85
- else if (msg && msg.type === 'error') {
85
+ else if (msg.type === 'error') {
86
86
  settled = true;
87
87
  cleanup();
88
88
  reject(new Error(`Worker ${i} error: ${msg.error}`));
89
89
  }
90
- else if (msg && msg.type === 'result') {
90
+ else if (msg.type === 'result') {
91
91
  settled = true;
92
92
  cleanup();
93
93
  resolve(msg.data);
94
94
  }
95
- else {
96
- settled = true;
97
- cleanup();
98
- resolve(msg);
99
- }
100
95
  };
101
96
  const errorHandler = (err) => {
102
97
  if (!settled) {
@@ -121,7 +116,7 @@ export const createWorkerPool = (workerUrl, poolSize) => {
121
116
  return Promise.all(promises);
122
117
  };
123
118
  const terminate = async () => {
124
- await Promise.all(workers.map(w => w.terminate()));
119
+ await Promise.all(workers.map((w) => w.terminate()));
125
120
  workers.length = 0;
126
121
  };
127
122
  return { dispatch, terminate, size };
@@ -49,10 +49,10 @@ export const isBinaryContent = (content) => {
49
49
  let nonPrintable = 0;
50
50
  for (let i = 0; i < sample.length; i++) {
51
51
  const code = sample.charCodeAt(i);
52
- if ((code < 9) || (code > 13 && code < 32) || code === 127)
52
+ if (code < 9 || (code > 13 && code < 32) || code === 127)
53
53
  nonPrintable++;
54
54
  }
55
- return (nonPrintable / sample.length) > 0.1;
55
+ return nonPrintable / sample.length > 0.1;
56
56
  };
57
57
  /**
58
58
  * LRU content cache — avoids re-reading the same source file for every
@@ -216,17 +216,35 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
216
216
  const toolWriter = new BufferedCSVWriter(path.join(csvDir, 'tool.csv'), 'id,name,filePath,description');
217
217
  // Multi-language node types share the same CSV shape (no isExported column)
218
218
  const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
219
- const MULTI_LANG_TYPES = ['Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
220
- 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'];
219
+ const MULTI_LANG_TYPES = [
220
+ 'Struct',
221
+ 'Enum',
222
+ 'Macro',
223
+ 'Typedef',
224
+ 'Union',
225
+ 'Namespace',
226
+ 'Trait',
227
+ 'Impl',
228
+ 'TypeAlias',
229
+ 'Const',
230
+ 'Static',
231
+ 'Property',
232
+ 'Record',
233
+ 'Delegate',
234
+ 'Annotation',
235
+ 'Constructor',
236
+ 'Template',
237
+ 'Module',
238
+ ];
221
239
  const multiLangWriters = new Map();
222
240
  for (const t of MULTI_LANG_TYPES) {
223
241
  multiLangWriters.set(t, new BufferedCSVWriter(path.join(csvDir, `${t.toLowerCase()}.csv`), multiLangHeader));
224
242
  }
225
243
  const codeWriterMap = {
226
- 'Function': functionWriter,
227
- 'Class': classWriter,
228
- 'Interface': interfaceWriter,
229
- 'CodeElement': codeElemWriter,
244
+ Function: functionWriter,
245
+ Class: classWriter,
246
+ Interface: interfaceWriter,
247
+ CodeElement: codeElemWriter,
230
248
  };
231
249
  const seenFileIds = new Set();
232
250
  // --- SINGLE PASS over all nodes ---
@@ -376,8 +394,22 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
376
394
  }
377
395
  }
378
396
  // Finish all node writers
379
- const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, sectionWriter, routeWriter, toolWriter, ...multiLangWriters.values()];
380
- await Promise.all(allWriters.map(w => w.finish()));
397
+ const allWriters = [
398
+ fileWriter,
399
+ folderWriter,
400
+ functionWriter,
401
+ classWriter,
402
+ interfaceWriter,
403
+ methodWriter,
404
+ codeElemWriter,
405
+ communityWriter,
406
+ processWriter,
407
+ sectionWriter,
408
+ routeWriter,
409
+ toolWriter,
410
+ ...multiLangWriters.values(),
411
+ ];
412
+ await Promise.all(allWriters.map((w) => w.finish()));
381
413
  // --- Stream relationship CSV ---
382
414
  const relCsvPath = path.join(csvDir, 'relations.csv');
383
415
  const relWriter = new BufferedCSVWriter(relCsvPath, 'from,to,type,confidence,reason,step');
@@ -395,11 +427,15 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
395
427
  // Build result map — only include tables that have rows
396
428
  const nodeFiles = new Map();
397
429
  const tableMap = [
398
- ['File', fileWriter], ['Folder', folderWriter],
399
- ['Function', functionWriter], ['Class', classWriter],
400
- ['Interface', interfaceWriter], ['Method', methodWriter],
430
+ ['File', fileWriter],
431
+ ['Folder', folderWriter],
432
+ ['Function', functionWriter],
433
+ ['Class', classWriter],
434
+ ['Interface', interfaceWriter],
435
+ ['Method', methodWriter],
401
436
  ['CodeElement', codeElemWriter],
402
- ['Community', communityWriter], ['Process', processWriter],
437
+ ['Community', communityWriter],
438
+ ['Process', processWriter],
403
439
  ['Section', sectionWriter],
404
440
  ['Route', routeWriter],
405
441
  ['Tool', toolWriter],
@@ -407,7 +443,10 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
407
443
  ];
408
444
  for (const [name, writer] of tableMap) {
409
445
  if (writer.rows > 0) {
410
- nodeFiles.set(name, { csvPath: path.join(csvDir, `${name.toLowerCase()}.csv`), rows: writer.rows });
446
+ nodeFiles.set(name, {
447
+ csvPath: path.join(csvDir, `${name.toLowerCase()}.csv`),
448
+ rows: writer.rows,
449
+ });
411
450
  }
412
451
  }
413
452
  // Restore original process listener limit
@@ -49,6 +49,11 @@ export declare const batchInsertNodesToLbug: (nodes: Array<{
49
49
  failed: number;
50
50
  }>;
51
51
  export declare const executeQuery: (cypher: string) => Promise<any[]>;
52
+ /**
53
+ * Execute a single parameterized query (prepare/execute pattern).
54
+ * Prevents Cypher injection by binding values as parameters.
55
+ */
56
+ export declare const executePrepared: (cypher: string, params: Record<string, any>) => Promise<any[]>;
52
57
  export declare const executeWithReusedStatement: (cypher: string, paramsList: Array<Record<string, any>>) => Promise<void>;
53
58
  export declare const getLbugStats: () => Promise<{
54
59
  nodes: number;
@@ -25,15 +25,15 @@ const DB_LOCK_RETRY_DELAY_MS = 500;
25
25
  */
26
26
  export const isDbBusyError = (err) => {
27
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'));
28
+ return (msg.includes('busy') ||
29
+ msg.includes('lock') ||
30
+ msg.includes('already in use') ||
31
+ msg.includes('could not set lock'));
32
32
  };
33
33
  const runWithSessionLock = async (operation) => {
34
34
  const previous = sessionLock;
35
35
  let release = null;
36
- sessionLock = new Promise(resolve => {
36
+ sessionLock = new Promise((resolve) => {
37
37
  release = resolve;
38
38
  });
39
39
  await previous;
@@ -77,19 +77,23 @@ export const withLbugDb = async (dbPath, operation) => {
77
77
  if (conn)
78
78
  await conn.close();
79
79
  }
80
- catch { /* best-effort */ }
80
+ catch {
81
+ /* best-effort */
82
+ }
81
83
  try {
82
84
  if (db)
83
85
  await db.close();
84
86
  }
85
- catch { /* best-effort */ }
87
+ catch {
88
+ /* best-effort */
89
+ }
86
90
  conn = null;
87
91
  db = null;
88
92
  currentDbPath = null;
89
93
  ftsLoaded = false;
90
94
  });
91
95
  // Sleep outside the lock — no need to block others while waiting
92
- await new Promise(resolve => setTimeout(resolve, DB_LOCK_RETRY_DELAY_MS * attempt));
96
+ await new Promise((resolve) => setTimeout(resolve, DB_LOCK_RETRY_DELAY_MS * attempt));
93
97
  }
94
98
  }
95
99
  // This line is unreachable — the loop either returns or throws inside,
@@ -212,7 +216,10 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
212
216
  let skippedRels = 0;
213
217
  let totalValidRels = 0;
214
218
  await new Promise((resolve, reject) => {
215
- const rl = createInterface({ input: createReadStream(csvResult.relCsvPath, 'utf-8'), crlfDelay: Infinity });
219
+ const rl = createInterface({
220
+ input: createReadStream(csvResult.relCsvPath, 'utf-8'),
221
+ crlfDelay: Infinity,
222
+ });
216
223
  let isFirst = true;
217
224
  rl.on('line', (line) => {
218
225
  if (isFirst) {
@@ -322,9 +329,24 @@ const COPY_CSV_OPTS = `(HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=
322
329
  // Multi-language table names that were created with backticks in CODE_ELEMENT_BASE
323
330
  // and must always be referenced with backticks in queries
324
331
  const BACKTICK_TABLES = new Set([
325
- 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
326
- 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation',
327
- 'Constructor', 'Template', 'Module',
332
+ 'Struct',
333
+ 'Enum',
334
+ 'Macro',
335
+ 'Typedef',
336
+ 'Union',
337
+ 'Namespace',
338
+ 'Trait',
339
+ 'Impl',
340
+ 'TypeAlias',
341
+ 'Const',
342
+ 'Static',
343
+ 'Property',
344
+ 'Record',
345
+ 'Delegate',
346
+ 'Annotation',
347
+ 'Constructor',
348
+ 'Template',
349
+ 'Module',
328
350
  ]);
329
351
  const escapeTableName = (table) => {
330
352
  return BACKTICK_TABLES.has(table) ? `\`${table}\`` : table;
@@ -362,7 +384,13 @@ const fallbackRelationshipInserts = async (validRelLines, validTables, getNodeLa
362
384
  }
363
385
  };
364
386
  /** Tables with isExported column (TypeScript/JS-native types) */
365
- const TABLES_WITH_EXPORTED = new Set(['Function', 'Class', 'Interface', 'Method', 'CodeElement']);
387
+ const TABLES_WITH_EXPORTED = new Set([
388
+ 'Function',
389
+ 'Class',
390
+ 'Interface',
391
+ 'Method',
392
+ 'CodeElement',
393
+ ]);
366
394
  const getCopyQuery = (table, filePath) => {
367
395
  const t = escapeTableName(table);
368
396
  if (table === 'File') {
@@ -427,16 +455,22 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
427
455
  query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`;
428
456
  }
429
457
  else if (label === 'Section') {
430
- const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
458
+ const descPart = properties.description
459
+ ? `, description: ${escapeValue(properties.description)}`
460
+ : '';
431
461
  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
462
  }
433
463
  else if (TABLES_WITH_EXPORTED.has(label)) {
434
- const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
464
+ const descPart = properties.description
465
+ ? `, description: ${escapeValue(properties.description)}`
466
+ : '';
435
467
  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}})`;
436
468
  }
437
469
  else {
438
470
  // Multi-language tables (Struct, Impl, Trait, Macro, etc.) — no isExported
439
- const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
471
+ const descPart = properties.description
472
+ ? `, description: ${escapeValue(properties.description)}`
473
+ : '';
440
474
  query = `CREATE (n:${t} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${escapeValue(properties.content || '')}${descPart}})`;
441
475
  }
442
476
  // Use per-query connection if dbPath provided (avoids lock conflicts)
@@ -506,15 +540,21 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
506
540
  query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`;
507
541
  }
508
542
  else if (label === 'Section') {
509
- const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
543
+ const descPart = properties.description
544
+ ? `, n.description = ${escapeValue(properties.description)}`
545
+ : '';
510
546
  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
547
  }
512
548
  else if (TABLES_WITH_EXPORTED.has(label)) {
513
- const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
549
+ const descPart = properties.description
550
+ ? `, n.description = ${escapeValue(properties.description)}`
551
+ : '';
514
552
  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}`;
515
553
  }
516
554
  else {
517
- const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
555
+ const descPart = properties.description
556
+ ? `, n.description = ${escapeValue(properties.description)}`
557
+ : '';
518
558
  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.content = ${escapeValue(properties.content || '')}${descPart}`;
519
559
  }
520
560
  await tempConn.query(query);
@@ -549,6 +589,23 @@ export const executeQuery = async (cypher) => {
549
589
  const rows = await result.getAll();
550
590
  return rows;
551
591
  };
592
+ /**
593
+ * Execute a single parameterized query (prepare/execute pattern).
594
+ * Prevents Cypher injection by binding values as parameters.
595
+ */
596
+ export const executePrepared = async (cypher, params) => {
597
+ if (!conn) {
598
+ throw new Error('LadybugDB not initialized. Call initLbug first.');
599
+ }
600
+ const stmt = await conn.prepare(cypher);
601
+ if (!stmt.isSuccess()) {
602
+ const errMsg = await stmt.getErrorMessage();
603
+ throw new Error(`Prepare failed: ${errMsg}`);
604
+ }
605
+ const queryResult = await conn.execute(stmt, params);
606
+ const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
607
+ return await result.getAll();
608
+ };
552
609
  export const executeWithReusedStatement = async (cypher, paramsList) => {
553
610
  if (!conn) {
554
611
  throw new Error('LadybugDB not initialized. Call initLbug first.');
@@ -629,12 +686,16 @@ export const loadCachedEmbeddings = async () => {
629
686
  if (embedding) {
630
687
  embeddings.push({
631
688
  nodeId,
632
- embedding: Array.isArray(embedding) ? embedding.map(Number) : Array.from(embedding).map(Number),
689
+ embedding: Array.isArray(embedding)
690
+ ? embedding.map(Number)
691
+ : Array.from(embedding).map(Number),
633
692
  });
634
693
  }
635
694
  }
636
695
  }
637
- catch { /* embedding table may not exist */ }
696
+ catch {
697
+ /* embedding table may not exist */
698
+ }
638
699
  return { embeddingNodeIds, embeddings };
639
700
  };
640
701
  export const closeLbug = async () => {
@@ -748,7 +809,9 @@ export const loadFTSExtension = async () => {
748
809
  }
749
810
  catch (err) {
750
811
  const msg = err?.message || '';
751
- if (msg.includes('already loaded') || msg.includes('already installed') || msg.includes('already exists')) {
812
+ if (msg.includes('already loaded') ||
813
+ msg.includes('already installed') ||
814
+ msg.includes('already exists')) {
752
815
  ftsLoaded = true;
753
816
  }
754
817
  else {
@@ -768,7 +831,7 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
768
831
  throw new Error('LadybugDB not initialized. Call initLbug first.');
769
832
  }
770
833
  await loadFTSExtension();
771
- const propList = properties.map(p => `'${p}'`).join(', ');
834
+ const propList = properties.map((p) => `'${p}'`).join(', ');
772
835
  const query = `CALL CREATE_FTS_INDEX('${tableName}', '${indexName}', [${propList}], stemmer := '${stemmer}')`;
773
836
  try {
774
837
  await conn.query(query);
@@ -8,12 +8,9 @@
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", "Section", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module", "Route", "Tool"];
12
- export type NodeTableName = typeof NODE_TABLES[number];
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", "HANDLES_ROUTE", "FETCHES", "HANDLES_TOOL", "ENTRY_POINT_OF", "WRAPS", "QUERIES"];
15
- export type RelType = typeof REL_TYPES[number];
16
- export declare const EMBEDDING_TABLE_NAME = "CodeEmbedding";
11
+ import { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME } from 'gitnexus-shared';
12
+ export { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME };
13
+ export type { NodeTableName, RelType } from 'gitnexus-shared';
17
14
  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)";
18
15
  export declare const FOLDER_SCHEMA = "\nCREATE NODE TABLE Folder (\n id STRING,\n name STRING,\n filePath STRING,\n PRIMARY KEY (id)\n)";
19
16
  export declare const FUNCTION_SCHEMA = "\nCREATE NODE TABLE Function (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
@@ -8,28 +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
- // ============================================================================
12
- // NODE TABLE NAMES
13
- // ============================================================================
14
- export const NODE_TABLES = [
15
- 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', 'Section',
16
- // Multi-language support
17
- 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
18
- 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module',
19
- 'Route',
20
- 'Tool'
21
- ];
22
- // ============================================================================
23
- // RELATION TABLE
24
- // ============================================================================
25
- export const REL_TABLE_NAME = 'CodeRelation';
26
- // Valid relation types
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'];
29
- // ============================================================================
30
- // EMBEDDING TABLE
31
- // ============================================================================
32
- export const EMBEDDING_TABLE_NAME = 'CodeEmbedding';
11
+ // Import from shared package (single source of truth) — used in DDL templates below
12
+ import { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME } from 'gitnexus-shared';
13
+ // Re-export so downstream consumers keep the same import path
14
+ export { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME };
33
15
  // ============================================================================
34
16
  // NODE TABLE SCHEMAS
35
17
  // ============================================================================
@@ -481,11 +463,5 @@ export const NODE_SCHEMA_QUERIES = [
481
463
  // MCP tools
482
464
  TOOL_SCHEMA,
483
465
  ];
484
- export const REL_SCHEMA_QUERIES = [
485
- RELATION_SCHEMA,
486
- ];
487
- export const SCHEMA_QUERIES = [
488
- ...NODE_SCHEMA_QUERIES,
489
- ...REL_SCHEMA_QUERIES,
490
- EMBEDDING_SCHEMA,
491
- ];
466
+ export const REL_SCHEMA_QUERIES = [RELATION_SCHEMA];
467
+ export const SCHEMA_QUERIES = [...NODE_SCHEMA_QUERIES, ...REL_SCHEMA_QUERIES, EMBEDDING_SCHEMA];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared Analysis Orchestrator
3
+ *
4
+ * Extracts the core analysis pipeline from the CLI analyze command into a
5
+ * reusable function that can be called from both the CLI and a server-side
6
+ * worker process.
7
+ *
8
+ * IMPORTANT: This module must NEVER call process.exit(). The caller (CLI
9
+ * wrapper or server worker) is responsible for process lifecycle.
10
+ */
11
+ export interface AnalyzeCallbacks {
12
+ onProgress: (phase: string, percent: number, message: string) => void;
13
+ onLog?: (message: string) => void;
14
+ }
15
+ export interface AnalyzeOptions {
16
+ force?: boolean;
17
+ embeddings?: boolean;
18
+ skipGit?: boolean;
19
+ /** Skip AGENTS.md and CLAUDE.md gitnexus block updates. */
20
+ skipAgentsMd?: boolean;
21
+ }
22
+ export interface AnalyzeResult {
23
+ repoName: string;
24
+ repoPath: string;
25
+ stats: {
26
+ files?: number;
27
+ nodes?: number;
28
+ edges?: number;
29
+ communities?: number;
30
+ processes?: number;
31
+ embeddings?: number;
32
+ };
33
+ alreadyUpToDate?: boolean;
34
+ /** The raw pipeline result — only populated when needed by callers (e.g. skill generation). */
35
+ pipelineResult?: any;
36
+ }
37
+ export declare const PHASE_LABELS: Record<string, string>;
38
+ /**
39
+ * Run the full GitNexus analysis pipeline.
40
+ *
41
+ * This is the shared core extracted from the CLI `analyze` command. It
42
+ * handles: pipeline execution, LadybugDB loading, FTS indexing, embedding
43
+ * generation, metadata persistence, and AI context file generation.
44
+ *
45
+ * The function communicates progress and log messages exclusively through
46
+ * the {@link AnalyzeCallbacks} interface — it never writes to stdout/stderr
47
+ * directly and never calls `process.exit()`.
48
+ */
49
+ export declare function runFullAnalysis(repoPath: string, options: AnalyzeOptions, callbacks: AnalyzeCallbacks): Promise<AnalyzeResult>;