gitnexus 1.4.7 → 1.4.8

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 (92) hide show
  1. package/README.md +22 -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.js +2 -1
  7. package/dist/cli/setup.js +78 -1
  8. package/dist/config/supported-languages.d.ts +30 -0
  9. package/dist/config/supported-languages.js +30 -0
  10. package/dist/core/embeddings/embedder.d.ts +6 -1
  11. package/dist/core/embeddings/embedder.js +65 -5
  12. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  13. package/dist/core/embeddings/http-client.d.ts +31 -0
  14. package/dist/core/embeddings/http-client.js +179 -0
  15. package/dist/core/embeddings/index.d.ts +1 -0
  16. package/dist/core/embeddings/index.js +1 -0
  17. package/dist/core/embeddings/types.d.ts +1 -1
  18. package/dist/core/graph/types.d.ts +2 -1
  19. package/dist/core/ingestion/ast-helpers.d.ts +80 -0
  20. package/dist/core/ingestion/ast-helpers.js +738 -0
  21. package/dist/core/ingestion/call-analysis.d.ts +73 -0
  22. package/dist/core/ingestion/call-analysis.js +490 -0
  23. package/dist/core/ingestion/call-processor.d.ts +48 -1
  24. package/dist/core/ingestion/call-processor.js +368 -7
  25. package/dist/core/ingestion/call-routing.d.ts +6 -0
  26. package/dist/core/ingestion/entry-point-scoring.js +36 -26
  27. package/dist/core/ingestion/framework-detection.d.ts +10 -2
  28. package/dist/core/ingestion/framework-detection.js +49 -12
  29. package/dist/core/ingestion/heritage-processor.js +47 -49
  30. package/dist/core/ingestion/import-processor.d.ts +1 -1
  31. package/dist/core/ingestion/import-processor.js +103 -194
  32. package/dist/core/ingestion/import-resolution.d.ts +101 -0
  33. package/dist/core/ingestion/import-resolution.js +251 -0
  34. package/dist/core/ingestion/language-config.d.ts +3 -0
  35. package/dist/core/ingestion/language-config.js +13 -0
  36. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  37. package/dist/core/ingestion/markdown-processor.js +124 -0
  38. package/dist/core/ingestion/mro-processor.js +8 -3
  39. package/dist/core/ingestion/named-binding-extraction.d.ts +9 -43
  40. package/dist/core/ingestion/named-binding-extraction.js +89 -79
  41. package/dist/core/ingestion/parsing-processor.d.ts +2 -2
  42. package/dist/core/ingestion/parsing-processor.js +14 -73
  43. package/dist/core/ingestion/pipeline.d.ts +10 -0
  44. package/dist/core/ingestion/pipeline.js +421 -4
  45. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  46. package/dist/core/ingestion/resolution-context.js +7 -4
  47. package/dist/core/ingestion/resolvers/index.d.ts +1 -1
  48. package/dist/core/ingestion/resolvers/index.js +1 -1
  49. package/dist/core/ingestion/resolvers/jvm.d.ts +2 -1
  50. package/dist/core/ingestion/resolvers/jvm.js +25 -9
  51. package/dist/core/ingestion/resolvers/php.d.ts +14 -0
  52. package/dist/core/ingestion/resolvers/php.js +43 -3
  53. package/dist/core/ingestion/resolvers/utils.d.ts +5 -0
  54. package/dist/core/ingestion/resolvers/utils.js +16 -0
  55. package/dist/core/ingestion/symbol-table.d.ts +16 -0
  56. package/dist/core/ingestion/symbol-table.js +20 -6
  57. package/dist/core/ingestion/tree-sitter-queries.d.ts +4 -4
  58. package/dist/core/ingestion/tree-sitter-queries.js +43 -2
  59. package/dist/core/ingestion/type-env.d.ts +28 -1
  60. package/dist/core/ingestion/type-env.js +419 -96
  61. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  62. package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
  63. package/dist/core/ingestion/type-extractors/csharp.js +149 -16
  64. package/dist/core/ingestion/type-extractors/index.d.ts +1 -1
  65. package/dist/core/ingestion/type-extractors/index.js +1 -1
  66. package/dist/core/ingestion/type-extractors/jvm.js +169 -66
  67. package/dist/core/ingestion/type-extractors/rust.js +35 -1
  68. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -2
  69. package/dist/core/ingestion/type-extractors/shared.js +5 -10
  70. package/dist/core/ingestion/type-extractors/swift.js +7 -6
  71. package/dist/core/ingestion/type-extractors/types.d.ts +37 -7
  72. package/dist/core/ingestion/type-extractors/typescript.js +141 -9
  73. package/dist/core/ingestion/utils.d.ts +2 -120
  74. package/dist/core/ingestion/utils.js +3 -1051
  75. package/dist/core/ingestion/workers/parse-worker.d.ts +13 -4
  76. package/dist/core/ingestion/workers/parse-worker.js +66 -87
  77. package/dist/core/lbug/csv-generator.js +18 -1
  78. package/dist/core/lbug/lbug-adapter.d.ts +10 -0
  79. package/dist/core/lbug/lbug-adapter.js +69 -4
  80. package/dist/core/lbug/schema.d.ts +5 -3
  81. package/dist/core/lbug/schema.js +26 -2
  82. package/dist/mcp/core/embedder.js +11 -3
  83. package/dist/mcp/core/lbug-adapter.js +12 -1
  84. package/dist/mcp/local/local-backend.d.ts +22 -0
  85. package/dist/mcp/local/local-backend.js +133 -29
  86. package/dist/mcp/resources.js +2 -0
  87. package/dist/mcp/tools.js +2 -2
  88. package/dist/server/api.d.ts +19 -1
  89. package/dist/server/api.js +66 -6
  90. package/dist/storage/git.d.ts +12 -0
  91. package/dist/storage/git.js +21 -0
  92. package/package.json +10 -2
