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
@@ -0,0 +1,60 @@
1
+ /**
2
+ * TypeScript and JavaScript language providers.
3
+ *
4
+ * Both languages share the same type extraction config (typescriptConfig),
5
+ * export checker (tsExportChecker), and named binding extractor
6
+ * (extractTsNamedBindings). They differ in file extensions, tree-sitter
7
+ * queries (TypeScript grammar has interface/type nodes), and language ID.
8
+ */
9
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
10
+ import { defineLanguage } from '../language-provider.js';
11
+ import { typeConfig as typescriptConfig } from '../type-extractors/typescript.js';
12
+ import { tsExportChecker } from '../export-detection.js';
13
+ import { resolveTypescriptImport, resolveJavascriptImport } from '../import-resolvers/standard.js';
14
+ import { extractTsNamedBindings } from '../named-bindings/typescript.js';
15
+ import { TYPESCRIPT_QUERIES, JAVASCRIPT_QUERIES } from '../tree-sitter-queries.js';
16
+ import { typescriptFieldExtractor } from '../field-extractors/typescript.js';
17
+ import { createFieldExtractor } from '../field-extractors/generic.js';
18
+ import { javascriptConfig } from '../field-extractors/configs/typescript-javascript.js';
19
+ const BUILT_INS = new Set([
20
+ 'console', 'log', 'warn', 'error', 'info', 'debug',
21
+ 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
22
+ 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
23
+ 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
24
+ 'JSON', 'parse', 'stringify',
25
+ 'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
26
+ 'Map', 'Set', 'WeakMap', 'WeakSet',
27
+ 'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
28
+ 'Math', 'Date', 'RegExp', 'Error',
29
+ 'require', 'import', 'export', 'fetch', 'Response', 'Request',
30
+ 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
31
+ 'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
32
+ 'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
33
+ 'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
34
+ 'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
35
+ 'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
36
+ 'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
37
+ 'hasOwnProperty', 'toString', 'valueOf',
38
+ ]);
39
+ export const typescriptProvider = defineLanguage({
40
+ id: SupportedLanguages.TypeScript,
41
+ extensions: ['.ts', '.tsx'],
42
+ treeSitterQueries: TYPESCRIPT_QUERIES,
43
+ typeConfig: typescriptConfig,
44
+ exportChecker: tsExportChecker,
45
+ importResolver: resolveTypescriptImport,
46
+ namedBindingExtractor: extractTsNamedBindings,
47
+ fieldExtractor: typescriptFieldExtractor,
48
+ builtInNames: BUILT_INS,
49
+ });
50
+ export const javascriptProvider = defineLanguage({
51
+ id: SupportedLanguages.JavaScript,
52
+ extensions: ['.js', '.jsx'],
53
+ treeSitterQueries: JAVASCRIPT_QUERIES,
54
+ typeConfig: typescriptConfig,
55
+ exportChecker: tsExportChecker,
56
+ importResolver: resolveJavascriptImport,
57
+ namedBindingExtractor: extractTsNamedBindings,
58
+ fieldExtractor: createFieldExtractor(javascriptConfig),
59
+ builtInNames: BUILT_INS,
60
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Markdown Processor
3
+ *
4
+ * Extracts structure from .md files using regex (no tree-sitter dependency).
5
+ * Creates Section nodes for headings with hierarchy, and IMPORTS edges for
6
+ * cross-file links.
7
+ */
8
+ import { KnowledgeGraph } from '../graph/types.js';
9
+ interface MdFile {
10
+ path: string;
11
+ content: string;
12
+ }
13
+ export declare const processMarkdown: (graph: KnowledgeGraph, files: MdFile[], allPathSet: Set<string>) => {
14
+ sections: number;
15
+ links: number;
16
+ };
17
+ export {};
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Markdown Processor
3
+ *
4
+ * Extracts structure from .md files using regex (no tree-sitter dependency).
5
+ * Creates Section nodes for headings with hierarchy, and IMPORTS edges for
6
+ * cross-file links.
7
+ */
8
+ import path from 'node:path';
9
+ import { generateId } from '../../lib/utils.js';
10
+ const HEADING_RE = /^(#{1,6})\s+(.+)$/;
11
+ const LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
12
+ const MD_EXTENSIONS = new Set(['.md', '.mdx']);
13
+ export const processMarkdown = (graph, files, allPathSet) => {
14
+ let totalSections = 0;
15
+ let totalLinks = 0;
16
+ for (const file of files) {
17
+ const ext = path.extname(file.path).toLowerCase();
18
+ if (!MD_EXTENSIONS.has(ext))
19
+ continue;
20
+ const fileNodeId = generateId('File', file.path);
21
+ // Skip if file node doesn't exist (shouldn't happen, structure-processor creates it)
22
+ if (!graph.getNode(fileNodeId))
23
+ continue;
24
+ const lines = file.content.split('\n');
25
+ // --- Extract headings and build hierarchy ---
26
+ // First pass: collect all heading positions so we can compute endLine spans
27
+ const headings = [];
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const match = lines[i].match(HEADING_RE);
30
+ if (!match)
31
+ continue;
32
+ headings.push({
33
+ level: match[1].length,
34
+ heading: match[2].trim(),
35
+ lineNum: i + 1, // 1-indexed
36
+ });
37
+ }
38
+ // Second pass: create nodes with proper endLine spans
39
+ const sectionStack = [];
40
+ for (let h = 0; h < headings.length; h++) {
41
+ const { level, heading, lineNum } = headings[h];
42
+ // endLine = line before next heading at same or higher level, or EOF
43
+ let endLine = lines.length;
44
+ for (let j = h + 1; j < headings.length; j++) {
45
+ if (headings[j].level <= level) {
46
+ endLine = headings[j].lineNum - 1;
47
+ break;
48
+ }
49
+ }
50
+ const sectionId = generateId('Section', `${file.path}:L${lineNum}:${heading}`);
51
+ const node = {
52
+ id: sectionId,
53
+ label: 'Section',
54
+ properties: {
55
+ name: heading,
56
+ filePath: file.path,
57
+ startLine: lineNum,
58
+ endLine,
59
+ level,
60
+ description: `h${level}`,
61
+ },
62
+ };
63
+ graph.addNode(node);
64
+ totalSections++;
65
+ // Find parent: pop stack until we find a level strictly less than current
66
+ while (sectionStack.length > 0 && sectionStack[sectionStack.length - 1].level >= level) {
67
+ sectionStack.pop();
68
+ }
69
+ const parentId = sectionStack.length > 0
70
+ ? sectionStack[sectionStack.length - 1].id
71
+ : fileNodeId;
72
+ graph.addRelationship({
73
+ id: generateId('CONTAINS', `${parentId}->${sectionId}`),
74
+ type: 'CONTAINS',
75
+ sourceId: parentId,
76
+ targetId: sectionId,
77
+ confidence: 1.0,
78
+ reason: 'markdown-heading',
79
+ });
80
+ sectionStack.push({ level, id: sectionId });
81
+ }
82
+ // --- Extract links to other files in the repo ---
83
+ const fileDir = path.dirname(file.path);
84
+ const seenLinks = new Set();
85
+ let linkMatch;
86
+ LINK_RE.lastIndex = 0;
87
+ while ((linkMatch = LINK_RE.exec(file.content)) !== null) {
88
+ const href = linkMatch[2];
89
+ // Skip external URLs, anchors, and mailto
90
+ if (href.startsWith('http://') || href.startsWith('https://') ||
91
+ href.startsWith('#') || href.startsWith('mailto:')) {
92
+ continue;
93
+ }
94
+ // Strip anchor fragments from local links
95
+ const cleanHref = href.split('#')[0];
96
+ if (!cleanHref)
97
+ continue;
98
+ // Resolve relative to the file's directory, then normalize
99
+ const resolved = path.posix.normalize(path.posix.join(fileDir, cleanHref));
100
+ if (allPathSet.has(resolved)) {
101
+ const targetFileId = generateId('File', resolved);
102
+ // Skip if target file node doesn't exist
103
+ if (!graph.getNode(targetFileId))
104
+ continue;
105
+ // Dedup: skip if we've already linked this file pair
106
+ const linkKey = `${fileNodeId}->${targetFileId}`;
107
+ if (seenLinks.has(linkKey))
108
+ continue;
109
+ seenLinks.add(linkKey);
110
+ const relId = generateId('IMPORTS', linkKey);
111
+ graph.addRelationship({
112
+ id: relId,
113
+ type: 'IMPORTS',
114
+ sourceId: fileNodeId,
115
+ targetId: targetFileId,
116
+ confidence: 0.8,
117
+ reason: 'markdown-link',
118
+ });
119
+ totalLinks++;
120
+ }
121
+ }
122
+ }
123
+ return { sections: totalSections, links: totalLinks };
124
+ };
@@ -19,7 +19,7 @@
19
19
  * Cypher: MATCH (c:Class)-[r:CodeRelation {type: 'OVERRIDES'}]->(m:Method)
20
20
  */
21
21
  import { generateId } from '../../lib/utils.js';
22
- import { SupportedLanguages } from '../../config/supported-languages.js';
22
+ import { getProvider } from './languages/index.js';
23
23
  // ---------------------------------------------------------------------------
24
24
  // Internal helpers
25
25
  // ---------------------------------------------------------------------------
@@ -158,10 +158,11 @@ function resolveByMroOrder(methodName, defs, mroOrder, reasonPrefix) {
158
158
  return {
159
159
  resolvedTo: match.methodId,
160
160
  reason: `${reasonPrefix}: ${match.className}::${methodName}`,
161
+ confidence: 0.9, // MRO-ordered resolution
161
162
  };
162
163
  }
163
164
  }
164
- return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition` };
165
+ return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition`, confidence: 0.7 };
165
166
  }
