@zuvia-software-solutions/code-mapper 1.4.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 (213) hide show
  1. package/README.md +215 -0
  2. package/dist/cli/ai-context.d.ts +19 -0
  3. package/dist/cli/ai-context.js +168 -0
  4. package/dist/cli/analyze.d.ts +7 -0
  5. package/dist/cli/analyze.js +325 -0
  6. package/dist/cli/augment.d.ts +7 -0
  7. package/dist/cli/augment.js +27 -0
  8. package/dist/cli/clean.d.ts +5 -0
  9. package/dist/cli/clean.js +56 -0
  10. package/dist/cli/eval-server.d.ts +25 -0
  11. package/dist/cli/eval-server.js +365 -0
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +102 -0
  14. package/dist/cli/lazy-action.d.ts +6 -0
  15. package/dist/cli/lazy-action.js +19 -0
  16. package/dist/cli/list.d.ts +2 -0
  17. package/dist/cli/list.js +27 -0
  18. package/dist/cli/mcp.d.ts +8 -0
  19. package/dist/cli/mcp.js +35 -0
  20. package/dist/cli/refresh.d.ts +12 -0
  21. package/dist/cli/refresh.js +165 -0
  22. package/dist/cli/serve.d.ts +5 -0
  23. package/dist/cli/serve.js +8 -0
  24. package/dist/cli/setup.d.ts +6 -0
  25. package/dist/cli/setup.js +218 -0
  26. package/dist/cli/status.d.ts +2 -0
  27. package/dist/cli/status.js +33 -0
  28. package/dist/cli/tool.d.ts +28 -0
  29. package/dist/cli/tool.js +87 -0
  30. package/dist/config/ignore-service.d.ts +32 -0
  31. package/dist/config/ignore-service.js +282 -0
  32. package/dist/config/supported-languages.d.ts +23 -0
  33. package/dist/config/supported-languages.js +52 -0
  34. package/dist/core/augmentation/engine.d.ts +22 -0
  35. package/dist/core/augmentation/engine.js +232 -0
  36. package/dist/core/embeddings/embedder.d.ts +35 -0
  37. package/dist/core/embeddings/embedder.js +171 -0
  38. package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
  39. package/dist/core/embeddings/embedding-pipeline.js +402 -0
  40. package/dist/core/embeddings/index.d.ts +5 -0
  41. package/dist/core/embeddings/index.js +6 -0
  42. package/dist/core/embeddings/text-generator.d.ts +20 -0
  43. package/dist/core/embeddings/text-generator.js +159 -0
  44. package/dist/core/embeddings/types.d.ts +60 -0
  45. package/dist/core/embeddings/types.js +23 -0
  46. package/dist/core/graph/graph.d.ts +4 -0
  47. package/dist/core/graph/graph.js +65 -0
  48. package/dist/core/graph/types.d.ts +69 -0
  49. package/dist/core/graph/types.js +3 -0
  50. package/dist/core/incremental/child-process.d.ts +8 -0
  51. package/dist/core/incremental/child-process.js +649 -0
  52. package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
  53. package/dist/core/incremental/refresh-coordinator.js +147 -0
  54. package/dist/core/incremental/types.d.ts +78 -0
  55. package/dist/core/incremental/types.js +153 -0
  56. package/dist/core/incremental/watcher.d.ts +63 -0
  57. package/dist/core/incremental/watcher.js +338 -0
  58. package/dist/core/ingestion/ast-cache.d.ts +12 -0
  59. package/dist/core/ingestion/ast-cache.js +34 -0
  60. package/dist/core/ingestion/call-processor.d.ts +34 -0
  61. package/dist/core/ingestion/call-processor.js +937 -0
  62. package/dist/core/ingestion/call-routing.d.ts +40 -0
  63. package/dist/core/ingestion/call-routing.js +97 -0
  64. package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
  65. package/dist/core/ingestion/cluster-enricher.js +151 -0
  66. package/dist/core/ingestion/community-processor.d.ts +26 -0
  67. package/dist/core/ingestion/community-processor.js +272 -0
  68. package/dist/core/ingestion/constants.d.ts +5 -0
  69. package/dist/core/ingestion/constants.js +8 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +317 -0
  72. package/dist/core/ingestion/export-detection.d.ts +11 -0
  73. package/dist/core/ingestion/export-detection.js +203 -0
  74. package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
  75. package/dist/core/ingestion/filesystem-walker.js +64 -0
  76. package/dist/core/ingestion/framework-detection.d.ts +42 -0
  77. package/dist/core/ingestion/framework-detection.js +405 -0
  78. package/dist/core/ingestion/heritage-processor.d.ts +15 -0
  79. package/dist/core/ingestion/heritage-processor.js +237 -0
  80. package/dist/core/ingestion/import-processor.d.ts +31 -0
  81. package/dist/core/ingestion/import-processor.js +416 -0
  82. package/dist/core/ingestion/language-config.d.ts +32 -0
  83. package/dist/core/ingestion/language-config.js +161 -0
  84. package/dist/core/ingestion/mro-processor.d.ts +32 -0
  85. package/dist/core/ingestion/mro-processor.js +343 -0
  86. package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
  87. package/dist/core/ingestion/named-binding-extraction.js +343 -0
  88. package/dist/core/ingestion/parsing-processor.d.ts +20 -0
  89. package/dist/core/ingestion/parsing-processor.js +282 -0
  90. package/dist/core/ingestion/pipeline.d.ts +3 -0
  91. package/dist/core/ingestion/pipeline.js +416 -0
  92. package/dist/core/ingestion/process-processor.d.ts +42 -0
  93. package/dist/core/ingestion/process-processor.js +357 -0
  94. package/dist/core/ingestion/resolution-context.d.ts +40 -0
  95. package/dist/core/ingestion/resolution-context.js +171 -0
  96. package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
  97. package/dist/core/ingestion/resolvers/csharp.js +101 -0
  98. package/dist/core/ingestion/resolvers/go.d.ts +8 -0
  99. package/dist/core/ingestion/resolvers/go.js +33 -0
  100. package/dist/core/ingestion/resolvers/index.d.ts +14 -0
  101. package/dist/core/ingestion/resolvers/index.js +10 -0
  102. package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
  103. package/dist/core/ingestion/resolvers/jvm.js +74 -0
  104. package/dist/core/ingestion/resolvers/php.d.ts +7 -0
  105. package/dist/core/ingestion/resolvers/php.js +30 -0
  106. package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
  107. package/dist/core/ingestion/resolvers/ruby.js +13 -0
  108. package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
  109. package/dist/core/ingestion/resolvers/rust.js +62 -0
  110. package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
  111. package/dist/core/ingestion/resolvers/standard.js +144 -0
  112. package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
  113. package/dist/core/ingestion/resolvers/utils.js +113 -0
  114. package/dist/core/ingestion/structure-processor.d.ts +4 -0
  115. package/dist/core/ingestion/structure-processor.js +39 -0
  116. package/dist/core/ingestion/symbol-table.d.ts +34 -0
  117. package/dist/core/ingestion/symbol-table.js +48 -0
  118. package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
  119. package/dist/core/ingestion/tree-sitter-queries.js +691 -0
  120. package/dist/core/ingestion/type-env.d.ts +52 -0
  121. package/dist/core/ingestion/type-env.js +349 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
  123. package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
  124. package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
  125. package/dist/core/ingestion/type-extractors/csharp.js +224 -0
  126. package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
  127. package/dist/core/ingestion/type-extractors/go.js +261 -0
  128. package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
  129. package/dist/core/ingestion/type-extractors/index.js +30 -0
  130. package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
  131. package/dist/core/ingestion/type-extractors/jvm.js +386 -0
  132. package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
  133. package/dist/core/ingestion/type-extractors/php.js +280 -0
  134. package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
  135. package/dist/core/ingestion/type-extractors/python.js +175 -0
  136. package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
  137. package/dist/core/ingestion/type-extractors/ruby.js +218 -0
  138. package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
  139. package/dist/core/ingestion/type-extractors/rust.js +290 -0
  140. package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
  141. package/dist/core/ingestion/type-extractors/shared.js +322 -0
  142. package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
  143. package/dist/core/ingestion/type-extractors/swift.js +140 -0
  144. package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
  145. package/dist/core/ingestion/type-extractors/types.js +4 -0
  146. package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
  147. package/dist/core/ingestion/type-extractors/typescript.js +227 -0
  148. package/dist/core/ingestion/utils.d.ts +73 -0
  149. package/dist/core/ingestion/utils.js +992 -0
  150. package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
  151. package/dist/core/ingestion/workers/parse-worker.js +1055 -0
  152. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  153. package/dist/core/ingestion/workers/worker-pool.js +123 -0
  154. package/dist/core/lbug/csv-generator.d.ts +28 -0
  155. package/dist/core/lbug/csv-generator.js +355 -0
  156. package/dist/core/lbug/lbug-adapter.d.ts +96 -0
  157. package/dist/core/lbug/lbug-adapter.js +753 -0
  158. package/dist/core/lbug/schema.d.ts +46 -0
  159. package/dist/core/lbug/schema.js +402 -0
  160. package/dist/core/search/bm25-index.d.ts +20 -0
  161. package/dist/core/search/bm25-index.js +123 -0
  162. package/dist/core/search/hybrid-search.d.ts +32 -0
  163. package/dist/core/search/hybrid-search.js +131 -0
  164. package/dist/core/search/query-cache.d.ts +18 -0
  165. package/dist/core/search/query-cache.js +47 -0
  166. package/dist/core/search/query-expansion.d.ts +19 -0
  167. package/dist/core/search/query-expansion.js +75 -0
  168. package/dist/core/search/reranker.d.ts +29 -0
  169. package/dist/core/search/reranker.js +122 -0
  170. package/dist/core/search/types.d.ts +154 -0
  171. package/dist/core/search/types.js +51 -0
  172. package/dist/core/semantic/tsgo-service.d.ts +67 -0
  173. package/dist/core/semantic/tsgo-service.js +355 -0
  174. package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
  175. package/dist/core/tree-sitter/parser-loader.js +71 -0
  176. package/dist/lib/memory-guard.d.ts +35 -0
  177. package/dist/lib/memory-guard.js +70 -0
  178. package/dist/lib/utils.d.ts +3 -0
  179. package/dist/lib/utils.js +6 -0
  180. package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
  181. package/dist/mcp/compatible-stdio-transport.js +209 -0
  182. package/dist/mcp/core/embedder.d.ts +24 -0
  183. package/dist/mcp/core/embedder.js +168 -0
  184. package/dist/mcp/core/lbug-adapter.d.ts +29 -0
  185. package/dist/mcp/core/lbug-adapter.js +330 -0
  186. package/dist/mcp/local/local-backend.d.ts +188 -0
  187. package/dist/mcp/local/local-backend.js +2759 -0
  188. package/dist/mcp/resources.d.ts +22 -0
  189. package/dist/mcp/resources.js +379 -0
  190. package/dist/mcp/server.d.ts +10 -0
  191. package/dist/mcp/server.js +217 -0
  192. package/dist/mcp/staleness.d.ts +10 -0
  193. package/dist/mcp/staleness.js +25 -0
  194. package/dist/mcp/tools.d.ts +21 -0
  195. package/dist/mcp/tools.js +202 -0
  196. package/dist/server/api.d.ts +5 -0
  197. package/dist/server/api.js +340 -0
  198. package/dist/server/mcp-http.d.ts +7 -0
  199. package/dist/server/mcp-http.js +95 -0
  200. package/dist/storage/git.d.ts +6 -0
  201. package/dist/storage/git.js +35 -0
  202. package/dist/storage/repo-manager.d.ts +87 -0
  203. package/dist/storage/repo-manager.js +249 -0
  204. package/dist/types/pipeline.d.ts +35 -0
  205. package/dist/types/pipeline.js +20 -0
  206. package/hooks/claude/code-mapper-hook.cjs +238 -0
  207. package/hooks/claude/pre-tool-use.sh +79 -0
  208. package/hooks/claude/session-start.sh +42 -0
  209. package/models/mlx-embedder.py +185 -0
  210. package/package.json +100 -0
  211. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  212. package/vendor/leiden/index.cjs +355 -0
  213. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,175 @@
