@optave/codegraph 3.5.0 → 3.6.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 (310) hide show
  1. package/README.md +35 -14
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +119 -127
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  6. package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
  7. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  8. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  10. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  11. package/dist/db/connection.d.ts +12 -2
  12. package/dist/db/connection.d.ts.map +1 -1
  13. package/dist/db/connection.js +81 -53
  14. package/dist/db/connection.js.map +1 -1
  15. package/dist/db/index.d.ts +1 -1
  16. package/dist/db/index.d.ts.map +1 -1
  17. package/dist/db/index.js +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/db/migrations.d.ts.map +1 -1
  20. package/dist/db/migrations.js +38 -32
  21. package/dist/db/migrations.js.map +1 -1
  22. package/dist/domain/analysis/context.d.ts.map +1 -1
  23. package/dist/domain/analysis/context.js +51 -66
  24. package/dist/domain/analysis/context.js.map +1 -1
  25. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  26. package/dist/domain/analysis/dependencies.js +62 -70
  27. package/dist/domain/analysis/dependencies.js.map +1 -1
  28. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  29. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  30. package/dist/domain/analysis/exports.d.ts.map +1 -1
  31. package/dist/domain/analysis/exports.js +29 -33
  32. package/dist/domain/analysis/exports.js.map +1 -1
  33. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  34. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  35. package/dist/domain/analysis/fn-impact.js +35 -65
  36. package/dist/domain/analysis/fn-impact.js.map +1 -1
  37. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  38. package/dist/domain/analysis/module-map.js +91 -6
  39. package/dist/domain/analysis/module-map.js.map +1 -1
  40. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  41. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  42. package/dist/domain/analysis/query-helpers.js +27 -0
  43. package/dist/domain/analysis/query-helpers.js.map +1 -0
  44. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  45. package/dist/domain/graph/builder/helpers.js +15 -9
  46. package/dist/domain/graph/builder/helpers.js.map +1 -1
  47. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  48. package/dist/domain/graph/builder/incremental.js +3 -2
  49. package/dist/domain/graph/builder/incremental.js.map +1 -1
  50. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  51. package/dist/domain/graph/builder/pipeline.js +69 -3
  52. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  53. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  54. package/dist/domain/graph/builder/stages/build-edges.js +7 -51
  55. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  56. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  57. package/dist/domain/graph/builder/stages/build-structure.js +7 -5
  58. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  59. package/dist/domain/graph/builder/stages/collect-files.js +2 -2
  60. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  61. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  62. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  63. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  64. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  65. package/dist/domain/graph/builder/stages/finalize.js +124 -105
  66. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  67. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  68. package/dist/domain/graph/builder/stages/insert-nodes.js +28 -15
  69. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  70. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  71. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  72. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  73. package/dist/domain/graph/resolve.d.ts +0 -4
  74. package/dist/domain/graph/resolve.d.ts.map +1 -1
  75. package/dist/domain/graph/resolve.js +32 -48
  76. package/dist/domain/graph/resolve.js.map +1 -1
  77. package/dist/domain/graph/watcher.d.ts.map +1 -1
  78. package/dist/domain/graph/watcher.js +12 -12
  79. package/dist/domain/graph/watcher.js.map +1 -1
  80. package/dist/domain/parser.d.ts +1 -1
  81. package/dist/domain/parser.d.ts.map +1 -1
  82. package/dist/domain/parser.js +164 -101
  83. package/dist/domain/parser.js.map +1 -1
  84. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  85. package/dist/domain/search/search/cli-formatter.js +88 -83
  86. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  87. package/dist/extractors/bash.d.ts +6 -0
  88. package/dist/extractors/bash.d.ts.map +1 -0
  89. package/dist/extractors/bash.js +91 -0
  90. package/dist/extractors/bash.js.map +1 -0
  91. package/dist/extractors/c.d.ts +6 -0
  92. package/dist/extractors/c.d.ts.map +1 -0
  93. package/dist/extractors/c.js +204 -0
  94. package/dist/extractors/c.js.map +1 -0
  95. package/dist/extractors/cpp.d.ts +6 -0
  96. package/dist/extractors/cpp.d.ts.map +1 -0
  97. package/dist/extractors/cpp.js +283 -0
  98. package/dist/extractors/cpp.js.map +1 -0
  99. package/dist/extractors/csharp.d.ts.map +1 -1
  100. package/dist/extractors/csharp.js +42 -54
  101. package/dist/extractors/csharp.js.map +1 -1
  102. package/dist/extractors/go.d.ts.map +1 -1
  103. package/dist/extractors/go.js +126 -130
  104. package/dist/extractors/go.js.map +1 -1
  105. package/dist/extractors/hcl.js +6 -6
  106. package/dist/extractors/hcl.js.map +1 -1
  107. package/dist/extractors/helpers.d.ts +32 -1
  108. package/dist/extractors/helpers.d.ts.map +1 -1
  109. package/dist/extractors/helpers.js +74 -0
  110. package/dist/extractors/helpers.js.map +1 -1
  111. package/dist/extractors/index.d.ts +6 -0
  112. package/dist/extractors/index.d.ts.map +1 -1
  113. package/dist/extractors/index.js +6 -0
  114. package/dist/extractors/index.js.map +1 -1
  115. package/dist/extractors/java.d.ts.map +1 -1
  116. package/dist/extractors/java.js +32 -47
  117. package/dist/extractors/java.js.map +1 -1
  118. package/dist/extractors/javascript.d.ts.map +1 -1
  119. package/dist/extractors/javascript.js +306 -292
  120. package/dist/extractors/javascript.js.map +1 -1
  121. package/dist/extractors/kotlin.d.ts +6 -0
  122. package/dist/extractors/kotlin.d.ts.map +1 -0
  123. package/dist/extractors/kotlin.js +275 -0
  124. package/dist/extractors/kotlin.js.map +1 -0
  125. package/dist/extractors/php.d.ts.map +1 -1
  126. package/dist/extractors/php.js +39 -44
  127. package/dist/extractors/php.js.map +1 -1
  128. package/dist/extractors/python.d.ts.map +1 -1
  129. package/dist/extractors/python.js +75 -93
  130. package/dist/extractors/python.js.map +1 -1
  131. package/dist/extractors/ruby.js +6 -13
  132. package/dist/extractors/ruby.js.map +1 -1
  133. package/dist/extractors/rust.d.ts.map +1 -1
  134. package/dist/extractors/rust.js +58 -83
  135. package/dist/extractors/rust.js.map +1 -1
  136. package/dist/extractors/scala.d.ts +6 -0
  137. package/dist/extractors/scala.d.ts.map +1 -0
  138. package/dist/extractors/scala.js +269 -0
  139. package/dist/extractors/scala.js.map +1 -0
  140. package/dist/extractors/swift.d.ts +6 -0
  141. package/dist/extractors/swift.d.ts.map +1 -0
  142. package/dist/extractors/swift.js +275 -0
  143. package/dist/extractors/swift.js.map +1 -0
  144. package/dist/features/ast.d.ts +2 -0
  145. package/dist/features/ast.d.ts.map +1 -1
  146. package/dist/features/ast.js +9 -24
  147. package/dist/features/ast.js.map +1 -1
  148. package/dist/features/audit.d.ts.map +1 -1
  149. package/dist/features/audit.js +17 -21
  150. package/dist/features/audit.js.map +1 -1
  151. package/dist/features/branch-compare.d.ts.map +1 -1
  152. package/dist/features/branch-compare.js +47 -3
  153. package/dist/features/branch-compare.js.map +1 -1
  154. package/dist/features/cfg.d.ts +7 -1
  155. package/dist/features/cfg.d.ts.map +1 -1
  156. package/dist/features/cfg.js +118 -62
  157. package/dist/features/cfg.js.map +1 -1
  158. package/dist/features/check.d.ts.map +1 -1
  159. package/dist/features/check.js +79 -62
  160. package/dist/features/check.js.map +1 -1
  161. package/dist/features/complexity-query.d.ts.map +1 -1
  162. package/dist/features/complexity-query.js +142 -137
  163. package/dist/features/complexity-query.js.map +1 -1
  164. package/dist/features/complexity.d.ts +7 -1
  165. package/dist/features/complexity.d.ts.map +1 -1
  166. package/dist/features/complexity.js +62 -1
  167. package/dist/features/complexity.js.map +1 -1
  168. package/dist/features/dataflow.d.ts +7 -1
  169. package/dist/features/dataflow.d.ts.map +1 -1
  170. package/dist/features/dataflow.js +356 -188
  171. package/dist/features/dataflow.js.map +1 -1
  172. package/dist/features/graph-enrichment.d.ts.map +1 -1
  173. package/dist/features/graph-enrichment.js +117 -104
  174. package/dist/features/graph-enrichment.js.map +1 -1
  175. package/dist/features/sequence.d.ts.map +1 -1
  176. package/dist/features/sequence.js +25 -4
  177. package/dist/features/sequence.js.map +1 -1
  178. package/dist/features/structure-query.d.ts.map +1 -1
  179. package/dist/features/structure-query.js +29 -4
  180. package/dist/features/structure-query.js.map +1 -1
  181. package/dist/features/structure.d.ts.map +1 -1
  182. package/dist/features/structure.js +35 -15
  183. package/dist/features/structure.js.map +1 -1
  184. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  185. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  186. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  187. package/dist/graph/algorithms/leiden/index.js +43 -28
  188. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  189. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  190. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  191. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  192. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  193. package/dist/graph/algorithms/leiden/partition.js +89 -106
  194. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  195. package/dist/graph/model.d.ts +2 -0
  196. package/dist/graph/model.d.ts.map +1 -1
  197. package/dist/graph/model.js +20 -8
  198. package/dist/graph/model.js.map +1 -1
  199. package/dist/infrastructure/config.d.ts +0 -8
  200. package/dist/infrastructure/config.d.ts.map +1 -1
  201. package/dist/infrastructure/config.js +73 -62
  202. package/dist/infrastructure/config.js.map +1 -1
  203. package/dist/infrastructure/registry.d.ts +0 -8
  204. package/dist/infrastructure/registry.d.ts.map +1 -1
  205. package/dist/infrastructure/registry.js +12 -14
  206. package/dist/infrastructure/registry.js.map +1 -1
  207. package/dist/mcp/server.d.ts.map +1 -1
  208. package/dist/mcp/server.js +45 -36
  209. package/dist/mcp/server.js.map +1 -1
  210. package/dist/presentation/audit.d.ts.map +1 -1
  211. package/dist/presentation/audit.js +61 -57
  212. package/dist/presentation/audit.js.map +1 -1
  213. package/dist/presentation/branch-compare.d.ts.map +1 -1
  214. package/dist/presentation/branch-compare.js +56 -38
  215. package/dist/presentation/branch-compare.js.map +1 -1
  216. package/dist/presentation/check.d.ts.map +1 -1
  217. package/dist/presentation/check.js +30 -32
  218. package/dist/presentation/check.js.map +1 -1
  219. package/dist/presentation/colors.d.ts.map +1 -1
  220. package/dist/presentation/colors.js +2 -0
  221. package/dist/presentation/colors.js.map +1 -1
  222. package/dist/presentation/complexity.d.ts.map +1 -1
  223. package/dist/presentation/complexity.js +25 -19
  224. package/dist/presentation/complexity.js.map +1 -1
  225. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  226. package/dist/presentation/queries-cli/exports.js +15 -15
  227. package/dist/presentation/queries-cli/exports.js.map +1 -1
  228. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  229. package/dist/presentation/queries-cli/impact.js +29 -19
  230. package/dist/presentation/queries-cli/impact.js.map +1 -1
  231. package/dist/types.d.ts +182 -7
  232. package/dist/types.d.ts.map +1 -1
  233. package/grammars/tree-sitter-bash.wasm +0 -0
  234. package/grammars/tree-sitter-c.wasm +0 -0
  235. package/grammars/tree-sitter-cpp.wasm +0 -0
  236. package/grammars/tree-sitter-kotlin.wasm +0 -0
  237. package/grammars/tree-sitter-scala.wasm +0 -0
  238. package/grammars/tree-sitter-swift.wasm +0 -0
  239. package/package.json +13 -7
  240. package/src/ast-analysis/engine.ts +147 -138
  241. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  242. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  243. package/src/db/connection.ts +90 -59
  244. package/src/db/index.ts +1 -0
  245. package/src/db/migrations.ts +36 -32
  246. package/src/domain/analysis/context.ts +73 -75
  247. package/src/domain/analysis/dependencies.ts +78 -68
  248. package/src/domain/analysis/exports.ts +45 -34
  249. package/src/domain/analysis/fn-impact.ts +67 -64
  250. package/src/domain/analysis/module-map.ts +103 -8
  251. package/src/domain/analysis/query-helpers.ts +35 -0
  252. package/src/domain/graph/builder/helpers.ts +12 -6
  253. package/src/domain/graph/builder/incremental.ts +3 -2
  254. package/src/domain/graph/builder/pipeline.ts +71 -3
  255. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  256. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  257. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  258. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  259. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  260. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  261. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  262. package/src/domain/graph/resolve.ts +34 -46
  263. package/src/domain/graph/watcher.ts +12 -14
  264. package/src/domain/parser.ts +168 -97
  265. package/src/domain/search/search/cli-formatter.ts +121 -94
  266. package/src/extractors/bash.ts +97 -0
  267. package/src/extractors/c.ts +212 -0
  268. package/src/extractors/cpp.ts +298 -0
  269. package/src/extractors/csharp.ts +53 -56
  270. package/src/extractors/go.ts +152 -134
  271. package/src/extractors/hcl.ts +6 -6
  272. package/src/extractors/helpers.ts +93 -1
  273. package/src/extractors/index.ts +6 -0
  274. package/src/extractors/java.ts +43 -48
  275. package/src/extractors/javascript.ts +328 -281
  276. package/src/extractors/kotlin.ts +293 -0
  277. package/src/extractors/php.ts +46 -40
  278. package/src/extractors/python.ts +81 -104
  279. package/src/extractors/ruby.ts +6 -13
  280. package/src/extractors/rust.ts +65 -85
  281. package/src/extractors/scala.ts +285 -0
  282. package/src/extractors/swift.ts +293 -0
  283. package/src/features/ast.ts +10 -25
  284. package/src/features/audit.ts +24 -20
  285. package/src/features/branch-compare.ts +51 -4
  286. package/src/features/cfg.ts +158 -65
  287. package/src/features/check.ts +90 -74
  288. package/src/features/complexity-query.ts +181 -163
  289. package/src/features/complexity.ts +64 -1
  290. package/src/features/dataflow.ts +462 -217
  291. package/src/features/graph-enrichment.ts +161 -117
  292. package/src/features/sequence.ts +27 -4
  293. package/src/features/structure-query.ts +43 -4
  294. package/src/features/structure.ts +50 -22
  295. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  296. package/src/graph/algorithms/leiden/index.ts +67 -28
  297. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  298. package/src/graph/algorithms/leiden/partition.ts +131 -98
  299. package/src/graph/model.ts +19 -7
  300. package/src/infrastructure/config.ts +60 -58
  301. package/src/infrastructure/registry.ts +17 -14
  302. package/src/mcp/server.ts +46 -37
  303. package/src/presentation/audit.ts +72 -67
  304. package/src/presentation/branch-compare.ts +54 -50
  305. package/src/presentation/check.ts +34 -34
  306. package/src/presentation/colors.ts +2 -0
  307. package/src/presentation/complexity.ts +39 -33
  308. package/src/presentation/queries-cli/exports.ts +17 -17
  309. package/src/presentation/queries-cli/impact.ts +30 -22
  310. package/src/types.ts +189 -7