166
167
  function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
167
168
  const classDefs = [];
@@ -179,21 +180,24 @@ function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
179
180
  return {
180
181
  resolvedTo: classDefs[0].methodId,
181
182
  reason: `class method wins: ${classDefs[0].className}::${methodName}`,
183
+ confidence: 0.95, // Class method is authoritative
182
184
  };
183
185
  }
184
186
  if (interfaceDefs.length > 1) {
185
187
  return {
186
188
  resolvedTo: null,
187
189
  reason: `ambiguous: ${methodName} defined in multiple interfaces: ${interfaceDefs.map(d => d.className).join(', ')}`,
190
+ confidence: 0.5,
188
191
  };
189
192
  }
190
193
  if (interfaceDefs.length === 1) {
191
194
  return {
192
195
  resolvedTo: interfaceDefs[0].methodId,
193
196
  reason: `single interface default: ${interfaceDefs[0].className}::${methodName}`,
197
+ confidence: 0.85, // Single interface, unambiguous
194
198
  };
195
199
  }
196
- return { resolvedTo: null, reason: 'no resolution found' };
200
+ return { resolvedTo: null, reason: 'no resolution found', confidence: 0.5 };
197
201
  }
198
202
  // ---------------------------------------------------------------------------
199
203
  // Main entry point
