@duytransipher/gitnexus 1.4.6-sipher.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 (224) 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/leiden/index.cjs +355 -0
  224. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,389 @@
1
+ import { extractRubyConstructorAssignment, extractSimpleTypeName, extractElementTypeFromString, extractVarName, resolveIterableElementType } from './shared.js';
2
+ /**
3
+ * Ruby type extractor — YARD annotation parsing.
4
+ *
5
+ * Ruby has no static type system, but the YARD documentation convention
6
+ * provides de facto type annotations via comments:
7
+ *
8
+ * # @param name [String] the user's name
9
+ * # @param repo [UserRepo] the repository
10
+ * # @return [User]
11
+ * def create(name, repo)
12
+ * repo.save
13
+ * end
14
+ *
15
+ * This extractor parses `@param name [Type]` patterns from comment nodes
16
+ * preceding method definitions and binds parameter names to their types.
17
+ *
18
+ * Resolution tiers:
19
+ * - Tier 0: YARD @param annotations (extractDeclaration pre-populates env)
20
+ * - Tier 1: Constructor inference via `user = User.new` (handled by scanConstructorBinding in typeConfig)
21
+ */
22
+ /** Regex to extract @param annotations: `@param name [Type]` */
23
+ const YARD_PARAM_RE = /@param\s+(\w+)\s+\[([^\]]+)\]/g;
24
+ /** Alternate YARD order: `@param [Type] name` */
25
+ const YARD_PARAM_ALT_RE = /@param\s+\[([^\]]+)\]\s+(\w+)/g;
26
+ /** Regex to extract @return annotations: `@return [Type]` */
27
+ const YARD_RETURN_RE = /@return\s+\[([^\]]+)\]/;
28
+ /**
29
+ * Extract the simple type name from a YARD type string.
30
+ * Handles:
31
+ * - Simple types: "String" → "String"
32
+ * - Qualified types: "Models::User" → "User"
33
+ * - Generic types: "Array<User>" → "Array"
34
+ * - Nullable types: "String, nil" → "String"
35
+ * - Union types: "String, Integer" → undefined (ambiguous)
36
+ */
37
+ const extractYardTypeName = (yardType) => {
38
+ const trimmed = yardType.trim();
39
+ // Handle nullable: "Type, nil" or "nil, Type"
40
+ // Use bracket-balanced split to avoid breaking on commas inside generics like Hash<Symbol, User>
41
+ const parts = [];
42
+ let depth = 0, start = 0;
43
+ for (let i = 0; i < trimmed.length; i++) {
44
+ if (trimmed[i] === '<')
45
+ depth++;
46
+ else if (trimmed[i] === '>')
47
+ depth--;
48
+ else if (trimmed[i] === ',' && depth === 0) {
49
+ parts.push(trimmed.slice(start, i).trim());
50
+ start = i + 1;
51
+ }
52
+ }
53
+ parts.push(trimmed.slice(start).trim());
54
+ const filtered = parts.filter(p => p !== '' && p !== 'nil');
55
+ if (filtered.length !== 1)
56
+ return undefined; // ambiguous union
57
+ const typePart = filtered[0];
58
+ // Handle qualified: "Models::User" → "User"
59
+ const segments = typePart.split('::');
60
+ const last = segments[segments.length - 1];
61
+ // Handle generic: "Array<User>" → "Array"
62
+ const genericMatch = last.match(/^(\w+)\s*[<{(]/);
63
+ if (genericMatch)
64
+ return genericMatch[1];
65
+ // Simple identifier check
66
+ if (/^\w+$/.test(last))
67
+ return last;
68
+ return undefined;
69
+ };
70
+ /**
71
+ * Collect YARD @param annotations from comment nodes preceding a method definition.
72
+ * Returns a map of paramName → typeName.
73
+ *
74
+ * In tree-sitter-ruby, comments are sibling nodes that appear before the method node.
75
+ * We walk backwards through preceding siblings collecting consecutive comment nodes.
76
+ */
77
+ const collectYardParams = (methodNode) => {
78
+ const params = new Map();
79
+ // In tree-sitter-ruby, YARD comments preceding a method inside a class body
80
+ // are placed as children of the `class` node, NOT as siblings of the `method`
81
+ // inside `body_statement`. The AST structure is:
82
+ //
83
+ // class
84
+ // constant = "ClassName"
85
+ // comment = "# @param ..." ← sibling of body_statement
86
+ // comment = "# @param ..." ← sibling of body_statement
87
+ // body_statement
88
+ // method ← method is here, no preceding siblings
89
+ //
90
+ // For top-level methods (outside classes), comments ARE direct siblings.
91
+ // We handle both by checking: if method has no preceding comment siblings,
92
+ // look at parent (body_statement) siblings instead.
93
+ const commentTexts = [];
94
+ const collectComments = (startNode) => {
95
+ let sibling = startNode.previousSibling;
96
+ while (sibling) {
97
+ if (sibling.type === 'comment') {
98
+ commentTexts.unshift(sibling.text);
99
+ }
100
+ else if (sibling.isNamed) {
101
+ break;
102
+ }
103
+ sibling = sibling.previousSibling;
104
+ }
105
+ };
106
+ // Try method's own siblings first (top-level methods)
107
+ collectComments(methodNode);
108
+ // If no comments found and parent is body_statement, check parent's siblings
109
+ if (commentTexts.length === 0 && methodNode.parent?.type === 'body_statement') {
110
+ collectComments(methodNode.parent);
111
+ }
112
+ // Parse all comment lines for @param annotations
113
+ const commentBlock = commentTexts.join('\n');
114
+ let match;
115
+ // Reset regex state
116
+ YARD_PARAM_RE.lastIndex = 0;
117
+ while ((match = YARD_PARAM_RE.exec(commentBlock)) !== null) {
118
+ const paramName = match[1];
119
+ const rawType = match[2];
120
+ const typeName = extractYardTypeName(rawType);
121
+ if (typeName) {
122
+ params.set(paramName, typeName);
123
+ }
124
+ }
125
+ // Also check alternate YARD order: @param [Type] name
126
+ YARD_PARAM_ALT_RE.lastIndex = 0;
127
+ while ((match = YARD_PARAM_ALT_RE.exec(commentBlock)) !== null) {
128
+ const rawType = match[1];
129
+ const paramName = match[2];
130
+ if (params.has(paramName))
131
+ continue; // standard format takes priority
132
+ const typeName = extractYardTypeName(rawType);
133
+ if (typeName) {
134
+ params.set(paramName, typeName);
135
+ }
136
+ }
137
+ return params;
138
+ };
139
+ /**
140
+ * Ruby node types that may carry type bindings.
141
+ * - `method`/`singleton_method`: YARD @param annotations (via extractDeclaration)
142
+ * - `assignment`: Constructor inference like `user = User.new` (via extractInitializer;
143
+ * extractDeclaration returns early for these nodes)
144
+ */
145
+ const DECLARATION_NODE_TYPES = new Set([
146
+ 'method',
147
+ 'singleton_method',
148
+ 'assignment',
149
+ ]);
150
+ /**
151
+ * Extract YARD annotations from method definitions.
152
+ * Pre-populates the scope env with parameter types before the
153
+ * standard parameter walk (which won't find types since Ruby has none).
154
+ */
155
+ const extractDeclaration = (node, env) => {
156
+ if (node.type !== 'method' && node.type !== 'singleton_method')
157
+ return;
158
+ const yardParams = collectYardParams(node);
159
+ if (yardParams.size === 0)
160
+ return;
161
+ // Pre-populate env with YARD type bindings for each parameter
162
+ for (const [paramName, typeName] of yardParams) {
163
+ env.set(paramName, typeName);
164
+ }
165
+ };
166
+ /**
167
+ * Ruby parameter extraction.
168
+ * Ruby parameters (identifiers inside method_parameters) have no inline
169
+ * type annotations. YARD types are already populated by extractDeclaration,
170
+ * so this is a no-op — the bindings are already in the env.
171
+ *
172
+ * We still register this to maintain the LanguageTypeConfig contract.
173
+ */
174
+ const extractParameter = (_node, _env) => {
175
+ // Ruby parameters have no type annotations.
176
+ // YARD types are pre-populated by extractDeclaration.
177
+ };
178
+ /**
179
+ * Ruby constructor inference: user = User.new or service = Models::User.new
180
+ * Uses the shared extractRubyConstructorAssignment helper for AST matching,
181
+ * then resolves against locally-known class names.
182
+ */
183
+ const extractInitializer = (node, env, classNames) => {
184
+ const result = extractRubyConstructorAssignment(node);
185
+ if (!result)
186
+ return;
187
+ if (env.has(result.varName))
188
+ return;
189
+ if (classNames.has(result.calleeName)) {
190
+ env.set(result.varName, result.calleeName);
191
+ }
192
+ };
193
+ /**
194
+ * Extract return type from YARD `@return [Type]` annotation preceding a method.
195
+ * Reuses the same comment-walking strategy as collectYardParams: try direct
196
+ * siblings first, fall back to parent (body_statement) siblings for class methods.
197
+ */
198
+ const extractReturnType = (node) => {
199
+ const search = (startNode) => {
200
+ let sibling = startNode.previousSibling;
201
+ while (sibling) {
202
+ if (sibling.type === 'comment') {
203
+ const match = YARD_RETURN_RE.exec(sibling.text);
204
+ if (match)
205
+ return extractYardTypeName(match[1]);
206
+ }
207
+ else if (sibling.isNamed) {
208
+ break;
209
+ }
210
+ sibling = sibling.previousSibling;
211
+ }
212
+ return undefined;
213
+ };
214
+ const result = search(node);
215
+ if (result)
216
+ return result;
217
+ if (node.parent?.type === 'body_statement') {
218
+ return search(node.parent);
219
+ }
220
+ return undefined;
221
+ };
222
+ /**
223
+ * Ruby constructor binding scanner: captures both `user = User.new` and
224
+ * plain call assignments like `user = get_user()`.
225
+ * The `.new` pattern returns the class name directly; plain calls return the
226
+ * callee name for return-type inference via SymbolTable lookup.
227
+ */
228
+ const scanConstructorBinding = (node) => {
229
+ // Try the .new pattern first (returns class name directly)
230
+ const newResult = extractRubyConstructorAssignment(node);
231
+ if (newResult)
232
+ return newResult;
233
+ // Plain call assignment: user = get_user() / user = Models.create()
234
+ if (node.type !== 'assignment')
235
+ return undefined;
236
+ const left = node.childForFieldName('left');
237
+ const right = node.childForFieldName('right');
238
+ if (!left || !right)
239
+ return undefined;
240
+ if (left.type !== 'identifier' && left.type !== 'constant')
241
+ return undefined;
242
+ if (right.type !== 'call')
243
+ return undefined;
244
+ const method = right.childForFieldName('method');
245
+ if (!method)
246
+ return undefined;
247
+ const calleeName = extractSimpleTypeName(method);
248
+ if (!calleeName)
249
+ return undefined;
250
+ return { varName: left.text, calleeName };
251
+ };
252
+ /** Ruby method node types that carry a parameter list. */
253
+ const RUBY_METHOD_NODE_TYPES = new Set(['method', 'singleton_method']);
254
+ const FOR_LOOP_NODE_TYPES = new Set(['for']);
255
+ /**
256
+ * Collect raw YARD @param type strings from comment nodes preceding a method.
257
+ * Unlike collectYardParams which returns simplified type names, this returns the
258
+ * raw bracket content (e.g., "Array<User>" not "Array") for element type extraction.
259
+ */
260
+ const collectYardRawParams = (methodNode) => {
261
+ const params = new Map();
262
+ const commentTexts = [];
263
+ const collectComments = (startNode) => {
264
+ let sibling = startNode.previousSibling;
265
+ while (sibling) {
266
+ if (sibling.type === 'comment') {
267
+ commentTexts.unshift(sibling.text);
268
+ }
269
+ else if (sibling.isNamed) {
270
+ break;
271
+ }
272
+ sibling = sibling.previousSibling;
273
+ }
274
+ };
275
+ collectComments(methodNode);
276
+ if (commentTexts.length === 0 && methodNode.parent?.type === 'body_statement') {
277
+ collectComments(methodNode.parent);
278
+ }
279
+ const commentBlock = commentTexts.join('\n');
280
+ let match;
281
+ YARD_PARAM_RE.lastIndex = 0;
282
+ while ((match = YARD_PARAM_RE.exec(commentBlock)) !== null) {
283
+ params.set(match[1], match[2]);
284
+ }
285
+ YARD_PARAM_ALT_RE.lastIndex = 0;
286
+ while ((match = YARD_PARAM_ALT_RE.exec(commentBlock)) !== null) {
287
+ if (!params.has(match[2]))
288
+ params.set(match[2], match[1]);
289
+ }
290
+ return params;
291
+ };
292
+ /**
293
+ * Walk up the AST from a for-statement to find the enclosing method,
294
+ * then search its YARD @param annotations for one named `iterableName`.
295
+ * Returns the element type extracted from the raw YARD type string.
296
+ *
297
+ * Example: `@param users [Array<User>]` → extracts "User" from "Array<User>".
298
+ */
299
+ const findRubyParamElementType = (iterableName, startNode) => {
300
+ let current = startNode.parent;
301
+ while (current) {
302
+ if (RUBY_METHOD_NODE_TYPES.has(current.type)) {
303
+ const rawParams = collectYardRawParams(current);
304
+ const rawType = rawParams.get(iterableName);
305
+ if (rawType)
306
+ return extractElementTypeFromString(rawType);
307
+ break;
308
+ }
309
+ current = current.parent;
310
+ }
311
+ return undefined;
312
+ };
313
+ /**
314
+ * Ruby: for user in users ... end
315
+ *
316
+ * tree-sitter-ruby `for` node structure:
317
+ * pattern field: the loop variable (identifier)
318
+ * value field: `in` node whose child is the iterable expression
319
+ *
320
+ * Tier 1c: resolves the element type via:
321
+ * 1. scopeEnv string — extractElementTypeFromString on the stored type
322
+ * 2. AST walk — walks up to the enclosing method's YARD @param to read Array<User> directly
323
+ *
324
+ * Ruby has no static types on loop variables, so this mainly works when the
325
+ * iterable has a YARD-annotated container type (e.g., `@param users [Array<User>]`).
326
+ */
327
+ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope }) => {
328
+ if (node.type !== 'for')
329
+ return;
330
+ // The loop variable is the `pattern` field (identifier).
331
+ const patternNode = node.childForFieldName('pattern');
332
+ if (!patternNode)
333
+ return;
334
+ const loopVarName = extractVarName(patternNode);
335
+ if (!loopVarName)
336
+ return;
337
+ // The iterable is inside the `value` field which is an `in` node wrapping the expression.
338
+ const inNode = node.childForFieldName('value');
339
+ if (!inNode)
340
+ return;
341
+ const iterableNode = inNode.firstNamedChild;
342
+ let iterableName;
343
+ if (iterableNode?.type === 'identifier') {
344
+ iterableName = iterableNode.text;
345
+ }
346
+ else if (iterableNode?.type === 'call') {
347
+ const method = iterableNode.childForFieldName('method');
348
+ if (method)
349
+ iterableName = method.text;
350
+ }
351
+ if (!iterableName)
352
+ return;
353
+ // Ruby has no extractFromTypeNode (no AST type annotations), pass a no-op.
354
+ const noopExtractFromTypeNode = () => undefined;
355
+ const elementType = resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, noopExtractFromTypeNode, findRubyParamElementType, undefined);
356
+ if (!elementType)
357
+ return;
358
+ scopeEnv.set(loopVarName, elementType);
359
+ };
360
+ /**
361
+ * Ruby: alias_user = user → assignment with left/right identifier fields.
362
+ * Only handles plain identifier RHS (not calls, not literals).
363
+ * Skips if LHS already has a resolved type in scopeEnv.
364
+ */
365
+ const extractPendingAssignment = (node, scopeEnv) => {
366
+ if (node.type !== 'assignment')
367
+ return undefined;
368
+ const lhsNode = node.childForFieldName('left');
369
+ if (!lhsNode || lhsNode.type !== 'identifier')
370
+ return undefined;
371
+ const varName = lhsNode.text;
372
+ if (scopeEnv.has(varName))
373
+ return undefined;
374
+ const rhsNode = node.childForFieldName('right');
375
+ if (!rhsNode || rhsNode.type !== 'identifier')
376
+ return undefined;
377
+ return { kind: 'copy', lhs: varName, rhs: rhsNode.text };
378
+ };
379
+ export const typeConfig = {
380
+ declarationNodeTypes: DECLARATION_NODE_TYPES,
381
+ forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
382
+ extractDeclaration,
383
+ extractParameter,
384
+ extractInitializer,
385
+ scanConstructorBinding,
386
+ extractReturnType,
387
+ extractForLoopBinding,
388
+ extractPendingAssignment,
389
+ };
@@ -0,0 +1,2 @@
1
+ import type { LanguageTypeConfig } from './types.js';
2
+ export declare const typeConfig: LanguageTypeConfig;