@@ -1,6 +1,7 @@
1
1
  import { SupportedLanguages } from '../../../config/supported-languages.js';
2
2
  import { type MixedChainStep } from '../utils.js';
3
3
  import type { ConstructorBinding } from '../type-env.js';
4
+ import type { NamedBinding } from '../import-resolution.js';
4
5
  import type { NodeLabel } from '../../graph/types.js';
5
6
  interface ParsedNode {
6
7
  id: string;
@@ -16,6 +17,7 @@ interface ParsedNode {
16
17
  astFrameworkReason?: string;
17
18
  description?: string;
18
19
  parameterCount?: number;
20
+ requiredParameterCount?: number;
19
21
  returnType?: string;
20
22
  };
21
23
  }
@@ -33,6 +35,8 @@ interface ParsedSymbol {
33
35
  nodeId: string;
34
36
  type: NodeLabel;
35
37
  parameterCount?: number;
38
+ requiredParameterCount?: number;
39
+ parameterTypes?: string[];
36
40
  returnType?: string;
37
41
  declaredType?: string;
38
42
  ownerId?: string;
@@ -42,10 +46,7 @@ export interface ExtractedImport {
42
46
  rawImportPath: string;
43
47
  language: SupportedLanguages;
44
48
  /** Named bindings from the import (e.g., import {User as U} → [{local:'U', exported:'User'}]) */
45
- namedBindings?: {
46
- local: string;
47
- exported: string;
48
- }[];
49
+ namedBindings?: NamedBinding[];
49
50
  }
50
51
  export interface ExtractedCall {
51
52
  filePath: string;
@@ -102,6 +103,12 @@ export interface FileConstructorBindings {
102
103
  filePath: string;
103
104
  bindings: ConstructorBinding[];
104
105
  }
106
+ /** File-scope type bindings from TypeEnv fixpoint — used for cross-file ExportedTypeMap. */
107
+ export interface FileTypeEnvBindings {
108
+ filePath: string;
109
+ /** [varName, typeName] pairs from file scope (scope = '') */
110
+ bindings: [string, string][];
111
+ }
105
112
  export interface ParseWorkerResult {
106
113
  nodes: ParsedNode[];
107
114
  relationships: ParsedRelationship[];
@@ -112,6 +119,8 @@ export interface ParseWorkerResult {
112
119
  heritage: ExtractedHeritage[];
113
120
  routes: ExtractedRoute[];
114
121
  constructorBindings: FileConstructorBindings[];
122
+ /** File-scope type bindings from TypeEnv fixpoint for exported symbol collection. */
123
+ typeEnvBindings: FileTypeEnvBindings[];
115
124
  skippedLanguages: Record<string, number>;
116
125
  fileCount: number;
117
126
  }
@@ -28,14 +28,13 @@ try {
28
28
  Kotlin = _require('tree-sitter-kotlin');
29
29
  }
30
30
  catch { }
31
- import { getLanguageFromFilename, FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature, countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, } from '../utils.js';
31
+ import { getLanguageFromFilename, FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise, getDefinitionNodeFromCaptures, findEnclosingClassId, getLabelFromCaptures, extractMethodSignature, countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, } from '../utils.js';
32
32
  import { buildTypeEnv } from '../type-env.js';
33
33
  import { isNodeExported } from '../export-detection.js';
34
34
  import { detectFrameworkFromAST } from '../framework-detection.js';
35
35
  import { typeConfigs } from '../type-extractors/index.js';
36
36
  import { generateId } from '../../../lib/utils.js';
37
- import { extractNamedBindings } from '../named-binding-extraction.js';
38
- import { appendKotlinWildcard } from '../resolvers/index.js';
37
+ import { namedBindingExtractors, preprocessImportPath } from '../import-resolution.js';
39
38
  import { callRouters } from '../call-routing.js';
40
39
  import { extractPropertyDeclaredType } from '../type-extractors/shared.js';
41
40
  // ============================================================================
@@ -97,61 +96,7 @@ const findEnclosingFunctionId = (node, filePath) => {
97
96
  }
98
97
  return null;
99
98
  };
100
- // ============================================================================
101
- // Label detection from capture map
102
- // ============================================================================
103
- const getLabelFromCaptures = (captureMap) => {
104
- // Skip imports (handled separately) and calls
105
- if (captureMap['import'] || captureMap['call'])
106
- return null;
107
- if (!captureMap['name'])
108
- return null;
109
- if (captureMap['definition.function'])
110
- return 'Function';
111
- if (captureMap['definition.class'])
112
- return 'Class';
113
- if (captureMap['definition.interface'])
114
- return 'Interface';
115
- if (captureMap['definition.method'])
116
- return 'Method';
117
- if (captureMap['definition.struct'])
118
- return 'Struct';
119
- if (captureMap['definition.enum'])
120
- return 'Enum';
121
- if (captureMap['definition.namespace'])
122
- return 'Namespace';
123
- if (captureMap['definition.module'])
124
- return 'Module';
125
- if (captureMap['definition.trait'])
126
- return 'Trait';
127
- if (captureMap['definition.impl'])
128
- return 'Impl';
129
- if (captureMap['definition.type'])
130
- return 'TypeAlias';
131
- if (captureMap['definition.const'])
132
- return 'Const';
133
- if (captureMap['definition.static'])
134
- return 'Static';
135
- if (captureMap['definition.typedef'])
136
- return 'Typedef';
137
- if (captureMap['definition.macro'])
138
- return 'Macro';
139
- if (captureMap['definition.union'])
140
- return 'Union';
141
- if (captureMap['definition.property'])
142
- return 'Property';
143
- if (captureMap['definition.record'])
144
- return 'Record';
145
- if (captureMap['definition.delegate'])
146
- return 'Delegate';
147
- if (captureMap['definition.annotation'])
148
- return 'Annotation';
149
- if (captureMap['definition.constructor'])
150
- return 'Constructor';
151
- if (captureMap['definition.template'])
152
- return 'Template';
153
- return 'CodeElement';
154
- };
99
+ // Label detection moved to shared getLabelFromCaptures in utils.ts
155
100
  // DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
156
101
  // ============================================================================
157
102
  // Process a batch of files
@@ -167,6 +112,7 @@ const processBatch = (files, onProgress) => {
167
112
  heritage: [],
168
113
  routes: [],
169
114
  constructorBindings: [],
115
+ typeEnvBindings: [],
170
116
  skippedLanguages: {},
171
117
  fileCount: 0,
172
118
  };
@@ -727,13 +673,6 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
727
673
  }
728
674
  result.fileCount++;
729
675
  onFileProcessed?.();
730
- // Build per-file type environment + constructor bindings in a single AST walk.
731
- // Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
732
- const typeEnv = buildTypeEnv(tree, language);
733
- const callRouter = callRouters[language];
734
- if (typeEnv.constructorBindings.length > 0) {
735
- result.constructorBindings.push({ filePath: file.path, bindings: [...typeEnv.constructorBindings] });
736
- }
737
676
  let matches;
738
677
  try {
739
678
  matches = query.matches(tree.rootNode);
@@ -742,6 +681,51 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
742
681
  console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
743
682
  continue;
744
683
  }
684
+ // Pre-pass: extract heritage from query matches to build parentMap for buildTypeEnv.
685
+ // Heritage edges (EXTENDS/IMPLEMENTS) are created by heritage-processor which runs
686
+ // in PARALLEL with call-processor, so the graph edges don't exist when buildTypeEnv
687
+ // runs. This pre-pass makes parent class information available for type resolution.
688
+ const fileParentMap = new Map();
689
+ for (const match of matches) {
690
+ const captureMap = {};
691
+ for (const c of match.captures) {
692
+ captureMap[c.name] = c.node;
693
+ }
694
+ if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
695
+ const className = captureMap['heritage.class'].text;
696
+ const parentName = captureMap['heritage.extends'].text;
697
+ // Skip Go named fields (only anonymous fields are struct embedding)
698
+ const extendsNode = captureMap['heritage.extends'];
699
+ const fieldDecl = extendsNode.parent;
700
+ if (fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name'))
701
+ continue;
702
+ let parents = fileParentMap.get(className);
703
+ if (!parents) {
704
+ parents = [];
705
+ fileParentMap.set(className, parents);
706
+ }
707
+ if (!parents.includes(parentName))
708
+ parents.push(parentName);
709
+ }
710
+ }
711
+ // Build per-file type environment + constructor bindings in a single AST walk.
712
+ // Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
713
+ const parentMap = fileParentMap;
714
+ const typeEnv = buildTypeEnv(tree, language, { parentMap });
715
+ const callRouter = callRouters[language];
716
+ if (typeEnv.constructorBindings.length > 0) {
717
+ result.constructorBindings.push({ filePath: file.path, bindings: [...typeEnv.constructorBindings] });
718
+ }
719
+ // Extract file-scope bindings for ExportedTypeMap (closes worker/sequential quality gap).
720
+ // Sequential path uses collectExportedBindings(typeEnv) directly; worker path serializes
721
+ // these bindings so the main thread can merge them into ExportedTypeMap.
722
+ const fileScope = typeEnv.env.get('');
723
+ if (fileScope && fileScope.size > 0) {
724
+ const bindings = [];
725
+ for (const [name, type] of fileScope)
726
+ bindings.push([name, type]);
727
+ result.typeEnvBindings.push({ filePath: file.path, bindings });
728
+ }
745
729
  for (const match of matches) {
746
730
  const captureMap = {};
747
731
  for (const c of match.captures) {
@@ -749,10 +733,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
749
733
  }
750
734
  // Extract import paths before skipping
751
735
  if (captureMap['import'] && captureMap['import.source']) {
752
- const rawImportPath = language === SupportedLanguages.Kotlin
753
- ? appendKotlinWildcard(captureMap['import.source'].text.replace(/['"<>]/g, ''), captureMap['import'])
754
- : captureMap['import.source'].text.replace(/['"<>]/g, '');
755
- const namedBindings = extractNamedBindings(captureMap['import'], language);
736
+ const rawImportPath = preprocessImportPath(captureMap['import.source'].text, captureMap['import'], language);
737
+ if (!rawImportPath)
738
+ continue;
739
+ const extractor = namedBindingExtractors[language];
740
+ const namedBindings = extractor ? extractor(captureMap['import']) : undefined;
756
741
  result.imports.push({
757
742
  filePath: file.path,
758
743
  rawImportPath,
@@ -941,24 +926,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
941
926
  continue;
942
927
  }
943
928
  }
944
- const nodeLabel = getLabelFromCaptures(captureMap);
929
+ const nodeLabel = getLabelFromCaptures(captureMap, language);
945
930
  if (!nodeLabel)
946
931
  continue;
947
- // C/C++: @definition.function is broad and also matches inline class methods (inside
948
- // a class/struct body). Those are already captured by @definition.method, so skip
949
- // the duplicate Function entry to prevent double-indexing in globalIndex.
950
- if ((language === SupportedLanguages.CPlusPlus || language === SupportedLanguages.C) &&
951
- nodeLabel === 'Function') {
952
- let ancestor = captureMap['definition.function']?.parent;
953
- while (ancestor) {
954
- if (ancestor.type === 'class_specifier' || ancestor.type === 'struct_specifier') {
955
- break; // inside a class body — duplicate of @definition.method
956
- }
957
- ancestor = ancestor.parent;
958
- }
959
- if (ancestor)
960
- continue; // found a class/struct ancestor → skip
961
- }
962
932
  const nameNode = captureMap['name'];
963
933
  // Synthesize name for constructors without explicit @name capture (e.g. Swift init)
964
934
  if (!nameNode && nodeLabel !== 'Constructor')
@@ -980,11 +950,15 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
980
950
  ? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
981
951
  : null;
982
952
  let parameterCount;
953
+ let requiredParameterCount;
954
+ let parameterTypes;
983
955
  let returnType;
984
956
  let declaredType;
985
957
  if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
986
958
  const sig = extractMethodSignature(definitionNode);
987
959
  parameterCount = sig.parameterCount;
960
+ requiredParameterCount = sig.requiredParameterCount;
961
+ parameterTypes = sig.parameterTypes;
988
962
  returnType = sig.returnType;
989
963
  // Language-specific return type fallback (e.g. Ruby YARD @return [Type])
990
964
  // Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
@@ -1018,6 +992,8 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1018
992
  } : {}),
1019
993
  ...(description !== undefined ? { description } : {}),
1020
994
  ...(parameterCount !== undefined ? { parameterCount } : {}),
995
+ ...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
996
+ ...(parameterTypes !== undefined ? { parameterTypes } : {}),
1021
997
  ...(returnType !== undefined ? { returnType } : {}),
1022
998
  },
1023
999
  });
@@ -1031,6 +1007,8 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1031
1007
  nodeId,
1032
1008
  type: nodeLabel,
1033
1009
  ...(parameterCount !== undefined ? { parameterCount } : {}),
1010
+ ...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
1011
+ ...(parameterTypes !== undefined ? { parameterTypes } : {}),
1034
1012
  ...(returnType !== undefined ? { returnType } : {}),
1035
1013
  ...(declaredType !== undefined ? { declaredType } : {}),
1036
1014
  ...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
@@ -1071,7 +1049,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1071
1049
  /** Accumulated result across sub-batches */
1072
1050
  let accumulated = {
1073
1051
  nodes: [], relationships: [], symbols: [],
1074
- imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0,
1052
+ imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], typeEnvBindings: [], skippedLanguages: {}, fileCount: 0,
1075
1053
  };
1076
1054
  let cumulativeProcessed = 0;
1077
1055
  const mergeResult = (target, src) => {
@@ -1084,6 +1062,7 @@ const mergeResult = (target, src) => {
1084
1062
  target.heritage.push(...src.heritage);
1085
1063
  target.routes.push(...src.routes);
1086
1064
  target.constructorBindings.push(...src.constructorBindings);
1065
+ target.typeEnvBindings.push(...src.typeEnvBindings);
1087
1066
  for (const [lang, count] of Object.entries(src.skippedLanguages)) {
1088
1067
  target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
1089
1068
  }
@@ -1106,7 +1085,7 @@ parentPort.on('message', (msg) => {
1106
1085
  if (msg && msg.type === 'flush') {
1107
1086
  parentPort.postMessage({ type: 'result', data: accumulated });
1108
1087
  // Reset for potential reuse
1109
- accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0 };
1088
+ accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], typeEnvBindings: [], skippedLanguages: {}, fileCount: 0 };
1110
1089
  cumulativeProcessed = 0;
1111
1090
  return;
1112
1091
  }
@@ -208,6 +208,8 @@ 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');
211
213
  // Multi-language node types share the same CSV shape (no isExported column)
212
214
  const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
213
215
  const MULTI_LANG_TYPES = ['Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
@@ -292,6 +294,20 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
292
294
  ].join(','));
293
295
  break;
294
296
  }
297
+ case 'Section': {
298
+ const content = await extractContent(node, contentCache);
299
+ await sectionWriter.addRow([
300
+ escapeCSVField(node.id),
301
+ escapeCSVField(node.properties.name || ''),
302
+ escapeCSVField(node.properties.filePath || ''),
303
+ escapeCSVNumber(node.properties.startLine, -1),
304
+ escapeCSVNumber(node.properties.endLine, -1),
305
+ escapeCSVNumber(node.properties.level, 1),
306
+ escapeCSVField(content),
307
+ escapeCSVField(node.properties.description || ''),
308
+ ].join(','));
309
+ break;
310
+ }
295
311
  default: {
296
312
  // Code element nodes (Function, Class, Interface, CodeElement)
297
313
  const writer = codeWriterMap[node.label];
@@ -329,7 +345,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
329
345
  }
330
346
  }
