gitnexus 1.4.8 → 1.4.10

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 (211) hide show
  1. package/README.md +7 -0
  2. package/dist/cli/index-repo.d.ts +15 -0
  3. package/dist/cli/index-repo.js +115 -0
  4. package/dist/cli/index.js +11 -2
  5. package/dist/cli/setup.js +12 -9
  6. package/dist/cli/wiki.d.ts +4 -0
  7. package/dist/cli/wiki.js +174 -53
  8. package/dist/config/supported-languages.d.ts +7 -5
  9. package/dist/config/supported-languages.js +6 -4
  10. package/dist/core/graph/graph.js +9 -1
  11. package/dist/core/graph/types.d.ts +10 -2
  12. package/dist/core/ingestion/call-processor.d.ts +18 -1
  13. package/dist/core/ingestion/call-processor.js +297 -38
  14. package/dist/core/ingestion/call-routing.d.ts +3 -18
  15. package/dist/core/ingestion/call-routing.js +0 -19
  16. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  17. package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
  18. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  19. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
  20. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  21. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  22. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  23. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  24. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  25. package/dist/core/ingestion/cobol-processor.js +1186 -0
  26. package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
  27. package/dist/core/ingestion/entry-point-scoring.js +18 -4
  28. package/dist/core/ingestion/export-detection.d.ts +47 -8
  29. package/dist/core/ingestion/export-detection.js +29 -50
  30. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  31. package/dist/core/ingestion/field-extractor.js +25 -0
  32. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  33. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
  34. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  35. package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
  36. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  37. package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
  38. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  39. package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
  40. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
  41. package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
  42. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  43. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  44. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  45. package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
  46. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  47. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  48. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  49. package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
  50. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  51. package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
  52. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  53. package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
  54. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  55. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
  56. package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
  57. package/dist/core/ingestion/field-extractors/generic.js +111 -0
  58. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  59. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  60. package/dist/core/ingestion/field-types.d.ts +59 -0
  61. package/dist/core/ingestion/field-types.js +2 -0
  62. package/dist/core/ingestion/framework-detection.d.ts +87 -0
  63. package/dist/core/ingestion/framework-detection.js +65 -2
  64. package/dist/core/ingestion/heritage-processor.js +15 -17
  65. package/dist/core/ingestion/import-processor.d.ts +9 -10
  66. package/dist/core/ingestion/import-processor.js +59 -14
  67. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
  68. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
  69. package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
  70. package/dist/core/ingestion/import-resolvers/dart.js +44 -0
  71. package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
  72. package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
  73. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +9 -1
  74. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.js +56 -0
  75. package/dist/core/ingestion/{resolvers → import-resolvers}/php.d.ts +6 -10
  76. package/dist/core/ingestion/{resolvers → import-resolvers}/php.js +7 -2
  77. package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
  78. package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
  79. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
  80. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
  81. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
  82. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
  83. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
  84. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
  85. package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
  86. package/dist/core/ingestion/import-resolvers/swift.js +23 -0
  87. package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
  88. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  89. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +0 -3
  90. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +0 -9
  91. package/dist/core/ingestion/language-config.d.ts +4 -1
  92. package/dist/core/ingestion/language-provider.d.ts +121 -0
  93. package/dist/core/ingestion/language-provider.js +24 -0
  94. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  95. package/dist/core/ingestion/languages/c-cpp.js +71 -0
  96. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  97. package/dist/core/ingestion/languages/cobol.js +26 -0
  98. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  99. package/dist/core/ingestion/languages/csharp.js +49 -0
  100. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  101. package/dist/core/ingestion/languages/dart.js +58 -0
  102. package/dist/core/ingestion/languages/go.d.ts +11 -0
  103. package/dist/core/ingestion/languages/go.js +28 -0
  104. package/dist/core/ingestion/languages/index.d.ts +38 -0
  105. package/dist/core/ingestion/languages/index.js +63 -0
  106. package/dist/core/ingestion/languages/java.d.ts +9 -0
  107. package/dist/core/ingestion/languages/java.js +29 -0
  108. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  109. package/dist/core/ingestion/languages/kotlin.js +53 -0
  110. package/dist/core/ingestion/languages/php.d.ts +8 -0
  111. package/dist/core/ingestion/languages/php.js +145 -0
  112. package/dist/core/ingestion/languages/python.d.ts +12 -0
  113. package/dist/core/ingestion/languages/python.js +39 -0
  114. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  115. package/dist/core/ingestion/languages/ruby.js +44 -0
  116. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  117. package/dist/core/ingestion/languages/rust.js +44 -0
  118. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  119. package/dist/core/ingestion/languages/swift.js +133 -0
  120. package/dist/core/ingestion/languages/typescript.d.ts +10 -0
  121. package/dist/core/ingestion/languages/typescript.js +60 -0
  122. package/dist/core/ingestion/mro-processor.js +14 -15
  123. package/dist/core/ingestion/{named-binding-extraction.d.ts → named-binding-processor.d.ts} +0 -9
  124. package/dist/core/ingestion/named-binding-processor.js +42 -0
  125. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  126. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  127. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  128. package/dist/core/ingestion/named-bindings/java.js +29 -0
  129. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  130. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  131. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  132. package/dist/core/ingestion/named-bindings/php.js +61 -0
  133. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  134. package/dist/core/ingestion/named-bindings/python.js +49 -0
  135. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  136. package/dist/core/ingestion/named-bindings/rust.js +64 -0
  137. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  138. package/dist/core/ingestion/named-bindings/types.js +6 -0
  139. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  140. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  141. package/dist/core/ingestion/parsing-processor.d.ts +5 -1
  142. package/dist/core/ingestion/parsing-processor.js +115 -16
  143. package/dist/core/ingestion/pipeline.js +925 -424
  144. package/dist/core/ingestion/resolution-context.js +1 -1
  145. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  146. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  147. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  148. package/dist/core/ingestion/route-extractors/middleware.js +143 -0
  149. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  150. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  151. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  152. package/dist/core/ingestion/route-extractors/php.js +21 -0
  153. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  154. package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
  155. package/dist/core/ingestion/tree-sitter-queries.d.ts +8 -7
  156. package/dist/core/ingestion/tree-sitter-queries.js +231 -9
  157. package/dist/core/ingestion/type-env.d.ts +14 -17
  158. package/dist/core/ingestion/type-env.js +66 -14
  159. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +1 -1
  160. package/dist/core/ingestion/type-extractors/csharp.js +1 -1
  161. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  162. package/dist/core/ingestion/type-extractors/dart.js +371 -0
  163. package/dist/core/ingestion/type-extractors/jvm.js +1 -1
  164. package/dist/core/ingestion/type-extractors/shared.d.ts +1 -13
  165. package/dist/core/ingestion/type-extractors/shared.js +9 -102
  166. package/dist/core/ingestion/type-extractors/swift.js +334 -4
  167. package/dist/core/ingestion/type-extractors/types.d.ts +3 -1
  168. package/dist/core/ingestion/{ast-helpers.d.ts → utils/ast-helpers.d.ts} +16 -13
  169. package/dist/core/ingestion/{ast-helpers.js → utils/ast-helpers.js} +111 -32
  170. package/dist/core/ingestion/{call-analysis.js → utils/call-analysis.js} +37 -0
  171. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  172. package/dist/core/ingestion/utils/event-loop.js +5 -0
  173. package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
  174. package/dist/core/ingestion/utils/language-detection.js +70 -0
  175. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  176. package/dist/core/ingestion/utils/verbose.js +7 -0
  177. package/dist/core/ingestion/workers/parse-worker.d.ts +43 -2
  178. package/dist/core/ingestion/workers/parse-worker.js +361 -150
  179. package/dist/core/lbug/csv-generator.js +34 -1
  180. package/dist/core/lbug/lbug-adapter.js +6 -0
  181. package/dist/core/lbug/schema.d.ts +5 -3
  182. package/dist/core/lbug/schema.js +39 -2
  183. package/dist/core/tree-sitter/parser-loader.js +7 -1
  184. package/dist/core/wiki/cursor-client.d.ts +31 -0
  185. package/dist/core/wiki/cursor-client.js +127 -0
  186. package/dist/core/wiki/generator.d.ts +28 -9
  187. package/dist/core/wiki/generator.js +115 -18
  188. package/dist/core/wiki/graph-queries.d.ts +4 -0
  189. package/dist/core/wiki/graph-queries.js +7 -1
  190. package/dist/core/wiki/llm-client.d.ts +2 -0
  191. package/dist/core/wiki/llm-client.js +8 -4
  192. package/dist/core/wiki/prompts.d.ts +3 -3
  193. package/dist/core/wiki/prompts.js +6 -0
  194. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  195. package/dist/mcp/core/lbug-adapter.js +11 -1
  196. package/dist/mcp/local/local-backend.d.ts +16 -5
  197. package/dist/mcp/local/local-backend.js +711 -74
  198. package/dist/mcp/tools.js +71 -2
  199. package/dist/storage/repo-manager.d.ts +3 -0
  200. package/package.json +14 -14
  201. package/dist/core/ingestion/import-resolution.d.ts +0 -101
  202. package/dist/core/ingestion/import-resolution.js +0 -251
  203. package/dist/core/ingestion/named-binding-extraction.js +0 -373
  204. package/dist/core/ingestion/resolvers/index.d.ts +0 -18
  205. package/dist/core/ingestion/resolvers/index.js +0 -13
  206. package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
  207. package/dist/core/ingestion/type-extractors/index.js +0 -31
  208. package/dist/core/ingestion/utils.d.ts +0 -20
  209. package/dist/core/ingestion/utils.js +0 -242
  210. package/scripts/patch-tree-sitter-swift.cjs +0 -74
  211. /package/dist/core/ingestion/{call-analysis.d.ts → utils/call-analysis.d.ts} +0 -0
