@duytransipher/gitnexus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +261 -0
  3. package/dist/cli/ai-context.d.ts +23 -0
  4. package/dist/cli/ai-context.js +265 -0
  5. package/dist/cli/analyze.d.ts +12 -0
  6. package/dist/cli/analyze.js +345 -0
  7. package/dist/cli/augment.d.ts +13 -0
  8. package/dist/cli/augment.js +33 -0
  9. package/dist/cli/clean.d.ts +10 -0
  10. package/dist/cli/clean.js +60 -0
  11. package/dist/cli/eval-server.d.ts +37 -0
  12. package/dist/cli/eval-server.js +389 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +137 -0
  15. package/dist/cli/lazy-action.d.ts +6 -0
  16. package/dist/cli/lazy-action.js +18 -0
  17. package/dist/cli/list.d.ts +6 -0
  18. package/dist/cli/list.js +30 -0
  19. package/dist/cli/mcp.d.ts +8 -0
  20. package/dist/cli/mcp.js +36 -0
  21. package/dist/cli/serve.d.ts +4 -0
  22. package/dist/cli/serve.js +6 -0
  23. package/dist/cli/setup.d.ts +8 -0
  24. package/dist/cli/setup.js +367 -0
  25. package/dist/cli/sipher-patched.d.ts +2 -0
  26. package/dist/cli/sipher-patched.js +77 -0
  27. package/dist/cli/skill-gen.d.ts +26 -0
  28. package/dist/cli/skill-gen.js +549 -0
  29. package/dist/cli/status.d.ts +6 -0
  30. package/dist/cli/status.js +36 -0
  31. package/dist/cli/tool.d.ts +60 -0
  32. package/dist/cli/tool.js +180 -0
  33. package/dist/cli/wiki.d.ts +15 -0
  34. package/dist/cli/wiki.js +365 -0
  35. package/dist/config/ignore-service.d.ts +26 -0
  36. package/dist/config/ignore-service.js +284 -0
  37. package/dist/config/supported-languages.d.ts +15 -0
  38. package/dist/config/supported-languages.js +16 -0
  39. package/dist/core/augmentation/engine.d.ts +26 -0
  40. package/dist/core/augmentation/engine.js +240 -0
  41. package/dist/core/embeddings/embedder.d.ts +60 -0
  42. package/dist/core/embeddings/embedder.js +251 -0
  43. package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
  44. package/dist/core/embeddings/embedding-pipeline.js +356 -0
  45. package/dist/core/embeddings/index.d.ts +9 -0
  46. package/dist/core/embeddings/index.js +9 -0
  47. package/dist/core/embeddings/text-generator.d.ts +24 -0
  48. package/dist/core/embeddings/text-generator.js +182 -0
  49. package/dist/core/embeddings/types.d.ts +87 -0
  50. package/dist/core/embeddings/types.js +32 -0
  51. package/dist/core/graph/graph.d.ts +2 -0
  52. package/dist/core/graph/graph.js +66 -0
  53. package/dist/core/graph/types.d.ts +66 -0
  54. package/dist/core/graph/types.js +1 -0
  55. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  56. package/dist/core/ingestion/ast-cache.js +35 -0
  57. package/dist/core/ingestion/call-processor.d.ts +23 -0
  58. package/dist/core/ingestion/call-processor.js +793 -0
  59. package/dist/core/ingestion/call-routing.d.ts +68 -0
  60. package/dist/core/ingestion/call-routing.js +129 -0
  61. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  62. package/dist/core/ingestion/cluster-enricher.js +170 -0
  63. package/dist/core/ingestion/community-processor.d.ts +39 -0
  64. package/dist/core/ingestion/community-processor.js +312 -0
  65. package/dist/core/ingestion/constants.d.ts +16 -0
  66. package/dist/core/ingestion/constants.js +16 -0
  67. package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
  68. package/dist/core/ingestion/entry-point-scoring.js +353 -0
  69. package/dist/core/ingestion/export-detection.d.ts +18 -0
  70. package/dist/core/ingestion/export-detection.js +231 -0
  71. package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
  72. package/dist/core/ingestion/filesystem-walker.js +81 -0
  73. package/dist/core/ingestion/framework-detection.d.ts +54 -0
  74. package/dist/core/ingestion/framework-detection.js +411 -0
  75. package/dist/core/ingestion/heritage-processor.d.ts +28 -0
  76. package/dist/core/ingestion/heritage-processor.js +251 -0
  77. package/dist/core/ingestion/import-processor.d.ts +34 -0
  78. package/dist/core/ingestion/import-processor.js +398 -0
  79. package/dist/core/ingestion/language-config.d.ts +46 -0
  80. package/dist/core/ingestion/language-config.js +167 -0
  81. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  82. package/dist/core/ingestion/mro-processor.js +369 -0
  83. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  84. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  85. package/dist/core/ingestion/parsing-processor.d.ts +19 -0
  86. package/dist/core/ingestion/parsing-processor.js +315 -0
  87. package/dist/core/ingestion/pipeline.d.ts +6 -0
  88. package/dist/core/ingestion/pipeline.js +401 -0
  89. package/dist/core/ingestion/process-processor.d.ts +51 -0
  90. package/dist/core/ingestion/process-processor.js +315 -0
  91. package/dist/core/ingestion/resolution-context.d.ts +53 -0
  92. package/dist/core/ingestion/resolution-context.js +132 -0
  93. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  94. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  95. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  96. package/dist/core/ingestion/resolvers/go.js +42 -0
  97. package/dist/core/ingestion/resolvers/index.d.ts +18 -0
  98. package/dist/core/ingestion/resolvers/index.js +13 -0
  99. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  100. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  101. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  102. package/dist/core/ingestion/resolvers/php.js +35 -0
  103. package/dist/core/ingestion/resolvers/python.d.ts +19 -0
  104. package/dist/core/ingestion/resolvers/python.js +52 -0
  105. package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
  106. package/dist/core/ingestion/resolvers/ruby.js +15 -0
  107. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  108. package/dist/core/ingestion/resolvers/rust.js +73 -0
  109. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  110. package/dist/core/ingestion/resolvers/standard.js +123 -0
  111. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  112. package/dist/core/ingestion/resolvers/utils.js +122 -0
  113. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  114. package/dist/core/ingestion/structure-processor.js +36 -0
  115. package/dist/core/ingestion/symbol-table.d.ts +63 -0
  116. package/dist/core/ingestion/symbol-table.js +85 -0
  117. package/dist/core/ingestion/tree-sitter-queries.d.ts +15 -0
  118. package/dist/core/ingestion/tree-sitter-queries.js +888 -0
  119. package/dist/core/ingestion/type-env.d.ts +49 -0
  120. package/dist/core/ingestion/type-env.js +613 -0
  121. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
  123. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  124. package/dist/core/ingestion/type-extractors/csharp.js +383 -0
  125. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  126. package/dist/core/ingestion/type-extractors/go.js +467 -0
  127. package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
  128. package/dist/core/ingestion/type-extractors/index.js +31 -0
  129. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  130. package/dist/core/ingestion/type-extractors/jvm.js +681 -0
  131. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  132. package/dist/core/ingestion/type-extractors/php.js +549 -0
  133. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  134. package/dist/core/ingestion/type-extractors/python.js +455 -0
  135. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  136. package/dist/core/ingestion/type-extractors/ruby.js +389 -0
  137. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  138. package/dist/core/ingestion/type-extractors/rust.js +456 -0
  139. package/dist/core/ingestion/type-extractors/shared.d.ts +145 -0
  140. package/dist/core/ingestion/type-extractors/shared.js +810 -0
  141. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  142. package/dist/core/ingestion/type-extractors/swift.js +137 -0
  143. package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
  144. package/dist/core/ingestion/type-extractors/types.js +1 -0
  145. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  146. package/dist/core/ingestion/type-extractors/typescript.js +494 -0
  147. package/dist/core/ingestion/utils.d.ts +138 -0
  148. package/dist/core/ingestion/utils.js +1290 -0
  149. package/dist/core/ingestion/workers/parse-worker.d.ts +122 -0
  150. package/dist/core/ingestion/workers/parse-worker.js +1126 -0
  151. package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
  152. package/dist/core/ingestion/workers/worker-pool.js +128 -0
  153. package/dist/core/lbug/csv-generator.d.ts +33 -0
  154. package/dist/core/lbug/csv-generator.js +366 -0
  155. package/dist/core/lbug/lbug-adapter.d.ts +103 -0
  156. package/dist/core/lbug/lbug-adapter.js +769 -0
  157. package/dist/core/lbug/schema.d.ts +53 -0
  158. package/dist/core/lbug/schema.js +430 -0
  159. package/dist/core/search/bm25-index.d.ts +23 -0
  160. package/dist/core/search/bm25-index.js +96 -0
  161. package/dist/core/search/hybrid-search.d.ts +49 -0
  162. package/dist/core/search/hybrid-search.js +118 -0
  163. package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
  164. package/dist/core/tree-sitter/parser-loader.js +63 -0
  165. package/dist/core/wiki/generator.d.ts +120 -0
  166. package/dist/core/wiki/generator.js +939 -0
  167. package/dist/core/wiki/graph-queries.d.ts +80 -0
  168. package/dist/core/wiki/graph-queries.js +238 -0
  169. package/dist/core/wiki/html-viewer.d.ts +10 -0
  170. package/dist/core/wiki/html-viewer.js +297 -0
  171. package/dist/core/wiki/llm-client.d.ts +43 -0
  172. package/dist/core/wiki/llm-client.js +186 -0
  173. package/dist/core/wiki/prompts.d.ts +53 -0
  174. package/dist/core/wiki/prompts.js +174 -0
  175. package/dist/lib/utils.d.ts +1 -0
  176. package/dist/lib/utils.js +3 -0
  177. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  178. package/dist/mcp/compatible-stdio-transport.js +200 -0
  179. package/dist/mcp/core/embedder.d.ts +27 -0
  180. package/dist/mcp/core/embedder.js +108 -0
  181. package/dist/mcp/core/lbug-adapter.d.ts +57 -0
  182. package/dist/mcp/core/lbug-adapter.js +455 -0
  183. package/dist/mcp/local/local-backend.d.ts +181 -0
  184. package/dist/mcp/local/local-backend.js +1722 -0
  185. package/dist/mcp/resources.d.ts +31 -0
  186. package/dist/mcp/resources.js +411 -0
  187. package/dist/mcp/server.d.ts +23 -0
  188. package/dist/mcp/server.js +296 -0
  189. package/dist/mcp/staleness.d.ts +15 -0
  190. package/dist/mcp/staleness.js +29 -0
  191. package/dist/mcp/tools.d.ts +24 -0
  192. package/dist/mcp/tools.js +292 -0
  193. package/dist/server/api.d.ts +10 -0
  194. package/dist/server/api.js +344 -0
  195. package/dist/server/mcp-http.d.ts +13 -0
  196. package/dist/server/mcp-http.js +100 -0
  197. package/dist/storage/git.d.ts +6 -0
  198. package/dist/storage/git.js +35 -0
  199. package/dist/storage/repo-manager.d.ts +138 -0
  200. package/dist/storage/repo-manager.js +299 -0
  201. package/dist/types/pipeline.d.ts +32 -0
  202. package/dist/types/pipeline.js +18 -0
  203. package/dist/unreal/bridge.d.ts +4 -0
  204. package/dist/unreal/bridge.js +113 -0
  205. package/dist/unreal/config.d.ts +6 -0
  206. package/dist/unreal/config.js +55 -0
  207. package/dist/unreal/types.d.ts +105 -0
  208. package/dist/unreal/types.js +1 -0
  209. package/hooks/claude/gitnexus-hook.cjs +238 -0
  210. package/hooks/claude/pre-tool-use.sh +79 -0
  211. package/hooks/claude/session-start.sh +42 -0
  212. package/package.json +100 -0
  213. package/scripts/ensure-cli-executable.cjs +21 -0
  214. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  215. package/scripts/setup-unreal-gitnexus.ps1 +191 -0
  216. package/skills/gitnexus-cli.md +82 -0
  217. package/skills/gitnexus-debugging.md +89 -0
  218. package/skills/gitnexus-exploring.md +78 -0
  219. package/skills/gitnexus-guide.md +64 -0
  220. package/skills/gitnexus-impact-analysis.md +97 -0
  221. package/skills/gitnexus-pr-review.md +163 -0
  222. package/skills/gitnexus-refactoring.md +121 -0
  223. package/vendor/GitNexusUnreal/GitNexusUnreal.uplugin +18 -0
  224. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/GitNexusUnreal.Build.cs +29 -0
  225. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Private/GitNexusBlueprintAnalyzerCommandlet.cpp +465 -0
  226. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Private/GitNexusUnrealModule.cpp +15 -0
  227. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Public/GitNexusBlueprintAnalyzerCommandlet.h +46 -0
  228. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Public/GitNexusUnrealModule.h +10 -0
  229. package/vendor/leiden/index.cjs +355 -0
  230. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,455 @@