331
347
  // Finish all node writers
332
- const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, ...multiLangWriters.values()];
348
+ const allWriters = [fileWriter, folderWriter, functionWriter, classWriter, interfaceWriter, methodWriter, codeElemWriter, communityWriter, processWriter, sectionWriter, ...multiLangWriters.values()];
333
349
  await Promise.all(allWriters.map(w => w.finish()));
334
350
  // --- Stream relationship CSV ---
335
351
  const relCsvPath = path.join(csvDir, 'relations.csv');
@@ -353,6 +369,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
353
369
  ['Interface', interfaceWriter], ['Method', methodWriter],
354
370
  ['CodeElement', codeElemWriter],
355
371
  ['Community', communityWriter], ['Process', processWriter],
372
+ ['Section', sectionWriter],
356
373
  ...Array.from(multiLangWriters.entries()).map(([name, w]) => [name, w]),
357
374
  ];
358
375
  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,9 @@ 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
+ }
326
383
  if (table === 'Method') {
327
384
  return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
328
385
  }
@@ -363,6 +420,10 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
363
420
  else if (label === 'Folder') {
364
421
  query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`;
365
422
  }
423
+ else if (label === 'Section') {
424
+ const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
425
+ 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}})`;
426
+ }
366
427
  else if (TABLES_WITH_EXPORTED.has(label)) {
367
428
  const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
368
429
  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 +499,10 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
438
499
  else if (label === 'Folder') {
439
500
  query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`;
440
501
  }
502
+ else if (label === 'Section') {
503
+ const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
504
+ 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}`;
505
+ }
441
506
  else if (TABLES_WITH_EXPORTED.has(label)) {
442
507
  const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
443
508
  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,7 +8,7 @@
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"];
12
12
  export type NodeTableName = typeof NODE_TABLES[number];
13
13
  export declare const REL_TABLE_NAME = "CodeRelation";
14
14
  export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "OVERRIDES", "MEMBER_OF", "STEP_IN_PROCESS"];
@@ -41,8 +41,10 @@ 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 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)";
45
+ 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 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 Section TO Section,\n FROM Section TO File,\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)";
46
+ export declare const EMBEDDING_DIMS: number;
47
+ export declare const EMBEDDING_SCHEMA: string;
46
48
  /**
47
49
  * Create vector index for semantic search
48
50
  * Uses HNSW (Hierarchical Navigable Small World) algorithm with cosine similarity
@@ -12,7 +12,7 @@
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
18
  'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'
@@ -171,6 +171,19 @@ export const ANNOTATION_SCHEMA = CODE_ELEMENT_BASE('Annotation');
171
171
  export const CONSTRUCTOR_SCHEMA = CODE_ELEMENT_BASE('Constructor');
172
172
  export const TEMPLATE_SCHEMA = CODE_ELEMENT_BASE('Template');
173
173
  export const MODULE_SCHEMA = CODE_ELEMENT_BASE('Module');
174
+ // Markdown heading sections
175
+ export const SECTION_SCHEMA = `
176
+ CREATE NODE TABLE Section (
177
+ id STRING,
178
+ name STRING,
179
+ filePath STRING,
180
+ startLine INT64,
181
+ endLine INT64,
182
+ level INT64,
183
+ content STRING,
184
+ description STRING,
185
+ PRIMARY KEY (id)
186
+ )`;
174
187
  // ============================================================================
175
188
  // RELATION TABLE SCHEMA
176
189
  // Single table with 'type' property - connects all node tables
@@ -202,6 +215,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
202
215
  FROM File TO \`Constructor\`,
203
216
  FROM File TO \`Template\`,
204
217
  FROM File TO \`Module\`,
218
+ FROM File TO Section,
205
219
  FROM Folder TO Folder,
206
220
  FROM Folder TO File,
207
221
  FROM Function TO Function,
@@ -266,6 +280,8 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
266
280
  FROM \`Template\` TO Interface,
267
281
  FROM \`Template\` TO \`Constructor\`,
268
282
  FROM \`Module\` TO \`Module\`,
283
+ FROM Section TO Section,
284
+ FROM Section TO File,
269
285
  FROM CodeElement TO Community,
270
286
  FROM Interface TO Community,
271
287
  FROM Interface TO Function,
@@ -373,10 +389,16 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
373
389
  // EMBEDDING TABLE SCHEMA
374
390
  // Separate table for vector storage to avoid copy-on-write overhead
375
391
  // ============================================================================
392
+ /** Embedding vector dimensions. Default 384 (snowflake-arctic-embed-xs). */
393
+ const _rawDims = parseInt(process.env.GITNEXUS_EMBEDDING_DIMS ?? '384', 10);
394
+ if (Number.isNaN(_rawDims) || _rawDims <= 0) {
395
+ throw new Error(`GITNEXUS_EMBEDDING_DIMS must be a positive integer, got "${process.env.GITNEXUS_EMBEDDING_DIMS}"`);
396
+ }
397
+ export const EMBEDDING_DIMS = _rawDims;
376
398
  export const EMBEDDING_SCHEMA = `
377
399
  CREATE NODE TABLE ${EMBEDDING_TABLE_NAME} (
378
400
  nodeId STRING,
379
- embedding FLOAT[384],
401
+ embedding FLOAT[${EMBEDDING_DIMS}],
380
402
  PRIMARY KEY (nodeId)
381
403
  )`;
382
404
  /**
@@ -419,6 +441,8 @@ export const NODE_SCHEMA_QUERIES = [
419
441
  CONSTRUCTOR_SCHEMA,
420
442
  TEMPLATE_SCHEMA,
421
443
  MODULE_SCHEMA,
444
+ // Markdown support
445
+ SECTION_SCHEMA,
422
446
  ];
423
447
  export const REL_SCHEMA_QUERIES = [
424
448
  RELATION_SCHEMA,