@@ -278,6 +278,12 @@ export const extractVarName = (node) => {
278
278
  if (inner)
279
279
  return extractVarName(inner);
280
280
  }
281
+ // Swift: pattern node wraps a simple_identifier
282
+ if (node.type === 'pattern') {
283
+ const inner = node.firstNamedChild;
284
+ if (inner)
285
+ return extractVarName(inner);
286
+ }
281
287
  return undefined;
282
288
  };
283
289
  /** Node types for function/method parameters with type annotations */
@@ -701,105 +707,6 @@ export const extractReturnTypeName = (raw, depth = 0) => {
701
707
  return undefined;
702
708
  return text;
703
709
  };
704
- // ── Property declared-type extraction ────────────────────────────────────
705
- // Shared between parse-worker (worker path) and parsing-processor (sequential path).
706
- /**
707
- * Extract the declared type of a property/field from its AST definition node.
708
- * Handles cross-language patterns:
709
- * - TypeScript: `name: Type` → type_annotation child
710
- * - Java: `Type name` → type child on field_declaration
711
- * - C#: `Type Name { get; set; }` → type child on property_declaration
712
- * - Go: `Name Type` → type child on field_declaration
713
- * - Kotlin: `var name: Type` → variable_declaration child with type field
714
- *
715
- * Returns the normalized type name, or undefined if no type can be extracted.
716
- */
717
- export const extractPropertyDeclaredType = (definitionNode) => {
718
- if (!definitionNode)
719
- return undefined;
720
- // Strategy 1: Look for a `type` or `type_annotation` named field
721
- const typeNode = definitionNode.childForFieldName?.('type');
722
- if (typeNode) {
723
- const typeName = extractSimpleTypeName(typeNode);
724
- if (typeName)
725
- return typeName;
726
- // Fallback: use the raw text (for complex types like User[] or List<User>)
727
- const text = typeNode.text?.trim();
728
- if (text && text.length < 100)
729
- return text;
730
- }
731
- // Strategy 2: Walk children looking for type_annotation (TypeScript pattern)
732
- for (let i = 0; i < definitionNode.childCount; i++) {
733
- const child = definitionNode.child(i);
734
- if (!child)
735
- continue;
736
- if (child.type === 'type_annotation') {
737
- // Type annotation has the actual type as a child
738
- for (let j = 0; j < child.childCount; j++) {
739
- const typeChild = child.child(j);
740
- if (typeChild && typeChild.type !== ':') {
741
- const typeName = extractSimpleTypeName(typeChild);
742
- if (typeName)
743
- return typeName;
744
- const text = typeChild.text?.trim();
745
- if (text && text.length < 100)
746
- return text;
747
- }
748
- }
749
- }
750
- }
751
- // Strategy 3: For Java field_declaration, the type is a sibling of variable_declarator
752
- // AST: (field_declaration type: (type_identifier) declarator: (variable_declarator ...))
753
- const parentDecl = definitionNode.parent;
754
- if (parentDecl) {
755
- const parentType = parentDecl.childForFieldName?.('type');
756
- if (parentType) {
757
- const typeName = extractSimpleTypeName(parentType);
758
- if (typeName)
759
- return typeName;
760
- }
761
- }
762
- // Strategy 4: Kotlin property_declaration — type is nested inside variable_declaration child
763
- // AST: (property_declaration (variable_declaration (simple_identifier) ":" (user_type (type_identifier))))
764
- // Kotlin's variable_declaration has NO named 'type' field — children are all positional.
765
- for (let i = 0; i < definitionNode.childCount; i++) {
766
- const child = definitionNode.child(i);
767
- if (child?.type === 'variable_declaration') {
768
- // Try named field first (works for other languages sharing this strategy)
769
- const varType = child.childForFieldName?.('type');
770
- if (varType) {
771
- const typeName = extractSimpleTypeName(varType);
772
- if (typeName)
773
- return typeName;
774
- const text = varType.text?.trim();
775
- if (text && text.length < 100)
776
- return text;
777
- }
778
- // Fallback: walk unnamed children for user_type / type_identifier (Kotlin)
779
- for (let j = 0; j < child.namedChildCount; j++) {
780
- const varChild = child.namedChild(j);
781
- if (varChild && (varChild.type === 'user_type' || varChild.type === 'type_identifier'
782
- || varChild.type === 'nullable_type' || varChild.type === 'generic_type')) {
783
- const typeName = extractSimpleTypeName(varChild);
784
- if (typeName)
785
- return typeName;
786
- }
787
- }
788
- }
789
- }
790
- // Strategy 5: PHP @var PHPDoc — look for preceding comment with @var Type
791
- // Handles pre-PHP-7.4 code: /** @var Address */ public $address;
792
- const prevSibling = definitionNode.previousNamedSibling ?? definitionNode.parent?.previousNamedSibling;
793
- if (prevSibling?.type === 'comment') {
794
- const commentText = prevSibling.text;
795
- const varMatch = commentText?.match(/@var\s+([A-Z][\w\\]*)/);
796
- if (varMatch) {
797
- // Strip namespace prefix: \App\Models\User → User
798
- const raw = varMatch[1];
799
- const base = raw.includes('\\') ? raw.split('\\').pop() : raw;
800
- if (base && /^[A-Z]\w*$/.test(base))
801
- return base;
802
- }
803
- }
804
- return undefined;
805
- };
710
+ // extractPropertyDeclaredType removed all 14 languages register a FieldExtractor
711
+ // via defineLanguage() which is the single source of truth for Property metadata
712
+ // (declaredType, visibility, isStatic, isReadonly).
@@ -1,8 +1,28 @@
1
- import { extractSimpleTypeName, extractVarName, hasTypeAnnotation } from './shared.js';
2
- import { findChild } from '../resolvers/utils.js';
1
+ import { findChild } from '../utils/ast-helpers.js';
2
+ import { extractSimpleTypeName, extractVarName, hasTypeAnnotation, extractElementTypeFromString, resolveIterableElementType } from './shared.js';
3
3
  const DECLARATION_NODE_TYPES = new Set([
4
4
  'property_declaration',
5
+ 'if_statement',
6
+ 'guard_statement',
5
7
  ]);
