@optave/codegraph 3.5.0 → 3.7.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 (346) hide show
  1. package/README.md +47 -21
  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 +206 -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/dart.d.ts +6 -0
  103. package/dist/extractors/dart.d.ts.map +1 -0
  104. package/dist/extractors/dart.js +277 -0
  105. package/dist/extractors/dart.js.map +1 -0
  106. package/dist/extractors/elixir.d.ts +9 -0
  107. package/dist/extractors/elixir.d.ts.map +1 -0
  108. package/dist/extractors/elixir.js +223 -0
  109. package/dist/extractors/elixir.js.map +1 -0
  110. package/dist/extractors/go.d.ts.map +1 -1
  111. package/dist/extractors/go.js +126 -130
  112. package/dist/extractors/go.js.map +1 -1
  113. package/dist/extractors/haskell.d.ts +8 -0
  114. package/dist/extractors/haskell.d.ts.map +1 -0
  115. package/dist/extractors/haskell.js +217 -0
  116. package/dist/extractors/haskell.js.map +1 -0
  117. package/dist/extractors/hcl.js +6 -6
  118. package/dist/extractors/hcl.js.map +1 -1
  119. package/dist/extractors/helpers.d.ts +32 -1
  120. package/dist/extractors/helpers.d.ts.map +1 -1
  121. package/dist/extractors/helpers.js +74 -0
  122. package/dist/extractors/helpers.js.map +1 -1
  123. package/dist/extractors/index.d.ts +12 -0
  124. package/dist/extractors/index.d.ts.map +1 -1
  125. package/dist/extractors/index.js +12 -0
  126. package/dist/extractors/index.js.map +1 -1
  127. package/dist/extractors/java.d.ts.map +1 -1
  128. package/dist/extractors/java.js +32 -47
  129. package/dist/extractors/java.js.map +1 -1
  130. package/dist/extractors/javascript.d.ts.map +1 -1
  131. package/dist/extractors/javascript.js +306 -292
  132. package/dist/extractors/javascript.js.map +1 -1
  133. package/dist/extractors/kotlin.d.ts +6 -0
  134. package/dist/extractors/kotlin.d.ts.map +1 -0
  135. package/dist/extractors/kotlin.js +275 -0
  136. package/dist/extractors/kotlin.js.map +1 -0
  137. package/dist/extractors/lua.d.ts +6 -0
  138. package/dist/extractors/lua.d.ts.map +1 -0
  139. package/dist/extractors/lua.js +162 -0
  140. package/dist/extractors/lua.js.map +1 -0
  141. package/dist/extractors/ocaml.d.ts +6 -0
  142. package/dist/extractors/ocaml.d.ts.map +1 -0
  143. package/dist/extractors/ocaml.js +236 -0
  144. package/dist/extractors/ocaml.js.map +1 -0
  145. package/dist/extractors/php.d.ts.map +1 -1
  146. package/dist/extractors/php.js +39 -44
  147. package/dist/extractors/php.js.map +1 -1
  148. package/dist/extractors/python.d.ts.map +1 -1
  149. package/dist/extractors/python.js +75 -93
  150. package/dist/extractors/python.js.map +1 -1
  151. package/dist/extractors/ruby.js +6 -13
  152. package/dist/extractors/ruby.js.map +1 -1
  153. package/dist/extractors/rust.d.ts.map +1 -1
  154. package/dist/extractors/rust.js +58 -83
  155. package/dist/extractors/rust.js.map +1 -1
  156. package/dist/extractors/scala.d.ts +6 -0
  157. package/dist/extractors/scala.d.ts.map +1 -0
  158. package/dist/extractors/scala.js +269 -0
  159. package/dist/extractors/scala.js.map +1 -0
  160. package/dist/extractors/swift.d.ts +6 -0
  161. package/dist/extractors/swift.d.ts.map +1 -0
  162. package/dist/extractors/swift.js +275 -0
  163. package/dist/extractors/swift.js.map +1 -0
  164. package/dist/extractors/zig.d.ts +9 -0
  165. package/dist/extractors/zig.d.ts.map +1 -0
  166. package/dist/extractors/zig.js +276 -0
  167. package/dist/extractors/zig.js.map +1 -0
  168. package/dist/features/ast.d.ts +2 -0
  169. package/dist/features/ast.d.ts.map +1 -1
  170. package/dist/features/ast.js +9 -24
  171. package/dist/features/ast.js.map +1 -1
  172. package/dist/features/audit.d.ts.map +1 -1
  173. package/dist/features/audit.js +17 -21
  174. package/dist/features/audit.js.map +1 -1
  175. package/dist/features/branch-compare.d.ts.map +1 -1
  176. package/dist/features/branch-compare.js +47 -3
  177. package/dist/features/branch-compare.js.map +1 -1
  178. package/dist/features/cfg.d.ts +7 -1
  179. package/dist/features/cfg.d.ts.map +1 -1
  180. package/dist/features/cfg.js +72 -61
  181. package/dist/features/cfg.js.map +1 -1
  182. package/dist/features/check.d.ts.map +1 -1
  183. package/dist/features/check.js +79 -62
  184. package/dist/features/check.js.map +1 -1
  185. package/dist/features/complexity-query.d.ts.map +1 -1
  186. package/dist/features/complexity-query.js +142 -137
  187. package/dist/features/complexity-query.js.map +1 -1
  188. package/dist/features/complexity.d.ts +7 -1
  189. package/dist/features/complexity.d.ts.map +1 -1
  190. package/dist/features/complexity.js +62 -1
  191. package/dist/features/complexity.js.map +1 -1
  192. package/dist/features/dataflow.d.ts +7 -1
  193. package/dist/features/dataflow.d.ts.map +1 -1
  194. package/dist/features/dataflow.js +356 -188
  195. package/dist/features/dataflow.js.map +1 -1
  196. package/dist/features/graph-enrichment.d.ts.map +1 -1
  197. package/dist/features/graph-enrichment.js +117 -104
  198. package/dist/features/graph-enrichment.js.map +1 -1
  199. package/dist/features/sequence.d.ts.map +1 -1
  200. package/dist/features/sequence.js +25 -4
  201. package/dist/features/sequence.js.map +1 -1
  202. package/dist/features/structure-query.d.ts.map +1 -1
  203. package/dist/features/structure-query.js +29 -4
  204. package/dist/features/structure-query.js.map +1 -1
  205. package/dist/features/structure.d.ts.map +1 -1
  206. package/dist/features/structure.js +35 -15
  207. package/dist/features/structure.js.map +1 -1
  208. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  209. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  210. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  211. package/dist/graph/algorithms/leiden/index.js +43 -28
  212. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  213. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  214. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  215. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  216. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  217. package/dist/graph/algorithms/leiden/partition.js +89 -106
  218. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  219. package/dist/graph/model.d.ts +2 -0
  220. package/dist/graph/model.d.ts.map +1 -1
  221. package/dist/graph/model.js +20 -8
  222. package/dist/graph/model.js.map +1 -1
  223. package/dist/infrastructure/config.d.ts +0 -8
  224. package/dist/infrastructure/config.d.ts.map +1 -1
  225. package/dist/infrastructure/config.js +73 -62
  226. package/dist/infrastructure/config.js.map +1 -1
  227. package/dist/infrastructure/registry.d.ts +0 -8
  228. package/dist/infrastructure/registry.d.ts.map +1 -1
  229. package/dist/infrastructure/registry.js +12 -14
  230. package/dist/infrastructure/registry.js.map +1 -1
  231. package/dist/mcp/server.d.ts.map +1 -1
  232. package/dist/mcp/server.js +45 -36
  233. package/dist/mcp/server.js.map +1 -1
  234. package/dist/presentation/audit.d.ts.map +1 -1
  235. package/dist/presentation/audit.js +61 -57
  236. package/dist/presentation/audit.js.map +1 -1
  237. package/dist/presentation/branch-compare.d.ts.map +1 -1
  238. package/dist/presentation/branch-compare.js +56 -38
  239. package/dist/presentation/branch-compare.js.map +1 -1
  240. package/dist/presentation/check.d.ts.map +1 -1
  241. package/dist/presentation/check.js +30 -32
  242. package/dist/presentation/check.js.map +1 -1
  243. package/dist/presentation/colors.d.ts.map +1 -1
  244. package/dist/presentation/colors.js +2 -0
  245. package/dist/presentation/colors.js.map +1 -1
  246. package/dist/presentation/complexity.d.ts.map +1 -1
  247. package/dist/presentation/complexity.js +25 -19
  248. package/dist/presentation/complexity.js.map +1 -1
  249. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  250. package/dist/presentation/queries-cli/exports.js +15 -15
  251. package/dist/presentation/queries-cli/exports.js.map +1 -1
  252. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  253. package/dist/presentation/queries-cli/impact.js +29 -19
  254. package/dist/presentation/queries-cli/impact.js.map +1 -1
  255. package/dist/types.d.ts +182 -7
  256. package/dist/types.d.ts.map +1 -1
  257. package/grammars/tree-sitter-bash.wasm +0 -0
  258. package/grammars/tree-sitter-c.wasm +0 -0
  259. package/grammars/tree-sitter-cpp.wasm +0 -0
  260. package/grammars/tree-sitter-dart.wasm +0 -0
  261. package/grammars/tree-sitter-elixir.wasm +0 -0
  262. package/grammars/tree-sitter-haskell.wasm +0 -0
  263. package/grammars/tree-sitter-kotlin.wasm +0 -0
  264. package/grammars/tree-sitter-lua.wasm +0 -0
  265. package/grammars/tree-sitter-ocaml.wasm +0 -0
  266. package/grammars/tree-sitter-scala.wasm +0 -0
  267. package/grammars/tree-sitter-swift.wasm +0 -0
  268. package/grammars/tree-sitter-zig.wasm +0 -0
  269. package/package.json +19 -7
  270. package/src/ast-analysis/engine.ts +147 -138
  271. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  272. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  273. package/src/db/connection.ts +90 -59
  274. package/src/db/index.ts +1 -0
  275. package/src/db/migrations.ts +36 -32
  276. package/src/domain/analysis/context.ts +73 -75
  277. package/src/domain/analysis/dependencies.ts +78 -68
  278. package/src/domain/analysis/exports.ts +45 -34
  279. package/src/domain/analysis/fn-impact.ts +67 -64
  280. package/src/domain/analysis/module-map.ts +103 -8
  281. package/src/domain/analysis/query-helpers.ts +35 -0
  282. package/src/domain/graph/builder/helpers.ts +12 -6
  283. package/src/domain/graph/builder/incremental.ts +3 -2
  284. package/src/domain/graph/builder/pipeline.ts +71 -3
  285. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  286. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  287. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  288. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  289. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  290. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  291. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  292. package/src/domain/graph/resolve.ts +34 -46
  293. package/src/domain/graph/watcher.ts +12 -14
  294. package/src/domain/parser.ts +222 -97
  295. package/src/domain/search/search/cli-formatter.ts +121 -94
  296. package/src/extractors/bash.ts +97 -0
  297. package/src/extractors/c.ts +212 -0
  298. package/src/extractors/cpp.ts +298 -0
  299. package/src/extractors/csharp.ts +53 -56
  300. package/src/extractors/dart.ts +304 -0
  301. package/src/extractors/elixir.ts +251 -0
  302. package/src/extractors/go.ts +152 -134
  303. package/src/extractors/haskell.ts +235 -0
  304. package/src/extractors/hcl.ts +6 -6
  305. package/src/extractors/helpers.ts +93 -1
  306. package/src/extractors/index.ts +12 -0
  307. package/src/extractors/java.ts +43 -48
  308. package/src/extractors/javascript.ts +328 -281
  309. package/src/extractors/kotlin.ts +293 -0
  310. package/src/extractors/lua.ts +169 -0
  311. package/src/extractors/ocaml.ts +259 -0
  312. package/src/extractors/php.ts +46 -40
  313. package/src/extractors/python.ts +81 -104
  314. package/src/extractors/ruby.ts +6 -13
  315. package/src/extractors/rust.ts +65 -85
  316. package/src/extractors/scala.ts +285 -0
  317. package/src/extractors/swift.ts +293 -0
  318. package/src/extractors/zig.ts +294 -0
  319. package/src/features/ast.ts +10 -25
  320. package/src/features/audit.ts +24 -20
  321. package/src/features/branch-compare.ts +51 -4
  322. package/src/features/cfg.ts +113 -65
  323. package/src/features/check.ts +90 -74
  324. package/src/features/complexity-query.ts +181 -163
  325. package/src/features/complexity.ts +64 -1
  326. package/src/features/dataflow.ts +462 -217
  327. package/src/features/graph-enrichment.ts +161 -117
  328. package/src/features/sequence.ts +27 -4
  329. package/src/features/structure-query.ts +43 -4
  330. package/src/features/structure.ts +50 -22
  331. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  332. package/src/graph/algorithms/leiden/index.ts +67 -28
  333. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  334. package/src/graph/algorithms/leiden/partition.ts +131 -98
  335. package/src/graph/model.ts +19 -7
  336. package/src/infrastructure/config.ts +60 -58
  337. package/src/infrastructure/registry.ts +17 -14
  338. package/src/mcp/server.ts +46 -37
  339. package/src/presentation/audit.ts +72 -67
  340. package/src/presentation/branch-compare.ts +54 -50
  341. package/src/presentation/check.ts +34 -34
  342. package/src/presentation/colors.ts +2 -0
  343. package/src/presentation/complexity.ts +39 -33
  344. package/src/presentation/queries-cli/exports.ts +17 -17
  345. package/src/presentation/queries-cli/impact.ts +30 -22
  346. package/src/types.ts +195 -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
 
