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,73 @@
1
+ import type { SyntaxNode } from './ast-helpers.js';
2
+ /** Node types representing call expressions across supported languages. */
3
+ export declare const CALL_EXPRESSION_TYPES: Set<string>;
4
+ /**
5
+ * Hard limit on chain depth to prevent runaway recursion.
6
+ * For `a.b().c().d()`, the chain has depth 2 (b and c before d).
7
+ */
8
+ export declare const MAX_CHAIN_DEPTH = 3;
9
+ /**
10
+ * Count direct arguments for a call expression across common tree-sitter grammars.
11
+ * Returns undefined when the argument container cannot be located cheaply.
12
+ */
13
+ export declare const countCallArguments: (callNode: SyntaxNode | null | undefined) => number | undefined;
14
+ type CallForm = 'free' | 'member' | 'constructor';
15
+ /**
16
+ * Infer whether a captured call site is a free call, member call, or constructor.
17
+ * Returns undefined if the form cannot be determined.
18
+ *
19
+ * Works by inspecting the AST structure between callNode (@call) and nameNode (@call.name).
20
+ * No tree-sitter query changes needed — the distinction is in the node types.
21
+ */
22
+ export declare const inferCallForm: (callNode: SyntaxNode, nameNode: SyntaxNode) => CallForm | undefined;
23
+ export declare const extractReceiverName: (nameNode: SyntaxNode) => string | undefined;
24
+ /**
25
+ * Extract the raw receiver AST node for a member call.
26
+ * Unlike extractReceiverName, this returns the receiver node regardless of its type —
27
+ * including call_expression / method_invocation nodes that appear in chained calls
28
+ * like `svc.getUser().save()`.
29
+ *
30
+ * Returns undefined when the call is not a member call or when no receiver node
31
+ * can be found (e.g. top-level free calls).
32
+ */
33
+ export declare const extractReceiverNode: (nameNode: SyntaxNode) => SyntaxNode | undefined;
34
+ /** One step in a mixed receiver chain. */
35
+ export type MixedChainStep = {
36
+ kind: 'field' | 'call';
37
+ name: string;
38
+ };
39
+ /**
40
+ * Walk a receiver AST node that is itself a call expression, accumulating the
41
+ * chain of intermediate method names up to MAX_CHAIN_DEPTH.
42
+ *
43
+ * For `svc.getUser().save()`, called with the receiver of `save` (getUser() call):
44
+ * returns { chain: ['getUser'], baseReceiverName: 'svc' }
45
+ *
46
+ * For `a.b().c().d()`, called with the receiver of `d` (c() call):
47
+ * returns { chain: ['b', 'c'], baseReceiverName: 'a' }
48
+ */
49
+ export declare function extractCallChain(receiverCallNode: SyntaxNode): {
50
+ chain: string[];
51
+ baseReceiverName: string | undefined;
52
+ } | undefined;
53
+ /**
54
+ * Walk a receiver AST node that may interleave field accesses and method calls,
55
+ * building a unified chain of steps up to MAX_CHAIN_DEPTH.
56
+ *
57
+ * For `svc.getUser().address.save()`, called with the receiver of `save`
58
+ * (`svc.getUser().address`, a field access node):
59
+ * returns { chain: [{ kind:'call', name:'getUser' }, { kind:'field', name:'address' }],
60
+ * baseReceiverName: 'svc' }
61
+ *
62
+ * For `user.getAddress().city.getName()`, called with receiver of `getName`
63
+ * (`user.getAddress().city`):
64
+ * returns { chain: [{ kind:'call', name:'getAddress' }, { kind:'field', name:'city' }],
65
+ * baseReceiverName: 'user' }
66
+ *
67
+ * Pure field chains and pure call chains are special cases (all steps same kind).
68
+ */
69
+ export declare function extractMixedChain(receiverNode: SyntaxNode): {
70
+ chain: MixedChainStep[];
71
+ baseReceiverName: string | undefined;
72
+ } | undefined;
73
+ export {};
@@ -0,0 +1,527 @@
1
+ import { CALL_ARGUMENT_LIST_TYPES } from './ast-helpers.js';
2
+ /** Node types representing call expressions across supported languages. */
3
+ export const CALL_EXPRESSION_TYPES = new Set([
4
+ 'call_expression', // TS/JS/C/C++/Go/Rust
5
+ 'method_invocation', // Java
6
+ 'member_call_expression', // PHP
7
+ 'nullsafe_member_call_expression', // PHP ?.
8
+ 'call', // Python/Ruby
9
+ 'invocation_expression', // C#
10
+ ]);
11
+ /**
12
+ * Hard limit on chain depth to prevent runaway recursion.
13
+ * For `a.b().c().d()`, the chain has depth 2 (b and c before d).
14
+ */
15
+ export const MAX_CHAIN_DEPTH = 3;
16
+ /**
17
+ * Count direct arguments for a call expression across common tree-sitter grammars.
18
+ * Returns undefined when the argument container cannot be located cheaply.
19
+ */
20
+ export const countCallArguments = (callNode) => {
21
+ if (!callNode)
22
+ return undefined;
23
+ // Direct field or direct child (most languages)
24
+ let argsNode = callNode.childForFieldName('arguments')
25
+ ?? callNode.children.find((child) => CALL_ARGUMENT_LIST_TYPES.has(child.type));
26
+ // Kotlin/Swift: call_expression → call_suffix → value_arguments
27
+ // Search one level deeper for languages that wrap arguments in a suffix node
28
+ if (!argsNode) {
29
+ for (const child of callNode.children) {
30
+ if (!child.isNamed)
31
+ continue;
32
+ const nested = child.children.find((gc) => CALL_ARGUMENT_LIST_TYPES.has(gc.type));
33
+ if (nested) {
34
+ argsNode = nested;
35
+ break;
36
+ }
37
+ }
38
+ }
39
+ if (!argsNode)
40
+ return undefined;
41
+ let count = 0;
42
+ for (const child of argsNode.children) {
43
+ if (!child.isNamed)
44
+ continue;
45
+ if (child.type === 'comment')
46
+ continue;
47
+ count++;
48
+ }
49
+ return count;
50
+ };
51
+ // ── Call-form discrimination (Phase 1, Step D) ─────────────────────────
52
+ /**
53
+ * AST node types that indicate a member-access wrapper around the callee name.
54
+ * When nameNode.parent.type is one of these, the call is a member call.
55
+ */
56
+ const MEMBER_ACCESS_NODE_TYPES = new Set([
57
+ 'member_expression', // TS/JS: obj.method()
58
+ 'attribute', // Python: obj.method()
59
+ 'member_access_expression', // C#: obj.Method()
60
+ 'field_expression', // Rust/C++: obj.method() / ptr->method()
61
+ 'selector_expression', // Go: obj.Method()
62
+ 'navigation_suffix', // Kotlin/Swift: obj.method() — nameNode sits inside navigation_suffix
63
+ 'member_binding_expression', // C#: user?.Method() — null-conditional access
64
+ 'unconditional_assignable_selector', // Dart: obj.method() — nameNode inside selector > unconditional_assignable_selector
65
+ ]);
66
+ /**
67
+ * Call node types that are inherently constructor invocations.
68
+ * Only includes patterns that the tree-sitter queries already capture as @call.
69
+ */
70
+ const CONSTRUCTOR_CALL_NODE_TYPES = new Set([
71
+ 'constructor_invocation', // Kotlin: Foo()
72
+ 'new_expression', // TS/JS/C++: new Foo()
73
+ 'object_creation_expression', // Java/C#/PHP: new Foo()
74
+ 'implicit_object_creation_expression', // C# 9: User u = new(...)
75
+ 'composite_literal', // Go: User{...}
76
+ 'struct_expression', // Rust: User { ... }
77
+ ]);
78
+ /**
79
+ * AST node types for scoped/qualified calls (e.g., Foo::new() in Rust, Foo::bar() in C++).
80
+ */
81
+ const SCOPED_CALL_NODE_TYPES = new Set([
82
+ 'scoped_identifier', // Rust: Foo::new()
83
+ 'qualified_identifier', // C++: ns::func()
84
+ ]);
85
+ /**
86
+ * Infer whether a captured call site is a free call, member call, or constructor.
87
+ * Returns undefined if the form cannot be determined.
88
+ *
89
+ * Works by inspecting the AST structure between callNode (@call) and nameNode (@call.name).
90
+ * No tree-sitter query changes needed — the distinction is in the node types.
91
+ */
92
+ export const inferCallForm = (callNode, nameNode) => {
93
+ // 1. Constructor: callNode itself is a constructor invocation (Kotlin)
94
+ if (CONSTRUCTOR_CALL_NODE_TYPES.has(callNode.type)) {
95
+ return 'constructor';
96
+ }
97
+ // 2. Member call: nameNode's parent is a member-access wrapper
98
+ const nameParent = nameNode.parent;
99
+ if (nameParent && MEMBER_ACCESS_NODE_TYPES.has(nameParent.type)) {
100
+ return 'member';
101
+ }
102
+ // 3. PHP: the callNode itself distinguishes member vs free calls
103
+ if (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression') {
104
+ return 'member';
105
+ }
106
+ if (callNode.type === 'scoped_call_expression') {
107
+ return 'member'; // static call Foo::bar()
108
+ }
109
+ // 4. Java method_invocation: member if it has an 'object' field
110
+ if (callNode.type === 'method_invocation' && callNode.childForFieldName('object')) {
111
+ return 'member';
112
+ }
113
+ // 4b. Ruby call with receiver: obj.method
114
+ if (callNode.type === 'call' && callNode.childForFieldName('receiver')) {
115
+ return 'member';
116
+ }
117
+ // 5. Scoped calls (Rust Foo::new(), C++ ns::func()): treat as free
118
+ // The receiver is a type, not an instance — handled differently in Phase 3
119
+ if (nameParent && SCOPED_CALL_NODE_TYPES.has(nameParent.type)) {
120
+ return 'free';
121
+ }
122
+ // 6. Default: if nameNode is a direct child of callNode, it's a free call
123
+ if (nameNode.parent === callNode || nameParent?.parent === callNode) {
124
+ return 'free';
125
+ }
126
+ return undefined;
127
+ };
128
+ /**
129
+ * Extract the receiver identifier for member calls.
130
+ * Only captures simple identifiers — returns undefined for complex expressions
131
+ * like getUser().save() or arr[0].method().
132
+ */
133
+ const SIMPLE_RECEIVER_TYPES = new Set([
134
+ 'identifier',
135
+ 'simple_identifier',
136
+ 'variable_name', // PHP $variable (tree-sitter-php)
137
+ 'name', // PHP name node
138
+ 'this', // TS/JS/Java/C# this.method()
139
+ 'self', // Rust/Python self.method()
140
+ 'super', // TS/JS/Java/Kotlin/Ruby super.method()
141
+ 'super_expression', // Kotlin wraps super in super_expression
142
+ 'base', // C# base.Method()
143
+ 'parent', // PHP parent::method()
144
+ 'constant', // Ruby CONSTANT.method() (uppercase identifiers)
145
+ ]);
146
+ export const extractReceiverName = (nameNode) => {
147
+ const parent = nameNode.parent;
148
+ if (!parent)
149
+ return undefined;
150
+ // PHP: member_call_expression / nullsafe_member_call_expression — receiver is on the callNode
151
+ // Java: method_invocation — receiver is the 'object' field on callNode
152
+ // For these, parent of nameNode is the call itself, so check the call's object field
153
+ const callNode = parent.parent ?? parent;
154
+ let receiver = null;
155
+ // Try standard field names used across grammars
156
+ receiver = parent.childForFieldName('object') // TS/JS member_expression, Python attribute, PHP, Java
157
+ ?? parent.childForFieldName('value') // Rust field_expression
158
+ ?? parent.childForFieldName('operand') // Go selector_expression
159
+ ?? parent.childForFieldName('expression') // C# member_access_expression
160
+ ?? parent.childForFieldName('argument'); // C++ field_expression
161
+ // Java method_invocation: 'object' field is on the callNode, not on nameNode's parent
162
+ if (!receiver && callNode.type === 'method_invocation') {
163
+ receiver = callNode.childForFieldName('object');
164
+ }
165
+ // PHP: member_call_expression has 'object' on the call node
166
+ if (!receiver && (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression')) {
167
+ receiver = callNode.childForFieldName('object');
168
+ }
169
+ // Ruby: call node has 'receiver' field
170
+ if (!receiver && parent.type === 'call') {
171
+ receiver = parent.childForFieldName('receiver');
172
+ }
173
+ // PHP scoped_call_expression (parent::method(), self::method()):
174
+ // nameNode's direct parent IS the scoped_call_expression (name is a direct child)
175
+ if (!receiver && (parent.type === 'scoped_call_expression' || callNode.type === 'scoped_call_expression')) {
176
+ const scopedCall = parent.type === 'scoped_call_expression' ? parent : callNode;
177
+ receiver = scopedCall.childForFieldName('scope');
178
+ // relative_scope wraps 'parent'/'self'/'static' — unwrap to get the keyword
179
+ if (receiver?.type === 'relative_scope') {
180
+ receiver = receiver.firstChild;
181
+ }
182
+ }
183
+ // Dart: unconditional_assignable_selector is inside a `selector`, which is a sibling
184
+ // of the receiver in the expression_statement. For `user.save()`, the previous named
185
+ // sibling of the `selector` is `identifier [user]`.
186
+ if (!receiver && parent.type === 'unconditional_assignable_selector') {
187
+ const selectorNode = parent.parent; // selector [.save]
188
+ if (selectorNode) {
189
+ receiver = selectorNode.previousNamedSibling;
190
+ }
191
+ }
192
+ // C# null-conditional: user?.Save() → conditional_access_expression wraps member_binding_expression
193
+ if (!receiver && parent.type === 'member_binding_expression') {
194
+ const condAccess = parent.parent;
195
+ if (condAccess?.type === 'conditional_access_expression') {
196
+ receiver = condAccess.firstNamedChild;
197
+ }
198
+ }
199
+ // Kotlin/Swift: navigation_expression target is the first child
200
+ if (!receiver && parent.type === 'navigation_suffix') {
201
+ const navExpr = parent.parent;
202
+ if (navExpr?.type === 'navigation_expression') {
203
+ // First named child is the target (receiver)
204
+ for (const child of navExpr.children) {
205
+ if (child.isNamed && child !== parent) {
206
+ receiver = child;
207
+ break;
208
+ }
209
+ }
210
+ }
211
+ }
212
+ if (!receiver)
213
+ return undefined;
214
+ // Only capture simple identifiers — refuse complex expressions
215
+ if (SIMPLE_RECEIVER_TYPES.has(receiver.type)) {
216
+ return receiver.text;
217
+ }
218
+ // Python super().method(): receiver is a call node `super()` — extract the function name
219
+ if (receiver.type === 'call') {
220
+ const func = receiver.childForFieldName('function');
221
+ if (func?.text === 'super')
222
+ return 'super';
223
+ }
224
+ return undefined;
225
+ };
226
+ /**
227
+ * Extract the raw receiver AST node for a member call.
228
+ * Unlike extractReceiverName, this returns the receiver node regardless of its type —
229
+ * including call_expression / method_invocation nodes that appear in chained calls
230
+ * like `svc.getUser().save()`.
231
+ *
232
+ * Returns undefined when the call is not a member call or when no receiver node
233
+ * can be found (e.g. top-level free calls).
234
+ */
235
+ export const extractReceiverNode = (nameNode) => {
236
+ const parent = nameNode.parent;
237
+ if (!parent)
238
+ return undefined;
239
+ const callNode = parent.parent ?? parent;
240
+ let receiver = null;
241
+ receiver = parent.childForFieldName('object')
242
+ ?? parent.childForFieldName('value')
243
+ ?? parent.childForFieldName('operand')
244
+ ?? parent.childForFieldName('expression')
245
+ ?? parent.childForFieldName('argument');
246
+ if (!receiver && callNode.type === 'method_invocation') {
247
+ receiver = callNode.childForFieldName('object');
248
+ }
249
+ if (!receiver && (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression')) {
250
+ receiver = callNode.childForFieldName('object');
251
+ }
252
+ if (!receiver && parent.type === 'call') {
253
+ receiver = parent.childForFieldName('receiver');
254
+ }
255
+ if (!receiver && (parent.type === 'scoped_call_expression' || callNode.type === 'scoped_call_expression')) {
256
+ const scopedCall = parent.type === 'scoped_call_expression' ? parent : callNode;
257
+ receiver = scopedCall.childForFieldName('scope');
258
+ if (receiver?.type === 'relative_scope') {
259
+ receiver = receiver.firstChild;
260
+ }
261
+ }
262
+ // Dart: unconditional_assignable_selector — receiver is previous sibling of the selector
263
+ if (!receiver && parent.type === 'unconditional_assignable_selector') {
264
+ const selectorNode = parent.parent;
265
+ if (selectorNode) {
266
+ receiver = selectorNode.previousNamedSibling;
267
+ }
268
+ }
269
+ if (!receiver && parent.type === 'member_binding_expression') {
270
+ const condAccess = parent.parent;
271
+ if (condAccess?.type === 'conditional_access_expression') {
272
+ receiver = condAccess.firstNamedChild;
273
+ }
274
+ }
275
+ if (!receiver && parent.type === 'navigation_suffix') {
276
+ const navExpr = parent.parent;
277
+ if (navExpr?.type === 'navigation_expression') {
278
+ for (const child of navExpr.children) {
279
+ if (child.isNamed && child !== parent) {
280
+ receiver = child;
281
+ break;
282
+ }
283
+ }
284
+ }
285
+ }
286
+ return receiver ?? undefined;
287
+ };
288
+ // ── Chained-call extraction ───────────────────────────────────────────────
289
+ /** Node types representing member/field access across languages. */
290
+ const FIELD_ACCESS_NODE_TYPES = new Set([
291
+ 'member_expression', // TS/JS
292
+ 'member_access_expression', // C#
293
+ 'selector_expression', // Go
294
+ 'field_expression', // Rust/C++
295
+ 'field_access', // Java
296
+ 'attribute', // Python
297
+ 'navigation_expression', // Kotlin/Swift
298
+ 'member_binding_expression', // C# null-conditional (user?.Address)
299
+ ]);
300
+ /**
301
+ * Walk a receiver AST node that is itself a call expression, accumulating the
302
+ * chain of intermediate method names up to MAX_CHAIN_DEPTH.
303
+ *
304
+ * For `svc.getUser().save()`, called with the receiver of `save` (getUser() call):
305
+ * returns { chain: ['getUser'], baseReceiverName: 'svc' }
306
+ *
307
+ * For `a.b().c().d()`, called with the receiver of `d` (c() call):
308
+ * returns { chain: ['b', 'c'], baseReceiverName: 'a' }
309
+ */
310
+ export function extractCallChain(receiverCallNode) {
311
+ const chain = [];
312
+ let current = receiverCallNode;
313
+ while (CALL_EXPRESSION_TYPES.has(current.type) && chain.length < MAX_CHAIN_DEPTH) {
314
+ // Extract the method name from this call node.
315
+ const funcNode = current.childForFieldName?.('function')
316
+ ?? current.childForFieldName?.('name')
317
+ ?? current.childForFieldName?.('method'); // Ruby `call` node
318
+ let methodName;
319
+ let innerReceiver = null;
320
+ if (funcNode) {
321
+ // member_expression / attribute: last named child is the method identifier
322
+ methodName = funcNode.lastNamedChild?.text ?? funcNode.text;
323
+ }
324
+ // Kotlin/Swift: call_expression exposes callee as firstNamedChild, not a field.
325
+ // navigation_expression: method name is in navigation_suffix → simple_identifier.
326
+ if (!funcNode && current.type === 'call_expression') {
327
+ const callee = current.firstNamedChild;
328
+ if (callee?.type === 'navigation_expression') {
329
+ const suffix = callee.lastNamedChild;
330
+ if (suffix?.type === 'navigation_suffix') {
331
+ methodName = suffix.lastNamedChild?.text;
332
+ // The receiver is the part of navigation_expression before the suffix
333
+ for (let i = 0; i < callee.namedChildCount; i++) {
334
+ const child = callee.namedChild(i);
335
+ if (child && child.type !== 'navigation_suffix') {
336
+ innerReceiver = child;
337
+ break;
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+ if (!methodName)
344
+ break;
345
+ chain.unshift(methodName); // build chain outermost-last
346
+ // Walk into the receiver of this call to continue the chain
347
+ if (!innerReceiver && funcNode) {
348
+ innerReceiver = funcNode.childForFieldName?.('object')
349
+ ?? funcNode.childForFieldName?.('value')
350
+ ?? funcNode.childForFieldName?.('operand')
351
+ ?? funcNode.childForFieldName?.('expression');
352
+ }
353
+ // Java method_invocation: object field is on the call node
354
+ if (!innerReceiver && current.type === 'method_invocation') {
355
+ innerReceiver = current.childForFieldName?.('object');
356
+ }
357
+ // PHP member_call_expression
358
+ if (!innerReceiver && (current.type === 'member_call_expression' || current.type === 'nullsafe_member_call_expression')) {
359
+ innerReceiver = current.childForFieldName?.('object');
360
+ }
361
+ // Ruby `call` node: receiver field is on the call node itself
362
+ if (!innerReceiver && current.type === 'call') {
363
+ innerReceiver = current.childForFieldName?.('receiver');
364
+ }
365
+ if (!innerReceiver)
366
+ break;
367
+ if (CALL_EXPRESSION_TYPES.has(innerReceiver.type)) {
368
+ current = innerReceiver; // continue walking
369
+ }
370
+ else {
371
+ // Reached a simple identifier — the base receiver
372
+ return { chain, baseReceiverName: innerReceiver.text || undefined };
373
+ }
374
+ }
375
+ return chain.length > 0 ? { chain, baseReceiverName: undefined } : undefined;
376
+ }
377
+ /**
378
+ * Walk a receiver AST node that may interleave field accesses and method calls,
379
+ * building a unified chain of steps up to MAX_CHAIN_DEPTH.
380
+ *
381
+ * For `svc.getUser().address.save()`, called with the receiver of `save`
382
+ * (`svc.getUser().address`, a field access node):
383
+ * returns { chain: [{ kind:'call', name:'getUser' }, { kind:'field', name:'address' }],
384
+ * baseReceiverName: 'svc' }
385
+ *
386
+ * For `user.getAddress().city.getName()`, called with receiver of `getName`
387
+ * (`user.getAddress().city`):
388
+ * returns { chain: [{ kind:'call', name:'getAddress' }, { kind:'field', name:'city' }],
389
+ * baseReceiverName: 'user' }
390
+ *
391
+ * Pure field chains and pure call chains are special cases (all steps same kind).
392
+ */
393
+ export function extractMixedChain(receiverNode) {
394
+ const chain = [];
395
+ let current = receiverNode;
396
+ while (chain.length < MAX_CHAIN_DEPTH) {
397
+ if (CALL_EXPRESSION_TYPES.has(current.type)) {
398
+ // ── Call expression: extract method name + inner receiver ────────────
399
+ const funcNode = current.childForFieldName?.('function')
400
+ ?? current.childForFieldName?.('name')
401
+ ?? current.childForFieldName?.('method');
402
+ let methodName;
403
+ let innerReceiver = null;
404
+ if (funcNode) {
405
+ methodName = funcNode.lastNamedChild?.text ?? funcNode.text;
406
+ }
407
+ // Kotlin/Swift: call_expression → navigation_expression
408
+ if (!funcNode && current.type === 'call_expression') {
409
+ const callee = current.firstNamedChild;
410
+ if (callee?.type === 'navigation_expression') {
411
+ const suffix = callee.lastNamedChild;
412
+ if (suffix?.type === 'navigation_suffix') {
413
+ methodName = suffix.lastNamedChild?.text;
414
+ for (let i = 0; i < callee.namedChildCount; i++) {
415
+ const child = callee.namedChild(i);
416
+ if (child && child.type !== 'navigation_suffix') {
417
+ innerReceiver = child;
418
+ break;
419
+ }
420
+ }
421
+ }
422
+ }
423
+ }
424
+ if (!methodName)
425
+ break;
426
+ chain.unshift({ kind: 'call', name: methodName });
427
+ if (!innerReceiver && funcNode) {
428
+ innerReceiver = funcNode.childForFieldName?.('object')
429
+ ?? funcNode.childForFieldName?.('value')
430
+ ?? funcNode.childForFieldName?.('operand')
431
+ ?? funcNode.childForFieldName?.('argument') // C/C++ field_expression
432
+ ?? funcNode.childForFieldName?.('expression')
433
+ ?? null;
434
+ }
435
+ if (!innerReceiver && current.type === 'method_invocation') {
436
+ innerReceiver = current.childForFieldName?.('object') ?? null;
437
+ }
438
+ if (!innerReceiver && (current.type === 'member_call_expression' || current.type === 'nullsafe_member_call_expression')) {
439
+ innerReceiver = current.childForFieldName?.('object') ?? null;
440
+ }
441
+ if (!innerReceiver && current.type === 'call') {
442
+ innerReceiver = current.childForFieldName?.('receiver') ?? null;
443
+ }
444
+ if (!innerReceiver)
445
+ break;
446
+ if (CALL_EXPRESSION_TYPES.has(innerReceiver.type) || FIELD_ACCESS_NODE_TYPES.has(innerReceiver.type)) {
447
+ current = innerReceiver;
448
+ }
449
+ else {
450
+ return { chain, baseReceiverName: innerReceiver.text || undefined };
451
+ }
452
+ }
453
+ else if (FIELD_ACCESS_NODE_TYPES.has(current.type)) {
454
+ // ── Field/member access: extract property name + inner object ─────────
455
+ let propertyName;
456
+ let innerObject = null;
457
+ if (current.type === 'navigation_expression') {
458
+ for (const child of current.children ?? []) {
459
+ if (child.type === 'navigation_suffix') {
460
+ for (const sc of child.children ?? []) {
461
+ if (sc.isNamed && sc.type !== '.') {
462
+ propertyName = sc.text;
463
+ break;
464
+ }
465
+ }
466
+ }
467
+ else if (child.isNamed && !innerObject) {
468
+ innerObject = child;
469
+ }
470
+ }
471
+ }
472
+ else if (current.type === 'attribute') {
473
+ innerObject = current.childForFieldName?.('object') ?? null;
474
+ propertyName = current.childForFieldName?.('attribute')?.text;
475
+ }
476
+ else {
477
+ innerObject = current.childForFieldName?.('object')
478
+ ?? current.childForFieldName?.('value')
479
+ ?? current.childForFieldName?.('operand')
480
+ ?? current.childForFieldName?.('argument') // C/C++ field_expression
481
+ ?? current.childForFieldName?.('expression')
482
+ ?? null;
483
+ propertyName = (current.childForFieldName?.('property')
484
+ ?? current.childForFieldName?.('field')
485
+ ?? current.childForFieldName?.('name'))?.text;
486
+ }
487
+ if (!propertyName)
488
+ break;
489
+ chain.unshift({ kind: 'field', name: propertyName });
490
+ if (!innerObject)
491
+ break;
492
+ if (CALL_EXPRESSION_TYPES.has(innerObject.type) || FIELD_ACCESS_NODE_TYPES.has(innerObject.type)) {
493
+ current = innerObject;
494
+ }
495
+ else {
496
+ return { chain, baseReceiverName: innerObject.text || undefined };
497
+ }
498
+ }
499
+ else if (current.type === 'selector') {
500
+ // ── Dart: flat selector siblings (user.address.save() uses selector nodes) ──
501
+ // Extract field name from unconditional_assignable_selector child
502
+ const uas = current.namedChildren?.find((c) => c.type === 'unconditional_assignable_selector');
503
+ const propertyName = uas?.namedChildren?.find((c) => c.type === 'identifier')?.text;
504
+ if (!propertyName)
505
+ break;
506
+ chain.unshift({ kind: 'field', name: propertyName });
507
+ // Walk to previous sibling for the next step in the chain
508
+ const prev = current.previousNamedSibling;
509
+ if (!prev)
510
+ break;
511
+ if (prev.type === 'selector') {
512
+ current = prev;
513
+ }
514
+ else {
515
+ // Base receiver (identifier or other terminal)
516
+ return { chain, baseReceiverName: prev.text || undefined };
517
+ }
518
+ }
519
+ else {
520
+ // Simple identifier — this is the base receiver
521
+ return chain.length > 0
522
+ ? { chain, baseReceiverName: current.text || undefined }
523
+ : undefined;
524
+ }
525
+ }
526
+ return chain.length > 0 ? { chain, baseReceiverName: undefined } : undefined;
527
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Yield control to the event loop so spinners/progress can render.
3
+ * Call periodically in hot loops to prevent UI freezes.
4
+ */
5
+ export declare const yieldToEventLoop: () => Promise<void>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Yield control to the event loop so spinners/progress can render.
3
+ * Call periodically in hot loops to prevent UI freezes.
4
+ */
5
+ export const yieldToEventLoop = () => new Promise(resolve => setImmediate(resolve));
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Language Detection — maps file paths to SupportedLanguages enum values.
3
+ */
4
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
5
+ /**
6
+ * Map file extension to SupportedLanguage enum.
7
+ * Returns null if the file extension is not recognized.
8
+ */
9
+ export declare const getLanguageFromFilename: (filename: string) => SupportedLanguages | null;