8
+ const FOR_LOOP_NODE_TYPES = new Set([
9
+ 'for_statement',
10
+ ]);
11
+ /**
12
+ * Unwrap Swift `await_expression` and `try_expression` nodes to find the inner
13
+ * call_expression or other value node. `try` nodes contain a `try_operator` child
14
+ * that must be skipped.
15
+ */
16
+ function unwrapSwiftExpression(node) {
17
+ if (node.type === 'await_expression' || node.type === 'try_expression') {
18
+ for (let i = 0; i < node.namedChildCount; i++) {
19
+ const child = node.namedChild(i);
20
+ if (child && child.type !== 'try_operator')
21
+ return unwrapSwiftExpression(child);
22
+ }
23
+ }
24
+ return node;
25
+ }
6
26
  /** Swift: let x: Foo = ... */
7
27
  const extractDeclaration = (node, env) => {
8
28
  // Swift property_declaration has pattern and type_annotation
@@ -53,8 +73,21 @@ const extractInitializer = (node, env, classNames) => {
53
73
  const varName = extractVarName(pattern) ?? pattern.text;
54
74
  if (!varName || env.has(varName))
55
75
  return;
56
- // Find call_expression in the value
57
- const callExpr = findChild(node, 'call_expression');
76
+ // Find call_expression in the value (unwrap await/try)
77
+ let callExpr = findChild(node, 'call_expression');
78
+ if (!callExpr) {
79
+ // Check for await_expression or try_expression wrapping a call_expression
80
+ for (let i = 0; i < node.namedChildCount; i++) {
81
+ const child = node.namedChild(i);
82
+ if (child && (child.type === 'await_expression' || child.type === 'try_expression')) {
83
+ const unwrapped = unwrapSwiftExpression(child);
84
+ if (unwrapped.type === 'call_expression') {
85
+ callExpr = unwrapped;
86
+ break;
87
+ }
88
+ }
89
+ }
90
+ }
58
91
  if (!callExpr)
59
92
  return;
60
93
  const callee = callExpr.firstNamedChild;
@@ -99,6 +132,14 @@ const scanConstructorBinding = (node) => {
99
132
  callExpr = child;
100
133
  break;
101
134
  }
135
+ // Unwrap await/try to find inner call_expression
136
+ if (child && (child.type === 'await_expression' || child.type === 'try_expression')) {
137
+ const unwrapped = unwrapSwiftExpression(child);
138
+ if (unwrapped.type === 'call_expression') {
139
+ callExpr = unwrapped;
140
+ break;
141
+ }
142
+ }
102
143
  }
103
144
  if (!callExpr)
104
145
  return undefined;
@@ -129,10 +170,299 @@ const scanConstructorBinding = (node) => {
129
170
  }
130
171
  return undefined;
131
172
  };