1
+ // code-mapper/src/core/ingestion/type-extractors/python.ts
2
+ /** @file python.ts
3
+ * @description Type extraction for Python (PEP 484/526 annotations and constructor inference) */
4
+ import { extractSimpleTypeName, extractVarName } from './shared.js';
5
+ const DECLARATION_NODE_TYPES = new Set([
6
+ 'assignment',
7
+ 'named_expression',
8
+ 'expression_statement',
9
+ ]);
10
+ // Python: x: Foo = ... (PEP 484 annotated assignment) or x: Foo (standalone annotation)
11
+ //
12
+ // tree-sitter-python grammar produces two distinct shapes:
13
+ // 1. Annotated assignment with value: `name: str = ""`
14
+ // Node type: assignment, Fields: left=identifier, type=identifier/type, right=value
15
+ // 2. Standalone annotation (no value): `name: str`
16
+ // Node type: expression_statement, Child: type { name=identifier, type=identifier/type }
17
+ // Both appear at file scope and inside class bodies (PEP 526 class variable annotations)
18
+ const extractDeclaration = (node, env) => {
19
+ if (node.type === 'expression_statement') {
20
+ // Standalone annotation: expression_statement > type { name: identifier, type: identifier }
21
+ const typeChild = node.firstNamedChild;
22
+ if (!typeChild || typeChild.type !== 'type')
23
+ return;
24
+ const nameNode = typeChild.childForFieldName('name');
25
+ const typeNode = typeChild.childForFieldName('type');
26
+ if (!nameNode || !typeNode)
27
+ return;
28
+ const varName = extractVarName(nameNode);
29
+ const inner = typeNode.type === 'type' ? (typeNode.firstNamedChild ?? typeNode) : typeNode;
30
+ const typeName = extractSimpleTypeName(inner) ?? inner.text;
31
+ if (varName && typeName)
32
+ env.set(varName, typeName);
33
+ return;
34
+ }
35
+ // Annotated assignment: left : type = value
36
+ const left = node.childForFieldName('left');
37
+ const typeNode = node.childForFieldName('type');
38
+ if (!left || !typeNode)
39
+ return;
40
+ const varName = extractVarName(left);
41
+ // extractSimpleTypeName handles identifiers and qualified names
42
+ // Python 3.10+ union syntax `User | None` is parsed as binary_operator,
43
+ // which extractSimpleTypeName doesn't handle -- fall back to raw text so
44
+ // stripNullable can process it at lookup time (e.g. "User | None" -> "User")
45
+ const inner = typeNode.type === 'type' ? (typeNode.firstNamedChild ?? typeNode) : typeNode;
46
+ const typeName = extractSimpleTypeName(inner) ?? inner.text;
47
+ if (varName && typeName)
48
+ env.set(varName, typeName);
49
+ };
50
+ // Python: parameter with type annotation
51
+ const extractParameter = (node, env) => {
52
+ let nameNode = null;
53
+ let typeNode = null;
54
+ if (node.type === 'parameter') {
55
+ nameNode = node.childForFieldName('name');
56
+ typeNode = node.childForFieldName('type');
57
+ }
58
+ else {
59
+ nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
60
+ typeNode = node.childForFieldName('type');
61
+ }
62
+ if (!nameNode || !typeNode)
63
+ return;
64
+ const varName = extractVarName(nameNode);
65
+ const typeName = extractSimpleTypeName(typeNode);
66
+ if (varName && typeName)
67
+ env.set(varName, typeName);
68
+ };
69
+ // Python: user = User("alice") -- infer type from call when callee is a known class
70
+ // Python constructors are syntactically identical to function calls, so we verify
71
+ // against classNames (which may include cross-file SymbolTable lookups)
72
+ // Also handles walrus operator: if (user := User("alice")):
73
+ const extractInitializer = (node, env, classNames) => {
74
+ let left;
75
+ let right;
76
+ if (node.type === 'named_expression') {
77
+ // Walrus operator: (user := User("alice"))
78
+ // tree-sitter-python: named_expression has 'name' and 'value' fields
79
+ left = node.childForFieldName('name');
80
+ right = node.childForFieldName('value');
81
+ }
82
+ else if (node.type === 'assignment') {
83
+ left = node.childForFieldName('left');
84
+ right = node.childForFieldName('right');
85
+ // Skip if already has type annotation -- extractDeclaration handled it
86
+ if (node.childForFieldName('type'))
87
+ return;
88
+ }
89
+ else {
90
+ return;
91
+ }
92
+ if (!left || !right)
93
+ return;
94
+ const varName = extractVarName(left);
95
+ if (!varName || env.has(varName))
96
+ return;
97
+ if (right.type !== 'call')
98
+ return;
99
+ const func = right.childForFieldName('function');
100
+ if (!func)
101
+ return;
102
+ // Support both direct calls (User()) and qualified calls (models.User())
103
+ // tree-sitter-python: direct -> identifier, qualified -> attribute
104
+ const calleeName = extractSimpleTypeName(func);
105
+ if (!calleeName)
106
+ return;
107
+ if (classNames.has(calleeName)) {
108
+ env.set(varName, calleeName);
109
+ }
110
+ };
111
+ // Python: user = User("alice") -- scan assignment/walrus for constructor-like calls
112
+ // Returns {varName, calleeName} without checking classNames (caller validates)
113
+ const scanConstructorBinding = (node) => {
114
+ let left;
115
+ let right;
116
+ if (node.type === 'named_expression') {
117
+ left = node.childForFieldName('name');
118
+ right = node.childForFieldName('value');
119
+ }
120
+ else if (node.type === 'assignment') {
121
+ left = node.childForFieldName('left');
122
+ right = node.childForFieldName('right');
123
+ if (node.childForFieldName('type'))
124
+ return undefined;
125
+ }
126
+ else {
127
+ return undefined;
128
+ }
129
+ if (!left || !right)
130
+ return undefined;
131
+ if (left.type !== 'identifier')
132
+ return undefined;
133
+ if (right.type !== 'call')
134
+ return undefined;
135
+ const func = right.childForFieldName('function');
136
+ if (!func)
137
+ return undefined;
138
+ const calleeName = extractSimpleTypeName(func);
139
+ if (!calleeName)
140
+ return undefined;
141
+ return { varName: left.text, calleeName };
142
+ };
143
+ // Python: alias = u -> assignment with left/right fields
144
+ // Also handles walrus operator: alias := u -> named_expression with name/value fields
145
+ const extractPendingAssignment = (node, scopeEnv) => {
146
+ let left;
147
+ let right;
148
+ if (node.type === 'assignment') {
149
+ left = node.childForFieldName('left');
150
+ right = node.childForFieldName('right');
151
+ }
152
+ else if (node.type === 'named_expression') {
153
+ left = node.childForFieldName('name');
154
+ right = node.childForFieldName('value');
155
+ }
156
+ else {
157
+ return undefined;
158
+ }
159
+ if (!left || !right)
160
+ return undefined;
161
+ const lhs = left.type === 'identifier' ? left.text : undefined;
162
+ if (!lhs || scopeEnv.has(lhs))
163
+ return undefined;
164
+ if (right.type === 'identifier')
165
+ return { lhs, rhs: right.text };
166
+ return undefined;
167
+ };
168
+ export const typeConfig = {
169
+ declarationNodeTypes: DECLARATION_NODE_TYPES,
170
+ extractDeclaration,
171
+ extractParameter,
172
+ extractInitializer,
173
+ scanConstructorBinding,
174
+ extractPendingAssignment,
175
+ };
@@ -0,0 +1,12 @@
1
+ /** @file ruby.ts
2
+ * @description Type extraction for Ruby via YARD annotation parsing and constructor inference
3
+ *
4
+ * Ruby has no static type system, but YARD provides de facto type annotations via comments:
5
+ * `# @param name [String]`, `# @return [User]`
6
+ *
7
+ * Resolution tiers:
8
+ * - Tier 0: YARD @param annotations (extractDeclaration pre-populates env)
9
+ * - Tier 1: Constructor inference via `user = User.new` (scanConstructorBinding)
10
+ */
11
+ import type { LanguageTypeConfig } from './types.js';
12
+ export declare const typeConfig: LanguageTypeConfig;
@@ -0,0 +1,218 @@
1
+ // code-mapper/src/core/ingestion/type-extractors/ruby.ts
2
+ /** @file ruby.ts
3
+ * @description Type extraction for Ruby via YARD annotation parsing and constructor inference
4
+ *
5
+ * Ruby has no static type system, but YARD provides de facto type annotations via comments:
6
+ * `# @param name [String]`, `# @return [User]`
7
+ *
8
+ * Resolution tiers:
9
+ * - Tier 0: YARD @param annotations (extractDeclaration pre-populates env)
10
+ * - Tier 1: Constructor inference via `user = User.new` (scanConstructorBinding)
11
+ */
12
+ import { extractRubyConstructorAssignment, extractSimpleTypeName } from './shared.js';
13
+ // Regex to extract @param annotations: `@param name [Type]`
14
+ const YARD_PARAM_RE = /@param\s+(\w+)\s+\[([^\]]+)\]/g;
15
+ // Alternate YARD order: `@param [Type] name`
16
+ const YARD_PARAM_ALT_RE = /@param\s+\[([^\]]+)\]\s+(\w+)/g;
17
+ // Regex to extract @return annotations: `@return [Type]`
18
+ const YARD_RETURN_RE = /@return\s+\[([^\]]+)\]/;
19
+ /**
20
+ * Extract the simple type name from a YARD type string
21
+ *
22
+ * Handles simple types ("String"), qualified ("Models::User" -> "User"),
23
+ * generic ("Array<User>" -> "Array"), nullable ("String, nil" -> "String"),
24
+ * and rejects ambiguous unions ("String, Integer" -> undefined)
25
+ */
26
+ const extractYardTypeName = (yardType) => {
27
+ const trimmed = yardType.trim();
28
+ // Handle nullable: "Type, nil" or "nil, Type"
29
+ // Use bracket-balanced split to avoid breaking on commas inside generics (e.g. Hash<Symbol, User>)
30
+ const parts = [];
31
+ let depth = 0, start = 0;
32
+ for (let i = 0; i < trimmed.length; i++) {
33
+ if (trimmed[i] === '<')
34
+ depth++;
35
+ else if (trimmed[i] === '>')
36
+ depth--;
37
+ else if (trimmed[i] === ',' && depth === 0) {
38
+ parts.push(trimmed.slice(start, i).trim());
39
+ start = i + 1;
40
+ }
41
+ }
42
+ parts.push(trimmed.slice(start).trim());
43
+ const filtered = parts.filter(p => p !== '' && p !== 'nil');
44
+ if (filtered.length !== 1)
45
+ return undefined; // ambiguous union
46
+ const typePart = filtered[0];
47
+ // Handle qualified: "Models::User" -> "User"
48
+ const segments = typePart.split('::');
49
+ const last = segments[segments.length - 1];
50
+ // Handle generic: "Array<User>" -> "Array"
51
+ const genericMatch = last.match(/^(\w+)\s*[<{(]/);
52
+ if (genericMatch)
53
+ return genericMatch[1];
54
+ // Simple identifier check
55
+ if (/^\w+$/.test(last))
56
+ return last;
57
+ return undefined;
58
+ };
59
+ /**
60
+ * Collect YARD @param annotations from comment nodes preceding a method definition
61
+ *
62
+ * In tree-sitter-ruby, comments are sibling nodes before the method node
63
+ * Walks backwards through preceding siblings collecting consecutive comment nodes
64
+ * @returns Map of paramName -> typeName
65
+ */
66
+ const collectYardParams = (methodNode) => {
67
+ const params = new Map();
68
+ // In tree-sitter-ruby, YARD comments preceding a method inside a class body are
69
+ // placed as children of the `class` node, NOT as siblings of the `method` inside
70
+ // `body_statement`. For top-level methods, comments ARE direct siblings
71
+ // We handle both: if method has no preceding comment siblings, check parent
72
+ // (body_statement) siblings instead
73
+ const commentTexts = [];
74
+ const collectComments = (startNode) => {
75
+ let sibling = startNode.previousSibling;
76
+ while (sibling) {
77
+ if (sibling.type === 'comment') {
78
+ commentTexts.unshift(sibling.text);
79
+ }
80
+ else if (sibling.isNamed) {
81
+ break;
82
+ }
83
+ sibling = sibling.previousSibling;
84
+ }
85
+ };
86
+ // Try method's own siblings first (top-level methods)
87
+ collectComments(methodNode);
88
+ // If no comments found and parent is body_statement, check parent's siblings
89
+ if (commentTexts.length === 0 && methodNode.parent?.type === 'body_statement') {
90
+ collectComments(methodNode.parent);
91
+ }
92
+ // Parse all comment lines for @param annotations
93
+ const commentBlock = commentTexts.join('\n');
94
+ let match;
95
+ // Reset regex state
96
+ YARD_PARAM_RE.lastIndex = 0;
97
+ while ((match = YARD_PARAM_RE.exec(commentBlock)) !== null) {
98
+ const paramName = match[1];
99
+ const rawType = match[2];
100
+ const typeName = extractYardTypeName(rawType);
101
+ if (typeName) {
102
+ params.set(paramName, typeName);
103
+ }
104
+ }
105
+ // Also check alternate YARD order: @param [Type] name
106
+ YARD_PARAM_ALT_RE.lastIndex = 0;
107
+ while ((match = YARD_PARAM_ALT_RE.exec(commentBlock)) !== null) {
108
+ const rawType = match[1];
109
+ const paramName = match[2];
110
+ if (params.has(paramName))
111
+ continue; // standard format takes priority
112
+ const typeName = extractYardTypeName(rawType);
113
+ if (typeName) {
114
+ params.set(paramName, typeName);
115
+ }
116
+ }
117
+ return params;
118
+ };
119
+ // Ruby node types that may carry type bindings:
120
+ // - method/singleton_method: YARD @param annotations (via extractDeclaration)
121
+ // - assignment: Constructor inference like `user = User.new` (via extractInitializer)
122
+ const DECLARATION_NODE_TYPES = new Set([
123
+ 'method',
124
+ 'singleton_method',
125
+ 'assignment',
126
+ ]);
127
+ // Extract YARD annotations from method definitions
128
+ // Pre-populates the scope env with parameter types before the standard parameter walk
129
+ const extractDeclaration = (node, env) => {
130
+ if (node.type !== 'method' && node.type !== 'singleton_method')
131
+ return;
132
+ const yardParams = collectYardParams(node);
133
+ if (yardParams.size === 0)
134
+ return;
135
+ // Pre-populate env with YARD type bindings for each parameter
136
+ for (const [paramName, typeName] of yardParams) {
137
+ env.set(paramName, typeName);
138
+ }
139
+ };
140
+ // Ruby parameters have no inline type annotations -- YARD types are already populated
141
+ // by extractDeclaration. No-op to satisfy the LanguageTypeConfig contract
142
+ const extractParameter = (_node, _env) => {
143
+ // no-op: YARD types are pre-populated by extractDeclaration
144
+ };
145
+ // Ruby constructor inference: user = User.new or service = Models::User.new
146
+ // Uses the shared extractRubyConstructorAssignment helper, then resolves against known class names
147
+ const extractInitializer = (node, env, classNames) => {
148
+ const result = extractRubyConstructorAssignment(node);
149
+ if (!result)
150
+ return;
151
+ if (env.has(result.varName))
152
+ return;
153
+ if (classNames.has(result.calleeName)) {
154
+ env.set(result.varName, result.calleeName);
155
+ }
156
+ };
157
+ // Extract return type from YARD `@return [Type]` annotation preceding a method
158
+ // Same comment-walking strategy as collectYardParams: try direct siblings first,
159
+ // fall back to parent (body_statement) siblings for class methods
160
+ const extractReturnType = (node) => {
161
+ const search = (startNode) => {
162
+ let sibling = startNode.previousSibling;
163
+ while (sibling) {
164
+ if (sibling.type === 'comment') {
165
+ const match = YARD_RETURN_RE.exec(sibling.text);
166
+ if (match)
167
+ return extractYardTypeName(match[1]);
168
+ }
169
+ else if (sibling.isNamed) {
170
+ break;
171
+ }
172
+ sibling = sibling.previousSibling;
173
+ }
174
+ return undefined;
175
+ };
176
+ const result = search(node);
177
+ if (result)
178
+ return result;
179
+ if (node.parent?.type === 'body_statement') {
180
+ return search(node.parent);
181
+ }
182
+ return undefined;
183
+ };
184
+ // Ruby constructor binding scanner: captures both `user = User.new` and plain call
185
+ // assignments like `user = get_user()`. The `.new` pattern returns the class name
186
+ // directly; plain calls return the callee name for return-type inference
187
+ const scanConstructorBinding = (node) => {
188
+ // Try the .new pattern first (returns class name directly)
189
+ const newResult = extractRubyConstructorAssignment(node);
190
+ if (newResult)
191
+ return newResult;
192
+ // Plain call assignment: user = get_user() / user = Models.create()
193
+ if (node.type !== 'assignment')
194
+ return undefined;
195
+ const left = node.childForFieldName('left');
196
+ const right = node.childForFieldName('right');
197
+ if (!left || !right)
198
+ return undefined;
199
+ if (left.type !== 'identifier' && left.type !== 'constant')
200
+ return undefined;
201
+ if (right.type !== 'call')
202
+ return undefined;
203
+ const method = right.childForFieldName('method');
204
+ if (!method)
205
+ return undefined;
206
+ const calleeName = extractSimpleTypeName(method);
207
+ if (!calleeName)
208
+ return undefined;
209
+ return { varName: left.text, calleeName };
210
+ };
211
+ export const typeConfig = {
212
+ declarationNodeTypes: DECLARATION_NODE_TYPES,
213
+ extractDeclaration,
214
+ extractParameter,
215
+ extractInitializer,
216
+ scanConstructorBinding,
217
+ extractReturnType,
218
+ };
@@ -0,0 +1,4 @@
1
+ /** @file rust.ts
2
+ * @description Type extraction for Rust (let bindings, pattern matching, struct literals) */
3
+ import type { LanguageTypeConfig } from './types.js';
4
+ export declare const typeConfig: LanguageTypeConfig;