@@ -4,9 +4,16 @@ import type {
4
4
  SubDeclaration,
5
5
  TreeSitterNode,
6
6
  TreeSitterTree,
7
- TypeMapEntry,
8
7
  } from '../types.js';
9
- import { findChild, goVisibility, MAX_WALK_DEPTH, nodeEndLine } from './helpers.js';
8
+ import {
9
+ findChild,
10
+ goVisibility,
11
+ lastPathSegment,
12
+ MAX_WALK_DEPTH,
13
+ nodeEndLine,
14
+ setTypeMapEntry,
15
+ stripQuotes,
16
+ } from './helpers.js';
10
17
 
11
18
  /**
12
19
  * Extract symbols from Go files.
@@ -106,43 +113,63 @@ function handleGoTypeDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
106
113
  if (!spec || spec.type !== 'type_spec') continue;
107
114
  const nameNode = spec.childForFieldName('name');
108
115
  const typeNode = spec.childForFieldName('type');
109
- if (nameNode && typeNode) {
110
- if (typeNode.type === 'struct_type') {
111
- const fields = extractStructFields(typeNode);
112
- ctx.definitions.push({
113
- name: nameNode.text,
114
- kind: 'struct',
115
- line: node.startPosition.row + 1,
116
- endLine: nodeEndLine(node),
117
- children: fields.length > 0 ? fields : undefined,
118
- });
119
- } else if (typeNode.type === 'interface_type') {
120
- ctx.definitions.push({
121
- name: nameNode.text,
122
- kind: 'interface',
123
- line: node.startPosition.row + 1,
124
- endLine: nodeEndLine(node),
125
- });
126
- for (let j = 0; j < typeNode.childCount; j++) {
127
- const member = typeNode.child(j);
128
- if (member && member.type === 'method_elem') {
129
- const methName = member.childForFieldName('name');
130
- if (methName) {
131
- ctx.definitions.push({
132
- name: `${nameNode.text}.${methName.text}`,
133
- kind: 'method',
134
- line: member.startPosition.row + 1,
135
- endLine: member.endPosition.row + 1,
136
- });
137
- }
138
- }
139
- }
140
- } else {
116
+ if (!nameNode || !typeNode) continue;
117
+
118
+ if (typeNode.type === 'struct_type') {
119
+ handleGoStructType(node, nameNode, typeNode, ctx);
120
+ } else if (typeNode.type === 'interface_type') {
121
+ handleGoInterfaceType(node, nameNode, typeNode, ctx);
122
+ } else {
123
+ ctx.definitions.push({
124
+ name: nameNode.text,
125
+ kind: 'type',
126
+ line: node.startPosition.row + 1,
127
+ endLine: nodeEndLine(node),
128
+ });
129
+ }
130
+ }
131
+ }
132
+
133
+ /** Handle a struct type_spec: emit struct definition with field children. */
134
+ function handleGoStructType(
135
+ declNode: TreeSitterNode,
136
+ nameNode: TreeSitterNode,
137
+ typeNode: TreeSitterNode,
138
+ ctx: ExtractorOutput,
139
+ ): void {
140
+ const fields = extractStructFields(typeNode);
141
+ ctx.definitions.push({
142
+ name: nameNode.text,
143
+ kind: 'struct',
144
+ line: declNode.startPosition.row + 1,
145
+ endLine: nodeEndLine(declNode),
146
+ children: fields.length > 0 ? fields : undefined,
147
+ });
148
+ }
149
+
150
+ /** Handle an interface type_spec: emit interface definition + method definitions. */
151
+ function handleGoInterfaceType(
152
+ declNode: TreeSitterNode,
153
+ nameNode: TreeSitterNode,
154
+ typeNode: TreeSitterNode,
155
+ ctx: ExtractorOutput,
156
+ ): void {
157
+ ctx.definitions.push({
158
+ name: nameNode.text,
159
+ kind: 'interface',
160
+ line: declNode.startPosition.row + 1,
161
+ endLine: nodeEndLine(declNode),
162
+ });
163
+ for (let j = 0; j < typeNode.childCount; j++) {
164
+ const member = typeNode.child(j);
165
+ if (member && member.type === 'method_elem') {
166
+ const methName = member.childForFieldName('name');
167
+ if (methName) {
141
168
  ctx.definitions.push({
142
- name: nameNode.text,
143
- kind: 'type',
144
- line: node.startPosition.row + 1,
145
- endLine: nodeEndLine(node),
169
+ name: `${nameNode.text}.${methName.text}`,
170
+ kind: 'method',
171
+ line: member.startPosition.row + 1,
172
+ endLine: member.endPosition.row + 1,
146
173
  });
147
174
  }
148
175
  }
@@ -170,9 +197,9 @@ function handleGoImportDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
170
197
  function extractGoImportSpec(spec: TreeSitterNode, ctx: ExtractorOutput): void {
171
198
  const pathNode = spec.childForFieldName('path');
172
199
  if (pathNode) {
173
- const importPath = pathNode.text.replace(/"/g, '');
200
+ const importPath = stripQuotes(pathNode.text);
174
201
  const nameNode = spec.childForFieldName('name');
175
- const alias = nameNode ? nameNode.text : (importPath.split('/').pop() ?? importPath);
202
+ const alias = nameNode ? nameNode.text : lastPathSegment(importPath);
176
203
  ctx.imports.push({
177
204
  source: importPath,
178
205
  names: [alias],
@@ -220,113 +247,104 @@ function extractGoTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void {
220
247
  extractGoTypeMapDepth(node, ctx, 0);
221
248
  }
222
249
 
223
- function setIfHigher(
224
- typeMap: Map<string, TypeMapEntry>,
225
- name: string,
226
- type: string,
227
- confidence: number,
250
+ /** Map identifiers in a typed declaration node to their type (confidence 0.9). */
251
+ function handleTypedIdentifiers(
252
+ node: TreeSitterNode,
253
+ typeMap: Map<string, { type: string; confidence: number }>,
228
254
  ): void {
229
- const existing = typeMap.get(name);
230
- if (!existing || confidence > existing.confidence) {
231
- typeMap.set(name, { type, confidence });
255
+ const typeNode = node.childForFieldName('type');
256
+ if (!typeNode) return;
257
+ const typeName = extractGoTypeName(typeNode);
258
+ if (!typeName) return;
259
+ for (let i = 0; i < node.childCount; i++) {
260
+ const child = node.child(i);
261
+ if (child && child.type === 'identifier') {
262
+ setTypeMapEntry(typeMap, child.text, typeName, 0.9);
263
+ }
232
264
  }
233
265
  }
234
266
 
235
- function extractGoTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, depth: number): void {
236
- if (depth >= MAX_WALK_DEPTH) return;
237
-
238
- // var x MyType = ... or var x, y MyType → var_declaration > var_spec (confidence 0.9)
239
- if (node.type === 'var_spec') {
240
- const typeNode = node.childForFieldName('type');
267
+ /** Infer type from a single RHS expression in a short var declaration. */
268
+ function inferShortVarType(
269
+ varNode: TreeSitterNode,
270
+ rhs: TreeSitterNode,
271
+ typeMap: Map<string, { type: string; confidence: number }>,
272
+ ): void {
273
+ // x := Struct{...} — composite literal (confidence 1.0)
274
+ if (rhs.type === 'composite_literal') {
275
+ const typeNode = rhs.childForFieldName('type');
241
276
  if (typeNode) {
242
277
  const typeName = extractGoTypeName(typeNode);
243
- if (typeName) {
244
- for (let i = 0; i < node.childCount; i++) {
245
- const child = node.child(i);
246
- if (child && child.type === 'identifier') {
247
- if (ctx.typeMap) setIfHigher(ctx.typeMap, child.text, typeName, 0.9);
248
- }
249
- }
278
+ if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 1.0);
279
+ }
280
+ }
281
+ // x := &Struct{...} address-of composite literal (confidence 1.0)
282
+ if (rhs.type === 'unary_expression') {
283
+ const operand = rhs.childForFieldName('operand');
284
+ if (operand && operand.type === 'composite_literal') {
285
+ const typeNode = operand.childForFieldName('type');
286
+ if (typeNode) {
287
+ const typeName = extractGoTypeName(typeNode);
288
+ if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 1.0);
250
289
  }
251
290
  }
252
291
  }
253
-
254
- // Function/method parameter types: parameter_declaration (confidence 0.9)
255
- if (node.type === 'parameter_declaration') {
256
- const typeNode = node.childForFieldName('type');
257
- if (typeNode) {
258
- const typeName = extractGoTypeName(typeNode);
259
- if (typeName) {
260
- for (let i = 0; i < node.childCount; i++) {
261
- const child = node.child(i);
262
- if (child && child.type === 'identifier') {
263
- if (ctx.typeMap) setIfHigher(ctx.typeMap, child.text, typeName, 0.9);
264
- }
265
- }
292
+ // x := NewFoo() or x := pkg.NewFoo() — factory function (confidence 0.7)
293
+ if (rhs.type === 'call_expression') {
294
+ const fn = rhs.childForFieldName('function');
295
+ if (fn && fn.type === 'selector_expression') {
296
+ const field = fn.childForFieldName('field');
297
+ if (field?.text.startsWith('New')) {
298
+ const typeName = field.text.slice(3);
299
+ if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 0.7);
266
300
  }
301
+ } else if (fn && fn.type === 'identifier' && fn.text.startsWith('New')) {
302
+ const typeName = fn.text.slice(3);
303
+ if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 0.7);
267
304
  }
268
305
  }
306
+ }
269
307
 
270
- // short_var_declaration: x := Struct{}, x := &Struct{}, x := NewFoo()
271
- // Handles multi-variable forms: x, y := A{}, B{}
272
- if (node.type === 'short_var_declaration') {
273
- const left = node.childForFieldName('left');
274
- const right = node.childForFieldName('right');
275
- if (left && right) {
276
- const lefts =
277
- left.type === 'expression_list'
278
- ? Array.from({ length: left.childCount }, (_, i) => left.child(i)).filter(
279
- (c): c is TreeSitterNode => c?.type === 'identifier',
280
- )
281
- : left.type === 'identifier'
282
- ? [left]
283
- : [];
284
- const rights =
285
- right.type === 'expression_list'
286
- ? Array.from({ length: right.childCount }, (_, i) => right.child(i)).filter(
287
- (c): c is TreeSitterNode => !!c?.type,
288
- )
289
- : [right];
290
-
291
- for (let idx = 0; idx < lefts.length; idx++) {
292
- const varNode = lefts[idx];
293
- const rhs = rights[idx];
294
- if (!varNode || !rhs) continue;
295
-
296
- // x := Struct{...} — composite literal (confidence 1.0)
297
- if (rhs.type === 'composite_literal') {
298
- const typeNode = rhs.childForFieldName('type');
299
- if (typeNode) {
300
- const typeName = extractGoTypeName(typeNode);
301
- if (typeName && ctx.typeMap) setIfHigher(ctx.typeMap, varNode.text, typeName, 1.0);
302
- }
303
- }
304
- // x := &Struct{...} — address-of composite literal (confidence 1.0)
305
- if (rhs.type === 'unary_expression') {
306
- const operand = rhs.childForFieldName('operand');
307
- if (operand && operand.type === 'composite_literal') {
308
- const typeNode = operand.childForFieldName('type');
309
- if (typeNode) {
310
- const typeName = extractGoTypeName(typeNode);
311
- if (typeName && ctx.typeMap) setIfHigher(ctx.typeMap, varNode.text, typeName, 1.0);
312
- }
313
- }
314
- }
315
- // x := NewFoo() or x := pkg.NewFoo() — factory function (confidence 0.7)
316
- if (rhs.type === 'call_expression') {
317
- const fn = rhs.childForFieldName('function');
318
- if (fn && fn.type === 'selector_expression') {
319
- const field = fn.childForFieldName('field');
320
- if (field?.text.startsWith('New')) {
321
- const typeName = field.text.slice(3);
322
- if (typeName && ctx.typeMap) setIfHigher(ctx.typeMap, varNode.text, typeName, 0.7);
323
- }
324
- } else if (fn && fn.type === 'identifier' && fn.text.startsWith('New')) {
325
- const typeName = fn.text.slice(3);
326
- if (typeName && ctx.typeMap) setIfHigher(ctx.typeMap, varNode.text, typeName, 0.7);
327
- }
328
- }
329
- }
308
+ /** Handle short_var_declaration: x := Struct{}, x := &Struct{}, x := NewFoo(). */
309
+ function handleShortVarDecl(
310
+ node: TreeSitterNode,
311
+ typeMap: Map<string, { type: string; confidence: number }>,
312
+ ): void {
313
+ const left = node.childForFieldName('left');
314
+ const right = node.childForFieldName('right');
315
+ if (!left || !right) return;
316
+
317
+ const lefts =
318
+ left.type === 'expression_list'
319
+ ? Array.from({ length: left.childCount }, (_, i) => left.child(i)).filter(
320
+ (c): c is TreeSitterNode => c?.type === 'identifier',
321
+ )
322
+ : left.type === 'identifier'
323
+ ? [left]
324
+ : [];
325
+ const rights =
326
+ right.type === 'expression_list'
327
+ ? Array.from({ length: right.childCount }, (_, i) => right.child(i)).filter(
328
+ (c): c is TreeSitterNode => !!c?.type,
329
+ )
330
+ : [right];
331
+
332
+ for (let idx = 0; idx < lefts.length; idx++) {
333
+ const varNode = lefts[idx];
334
+ const rhs = rights[idx];
335
+ if (!varNode || !rhs) continue;
336
+ inferShortVarType(varNode, rhs, typeMap);
337
+ }
338
+ }
339
+
340
+ function extractGoTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, depth: number): void {
341
+ if (depth >= MAX_WALK_DEPTH) return;
342
+
343
+ if (ctx.typeMap) {
344
+ if (node.type === 'var_spec' || node.type === 'parameter_declaration') {
345
+ handleTypedIdentifiers(node, ctx.typeMap);
346
+ } else if (node.type === 'short_var_declaration') {
347
+ handleShortVarDecl(node, ctx.typeMap);
330
348
  }
331
349
  }
332
350
 
@@ -6,7 +6,7 @@ import type {
6
6
  TreeSitterNode,
7
7
  TreeSitterTree,
8
8
  } from '../types.js';
9
- import { nodeEndLine } from './helpers.js';
9
+ import { nodeEndLine, stripQuotes } from './helpers.js';
10
10
 
11
11
  /**
12
12
  * Extract symbols from HCL (Terraform) files.
@@ -80,18 +80,18 @@ function resolveHclBlockName(blockType: string, strings: TreeSitterNode[]): stri
80
80
  const s0 = strings[0];
81
81
  const s1 = strings[1];
82
82
  if (blockType === 'resource' && s0 && s1) {
83
- return `${s0.text.replace(/"/g, '')}.${s1.text.replace(/"/g, '')}`;
83
+ return `${stripQuotes(s0.text)}.${stripQuotes(s1.text)}`;
84
84
  }
85
85
  if (blockType === 'data' && s0 && s1) {
86
- return `data.${s0.text.replace(/"/g, '')}.${s1.text.replace(/"/g, '')}`;
86
+ return `data.${stripQuotes(s0.text)}.${stripQuotes(s1.text)}`;
87
87
  }
88
88
  if ((blockType === 'variable' || blockType === 'output' || blockType === 'module') && s0) {
89
- return `${blockType}.${s0.text.replace(/"/g, '')}`;
89
+ return `${blockType}.${stripQuotes(s0.text)}`;
90
90
  }
91
91
  if (blockType === 'locals') return 'locals';
92
92
  if (blockType === 'terraform' || blockType === 'provider') {
93
93
  let name = blockType;
94
- if (s0) name += `.${s0.text.replace(/"/g, '')}`;
94
+ if (s0) name += `.${stripQuotes(s0.text)}`;
95
95
  return name;
96
96
  }
97
97
  return '';
@@ -126,7 +126,7 @@ function extractHclModuleSource(
126
126
  const key = attr.childForFieldName('key') || attr.child(0);
127
127
  const val = attr.childForFieldName('val') || attr.child(2);
128
128
  if (key && key.text === 'source' && val) {
129
- const src = val.text.replace(/"/g, '');
129
+ const src = stripQuotes(val.text);
130
130
  if (src.startsWith('./') || src.startsWith('../')) {
131
131
  ctx.imports.push({ source: src, names: [], line: attr.startPosition.row + 1 });
132
132
  }
@@ -1,4 +1,4 @@
1
- import type { TreeSitterNode } from '../types.js';
1
+ import type { SubDeclaration, TreeSitterNode, TypeMapEntry } from '../types.js';
2
2
 
3
3
  /**
4
4
  * Maximum recursion depth for tree-sitter AST walkers.
@@ -18,6 +18,22 @@ export function findChild(node: TreeSitterNode, type: string): TreeSitterNode |
18
18
  return null;
19
19
  }
20
20
 
21
+ /**
22
+ * Merge a type-map entry, keeping the higher-confidence one.
23
+ * Shared across all language extractors that build type maps for call resolution.
24
+ */
25
+ export function setTypeMapEntry(
26
+ typeMap: Map<string, TypeMapEntry>,
27
+ name: string,
28
+ type: string,
29
+ confidence: number,
30
+ ): void {
31
+ const existing = typeMap.get(name);
32
+ if (!existing || confidence > existing.confidence) {
33
+ typeMap.set(name, { type, confidence });
34
+ }
35
+ }
36
+
21
37
  /**
22
38
  * Extract visibility from a node by scanning its children for modifier keywords.
23
39
  * Works for Java, C#, PHP, and similar languages where modifiers are child nodes.
@@ -70,6 +86,82 @@ export function rustVisibility(node: TreeSitterNode): 'public' | 'private' {
70
86
  return 'private';
71
87
  }
72
88
 
89
+ // ── Parser abstraction helpers ─────────────────────────────────────────────
90
+
91
+ /**
92
+ * Walk up the parent chain to find an enclosing node whose type is in `typeNames`.
93
+ * Returns the text of `nameField` (default `'name'`) on the matching ancestor, or null.
94
+ *
95
+ * Replaces per-language `findParentClass` / `findParentType` / `findCurrentImpl` helpers.
96
+ */
97
+ export function findParentNode(
98
+ node: TreeSitterNode,
99
+ typeNames: readonly string[],
100
+ nameField: string = 'name',
101
+ ): string | null {
102
+ let current = node.parent;
103
+ while (current) {
104
+ if (typeNames.includes(current.type)) {
105
+ const nameNode = current.childForFieldName(nameField);
106
+ return nameNode ? nameNode.text : null;
107
+ }
108
+ current = current.parent;
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * Extract child declarations from a container node's body.
115
+ * Finds the body via `bodyFields` (tries childForFieldName then findChild for each),
116
+ * iterates its children, filters by `memberType`, extracts `nameField`, and returns SubDeclarations.
117
+ *
118
+ * Replaces per-language extractStructFields / extractEnumVariants / extractEnumConstants helpers
119
+ * for the common case where each member has a direct name field.
120
+ */
121
+ export function extractBodyMembers(
122
+ containerNode: TreeSitterNode,
123
+ bodyFields: readonly string[],
124
+ memberType: string,
125
+ kind: SubDeclaration['kind'],
126
+ nameField: string = 'name',
127
+ visibility?: (member: TreeSitterNode) => SubDeclaration['visibility'],
128
+ ): SubDeclaration[] {
129
+ const members: SubDeclaration[] = [];
130
+ let body: TreeSitterNode | null = null;
131
+ for (const field of bodyFields) {
132
+ body = containerNode.childForFieldName(field) || findChild(containerNode, field);
133
+ if (body) break;
134
+ }
135
+ if (!body) return members;
136
+ for (let i = 0; i < body.childCount; i++) {
137
+ const member = body.child(i);
138
+ if (!member || member.type !== memberType) continue;
139
+ const nn = member.childForFieldName(nameField);
140
+ if (nn) {
141
+ const entry: SubDeclaration = { name: nn.text, kind, line: member.startPosition.row + 1 };
142
+ if (visibility) entry.visibility = visibility(member);
143
+ members.push(entry);
144
+ }
145
+ }
146
+ return members;
147
+ }
148
+
149
+ /**
150
+ * Strip leading/trailing quotes (single, double, or backtick) from a string.
151
+ * Strips only the leading/trailing delimiter; interior quotes are untouched.
152
+ */
153
+ export function stripQuotes(text: string): string {
154
+ return text.replace(/^['"`]|['"`]$/g, '');
155
+ }
156
+
157
+ /**
158
+ * Extract the last segment of a delimited path.
159
+ * e.g. `lastPathSegment('java.util.List', '.')` → `'List'`
160
+ */
161
+ export function lastPathSegment(path: string, separator: string = '/'): string {
162
+ return path.split(separator).pop() ?? path;
163
+ }
164
+
73
165
  export function extractModifierVisibility(
74
166
  node: TreeSitterNode,
75
167
  modifierTypes: Set<string> = DEFAULT_MODIFIER_TYPES,
@@ -1,9 +1,15 @@
1
+ export { extractBashSymbols } from './bash.js';
2
+ export { extractCSymbols } from './c.js';
3
+ export { extractCppSymbols } from './cpp.js';
1
4
  export { extractCSharpSymbols } from './csharp.js';
2
5
  export { extractGoSymbols } from './go.js';
3
6
  export { extractHCLSymbols } from './hcl.js';
4
7
  export { extractJavaSymbols } from './java.js';
5
8
  export { extractSymbols } from './javascript.js';
9
+ export { extractKotlinSymbols } from './kotlin.js';
6
10
  export { extractPHPSymbols } from './php.js';
7
11
  export { extractPythonSymbols } from './python.js';
8
12
  export { extractRubySymbols } from './ruby.js';
9
13
  export { extractRustSymbols } from './rust.js';
14
+ export { extractScalaSymbols } from './scala.js';
15
+ export { extractSwiftSymbols } from './swift.js';
@@ -6,7 +6,14 @@ import type {
6
6
  TreeSitterTree,
7
7
  TypeMapEntry,
8
8
  } from '../types.js';
9
- import { extractModifierVisibility, findChild, nodeEndLine } from './helpers.js';
9
+ import {
10
+ extractBodyMembers,
11
+ extractModifierVisibility,
12
+ findChild,
13
+ findParentNode,
14
+ lastPathSegment,
15
+ nodeEndLine,
16
+ } from './helpers.js';
10
17
 
11
18
  /**
12
19
  * Extract symbols from Java files.
@@ -104,6 +111,25 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
104
111
  }
105
112
  }
106
113
 
114
+ const JAVA_TYPE_NODE_TYPES = new Set(['type_identifier', 'identifier', 'generic_type']);
115
+
116
+ /** Resolve interface name from a type node (handles generic_type unwrapping). */
117
+ function resolveJavaIfaceName(node: TreeSitterNode): string | undefined {
118
+ return node.type === 'generic_type' ? node.child(0)?.text : node.text;
119
+ }
120
+
121
+ /** Push a single interface type node as an implements entry. */
122
+ function pushJavaIface(
123
+ node: TreeSitterNode,
124
+ className: string,
125
+ line: number,
126
+ ctx: ExtractorOutput,
127
+ ): void {
128
+ if (!JAVA_TYPE_NODE_TYPES.has(node.type)) return;
129
+ const ifaceName = resolveJavaIfaceName(node);
130
+ if (ifaceName) ctx.classes.push({ name: className, implements: ifaceName, line });
131
+ }
132
+
107
133
  function extractJavaInterfaces(
108
134
  interfaces: TreeSitterNode,
109
135
  className: string,
@@ -112,28 +138,15 @@ function extractJavaInterfaces(
112
138
  ): void {
113
139
  for (let i = 0; i < interfaces.childCount; i++) {
114
140
  const child = interfaces.child(i);
115
- if (
116
- child &&
117
- (child.type === 'type_identifier' ||
118
- child.type === 'identifier' ||
119
- child.type === 'type_list' ||
120
- child.type === 'generic_type')
121
- ) {
122
- if (child.type === 'type_list') {
123
- for (let j = 0; j < child.childCount; j++) {
124
- const t = child.child(j);
125
- if (
126
- t &&
127
- (t.type === 'type_identifier' || t.type === 'identifier' || t.type === 'generic_type')
128
- ) {
129
- const ifaceName = t.type === 'generic_type' ? t.child(0)?.text : t.text;
130
- if (ifaceName) ctx.classes.push({ name: className, implements: ifaceName, line });
131
- }
132
- }
133
- } else {
134
- const ifaceName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
135
- if (ifaceName) ctx.classes.push({ name: className, implements: ifaceName, line });
141
+ if (!child) continue;
142
+
143
+ if (child.type === 'type_list') {
144
+ for (let j = 0; j < child.childCount; j++) {
145
+ const t = child.child(j);
146
+ if (t) pushJavaIface(t, className, line, ctx);
136
147
  }
148
+ } else {
149
+ pushJavaIface(child, className, line, ctx);
137
150
  }
138
151
  }
139
152
  }
@@ -218,7 +231,7 @@ function handleJavaImportDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
218
231
  const child = node.child(i);
219
232
  if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
220
233
  const fullPath = child.text;
221
- const lastName = fullPath.split('.').pop() ?? fullPath;
234
+ const lastName = lastPathSegment(fullPath, '.');
222
235
  ctx.imports.push({
223
236
  source: fullPath,
224
237
  names: [lastName],
@@ -263,20 +276,13 @@ function handleJavaObjectCreation(node: TreeSitterNode, ctx: ExtractorOutput): v
263
276
  if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
264
277
  }
265
278
 
279
+ const JAVA_PARENT_TYPES = [
280
+ 'class_declaration',
281
+ 'enum_declaration',
282
+ 'interface_declaration',
283
+ ] as const;
266
284
  function findJavaParentClass(node: TreeSitterNode): string | null {
267
- let current = node.parent;
268
- while (current) {
269
- if (
270
- current.type === 'class_declaration' ||
271
- current.type === 'enum_declaration' ||
272
- current.type === 'interface_declaration'
273
- ) {
274
- const nameNode = current.childForFieldName('name');
275
- return nameNode ? nameNode.text : null;
276
- }
277
- current = current.parent;
278
- }
279
- return null;
285
+ return findParentNode(node, JAVA_PARENT_TYPES);
280
286
  }
281
287
 
282
288
  // ── Child extraction helpers ────────────────────────────────────────────────
@@ -333,16 +339,5 @@ function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
333
339
  }
334
340
 
335
341
  function extractEnumConstants(enumNode: TreeSitterNode): SubDeclaration[] {
336
- const constants: SubDeclaration[] = [];
337
- const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_body');
338
- if (!body) return constants;
339
- for (let i = 0; i < body.childCount; i++) {
340
- const member = body.child(i);
341
- if (!member || member.type !== 'enum_constant') continue;
342
- const nameNode = member.childForFieldName('name');
343
- if (nameNode) {
344
- constants.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
345
- }
346
- }
347
- return constants;
342
+ return extractBodyMembers(enumNode, ['body', 'enum_body'], 'enum_constant', 'constant');
348
343
  }