173
+ /**
174
+ * Extract the variable name from an if_statement or guard_statement with optional binding.
175
+ * Pattern: `if let varName = expr` / `guard let varName = expr`
176
+ * AST: if_statement/guard_statement contains value_binding_pattern, then simple_identifier (varName),
177
+ * then call_expression/simple_identifier/navigation_expression (value).
178
+ */
179
+ function extractIfGuardBinding(node, scopeEnv) {
180
+ // Find value_binding_pattern to confirm this is an optional binding
181
+ let hasValueBinding = false;
182
+ let varName;
183
+ let valueNode = null;
184
+ for (let i = 0; i < node.namedChildCount; i++) {
185
+ const child = node.namedChild(i);
186
+ if (!child)
187
+ continue;
188
+ if (child.type === 'value_binding_pattern') {
189
+ hasValueBinding = true;
190
+ continue;
191
+ }
192
+ if (hasValueBinding && !varName && child.type === 'simple_identifier') {
193
+ varName = child.text;
194
+ continue;
195
+ }
196
+ if (varName && !valueNode) {
197
+ // Skip type annotations and binding operators
198
+ if (child.type === 'type_annotation')
199
+ continue;
200
+ valueNode = child;
201
+ break;
202
+ }
203
+ }
204
+ if (!hasValueBinding || !varName || !valueNode || scopeEnv.has(varName))
205
+ return undefined;
206
+ // Unwrap await/try
207
+ valueNode = unwrapSwiftExpression(valueNode);
208
+ // simple_identifier → copy
209
+ if (valueNode.type === 'simple_identifier') {
210
+ return { kind: 'copy', lhs: varName, rhs: valueNode.text };
211
+ }
212
+ // navigation_expression → fieldAccess
213
+ if (valueNode.type === 'navigation_expression') {
214
+ const receiver = valueNode.firstNamedChild;
215
+ const suffix = valueNode.lastNamedChild;
216
+ if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
217
+ const field = suffix.lastNamedChild;
218
+ if (field?.type === 'simple_identifier') {
219
+ return { kind: 'fieldAccess', lhs: varName, receiver: receiver.text, field: field.text };
220
+ }
221
+ }
222
+ return undefined;
223
+ }
224
+ // call_expression → callResult or methodCallResult
225
+ if (valueNode.type === 'call_expression') {
226
+ const callee = valueNode.firstNamedChild;
227
+ if (!callee)
228
+ return undefined;
229
+ if (callee.type === 'simple_identifier') {
230
+ return { kind: 'callResult', lhs: varName, callee: callee.text };
231
+ }
232
+ if (callee.type === 'navigation_expression') {
233
+ const receiver = callee.firstNamedChild;
234
+ const suffix = callee.lastNamedChild;
235
+ if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
236
+ const method = suffix.lastNamedChild;
237
+ if (method?.type === 'simple_identifier') {
238
+ return { kind: 'methodCallResult', lhs: varName, receiver: receiver.text, method: method.text };
239
+ }
240
+ }
241
+ }
242
+ }
243
+ return undefined;
244
+ }
245
+ /**
246
+ * Swift: extract pending assignments for Tier 2 return-type propagation.
247
+ * Handles:
248
+ * let user = getUser() → callResult
249
+ * let result = user.save() → methodCallResult
250
+ * let name = user.name → fieldAccess
251
+ * let copy = user → copy
252
+ * let user = await getUser() → callResult (unwrapped)
253
+ * let user = try getUser() → callResult (unwrapped)
254
+ * if let user = getUser() → callResult (optional binding)
255
+ * guard let user = getUser() → callResult (optional binding)
256
+ */
257
+ const extractPendingAssignment = (node, scopeEnv) => {
258
+ // Handle if_statement and guard_statement optional bindings
259
+ if (node.type === 'if_statement' || node.type === 'guard_statement') {
260
+ return extractIfGuardBinding(node, scopeEnv);
261
+ }
262
+ if (node.type !== 'property_declaration')
263
+ return undefined;
264
+ // Skip if type annotation exists — extractDeclaration handles it
265
+ if (hasTypeAnnotation(node))
266
+ return undefined;
267
+ // Find the variable name from the pattern child
268
+ let lhs;
269
+ for (let i = 0; i < node.namedChildCount; i++) {
270
+ const child = node.namedChild(i);
271
+ if (child?.type === 'pattern') {
272
+ lhs = child.text;
273
+ break;
274
+ }
275
+ }
276
+ if (!lhs || scopeEnv.has(lhs))
277
+ return undefined;
278
+ // Find the value expression (last meaningful named child after pattern)
279
+ let valueNode = null;
280
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
281
+ const child = node.namedChild(i);
282
+ if (!child)
283
+ continue;
284
+ if (child.type === 'pattern' || child.type === 'value_binding_pattern' || child.type === 'type_annotation')
285
+ continue;
286
+ valueNode = child;
287
+ break;
288
+ }
289
+ if (!valueNode)
290
+ return undefined;
291
+ // Unwrap await/try expressions (Feature 2)
292
+ valueNode = unwrapSwiftExpression(valueNode);
293
+ // let copy = user → copy
294
+ if (valueNode.type === 'simple_identifier') {
295
+ return { kind: 'copy', lhs, rhs: valueNode.text };
296
+ }
297
+ // let name = user.name → fieldAccess
298
+ if (valueNode.type === 'navigation_expression') {
299
+ const receiver = valueNode.firstNamedChild;
300
+ const suffix = valueNode.lastNamedChild;
301
+ if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
302
+ const field = suffix.lastNamedChild;
303
+ if (field?.type === 'simple_identifier') {
304
+ return { kind: 'fieldAccess', lhs, receiver: receiver.text, field: field.text };
305
+ }
306
+ }
307
+ return undefined;
308
+ }
309
+ // Call expressions
310
+ if (valueNode.type === 'call_expression') {
311
+ const callee = valueNode.firstNamedChild;
312
+ if (!callee)
313
+ return undefined;
314
+ // let user = getUser() → callResult
315
+ if (callee.type === 'simple_identifier') {
316
+ return { kind: 'callResult', lhs, callee: callee.text };
317
+ }
318
+ // let result = user.save() → methodCallResult
319
+ if (callee.type === 'navigation_expression') {
320
+ const receiver = callee.firstNamedChild;
321
+ const suffix = callee.lastNamedChild;
322
+ if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
323
+ const method = suffix.lastNamedChild;
324
+ if (method?.type === 'simple_identifier') {
325
+ return { kind: 'methodCallResult', lhs, receiver: receiver.text, method: method.text };
326
+ }
327
+ }
328
+ }
329
+ }
330
+ return undefined;
331
+ };
332
+ /**
333
+ * Swift: extract loop variable type binding from `for item in collection`.
334
+ * AST: for_statement with pattern > simple_identifier (loop var) and
335
+ * a simple_identifier/call_expression (collection).
336
+ */
337
+ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, returnTypeLookup }) => {
338
+ if (node.type !== 'for_statement')
339
+ return;
340
+ // Find the loop variable from the pattern child
341
+ let loopVarName;
342
+ let iterableNode = null;
343
+ // for_statement children: pattern (loop var), then the iterable expression, then the body
344
+ for (let i = 0; i < node.namedChildCount; i++) {
345
+ const child = node.namedChild(i);
346
+ if (!child)
347
+ continue;
348
+ if (child.type === 'pattern' || child.type === 'simple_identifier') {
349
+ if (!loopVarName) {
350
+ // Extract a simple identifier from the pattern. Skip non-trivial patterns
351
+ // (e.g. tuple destructuring `for (a, b) in ...`) to avoid polluting scopeEnv.
352
+ const varName = extractVarName(child) ?? (child.type === 'simple_identifier' ? child.text : undefined);
353
+ if (!varName)
354
+ return; // Non-simple pattern — bail out
355
+ loopVarName = varName;
356
+ continue;
357
+ }
358
+ }
359
+ // After we found the loop var, the next expression-like node is the iterable
360
+ if (loopVarName && !iterableNode) {
361
+ if (child.type === 'simple_identifier' || child.type === 'call_expression' ||
362
+ child.type === 'navigation_expression') {
363
+ iterableNode = child;
364
+ break;
365
+ }
366
+ }
367
+ }
368
+ if (!loopVarName || !iterableNode)
369
+ return;
370
+ let iterableName;
371
+ let callExprElementType;
372
+ if (iterableNode.type === 'simple_identifier') {
373
+ iterableName = iterableNode.text;
374
+ }
375
+ else if (iterableNode.type === 'navigation_expression') {
376
+ // collection.property
377
+ const suffix = iterableNode.lastNamedChild;
378
+ if (suffix?.type === 'navigation_suffix') {
379
+ const prop = suffix.lastNamedChild;
380
+ if (prop?.type === 'simple_identifier')
381
+ iterableName = prop.text;
382
+ }
383
+ else if (suffix?.type === 'simple_identifier') {
384
+ iterableName = suffix.text;
385
+ }
386
+ }
387
+ else if (iterableNode.type === 'call_expression') {
388
+ // getItems() or collection.values()
389
+ const fn = iterableNode.firstNamedChild;
390
+ let callee;
391
+ if (fn?.type === 'simple_identifier') {
392
+ callee = fn.text;
393
+ }
394
+ else if (fn?.type === 'navigation_expression') {
395
+ const obj = fn.firstNamedChild;
396
+ const suffix = fn.lastNamedChild;
397
+ if (obj?.type === 'simple_identifier')
398
+ iterableName = obj.text;
399
+ if (suffix?.type === 'navigation_suffix') {
400
+ const m = suffix.lastNamedChild;
401
+ if (m?.type === 'simple_identifier')
402
+ callee = m.text;
403
+ }
404
+ else if (suffix?.type === 'simple_identifier') {
405
+ callee = suffix.text;
406
+ }
407
+ }
408
+ if (callee) {
409
+ const rawReturn = returnTypeLookup.lookupRawReturnType(callee);
410
+ if (rawReturn)
411
+ callExprElementType = extractElementTypeFromString(rawReturn);
412
+ }
413
+ }
414
+ if (!iterableName && !callExprElementType)
415
+ return;
416
+ let elementType;
417
+ if (callExprElementType) {
418
+ elementType = callExprElementType;
419
+ }
420
+ else if (iterableName) {
421
+ // Try to resolve element type from the iterable's declared type
422
+ elementType = resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, extractSwiftElementTypeFromTypeNode);
423
+ }
424
+ if (elementType && !scopeEnv.has(loopVarName)) {
425
+ scopeEnv.set(loopVarName, elementType);
426
+ }
427
+ };
428
+ /**
429
+ * Extract element type from a Swift type annotation AST node.
430
+ * Handles: [User] (array sugar), Array<User>, Set<User>, etc.
431
+ */
432
+ function extractSwiftElementTypeFromTypeNode(typeNode) {
433
+ // Swift array sugar: [User] — parsed as array_type > user_type > type_identifier
434
+ if (typeNode.type === 'array_type') {
435
+ const inner = typeNode.firstNamedChild;
436
+ if (inner)
437
+ return extractSimpleTypeName(inner);
438
+ }
439
+ // Generic type: Array<User>, Set<User>
440
+ if (typeNode.type === 'user_type') {
441
+ // Check for generic args: user_type > type_identifier + type_arguments
442
+ for (let i = 0; i < typeNode.namedChildCount; i++) {
443
+ const child = typeNode.namedChild(i);
444
+ if (child?.type === 'type_arguments') {
445
+ const lastArg = child.lastNamedChild;
446
+ if (lastArg)
447
+ return extractSimpleTypeName(lastArg);
448
+ }
449
+ }
450
+ }
451
+ // type_annotation wrapping
452
+ if (typeNode.type === 'type_annotation') {
453
+ const inner = typeNode.firstNamedChild;
454
+ if (inner)
455
+ return extractSwiftElementTypeFromTypeNode(inner);
456
+ }
457
+ return undefined;
458
+ }
132
459
  export const typeConfig = {
133
460
  declarationNodeTypes: DECLARATION_NODE_TYPES,
461
+ forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
134
462
  extractDeclaration,
135
463
  extractParameter,
136
464
  extractInitializer,
137
465
  scanConstructorBinding,
466
+ extractPendingAssignment,
467
+ extractForLoopBinding,
138
468
  };