@@ -215,9 +219,10 @@ export function computeMRO(graph) {
215
219
  if (!language)
216
220
  continue;
217
221
  const className = classNode.properties.name;
218
- // Compute linearized MRO depending on language
222
+ // Compute linearized MRO depending on language strategy
223
+ const provider = getProvider(language);
219
224
  let mroOrder;
220
- if (language === SupportedLanguages.Python) {
225
+ if (provider.mroStrategy === 'c3') {
221
226
  const c3Result = c3Linearize(classId, parentMap, c3Cache);
222
227
  mroOrder = c3Result ?? gatherAncestors(classId, parentMap);
223
228
  }
@@ -260,8 +265,8 @@ export function computeMRO(graph) {
260
265
  }
261
266
  // Detect collisions: methods defined in 2+ different ancestors
262
267
  const ambiguities = [];
263
- // Compute transitive edge types once per class (only needed for C#/Java)
264
- const needsEdgeTypes = language === SupportedLanguages.CSharp || language === SupportedLanguages.Java || language === SupportedLanguages.Kotlin;
268
+ // Compute transitive edge types once per class (only needed for implements-split languages)
269
+ const needsEdgeTypes = provider.mroStrategy === 'implements-split';
265
270
  const classEdgeTypes = needsEdgeTypes
266
271
  ? buildTransitiveEdgeTypes(classId, parentMap, parentEdgeType)
267
272
  : undefined;
@@ -277,22 +282,21 @@ export function computeMRO(graph) {
277
282
  if (ownDefinesIt)
278
283
  continue;
279
284
  let resolution;
280
- switch (language) {
281
- case SupportedLanguages.CPlusPlus:
282
- resolution = resolveByMroOrder(methodName, defs, mroOrder, 'C++ leftmost base');
285
+ switch (provider.mroStrategy) {
286
+ case 'leftmost-base':
287
+ resolution = resolveByMroOrder(methodName, defs, mroOrder, 'leftmost base');
283
288
  break;
284
- case SupportedLanguages.CSharp:
285
- case SupportedLanguages.Java:
286
- case SupportedLanguages.Kotlin:
289
+ case 'implements-split':
287
290
  resolution = resolveCsharpJava(methodName, defs, classEdgeTypes);
288
291
  break;
289
- case SupportedLanguages.Python:
290
- resolution = resolveByMroOrder(methodName, defs, mroOrder, 'Python C3 MRO');
292
+ case 'c3':
293
+ resolution = resolveByMroOrder(methodName, defs, mroOrder, 'C3 MRO');
291
294
  break;
292
- case SupportedLanguages.Rust:
295
+ case 'qualified-syntax':
293
296
  resolution = {
294
297
  resolvedTo: null,
295
- reason: `Rust requires qualified syntax: <Type as Trait>::${methodName}()`,
298
+ reason: `requires qualified syntax: <Type as Trait>::${methodName}()`,
299
+ confidence: 0.5,
296
300
  };
297
301
  break;
298
302
  default:
@@ -316,7 +320,7 @@ export function computeMRO(graph) {
316
320
  sourceId: classId,
317
321
  targetId: resolution.resolvedTo,
318
322
  type: 'OVERRIDES',
319
- confidence: 1.0,
323
+ confidence: resolution.confidence,
320
324
  reason: resolution.reason,
321
325
  });
322
326
  overrideEdges++;
@@ -0,0 +1,18 @@
1
+ import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
2
+ import type { NamedImportMap } from './import-processor.js';
3
+ /**
4
+ * Walk a named-binding re-export chain through NamedImportMap.
5
+ *
6
+ * When file A imports { User } from B, and B re-exports { User } from C,
7
+ * the NamedImportMap for A points to B, but B has no User definition.
8
+ * This function follows the chain: A→B→C until a definition is found.
9
+ *
10
+ * Returns the definitions found at the end of the chain, or null if the
11
+ * chain breaks (missing binding, circular reference, or depth exceeded).
12
+ * Max depth 5 to prevent infinite loops.
13
+ *
14
+ * @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
15
+ * complete unfiltered result. Passing a file-filtered subset will cause
16
+ * silent misses at depth=0 for non-aliased bindings.
17
+ */
18
+ export declare function walkBindingChain(name: string, currentFilePath: string, symbolTable: SymbolTable, namedImportMap: NamedImportMap, allDefs: SymbolDefinition[]): SymbolDefinition[] | null;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Walk a named-binding re-export chain through NamedImportMap.
3
+ *
4
+ * When file A imports { User } from B, and B re-exports { User } from C,
5
+ * the NamedImportMap for A points to B, but B has no User definition.
6
+ * This function follows the chain: A→B→C until a definition is found.
7
+ *
8
+ * Returns the definitions found at the end of the chain, or null if the
9
+ * chain breaks (missing binding, circular reference, or depth exceeded).
10
+ * Max depth 5 to prevent infinite loops.
11
+ *
12
+ * @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
13
+ * complete unfiltered result. Passing a file-filtered subset will cause
14
+ * silent misses at depth=0 for non-aliased bindings.
15
+ */
16
+ export function walkBindingChain(name, currentFilePath, symbolTable, namedImportMap, allDefs) {
17
+ let lookupFile = currentFilePath;
18
+ let lookupName = name;
19
+ const visited = new Set();
20
+ for (let depth = 0; depth < 5; depth++) {
21
+ const bindings = namedImportMap.get(lookupFile);
22
+ if (!bindings)
23
+ return null;
24
+ const binding = bindings.get(lookupName);
25
+ if (!binding)
26
+ return null;
27
+ const key = `${binding.sourcePath}:${binding.exportedName}`;
28
+ if (visited.has(key))
29
+ return null; // circular
30
+ visited.add(key);
31
+ const targetName = binding.exportedName;
32
+ const resolvedDefs = targetName !== lookupName || depth > 0
33
+ ? symbolTable.lookupFuzzy(targetName).filter(def => def.filePath === binding.sourcePath)
34
+ : allDefs.filter(def => def.filePath === binding.sourcePath);
35
+ if (resolvedDefs.length > 0)
36
+ return resolvedDefs;
37
+ // No definition in source file → follow re-export chain
38
+ lookupFile = binding.sourcePath;
39
+ lookupName = targetName;
40
+ }
41
+ return null;
42
+ }
@@ -0,0 +1,3 @@
1
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractCSharpNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
@@ -0,0 +1,37 @@
1
+ export function extractCSharpNamedBindings(importNode) {
2
+ // using_directive — three forms:
3
+ // using Alias = NS.Type; → aliasIdent + qualifiedName
4
+ // using static NS.Type; → static + qualifiedName (no alias)
5
+ // using NS; → qualifiedName only (namespace, not capturable)
6
+ if (importNode.type !== 'using_directive')
7
+ return undefined;
8
+ let aliasIdent = null;
9
+ let qualifiedName = null;
10
+ let isStatic = false;
11
+ for (let i = 0; i < importNode.childCount; i++) {
12
+ const child = importNode.child(i);
13
+ if (child?.text === 'static')
14
+ isStatic = true;
15
+ }
16
+ for (let i = 0; i < importNode.namedChildCount; i++) {
17
+ const child = importNode.namedChild(i);
18
+ if (child?.type === 'identifier' && !aliasIdent)
19
+ aliasIdent = child;
20
+ else if (child?.type === 'qualified_name')
21
+ qualifiedName = child;
22
+ }
23
+ // Form 1: using Alias = NS.Type;
24
+ if (aliasIdent && qualifiedName) {
25
+ const fullText = qualifiedName.text;
26
+ const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
27
+ return [{ local: aliasIdent.text, exported: exportedName }];
28
+ }
29
+ // Form 2: using static NS.Type; — last segment is the class name
30
+ if (isStatic && qualifiedName) {
31
+ const fullText = qualifiedName.text;
32
+ const lastSegment = fullText.includes('.') ? fullText.split('.').pop() : fullText;
33
+ return [{ local: lastSegment, exported: lastSegment }];
34
+ }
35
+ // Form 3: using NS; — namespace import, can't resolve to per-symbol bindings
36
+ return undefined;
37
+ }
@@ -0,0 +1,3 @@
1
+ import { type SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractJavaNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
@@ -0,0 +1,29 @@
1
+ import { findChild } from '../utils/ast-helpers.js';
2
+ export function extractJavaNamedBindings(importNode) {
3
+ // import_declaration > scoped_identifier "com.example.models.User"
4
+ // Wildcard imports (.*) don't produce named bindings
5
+ if (importNode.type !== 'import_declaration')
6
+ return undefined;
7
+ // Check for asterisk (wildcard import) and static modifier
8
+ let isStatic = false;
9
+ for (let i = 0; i < importNode.childCount; i++) {
10
+ const child = importNode.child(i);
11
+ if (child?.type === 'asterisk')
12
+ return undefined;
13
+ if (child?.text === 'static')
14
+ isStatic = true;
15
+ }
16
+ const scopedId = findChild(importNode, 'scoped_identifier');
17
+ if (!scopedId)
18
+ return undefined;
19
+ const fullText = scopedId.text;
20
+ const lastDot = fullText.lastIndexOf('.');
21
+ if (lastDot === -1)
22
+ return undefined;
23
+ const name = fullText.slice(lastDot + 1);
24
+ // Non-static: skip lowercase names — those are package imports, not class imports.
25
+ // Static: allow lowercase — `import static models.UserFactory.getUser` imports a method.
26
+ if (!isStatic && name[0] && name[0] === name[0].toLowerCase())
27
+ return undefined;
28
+ return [{ local: name, exported: name }];
29
+ }
@@ -0,0 +1,3 @@
1
+ import { type SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractKotlinNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
@@ -0,0 +1,36 @@
1
+ import { findChild } from '../utils/ast-helpers.js';
2
+ export function extractKotlinNamedBindings(importNode) {
3
+ // import_header > identifier + import_alias > simple_identifier
4
+ if (importNode.type !== 'import_header')
5
+ return undefined;
6
+ const fullIdent = findChild(importNode, 'identifier');
7
+ if (!fullIdent)
8
+ return undefined;
9
+ const fullText = fullIdent.text;
10
+ const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
11
+ const importAlias = findChild(importNode, 'import_alias');
12
+ if (importAlias) {
13
+ // Aliased: import com.example.User as U
14
+ const aliasIdent = findChild(importAlias, 'simple_identifier');
15
+ if (!aliasIdent)
16
+ return undefined;
17
+ return [{ local: aliasIdent.text, exported: exportedName }];
18
+ }
19
+ // Non-aliased: import com.example.User → local="User", exported="User"
20
+ // Also handles top-level function imports: import models.getUser → local="getUser"
21
+ // Skip wildcard imports (ending in *)
22
+ if (fullText.endsWith('.*') || fullText.endsWith('*'))
23
+ return undefined;
24
+ // Skip class-member imports (e.g., import util.OneArg.writeAudit) where the
25
+ // second-to-last segment is PascalCase (a class name). Multiple member imports
26
+ // with the same function name would collide in NamedImportMap, breaking
27
+ // arity-based disambiguation. Top-level function imports (import models.getUser)
28
+ // and class imports (import models.User) have package-only prefixes.
29
+ const segments = fullText.split('.');
30
+ if (segments.length >= 3) {
31
+ const parentSegment = segments[segments.length - 2];
32
+ if (parentSegment[0] && parentSegment[0] === parentSegment[0].toUpperCase())
33
+ return undefined;
34
+ }
35
+ return [{ local: exportedName, exported: exportedName }];
36
+ }
@@ -0,0 +1,3 @@
1
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractPhpNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
@@ -0,0 +1,61 @@
1
+ export function extractPhpNamedBindings(importNode) {
2
+ // namespace_use_declaration > namespace_use_clause* (flat)
3
+ // namespace_use_declaration > namespace_use_group > namespace_use_clause* (grouped)
4
+ if (importNode.type !== 'namespace_use_declaration')
5
+ return undefined;
6
+ // Skip 'use function' and 'use const' declarations — these import callables/constants,
7
+ // not class types, and should not be added to namedImportMap as type bindings.
8
+ const useTypeNode = importNode.childForFieldName?.('type');
9
+ if (useTypeNode && (useTypeNode.text === 'function' || useTypeNode.text === 'const')) {
10
+ return undefined;
11
+ }
12
+ const bindings = [];
13
+ // Collect all clauses — from direct children AND from namespace_use_group
14
+ const clauses = [];
15
+ for (let i = 0; i < importNode.namedChildCount; i++) {
16
+ const child = importNode.namedChild(i);
17
+ if (child?.type === 'namespace_use_clause') {
18
+ clauses.push(child);
19
+ }
20
+ else if (child?.type === 'namespace_use_group') {
21
+ for (let j = 0; j < child.namedChildCount; j++) {
22
+ const groupChild = child.namedChild(j);
23
+ if (groupChild?.type === 'namespace_use_clause')
24
+ clauses.push(groupChild);
25
+ }
26
+ }
27
+ }
28
+ for (const clause of clauses) {
29
+ // Flat imports: qualified_name + name (alias)
30
+ let qualifiedName = null;
31
+ const names = [];
32
+ for (let j = 0; j < clause.namedChildCount; j++) {
33
+ const child = clause.namedChild(j);
34
+ if (child?.type === 'qualified_name')
35
+ qualifiedName = child;
36
+ else if (child?.type === 'name')
37
+ names.push(child);
38
+ }
39
+ if (qualifiedName && names.length > 0) {
40
+ // Flat aliased import: use App\Models\Repo as R;
41
+ const fullText = qualifiedName.text;
42
+ const exportedName = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
43
+ bindings.push({ local: names[0].text, exported: exportedName });
44
+ }
45
+ else if (qualifiedName && names.length === 0) {
46
+ // Flat non-aliased import: use App\Models\User;
47
+ const fullText = qualifiedName.text;
48
+ const lastSegment = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
49
+ bindings.push({ local: lastSegment, exported: lastSegment });
50
+ }
51
+ else if (!qualifiedName && names.length >= 2) {
52
+ // Grouped aliased import: {Repo as R} — first name = exported, second = alias
53
+ bindings.push({ local: names[1].text, exported: names[0].text });
54
+ }
55
+ else if (!qualifiedName && names.length === 1) {
56
+ // Grouped non-aliased import: {User} in use App\Models\{User, Repo as R}
57
+ bindings.push({ local: names[0].text, exported: names[0].text });
58
+ }
59
+ }
60
+ return bindings.length > 0 ? bindings : undefined;
61
+ }
@@ -0,0 +1,3 @@
1
+ import { type SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractPythonNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
@@ -0,0 +1,49 @@
1
+ import { findChild } from '../utils/ast-helpers.js';
2
+ export function extractPythonNamedBindings(importNode) {
3
+ // Handle: from x import User, Repo as R
4
+ if (importNode.type === 'import_from_statement') {
5
+ const bindings = [];
6
+ for (let i = 0; i < importNode.namedChildCount; i++) {
7
+ const child = importNode.namedChild(i);
8
+ if (!child)
9
+ continue;
10
+ if (child.type === 'dotted_name') {
11
+ // Skip the module_name (first dotted_name is the source module)
12
+ const fieldName = importNode.childForFieldName?.('module_name');
13
+ if (fieldName && child.startIndex === fieldName.startIndex)
14
+ continue;
15
+ // This is an imported name: from x import User
16
+ const name = child.text;
17
+ if (name)
18
+ bindings.push({ local: name, exported: name });
19
+ }
20
+ if (child.type === 'aliased_import') {
21
+ // from x import Repo as R
22
+ const dottedName = findChild(child, 'dotted_name');
23
+ const aliasIdent = findChild(child, 'identifier');
24
+ if (dottedName && aliasIdent) {
25
+ bindings.push({ local: aliasIdent.text, exported: dottedName.text });
26
+ }
27
+ }
28
+ }
29
+ return bindings.length > 0 ? bindings : undefined;
30
+ }
31
+ // Handle: import numpy as np (import_statement with aliased_import child)
32
+ // Tagged with isModuleAlias so applyImportResult routes these directly to
33
+ // moduleAliasMap (e.g. "np" → "numpy.py") instead of namedImportMap.
34
+ if (importNode.type === 'import_statement') {
35
+ const bindings = [];
36
+ for (let i = 0; i < importNode.namedChildCount; i++) {
37
+ const child = importNode.namedChild(i);
38
+ if (!child || child.type !== 'aliased_import')
39
+ continue;
40
+ const dottedName = findChild(child, 'dotted_name');
41
+ const aliasIdent = findChild(child, 'identifier');
42
+ if (dottedName && aliasIdent) {
43
+ bindings.push({ local: aliasIdent.text, exported: dottedName.text, isModuleAlias: true });
44
+ }
45
+ }
46
+ return bindings.length > 0 ? bindings : undefined;
47
+ }
48
+ return undefined;
49
+ }
@@ -0,0 +1,3 @@
1
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
2
+ import type { NamedBinding } from './types.js';
3
+ export declare function extractRustNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;