1
+ import { extractSimpleTypeName, extractVarName, extractElementTypeFromString, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition } from './shared.js';
2
+ const DECLARATION_NODE_TYPES = new Set([
3
+ 'assignment',
4
+ 'named_expression',
5
+ 'expression_statement',
6
+ ]);
7
+ /** Python: x: Foo = ... (PEP 484 annotated assignment) or x: Foo (standalone annotation).
8
+ *
9
+ * tree-sitter-python grammar produces two distinct shapes:
10
+ *
11
+ * 1. Annotated assignment with value: `name: str = ""`
12
+ * Node type: `assignment`
13
+ * Fields: left=identifier, type=identifier/type, right=value
14
+ *
15
+ * 2. Standalone annotation (no value): `name: str`
16
+ * Node type: `expression_statement`
17
+ * Child: `type` node with fields name=identifier, type=identifier/type
18
+ *
19
+ * Both appear at file scope and inside class bodies (PEP 526 class variable annotations).
20
+ */
21
+ const extractDeclaration = (node, env) => {
22
+ if (node.type === 'expression_statement') {
23
+ // Standalone annotation: expression_statement > type { name: identifier, type: identifier }
24
+ const typeChild = node.firstNamedChild;
25
+ if (!typeChild || typeChild.type !== 'type')
26
+ return;
27
+ const nameNode = typeChild.childForFieldName('name');
28
+ const typeNode = typeChild.childForFieldName('type');
29
+ if (!nameNode || !typeNode)
30
+ return;
31
+ const varName = extractVarName(nameNode);
32
+ const inner = typeNode.type === 'type' ? (typeNode.firstNamedChild ?? typeNode) : typeNode;
33
+ const typeName = extractSimpleTypeName(inner) ?? inner.text;
34
+ if (varName && typeName)
35
+ env.set(varName, typeName);
36
+ return;
37
+ }
38
+ // Annotated assignment: left : type = value
39
+ const left = node.childForFieldName('left');
40
+ const typeNode = node.childForFieldName('type');
41
+ if (!left || !typeNode)
42
+ return;
43
+ const varName = extractVarName(left);
44
+ // extractSimpleTypeName handles identifiers and qualified names.
45
+ // Python 3.10+ union syntax `User | None` is parsed as binary_operator,
46
+ // which extractSimpleTypeName doesn't handle. Fall back to raw text so
47
+ // stripNullable can process it at lookup time (e.g., "User | None" → "User").
48
+ const inner = typeNode.type === 'type' ? (typeNode.firstNamedChild ?? typeNode) : typeNode;
49
+ const typeName = extractSimpleTypeName(inner) ?? inner.text;
50
+ if (varName && typeName)
51
+ env.set(varName, typeName);
52
+ };
53
+ /** Python: parameter with type annotation */
54
+ const extractParameter = (node, env) => {
55
+ let nameNode = null;
56
+ let typeNode = null;
57
+ if (node.type === 'parameter') {
58
+ nameNode = node.childForFieldName('name');
59
+ typeNode = node.childForFieldName('type');
60
+ }
61
+ else {
62
+ nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
63
+ typeNode = node.childForFieldName('type');
64
+ // Python typed_parameter: name is a positional child (identifier), not a named field
65
+ if (!nameNode && node.type === 'typed_parameter') {
66
+ nameNode = node.firstNamedChild?.type === 'identifier' ? node.firstNamedChild : null;
67
+ }
68
+ }
69
+ if (!nameNode || !typeNode)
70
+ return;
71
+ const varName = extractVarName(nameNode);
72
+ const typeName = extractSimpleTypeName(typeNode);
73
+ if (varName && typeName)
74
+ env.set(varName, typeName);
75
+ };
76
+ /** Python: user = User("alice") — infer type from call when callee is a known class.
77
+ * Python constructors are syntactically identical to function calls, so we verify
78
+ * against classNames (which may include cross-file SymbolTable lookups).
79
+ * Also handles walrus operator: if (user := User("alice")): */
80
+ const extractInitializer = (node, env, classNames) => {
81
+ let left;
82
+ let right;
83
+ if (node.type === 'named_expression') {
84
+ // Walrus operator: (user := User("alice"))
85
+ // tree-sitter-python: named_expression has 'name' and 'value' fields
86
+ left = node.childForFieldName('name');
87
+ right = node.childForFieldName('value');
88
+ }
89
+ else if (node.type === 'assignment') {
90
+ left = node.childForFieldName('left');
91
+ right = node.childForFieldName('right');
92
+ // Skip if already has type annotation — extractDeclaration handled it
93
+ if (node.childForFieldName('type'))
94
+ return;
95
+ }
96
+ else {
97
+ return;
98
+ }
99
+ if (!left || !right)
100
+ return;
101
+ const varName = extractVarName(left);
102
+ if (!varName || env.has(varName))
103
+ return;
104
+ if (right.type !== 'call')
105
+ return;
106
+ const func = right.childForFieldName('function');
107
+ if (!func)
108
+ return;
109
+ // Support both direct calls (User()) and qualified calls (models.User())
110
+ // tree-sitter-python: direct → identifier, qualified → attribute
111
+ const calleeName = extractSimpleTypeName(func);
112
+ if (!calleeName)
113
+ return;
114
+ if (classNames.has(calleeName)) {
115
+ env.set(varName, calleeName);
116
+ }
117
+ };
118
+ /** Python: user = User("alice") — scan assignment/walrus for constructor-like calls.
119
+ * Returns {varName, calleeName} without checking classNames (caller validates). */
120
+ const scanConstructorBinding = (node) => {
121
+ let left;
122
+ let right;
123
+ if (node.type === 'named_expression') {
124
+ left = node.childForFieldName('name');
125
+ right = node.childForFieldName('value');
126
+ }
127
+ else if (node.type === 'assignment') {
128
+ left = node.childForFieldName('left');
129
+ right = node.childForFieldName('right');
130
+ if (node.childForFieldName('type'))
131
+ return undefined;
132
+ }
133
+ else {
134
+ return undefined;
135
+ }
136
+ if (!left || !right)
137
+ return undefined;
138
+ if (left.type !== 'identifier')
139
+ return undefined;
140
+ if (right.type !== 'call')
141
+ return undefined;
142
+ const func = right.childForFieldName('function');
143
+ if (!func)
144
+ return undefined;
145
+ const calleeName = extractSimpleTypeName(func);
146
+ if (!calleeName)
147
+ return undefined;
148
+ return { varName: left.text, calleeName };
149
+ };
150
+ const FOR_LOOP_NODE_TYPES = new Set([
151
+ 'for_statement',
152
+ ]);
153
+ /** Python function/method node types that carry a parameters list. */
154
+ const PY_FUNCTION_NODE_TYPES = new Set([
155
+ 'function_definition', 'decorated_definition',
156
+ ]);
157
+ /**
158
+ * Extract element type from a Python type annotation AST node.
159
+ * Handles:
160
+ * subscript "List[User]" → extractElementTypeFromString("List[User]") → "User"
161
+ * generic_type → extractGenericTypeArgs → first arg
162
+ * Falls back to text-based extraction.
163
+ */
164
+ const extractPyElementTypeFromAnnotation = (typeNode, pos = 'last') => {
165
+ // Unwrap 'type' wrapper node to get to the actual type (e.g., type > generic_type)
166
+ const inner = typeNode.type === 'type' ? (typeNode.firstNamedChild ?? typeNode) : typeNode;
167
+ // Python subscript: List[User], Sequence[User] — use raw text
168
+ if (inner.type === 'subscript') {
169
+ return extractElementTypeFromString(inner.text, pos);
170
+ }
171
+ // generic_type: dict[str, User] — tree-sitter-python uses type_parameter child
172
+ if (inner.type === 'generic_type') {
173
+ // Try standard extractGenericTypeArgs first (handles type_arguments)
174
+ const args = extractGenericTypeArgs(inner);
175
+ if (args.length >= 1)
176
+ return pos === 'first' ? args[0] : args[args.length - 1];
177
+ // Fallback: look for type_parameter child (tree-sitter-python specific)
178
+ for (let i = 0; i < inner.namedChildCount; i++) {
179
+ const child = inner.namedChild(i);
180
+ if (child?.type === 'type_parameter') {
181
+ if (pos === 'first') {
182
+ const firstArg = child.firstNamedChild;
183
+ if (firstArg)
184
+ return extractSimpleTypeName(firstArg);
185
+ }
186
+ else {
187
+ const lastArg = child.lastNamedChild;
188
+ if (lastArg)
189
+ return extractSimpleTypeName(lastArg);
190
+ }
191
+ }
192
+ }
193
+ }
194
+ // Fallback: raw text extraction (handles User[], [User], etc.)
195
+ return extractElementTypeFromString(inner.text, pos);
196
+ };
197
+ /**
198
+ * Walk up the AST from a for-statement to find the enclosing function definition,
199
+ * then search its parameters for one named `iterableName`.
200
+ * Returns the element type extracted from its type annotation, or undefined.
201
+ *
202
+ * Handles both `parameter` and `typed_parameter` node types in tree-sitter-python.
203
+ * `typed_parameter` may not expose the name as a `name` field — falls back to
204
+ * checking the first identifier-type named child.
205
+ */
206
+ const findPyParamElementType = (iterableName, startNode, pos = 'last') => {
207
+ let current = startNode.parent;
208
+ while (current) {
209
+ if (current.type === 'function_definition') {
210
+ const paramsNode = current.childForFieldName('parameters');
211
+ if (paramsNode) {
212
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
213
+ const param = paramsNode.namedChild(i);
214
+ if (!param)
215
+ continue;
216
+ // Try named `name` field first (parameter node), then first identifier child
217
+ // (typed_parameter node may store name as first positional child)
218
+ const nameNode = param.childForFieldName('name')
219
+ ?? (param.firstNamedChild?.type === 'identifier' ? param.firstNamedChild : null);
220
+ if (nameNode?.text !== iterableName)
221
+ continue;
222
+ // Try `type` field, then last named child (typed_parameter stores type last)
223
+ const typeAnnotation = param.childForFieldName('type')
224
+ ?? (param.namedChildCount >= 2 ? param.namedChild(param.namedChildCount - 1) : null);
225
+ if (typeAnnotation && typeAnnotation !== nameNode) {
226
+ return extractPyElementTypeFromAnnotation(typeAnnotation, pos);
227
+ }
228
+ }
229
+ }
230
+ break;
231
+ }
232
+ current = current.parent;
233
+ }
234
+ return undefined;
235
+ };
236
+ /**
237
+ * Extracts iterableName and methodName from a call expression like `data.items()`.
238
+ * Returns undefined if the call doesn't match the expected pattern.
239
+ */
240
+ const extractMethodCall = (callNode) => {
241
+ const fn = callNode.childForFieldName('function');
242
+ if (fn?.type !== 'attribute')
243
+ return undefined;
244
+ const obj = fn.firstNamedChild;
245
+ if (obj?.type !== 'identifier')
246
+ return undefined;
247
+ const method = fn.lastNamedChild;
248
+ const methodName = (method?.type === 'identifier' && method !== obj) ? method.text : undefined;
249
+ return { iterableName: obj.text, methodName };
250
+ };
251
+ /**
252
+ * Collects all identifier nodes from a pattern, descending into nested tuple_patterns.
253
+ * For `i, (k, v)` returns [i, k, v]. For `key, value` returns [key, value].
254
+ */
255
+ const collectPatternIdentifiers = (pattern) => {
256
+ const vars = [];
257
+ for (let i = 0; i < pattern.namedChildCount; i++) {
258
+ const child = pattern.namedChild(i);
259
+ if (child?.type === 'identifier') {
260
+ vars.push(child);
261
+ }
262
+ else if (child?.type === 'tuple_pattern') {
263
+ vars.push(...collectPatternIdentifiers(child));
264
+ }
265
+ }
266
+ return vars;
267
+ };
268
+ /**
269
+ * Python: for user in users: where users has a known container type annotation.
270
+ *
271
+ * AST node: `for_statement` with `left` (loop variable) and `right` (iterable).
272
+ *
273
+ * Tier 1c: resolves the element type via three strategies in priority order:
274
+ * 1. declarationTypeNodes — raw type annotation AST node (covers stored container types)
275
+ * 2. scopeEnv string — extractElementTypeFromString on the stored type
276
+ * 3. AST walk — walks up to the enclosing function's parameters to read List[User] directly
277
+ *
278
+ * Also handles `enumerate(iterable)` — unwraps the outer call and skips the integer
279
+ * index variable so the value variable still resolves to the element type.
280
+ */
281
+ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, returnTypeLookup }) => {
282
+ if (node.type !== 'for_statement')
283
+ return;
284
+ const rightNode = node.childForFieldName('right');
285
+ let iterableName;
286
+ let methodName;
287
+ let callExprElementType;
288
+ let isEnumerate = false;
289
+ // Extract iterable info from the `right` field — may be identifier, attribute, or call.
290
+ if (rightNode?.type === 'identifier') {
291
+ iterableName = rightNode.text;
292
+ }
293
+ else if (rightNode?.type === 'attribute') {
294
+ const prop = rightNode.lastNamedChild;
295
+ if (prop)
296
+ iterableName = prop.text;
297
+ }
298
+ else if (rightNode?.type === 'call') {
299
+ const fn = rightNode.childForFieldName('function');
300
+ if (fn?.type === 'identifier' && fn.text === 'enumerate') {
301
+ // enumerate(iterable) or enumerate(d.items()) — unwrap to inner iterable.
302
+ isEnumerate = true;
303
+ const innerArg = rightNode.childForFieldName('arguments')?.firstNamedChild;
304
+ if (innerArg?.type === 'identifier') {
305
+ iterableName = innerArg.text;
306
+ }
307
+ else if (innerArg?.type === 'call') {
308
+ const extracted = extractMethodCall(innerArg);
309
+ if (extracted)
310
+ ({ iterableName, methodName } = extracted);
311
+ }
312
+ }
313
+ else if (fn?.type === 'attribute') {
314
+ // data.items() → call > function: attribute > identifier('data') + identifier('items')
315
+ const extracted = extractMethodCall(rightNode);
316
+ if (extracted)
317
+ ({ iterableName, methodName } = extracted);
318
+ }
319
+ else if (fn?.type === 'identifier') {
320
+ // Direct function call: for user in get_users() (Phase 7.3 — return-type path)
321
+ const rawReturn = returnTypeLookup.lookupRawReturnType(fn.text);
322
+ if (rawReturn)
323
+ callExprElementType = extractElementTypeFromString(rawReturn);
324
+ }
325
+ }
326
+ if (!iterableName && !callExprElementType)
327
+ return;
328
+ let elementType;
329
+ if (callExprElementType) {
330
+ elementType = callExprElementType;
331
+ }
332
+ else {
333
+ const containerTypeName = scopeEnv.get(iterableName);
334
+ const typeArgPos = methodToTypeArgPosition(methodName, containerTypeName);
335
+ elementType = resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, extractPyElementTypeFromAnnotation, findPyParamElementType, typeArgPos);
336
+ }
337
+ if (!elementType)
338
+ return;
339
+ // The loop variable is the `left` field — identifier or pattern_list.
340
+ const leftNode = node.childForFieldName('left');
341
+ if (!leftNode)
342
+ return;
343
+ if (leftNode.type === 'pattern_list' || leftNode.type === 'tuple_pattern') {
344
+ // Tuple unpacking: `key, value` or `i, (k, v)` or `(k, v)` — bind the last identifier to element type.
345
+ // With enumerate, skip binding if there's only one var (just the index, no value to bind).
346
+ const vars = collectPatternIdentifiers(leftNode);
347
+ if (vars.length > 0 && (!isEnumerate || vars.length > 1)) {
348
+ scopeEnv.set(vars[vars.length - 1].text, elementType);
349
+ }
350
+ return;
351
+ }
352
+ const loopVarName = extractVarName(leftNode);
353
+ if (loopVarName)
354
+ scopeEnv.set(loopVarName, elementType);
355
+ };
356
+ /** Python: alias = u → assignment with left/right fields.
357
+ * Also handles walrus operator: alias := u → named_expression with name/value fields. */
358
+ const extractPendingAssignment = (node, scopeEnv) => {
359
+ let left;
360
+ let right;
361
+ if (node.type === 'assignment') {
362
+ left = node.childForFieldName('left');
363
+ right = node.childForFieldName('right');
364
+ }
365
+ else if (node.type === 'named_expression') {
366
+ left = node.childForFieldName('name');
367
+ right = node.childForFieldName('value');
368
+ }
369
+ else {
370
+ return undefined;
371
+ }
372
+ if (!left || !right)
373
+ return undefined;
374
+ const lhs = left.type === 'identifier' ? left.text : undefined;
375
+ if (!lhs || scopeEnv.has(lhs))
376
+ return undefined;
377
+ if (right.type === 'identifier')
378
+ return { kind: 'copy', lhs, rhs: right.text };
379
+ return undefined;
380
+ };
381
+ /**
382
+ * Python match/case `as` pattern binding: `case User() as u:`
383
+ *
384
+ * AST structure (tree-sitter-python):
385
+ * as_pattern
386
+ * alias: as_pattern_target ← the bound variable name (e.g. "u")
387
+ * children[0]: case_pattern ← wraps class_pattern (or is class_pattern directly)
388
+ * class_pattern
389
+ * dotted_name ← the class name (e.g. "User")
390
+ *
391
+ * The `alias` field is an `as_pattern_target` node whose `.text` is the identifier.
392
+ * The class name lives in the first non-alias named child: either a `case_pattern`
393
+ * wrapping a `class_pattern`, or a direct `class_pattern`.
394
+ *
395
+ * Conservative: returns undefined when:
396
+ * - The node is not an `as_pattern`
397
+ * - The pattern side is not a class_pattern (e.g. guard or literal match)
398
+ * - The variable was already bound in scopeEnv
399
+ */
400
+ const extractPatternBinding = (node, scopeEnv) => {
401
+ if (node.type !== 'as_pattern')
402
+ return undefined;
403
+ // as_pattern: `case User() as u:` — binds matched value to a name.
404
+ // Try named field first (future grammar versions may expose it), fall back to positional.
405
+ if (node.namedChildCount < 2)
406
+ return undefined;
407
+ const patternChild = node.namedChild(0);
408
+ const varNameNode = node.childForFieldName('alias')
409
+ ?? node.namedChild(node.namedChildCount - 1);
410
+ if (!patternChild || !varNameNode)
411
+ return undefined;
412
+ if (varNameNode.type !== 'identifier')
413
+ return undefined;
414
+ const varName = varNameNode.text;
415
+ if (!varName || scopeEnv.has(varName))
416
+ return undefined;
417
+ // Find the class_pattern — may be direct or wrapped in case_pattern.
418
+ let classPattern = null;
419
+ if (patternChild.type === 'class_pattern') {
420
+ classPattern = patternChild;
421
+ }
422
+ else if (patternChild.type === 'case_pattern') {
423
+ // Unwrap one level: case_pattern wraps class_pattern
424
+ for (let j = 0; j < patternChild.namedChildCount; j++) {
425
+ const inner = patternChild.namedChild(j);
426
+ if (inner?.type === 'class_pattern') {
427
+ classPattern = inner;
428
+ break;
429
+ }
430
+ }
431
+ }
432
+ if (!classPattern)
433
+ return undefined;
434
+ // class_pattern children: dotted_name (the class name) + optional keyword_pattern args.
435
+ const classNameNode = classPattern.firstNamedChild;
436
+ if (!classNameNode || (classNameNode.type !== 'dotted_name' && classNameNode.type !== 'identifier'))
437
+ return undefined;
438
+ const typeName = classNameNode.text;
439
+ if (!typeName)
440
+ return undefined;
441
+ return { varName, typeName };
442
+ };
443
+ const PATTERN_BINDING_NODE_TYPES = new Set(['as_pattern']);
444
+ export const typeConfig = {
445
+ declarationNodeTypes: DECLARATION_NODE_TYPES,
446
+ forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
447
+ patternBindingNodeTypes: PATTERN_BINDING_NODE_TYPES,
448
+ extractDeclaration,
449
+ extractParameter,
450
+ extractInitializer,
451
+ scanConstructorBinding,
452
+ extractForLoopBinding,
453
+ extractPendingAssignment,
454
+ extractPatternBinding,
455
+ };
@@ -0,0 +1,2 @@
1
+ import type { LanguageTypeConfig } from './types.js';
2
+ export declare const typeConfig: LanguageTypeConfig;