@@ -1,4 +1,4 @@
1
- import type { SyntaxNode } from '../utils.js';
1
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
2
2
  /** Extracts type bindings from a declaration node into the env map */
3
3
  export type TypeBindingExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
4
4
  /** Extracts type bindings from a parameter node into the env map */
@@ -75,6 +75,8 @@ export type PendingAssignment = {
75
75
  kind: 'callResult';
76
76
  lhs: string;
77
77
  callee: string;
78
+ calleeFqn?: string;
79
+ line?: number;
78
80
  } | {
79
81
  kind: 'fieldAccess';
80
82
  lhs: string;
@@ -1,6 +1,6 @@
1
1
  import type Parser from 'tree-sitter';
2
- import { SupportedLanguages } from '../../config/supported-languages.js';
3
- import type { NodeLabel } from '../graph/types.js';
2
+ import type { NodeLabel } from '../../graph/types.js';
3
+ import type { LanguageProvider } from '../language-provider.js';
4
4
  /** Tree-sitter AST node. Re-exported for use across ingestion modules. */
5
5
  export type SyntaxNode = Parser.SyntaxNode;
6
6
  /**
@@ -29,20 +29,13 @@ export declare const CONTAINER_TYPE_TO_LABEL: Record<string, string>;
29
29
  export declare function isKotlinClassMethod(captureNode: {
30
30
  parent?: any;
31
31
  } | null | undefined): boolean;
32
- /**
33
- * C/C++: check if a Function capture is inside a class/struct body.
34
- * If true, the function is already captured by @definition.method and should be skipped
35
- * to prevent double-indexing in globalIndex.
36
- */
37
- export declare function isCppDuplicateClassFunction(functionNode: {
38
- parent?: any;
39
- } | null | undefined, nodeLabel: string, language: SupportedLanguages): boolean;
40
32
  /**
41
33
  * Determine the graph node label from a tree-sitter capture map.
42
- * Handles language-specific reclassification (C/C++ duplicate skipping, Kotlin Method promotion).
34
+ * Handles language-specific reclassification via the provider's labelOverride hook
35
+ * (e.g. C/C++ duplicate skipping, Kotlin Method promotion).
43
36
  * Returns null if the capture should be skipped (import, call, C/C++ duplicate, missing name).
44
37
  */
45
- export declare function getLabelFromCaptures(captureMap: Record<string, any>, language: SupportedLanguages): NodeLabel | null;
38
+ export declare function getLabelFromCaptures(captureMap: Record<string, any>, provider: LanguageProvider): NodeLabel | null;
46
39
  /** Walk up AST to find enclosing class/struct/interface/impl, return its generateId or null.
47
40
  * For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct). */
48
41
  export declare const findEnclosingClassId: (node: any, filePath: string) => string | null;
@@ -57,7 +50,7 @@ export declare const findSiblingChild: (parent: any, siblingType: string, childT
57
50
  */
58
51
  export declare const extractFunctionName: (node: SyntaxNode) => {
59
52
  funcName: string | null;
60
- label: string;
53
+ label: NodeLabel;
61
54
  };
62
55
  export interface MethodSignature {
63
56
  parameterCount: number | undefined;
@@ -78,3 +71,13 @@ export declare const CALL_ARGUMENT_LIST_TYPES: Set<string>;
78
71
  * Works across languages by looking for common AST patterns.
79
72
  */
80
73
  export declare const extractMethodSignature: (node: SyntaxNode | null | undefined) => MethodSignature;
74
+ /** Walk an AST node depth-first, returning the first descendant with the given type. */
75
+ export declare function findDescendant(node: any, type: string): any;
76
+ /** Extract the text content from a string or encapsed_string AST node. */
77
+ export declare function extractStringContent(node: any): string | null;
78
+ /** Check if a C/C++ function_definition is inside a class or struct body.
79
+ * Used by the C/C++ labelOverride to skip duplicate function captures
80
+ * that are already covered by definition.method queries. */
81
+ export declare function isCppInsideClassOrStruct(functionNode: SyntaxNode): boolean;
82
+ /** Find the first direct named child of a tree-sitter node matching the given type. */
83
+ export declare function findChild(node: SyntaxNode, type: string): SyntaxNode | null;