@@ -0,0 +1,235 @@
1
+ import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
2
+ import { findChild, nodeEndLine } from './helpers.js';
3
+
4
+ /**
5
+ * Extract symbols from Haskell files.
6
+ *
7
+ * Note: tree-sitter-haskell uses `type_synomym` (misspelled) for type aliases.
8
+ */
9
+ export function extractHaskellSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
10
+ const ctx: ExtractorOutput = {
11
+ definitions: [],
12
+ calls: [],
13
+ imports: [],
14
+ classes: [],
15
+ exports: [],
16
+ typeMap: new Map(),
17
+ };
18
+
19
+ walkHaskellNode(tree.rootNode, ctx);
20
+ return ctx;
21
+ }
22
+
23
+ function walkHaskellNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
24
+ switch (node.type) {
25
+ case 'function':
26
+ handleHaskellFunction(node, ctx);
27
+ break;
28
+ case 'bind':
29
+ handleHaskellBind(node, ctx);
30
+ break;
31
+ case 'data_type':
32
+ handleHaskellDataType(node, ctx);
33
+ break;
34
+ case 'newtype':
35
+ handleHaskellNewtype(node, ctx);
36
+ break;
37
+ case 'type_synomym':
38
+ handleHaskellTypeSynonym(node, ctx);
39
+ break;
40
+ case 'class':
41
+ handleHaskellClass(node, ctx);
42
+ break;
43
+ case 'instance':
44
+ handleHaskellInstance(node, ctx);
45
+ break;
46
+ case 'import':
47
+ handleHaskellImport(node, ctx);
48
+ break;
49
+ case 'apply':
50
+ handleHaskellApply(node, ctx);
51
+ break;
52
+ }
53
+
54
+ for (let i = 0; i < node.childCount; i++) {
55
+ const child = node.child(i);
56
+ if (child) walkHaskellNode(child, ctx);
57
+ }
58
+ }
59
+
60
+ function handleHaskellFunction(node: TreeSitterNode, ctx: ExtractorOutput): void {
61
+ const nameNode = node.childForFieldName('name');
62
+ if (!nameNode) return;
63
+
64
+ const params = extractHaskellParams(node);
65
+
66
+ ctx.definitions.push({
67
+ name: nameNode.text,
68
+ kind: 'function',
69
+ line: node.startPosition.row + 1,
70
+ endLine: nodeEndLine(node),
71
+ children: params.length > 0 ? params : undefined,
72
+ });
73
+ }
74
+
75
+ function extractHaskellParams(funcNode: TreeSitterNode): SubDeclaration[] {
76
+ const params: SubDeclaration[] = [];
77
+ // Haskell function patterns are positional children
78
+ for (let i = 0; i < funcNode.childCount; i++) {
79
+ const child = funcNode.child(i);
80
+ if (!child) continue;
81
+ if (child.type === 'patterns' || child.type === 'parameter') {
82
+ for (let j = 0; j < child.childCount; j++) {
83
+ const pat = child.child(j);
84
+ if (pat && (pat.type === 'variable' || pat.type === 'identifier')) {
85
+ params.push({ name: pat.text, kind: 'parameter', line: pat.startPosition.row + 1 });
86
+ }
87
+ }
88
+ }
89
+ if (child.type === 'variable' && i > 0) {
90
+ // Pattern parameters after the function name
91
+ params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
92
+ }
93
+ }
94
+ return params;
95
+ }
96
+
97
+ function handleHaskellBind(node: TreeSitterNode, ctx: ExtractorOutput): void {
98
+ const nameNode = node.childForFieldName('name');
99
+ if (!nameNode) return;
100
+
101
+ ctx.definitions.push({
102
+ name: nameNode.text,
103
+ kind: 'variable',
104
+ line: node.startPosition.row + 1,
105
+ endLine: nodeEndLine(node),
106
+ });
107
+ }
108
+
109
+ function handleHaskellDataType(node: TreeSitterNode, ctx: ExtractorOutput): void {
110
+ const nameNode = node.childForFieldName('name');
111
+ if (!nameNode) return;
112
+ const name = nameNode.text;
113
+
114
+ const children: SubDeclaration[] = [];
115
+ // Extract constructors
116
+ const constructors = node.childForFieldName('constructors');
117
+ if (constructors) {
118
+ for (let i = 0; i < constructors.childCount; i++) {
119
+ const ctor = constructors.child(i);
120
+ if (!ctor) continue;
121
+ if (ctor.type === 'data_constructor' || ctor.type === 'gadt_constructor') {
122
+ const ctorName = findChild(ctor, 'constructor') || findChild(ctor, 'constructor_operator');
123
+ if (ctorName) {
124
+ children.push({
125
+ name: ctorName.text,
126
+ kind: 'property',
127
+ line: ctor.startPosition.row + 1,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ ctx.definitions.push({
135
+ name,
136
+ kind: 'type',
137
+ line: node.startPosition.row + 1,
138
+ endLine: nodeEndLine(node),
139
+ children: children.length > 0 ? children : undefined,
140
+ });
141
+ }
142
+
143
+ function handleHaskellNewtype(node: TreeSitterNode, ctx: ExtractorOutput): void {
144
+ const nameNode = node.childForFieldName('name');
145
+ if (!nameNode) return;
146
+
147
+ ctx.definitions.push({
148
+ name: nameNode.text,
149
+ kind: 'type',
150
+ line: node.startPosition.row + 1,
151
+ endLine: nodeEndLine(node),
152
+ });
153
+ }
154
+
155
+ function handleHaskellTypeSynonym(node: TreeSitterNode, ctx: ExtractorOutput): void {
156
+ const nameNode = node.childForFieldName('name');
157
+ if (!nameNode) return;
158
+
159
+ ctx.definitions.push({
160
+ name: nameNode.text,
161
+ kind: 'type',
162
+ line: node.startPosition.row + 1,
163
+ endLine: nodeEndLine(node),
164
+ });
165
+ }
166
+
167
+ function handleHaskellClass(node: TreeSitterNode, ctx: ExtractorOutput): void {
168
+ const nameNode = node.childForFieldName('name');
169
+ if (!nameNode) return;
170
+
171
+ ctx.definitions.push({
172
+ name: nameNode.text,
173
+ kind: 'class',
174
+ line: node.startPosition.row + 1,
175
+ endLine: nodeEndLine(node),
176
+ });
177
+ }
178
+
179
+ function handleHaskellInstance(node: TreeSitterNode, ctx: ExtractorOutput): void {
180
+ const nameNode = node.childForFieldName('name');
181
+ if (!nameNode) return;
182
+
183
+ ctx.definitions.push({
184
+ name: nameNode.text,
185
+ kind: 'class',
186
+ line: node.startPosition.row + 1,
187
+ endLine: nodeEndLine(node),
188
+ });
189
+ }
190
+
191
+ function handleHaskellImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
192
+ const moduleNode = node.childForFieldName('module');
193
+ if (!moduleNode) return;
194
+
195
+ const source = moduleNode.text;
196
+ const names: string[] = [];
197
+
198
+ const alias = node.childForFieldName('alias');
199
+ if (alias) names.push(alias.text);
200
+
201
+ const importList = node.childForFieldName('names');
202
+ if (importList) {
203
+ for (let i = 0; i < importList.childCount; i++) {
204
+ const item = importList.child(i);
205
+ if (
206
+ item &&
207
+ (item.type === 'variable' || item.type === 'constructor' || item.type === 'type')
208
+ ) {
209
+ names.push(item.text);
210
+ }
211
+ }
212
+ }
213
+
214
+ ctx.imports.push({
215
+ source,
216
+ names: names.length > 0 ? names : [source.split('.').pop() || source],
217
+ line: node.startPosition.row + 1,
218
+ });
219
+ }
220
+
221
+ function handleHaskellApply(node: TreeSitterNode, ctx: ExtractorOutput): void {
222
+ const funcNode = node.childForFieldName('function');
223
+ if (!funcNode) return;
224
+
225
+ // Only record named function applications, not complex expressions
226
+ if (
227
+ funcNode.type === 'variable' ||
228
+ funcNode.type === 'constructor' ||
229
+ funcNode.type === 'identifier'
230
+ ) {
231
+ ctx.calls.push({ name: funcNode.text, line: node.startPosition.row + 1 });
232
+ } else if (funcNode.type === 'qualified_variable' || funcNode.type === 'qualified_constructor') {
233
+ ctx.calls.push({ name: funcNode.text, line: node.startPosition.row + 1 });
234
+ }
235
+ }
@@ -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
  }