@optave/codegraph 3.8.0 → 3.9.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 +13 -8
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +137 -86
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/metrics.d.ts +0 -3
  6. package/dist/ast-analysis/metrics.d.ts.map +1 -1
  7. package/dist/ast-analysis/metrics.js +30 -13
  8. package/dist/ast-analysis/metrics.js.map +1 -1
  9. package/dist/ast-analysis/shared.d.ts.map +1 -1
  10. package/dist/ast-analysis/shared.js +24 -19
  11. package/dist/ast-analysis/shared.js.map +1 -1
  12. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  13. package/dist/ast-analysis/visitor-utils.js +55 -39
  14. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  15. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitor.js +91 -70
  17. package/dist/ast-analysis/visitor.js.map +1 -1
  18. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  19. package/dist/ast-analysis/visitors/ast-store-visitor.js +54 -58
  20. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  21. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  22. package/dist/ast-analysis/visitors/complexity-visitor.js +81 -39
  23. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  24. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  25. package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
  26. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  27. package/dist/cli/commands/branch-compare.d.ts.map +1 -1
  28. package/dist/cli/commands/branch-compare.js +4 -0
  29. package/dist/cli/commands/branch-compare.js.map +1 -1
  30. package/dist/cli/commands/diff-impact.d.ts.map +1 -1
  31. package/dist/cli/commands/diff-impact.js +2 -1
  32. package/dist/cli/commands/diff-impact.js.map +1 -1
  33. package/dist/cli/commands/info.d.ts.map +1 -1
  34. package/dist/cli/commands/info.js +3 -2
  35. package/dist/cli/commands/info.js.map +1 -1
  36. package/dist/cli/commands/watch.d.ts.map +1 -1
  37. package/dist/cli/commands/watch.js +16 -2
  38. package/dist/cli/commands/watch.js.map +1 -1
  39. package/dist/db/connection.d.ts.map +1 -1
  40. package/dist/db/connection.js +29 -26
  41. package/dist/db/connection.js.map +1 -1
  42. package/dist/db/query-builder.d.ts.map +1 -1
  43. package/dist/db/query-builder.js +16 -5
  44. package/dist/db/query-builder.js.map +1 -1
  45. package/dist/db/repository/base.d.ts +16 -0
  46. package/dist/db/repository/base.d.ts.map +1 -1
  47. package/dist/db/repository/base.js +31 -0
  48. package/dist/db/repository/base.js.map +1 -1
  49. package/dist/db/repository/native-repository.d.ts +7 -1
  50. package/dist/db/repository/native-repository.d.ts.map +1 -1
  51. package/dist/db/repository/native-repository.js +100 -1
  52. package/dist/db/repository/native-repository.js.map +1 -1
  53. package/dist/db/repository/nodes.d.ts.map +1 -1
  54. package/dist/db/repository/nodes.js +8 -4
  55. package/dist/db/repository/nodes.js.map +1 -1
  56. package/dist/db/repository/sqlite-repository.d.ts +4 -0
  57. package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
  58. package/dist/db/repository/sqlite-repository.js +51 -0
  59. package/dist/db/repository/sqlite-repository.js.map +1 -1
  60. package/dist/domain/analysis/brief.d.ts.map +1 -1
  61. package/dist/domain/analysis/brief.js +13 -17
  62. package/dist/domain/analysis/brief.js.map +1 -1
  63. package/dist/domain/analysis/context.d.ts.map +1 -1
  64. package/dist/domain/analysis/context.js +14 -11
  65. package/dist/domain/analysis/context.js.map +1 -1
  66. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  67. package/dist/domain/analysis/dependencies.js +64 -59
  68. package/dist/domain/analysis/dependencies.js.map +1 -1
  69. package/dist/domain/analysis/fn-impact.d.ts +2 -7
  70. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  71. package/dist/domain/analysis/fn-impact.js +33 -31
  72. package/dist/domain/analysis/fn-impact.js.map +1 -1
  73. package/dist/domain/analysis/implementations.d.ts.map +1 -1
  74. package/dist/domain/analysis/implementations.js +11 -19
  75. package/dist/domain/analysis/implementations.js.map +1 -1
  76. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  77. package/dist/domain/analysis/module-map.js +55 -76
  78. package/dist/domain/analysis/module-map.js.map +1 -1
  79. package/dist/domain/analysis/query-helpers.d.ts +7 -0
  80. package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
  81. package/dist/domain/analysis/query-helpers.js +15 -1
  82. package/dist/domain/analysis/query-helpers.js.map +1 -1
  83. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  84. package/dist/domain/graph/builder/pipeline.js +352 -107
  85. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  86. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  87. package/dist/domain/graph/builder/stages/build-edges.js +49 -18
  88. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  90. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  91. package/dist/domain/graph/builder/stages/finalize.js +2 -2
  92. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  93. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  94. package/dist/domain/graph/builder/stages/insert-nodes.js +32 -21
  95. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  96. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  97. package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
  98. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  99. package/dist/domain/graph/cycles.d.ts +6 -0
  100. package/dist/domain/graph/cycles.d.ts.map +1 -1
  101. package/dist/domain/graph/cycles.js +114 -22
  102. package/dist/domain/graph/cycles.js.map +1 -1
  103. package/dist/domain/graph/resolve.js +1 -1
  104. package/dist/domain/graph/resolve.js.map +1 -1
  105. package/dist/domain/graph/watcher.d.ts +2 -0
  106. package/dist/domain/graph/watcher.d.ts.map +1 -1
  107. package/dist/domain/graph/watcher.js +170 -75
  108. package/dist/domain/graph/watcher.js.map +1 -1
  109. package/dist/domain/parser.d.ts +3 -4
  110. package/dist/domain/parser.d.ts.map +1 -1
  111. package/dist/domain/parser.js +141 -89
  112. package/dist/domain/parser.js.map +1 -1
  113. package/dist/domain/search/generator.js +1 -1
  114. package/dist/domain/search/generator.js.map +1 -1
  115. package/dist/domain/search/models.d.ts +4 -3
  116. package/dist/domain/search/models.d.ts.map +1 -1
  117. package/dist/domain/search/models.js +23 -8
  118. package/dist/domain/search/models.js.map +1 -1
  119. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  120. package/dist/domain/search/search/hybrid.js +29 -18
  121. package/dist/domain/search/search/hybrid.js.map +1 -1
  122. package/dist/extractors/go.js +36 -33
  123. package/dist/extractors/go.js.map +1 -1
  124. package/dist/extractors/helpers.d.ts.map +1 -1
  125. package/dist/extractors/helpers.js +40 -29
  126. package/dist/extractors/helpers.js.map +1 -1
  127. package/dist/extractors/java.js +58 -46
  128. package/dist/extractors/java.js.map +1 -1
  129. package/dist/extractors/javascript.js +65 -54
  130. package/dist/extractors/javascript.js.map +1 -1
  131. package/dist/extractors/kotlin.js +84 -78
  132. package/dist/extractors/kotlin.js.map +1 -1
  133. package/dist/extractors/python.js +29 -24
  134. package/dist/extractors/python.js.map +1 -1
  135. package/dist/extractors/rust.js +41 -32
  136. package/dist/extractors/rust.js.map +1 -1
  137. package/dist/extractors/solidity.js +58 -67
  138. package/dist/extractors/solidity.js.map +1 -1
  139. package/dist/extractors/swift.js +83 -81
  140. package/dist/extractors/swift.js.map +1 -1
  141. package/dist/extractors/zig.js +58 -60
  142. package/dist/extractors/zig.js.map +1 -1
  143. package/dist/features/ast.d.ts +16 -14
  144. package/dist/features/ast.d.ts.map +1 -1
  145. package/dist/features/ast.js +83 -81
  146. package/dist/features/ast.js.map +1 -1
  147. package/dist/features/audit.d.ts.map +1 -1
  148. package/dist/features/audit.js +8 -6
  149. package/dist/features/audit.js.map +1 -1
  150. package/dist/features/branch-compare.d.ts.map +1 -1
  151. package/dist/features/branch-compare.js +69 -72
  152. package/dist/features/branch-compare.js.map +1 -1
  153. package/dist/features/communities.d.ts.map +1 -1
  154. package/dist/features/communities.js +19 -7
  155. package/dist/features/communities.js.map +1 -1
  156. package/dist/features/complexity.d.ts.map +1 -1
  157. package/dist/features/complexity.js +120 -125
  158. package/dist/features/complexity.js.map +1 -1
  159. package/dist/features/dataflow.d.ts.map +1 -1
  160. package/dist/features/dataflow.js +136 -137
  161. package/dist/features/dataflow.js.map +1 -1
  162. package/dist/features/flow.d.ts.map +1 -1
  163. package/dist/features/flow.js +84 -79
  164. package/dist/features/flow.js.map +1 -1
  165. package/dist/features/structure-query.d.ts.map +1 -1
  166. package/dist/features/structure-query.js +69 -65
  167. package/dist/features/structure-query.js.map +1 -1
  168. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  169. package/dist/graph/algorithms/leiden/optimiser.js +70 -55
  170. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  171. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  172. package/dist/graph/algorithms/leiden/partition.js +288 -266
  173. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  174. package/dist/graph/model.d.ts.map +1 -1
  175. package/dist/graph/model.js +5 -1
  176. package/dist/graph/model.js.map +1 -1
  177. package/dist/infrastructure/config.d.ts.map +1 -1
  178. package/dist/infrastructure/config.js +6 -4
  179. package/dist/infrastructure/config.js.map +1 -1
  180. package/dist/infrastructure/suppress.d.ts +25 -0
  181. package/dist/infrastructure/suppress.d.ts.map +1 -0
  182. package/dist/infrastructure/suppress.js +43 -0
  183. package/dist/infrastructure/suppress.js.map +1 -0
  184. package/dist/mcp/server.d.ts.map +1 -1
  185. package/dist/mcp/server.js +29 -24
  186. package/dist/mcp/server.js.map +1 -1
  187. package/dist/presentation/dataflow.d.ts.map +1 -1
  188. package/dist/presentation/dataflow.js +47 -38
  189. package/dist/presentation/dataflow.js.map +1 -1
  190. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
  191. package/dist/presentation/diff-impact-mermaid.js +60 -51
  192. package/dist/presentation/diff-impact-mermaid.js.map +1 -1
  193. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  194. package/dist/presentation/queries-cli/exports.js +20 -14
  195. package/dist/presentation/queries-cli/exports.js.map +1 -1
  196. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  197. package/dist/presentation/queries-cli/impact.js +15 -13
  198. package/dist/presentation/queries-cli/impact.js.map +1 -1
  199. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  200. package/dist/presentation/queries-cli/inspect.js +101 -79
  201. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  202. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  203. package/dist/presentation/queries-cli/overview.js +25 -16
  204. package/dist/presentation/queries-cli/overview.js.map +1 -1
  205. package/dist/presentation/queries-cli/path.js +26 -20
  206. package/dist/presentation/queries-cli/path.js.map +1 -1
  207. package/dist/presentation/result-formatter.d.ts +10 -0
  208. package/dist/presentation/result-formatter.d.ts.map +1 -1
  209. package/dist/presentation/result-formatter.js +16 -1
  210. package/dist/presentation/result-formatter.js.map +1 -1
  211. package/dist/presentation/viewer.d.ts.map +1 -1
  212. package/dist/presentation/viewer.js +18 -12
  213. package/dist/presentation/viewer.js.map +1 -1
  214. package/dist/shared/errors.d.ts +5 -0
  215. package/dist/shared/errors.d.ts.map +1 -1
  216. package/dist/shared/errors.js +5 -0
  217. package/dist/shared/errors.js.map +1 -1
  218. package/dist/shared/hierarchy.d.ts +8 -2
  219. package/dist/shared/hierarchy.d.ts.map +1 -1
  220. package/dist/shared/hierarchy.js +42 -1
  221. package/dist/shared/hierarchy.js.map +1 -1
  222. package/dist/shared/normalize.d.ts +6 -1
  223. package/dist/shared/normalize.d.ts.map +1 -1
  224. package/dist/shared/normalize.js +20 -12
  225. package/dist/shared/normalize.js.map +1 -1
  226. package/dist/shared/paginate.d.ts +0 -9
  227. package/dist/shared/paginate.d.ts.map +1 -1
  228. package/dist/shared/paginate.js +0 -15
  229. package/dist/shared/paginate.js.map +1 -1
  230. package/dist/types.d.ts +12 -5
  231. package/dist/types.d.ts.map +1 -1
  232. package/grammars/tree-sitter-erlang.wasm +0 -0
  233. package/grammars/tree-sitter-gleam.wasm +0 -0
  234. package/package.json +9 -9
  235. package/src/ast-analysis/engine.ts +176 -104
  236. package/src/ast-analysis/metrics.ts +33 -11
  237. package/src/ast-analysis/shared.ts +33 -24
  238. package/src/ast-analysis/visitor-utils.ts +52 -32
  239. package/src/ast-analysis/visitor.ts +132 -71
  240. package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
  241. package/src/ast-analysis/visitors/complexity-visitor.ts +89 -40
  242. package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
  243. package/src/cli/commands/branch-compare.ts +4 -0
  244. package/src/cli/commands/diff-impact.ts +2 -1
  245. package/src/cli/commands/info.ts +3 -2
  246. package/src/cli/commands/watch.ts +16 -2
  247. package/src/db/connection.ts +29 -28
  248. package/src/db/query-builder.ts +15 -3
  249. package/src/db/repository/base.ts +34 -0
  250. package/src/db/repository/native-repository.ts +104 -1
  251. package/src/db/repository/nodes.ts +13 -8
  252. package/src/db/repository/sqlite-repository.ts +55 -0
  253. package/src/domain/analysis/brief.ts +15 -25
  254. package/src/domain/analysis/context.ts +17 -10
  255. package/src/domain/analysis/dependencies.ts +77 -81
  256. package/src/domain/analysis/fn-impact.ts +36 -43
  257. package/src/domain/analysis/implementations.ts +11 -17
  258. package/src/domain/analysis/module-map.ts +58 -92
  259. package/src/domain/analysis/query-helpers.ts +18 -1
  260. package/src/domain/graph/builder/pipeline.ts +409 -99
  261. package/src/domain/graph/builder/stages/build-edges.ts +45 -19
  262. package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
  263. package/src/domain/graph/builder/stages/finalize.ts +2 -2
  264. package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
  265. package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
  266. package/src/domain/graph/cycles.ts +110 -23
  267. package/src/domain/graph/resolve.ts +1 -1
  268. package/src/domain/graph/watcher.ts +202 -96
  269. package/src/domain/parser.ts +143 -89
  270. package/src/domain/search/generator.ts +1 -1
  271. package/src/domain/search/models.ts +26 -7
  272. package/src/domain/search/search/hybrid.ts +69 -51
  273. package/src/extractors/go.ts +43 -33
  274. package/src/extractors/helpers.ts +37 -23
  275. package/src/extractors/java.ts +66 -47
  276. package/src/extractors/javascript.ts +66 -54
  277. package/src/extractors/kotlin.ts +84 -77
  278. package/src/extractors/python.ts +31 -25
  279. package/src/extractors/rust.ts +37 -29
  280. package/src/extractors/solidity.ts +57 -61
  281. package/src/extractors/swift.ts +81 -80
  282. package/src/extractors/zig.ts +58 -61
  283. package/src/features/ast.ts +130 -110
  284. package/src/features/audit.ts +8 -6
  285. package/src/features/branch-compare.ts +105 -79
  286. package/src/features/communities.ts +25 -10
  287. package/src/features/complexity.ts +171 -134
  288. package/src/features/dataflow.ts +165 -175
  289. package/src/features/flow.ts +129 -92
  290. package/src/features/structure-query.ts +79 -64
  291. package/src/graph/algorithms/leiden/optimiser.ts +99 -55
  292. package/src/graph/algorithms/leiden/partition.ts +359 -294
  293. package/src/graph/model.ts +6 -1
  294. package/src/infrastructure/config.ts +6 -4
  295. package/src/infrastructure/suppress.ts +47 -0
  296. package/src/mcp/server.ts +53 -37
  297. package/src/presentation/dataflow.ts +50 -44
  298. package/src/presentation/diff-impact-mermaid.ts +104 -62
  299. package/src/presentation/queries-cli/exports.ts +21 -13
  300. package/src/presentation/queries-cli/impact.ts +15 -13
  301. package/src/presentation/queries-cli/inspect.ts +100 -81
  302. package/src/presentation/queries-cli/overview.ts +26 -16
  303. package/src/presentation/queries-cli/path.ts +33 -25
  304. package/src/presentation/result-formatter.ts +19 -1
  305. package/src/presentation/viewer.ts +42 -14
  306. package/src/shared/errors.ts +6 -0
  307. package/src/shared/hierarchy.ts +50 -2
  308. package/src/shared/normalize.ts +31 -12
  309. package/src/shared/paginate.ts +0 -17
  310. package/src/types.ts +26 -5
@@ -100,78 +100,75 @@ function handleZigVariable(node: TreeSitterNode, ctx: ExtractorOutput): void {
100
100
  if (!nameNode) return;
101
101
  const name = nameNode.text;
102
102
 
103
- // Check if this is a struct/enum/union definition
103
+ if (tryHandleZigContainerDecl(node, name, ctx)) return;
104
+ if (tryHandleZigImportDecl(node, name, ctx)) return;
105
+
106
+ // Regular constant/variable
107
+ const isConst = hasChildText(node, 'const');
108
+ ctx.definitions.push({
109
+ name,
110
+ kind: isConst ? 'constant' : 'variable',
111
+ line: node.startPosition.row + 1,
112
+ endLine: nodeEndLine(node),
113
+ });
114
+ }
115
+
116
+ /** Try to handle a variable_declaration as a struct/enum/union. Returns true if handled. */
117
+ function tryHandleZigContainerDecl(
118
+ node: TreeSitterNode,
119
+ name: string,
120
+ ctx: ExtractorOutput,
121
+ ): boolean {
104
122
  for (let i = 0; i < node.childCount; i++) {
105
123
  const child = node.child(i);
106
124
  if (!child) continue;
125
+ const containerKind = zigContainerKind(child.type);
126
+ if (!containerKind) continue;
107
127
 
108
- if (child.type === 'struct_declaration') {
109
- const members = extractZigContainerFields(child);
110
- ctx.definitions.push({
111
- name,
112
- kind: 'struct',
113
- line: node.startPosition.row + 1,
114
- endLine: nodeEndLine(node),
115
- children: members.length > 0 ? members : undefined,
116
- visibility: isZigPub(node) ? 'public' : undefined,
117
- });
118
- extractZigContainerMethods(child, name, ctx);
119
- return;
120
- }
121
- if (child.type === 'enum_declaration') {
122
- ctx.definitions.push({
123
- name,
124
- kind: 'enum',
125
- line: node.startPosition.row + 1,
126
- endLine: nodeEndLine(node),
127
- visibility: isZigPub(node) ? 'public' : undefined,
128
- });
129
- return;
130
- }
131
- if (child.type === 'union_declaration') {
132
- ctx.definitions.push({
133
- name,
134
- kind: 'struct',
135
- line: node.startPosition.row + 1,
136
- endLine: nodeEndLine(node),
137
- visibility: isZigPub(node) ? 'public' : undefined,
138
- });
139
- return;
140
- }
128
+ const members = child.type === 'struct_declaration' ? extractZigContainerFields(child) : [];
129
+ ctx.definitions.push({
130
+ name,
131
+ kind: containerKind,
132
+ line: node.startPosition.row + 1,
133
+ endLine: nodeEndLine(node),
134
+ children: members.length > 0 ? members : undefined,
135
+ visibility: isZigPub(node) ? 'public' : undefined,
136
+ });
137
+ if (child.type === 'struct_declaration') extractZigContainerMethods(child, name, ctx);
138
+ return true;
141
139
  }
140
+ return false;
141
+ }
142
142
 
143
- // Check for @import
143
+ /** Map a Zig container node type to a definition kind, or undefined if not a container. */
144
+ function zigContainerKind(nodeType: string): 'struct' | 'enum' | undefined {
145
+ if (nodeType === 'struct_declaration' || nodeType === 'union_declaration') return 'struct';
146
+ if (nodeType === 'enum_declaration') return 'enum';
147
+ return undefined;
148
+ }
149
+
150
+ /** Try to handle a variable_declaration as an @import. Returns true if handled. */
151
+ function tryHandleZigImportDecl(node: TreeSitterNode, name: string, ctx: ExtractorOutput): boolean {
144
152
  for (let i = 0; i < node.childCount; i++) {
145
153
  const child = node.child(i);
146
- if (!child) continue;
147
- if (child.type === 'builtin_function') {
148
- const builtinId = findChild(child, 'builtin_identifier');
149
- if (builtinId?.text === '@import') {
150
- const args = findChild(child, 'arguments');
151
- if (args) {
152
- const strArg = findChild(args, 'string_literal') || findChild(args, 'string');
153
- if (strArg) {
154
- const source = strArg.text.replace(/^"|"$/g, '');
155
- ctx.imports.push({
156
- source,
157
- names: [name],
158
- line: node.startPosition.row + 1,
159
- });
160
- return;
161
- }
162
- }
163
- }
154
+ if (!child || child.type !== 'builtin_function') continue;
155
+ const source = extractZigImportSource(child);
156
+ if (source) {
157
+ ctx.imports.push({ source, names: [name], line: node.startPosition.row + 1 });
158
+ return true;
164
159
  }
165
160
  }
161
+ return false;
162
+ }
166
163
 
167
- // Regular constant/variable
168
- const isConst = hasChildText(node, 'const');
169
- ctx.definitions.push({
170
- name,
171
- kind: isConst ? 'constant' : 'variable',
172
- line: node.startPosition.row + 1,
173
- endLine: nodeEndLine(node),
174
- });
164
+ /** Extract the import source path from a builtin_function node if it is @import. */
165
+ function extractZigImportSource(builtinNode: TreeSitterNode): string | null {
166
+ const builtinId = findChild(builtinNode, 'builtin_identifier');
167
+ if (builtinId?.text !== '@import') return null;
168
+ const args = findChild(builtinNode, 'arguments');
169
+ if (!args) return null;
170
+ const strArg = findChild(args, 'string_literal') || findChild(args, 'string');
171
+ return strArg ? strArg.text.replace(/^"|"$/g, '') : null;
175
172
  }
176
173
 
177
174
  function extractZigContainerFields(container: TreeSitterNode): SubDeclaration[] {
@@ -58,38 +58,11 @@ function findParentDef(defs: Definition[], line: number): Definition | null {
58
58
  return best;
59
59
  }
60
60
 
61
- // ─── Build ────────────────────────────────────────────────────────────
61
+ // ─── Build helpers ───────────────────────────────────────────────────
62
62
 
63
- export async function buildAstNodes(
64
- db: BetterSqlite3Database,
65
- fileSymbols: Map<string, FileSymbols>,
66
- _rootDir: string,
67
- engineOpts?: {
68
- nativeDb?: {
69
- bulkInsertAstNodes(
70
- batches: Array<{
71
- file: string;
72
- nodes: Array<{
73
- line: number;
74
- kind: string;
75
- name: string;
76
- text?: string | null;
77
- receiver?: string | null;
78
- }>;
79
- }>,
80
- ): number;
81
- };
82
- suspendJsDb?: () => void;
83
- resumeJsDb?: () => void;
84
- },
85
- ): Promise<void> {
86
- // ── Native bulk-insert fast path ──────────────────────────────────────
87
- // Uses NativeDatabase persistent connection (Phase 6.15+).
88
- // Standalone napi functions were removed in 6.17.
89
- const nativeDb = engineOpts?.nativeDb;
90
- if (nativeDb?.bulkInsertAstNodes) {
91
- let needsJsFallback = false;
92
- const batches: Array<{
63
+ interface NativeDbHandle {
64
+ bulkInsertAstNodes(
65
+ batches: Array<{
93
66
  file: string;
94
67
  nodes: Array<{
95
68
  line: number;
@@ -98,47 +71,127 @@ export async function buildAstNodes(
98
71
  text?: string | null;
99
72
  receiver?: string | null;
100
73
  }>;
101
- }> = [];
102
-
103
- for (const [relPath, symbols] of fileSymbols) {
104
- if (Array.isArray(symbols.astNodes)) {
105
- batches.push({
106
- file: relPath,
107
- nodes: symbols.astNodes.map((n) => ({
108
- line: n.line,
109
- kind: n.kind,
110
- name: n.name,
111
- text: n.text,
112
- receiver: n.receiver ?? '',
113
- })),
114
- });
115
- } else if (symbols.calls || symbols._tree) {
116
- needsJsFallback = true;
117
- break;
118
- }
119
- }
74
+ }>,
75
+ ): number;
76
+ }
120
77
 
121
- if (!needsJsFallback) {
122
- const expectedNodes = batches.reduce((s, b) => s + b.nodes.length, 0);
123
- let inserted: number;
124
- try {
125
- engineOpts?.suspendJsDb?.();
126
- inserted = nativeDb.bulkInsertAstNodes(batches);
127
- } finally {
128
- engineOpts?.resumeJsDb?.();
129
- }
130
- if (inserted === expectedNodes) {
131
- debug(`AST extraction (native bulk): ${inserted} nodes stored`);
132
- return;
133
- }
134
- debug(
135
- `AST extraction (native bulk): expected ${expectedNodes}, got ${inserted} — falling back to JS`,
136
- );
137
- // fall through to JS path
78
+ interface EngineOpts {
79
+ nativeDb?: NativeDbHandle;
80
+ suspendJsDb?: () => void;
81
+ resumeJsDb?: () => void;
82
+ }
83
+
84
+ /**
85
+ * Attempt native bulk-insert of AST nodes.
86
+ * Returns `true` if all nodes were inserted natively, `false` if JS fallback is needed.
87
+ */
88
+ function tryNativeBulkInsert(
89
+ fileSymbols: Map<string, FileSymbols>,
90
+ engineOpts: EngineOpts | undefined,
91
+ ): boolean {
92
+ const nativeDb = engineOpts?.nativeDb;
93
+ if (!nativeDb?.bulkInsertAstNodes) return false;
94
+
95
+ const batches: Array<{
96
+ file: string;
97
+ nodes: Array<{
98
+ line: number;
99
+ kind: string;
100
+ name: string;
101
+ text?: string | null;
102
+ receiver?: string | null;
103
+ }>;
104
+ }> = [];
105
+
106
+ for (const [relPath, symbols] of fileSymbols) {
107
+ if (Array.isArray(symbols.astNodes)) {
108
+ batches.push({
109
+ file: relPath,
110
+ nodes: symbols.astNodes.map((n) => ({
111
+ line: n.line,
112
+ kind: n.kind,
113
+ name: n.name,
114
+ text: n.text,
115
+ receiver: n.receiver ?? '',
116
+ })),
117
+ });
118
+ } else if (symbols.calls || symbols._tree) {
119
+ return false; // needs JS fallback
138
120
  }
139
121
  }
140
122
 
141
- // ── JS fallback path ──────────────────────────────────────────────────
123
+ const expectedNodes = batches.reduce((s, b) => s + b.nodes.length, 0);
124
+ let inserted: number;
125
+ try {
126
+ engineOpts?.suspendJsDb?.();
127
+ inserted = nativeDb.bulkInsertAstNodes(batches);
128
+ } finally {
129
+ engineOpts?.resumeJsDb?.();
130
+ }
131
+
132
+ if (inserted === expectedNodes) {
133
+ debug(`AST extraction (native bulk): ${inserted} nodes stored`);
134
+ return true;
135
+ }
136
+ debug(
137
+ `AST extraction (native bulk): expected ${expectedNodes}, got ${inserted} — falling back to JS`,
138
+ );
139
+ return false;
140
+ }
141
+
142
+ /** Collect AST rows for a single file, resolving parent node IDs. */
143
+ function collectFileAstRows(
144
+ db: BetterSqlite3Database,
145
+ relPath: string,
146
+ symbols: FileSymbols,
147
+ ): AstRow[] {
148
+ const defs = symbols.definitions || [];
149
+ const nodeIdMap = new Map<string, number>();
150
+ for (const row of bulkNodeIdsByFile(db, relPath)) {
151
+ nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
152
+ }
153
+
154
+ if (Array.isArray(symbols.astNodes)) {
155
+ return symbols.astNodes.map((n) => {
156
+ const parentDef = findParentDef(defs, n.line);
157
+ const parentNodeId = parentDef
158
+ ? nodeIdMap.get(`${parentDef.name}|${parentDef.kind}|${parentDef.line}`) || null
159
+ : null;
160
+ return {
161
+ file: relPath,
162
+ line: n.line,
163
+ kind: n.kind,
164
+ name: n.name,
165
+ text: n.text || null,
166
+ receiver: n.receiver || null,
167
+ parentNodeId,
168
+ };
169
+ });
170
+ }
171
+
172
+ // WASM fallback — walk tree if available
173
+ const ext = path.extname(relPath).toLowerCase();
174
+ if (WALK_EXTENSIONS.has(ext) && symbols._tree) {
175
+ const rows: AstRow[] = [];
176
+ walkAst(symbols._tree.rootNode, defs, relPath, rows, nodeIdMap);
177
+ return rows;
178
+ }
179
+
180
+ return [];
181
+ }
182
+
183
+ // ─── Build ────────────────────────────────────────────────────────────
184
+
185
+ export async function buildAstNodes(
186
+ db: BetterSqlite3Database,
187
+ fileSymbols: Map<string, FileSymbols>,
188
+ _rootDir: string,
189
+ engineOpts?: EngineOpts,
190
+ ): Promise<void> {
191
+ // Native bulk-insert fast path (Phase 6.15+)
192
+ if (tryNativeBulkInsert(fileSymbols, engineOpts)) return;
193
+
194
+ // JS fallback path
142
195
  let insertStmt: ReturnType<BetterSqlite3Database['prepare']>;
143
196
  try {
144
197
  insertStmt = db.prepare(
@@ -156,43 +209,8 @@ export async function buildAstNodes(
156
209
  });
157
210
 
158
211
  const allRows: AstRow[] = [];
159
-
160
212
  for (const [relPath, symbols] of fileSymbols) {
161
- const defs = symbols.definitions || [];
162
-
163
- const nodeIdMap = new Map<string, number>();
164
- for (const row of bulkNodeIdsByFile(db, relPath)) {
165
- nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
166
- }
167
-
168
- if (Array.isArray(symbols.astNodes)) {
169
- // Native engine provided AST nodes (may be empty for files with no AST content)
170
- for (const n of symbols.astNodes) {
171
- const parentDef = findParentDef(defs, n.line);
172
- let parentNodeId: number | null = null;
173
- if (parentDef) {
174
- parentNodeId =
175
- nodeIdMap.get(`${parentDef.name}|${parentDef.kind}|${parentDef.line}`) || null;
176
- }
177
- allRows.push({
178
- file: relPath,
179
- line: n.line,
180
- kind: n.kind,
181
- name: n.name,
182
- text: n.text || null,
183
- receiver: n.receiver || null,
184
- parentNodeId,
185
- });
186
- }
187
- } else {
188
- // WASM fallback — walk tree if available
189
- const ext = path.extname(relPath).toLowerCase();
190
- if (WALK_EXTENSIONS.has(ext) && symbols._tree) {
191
- const astRows: AstRow[] = [];
192
- walkAst(symbols._tree.rootNode, defs, relPath, astRows, nodeIdMap);
193
- allRows.push(...astRows);
194
- }
195
- }
213
+ allRows.push(...collectFileAstRows(db, relPath, symbols));
196
214
  }
197
215
 
198
216
  if (allRows.length > 0) {
@@ -350,23 +368,25 @@ export function astQuery(
350
368
  if (outputResult(data, 'results', opts)) return;
351
369
 
352
370
  if (data.results.length === 0) {
353
- console.log(`No AST nodes found${pattern ? ` matching "${pattern}"` : ''}.`);
371
+ process.stdout.write(`No AST nodes found${pattern ? ` matching "${pattern}"` : ''}.\n`);
354
372
  return;
355
373
  }
356
374
 
357
375
  const kindLabel = opts.kind ? ` (kind: ${opts.kind})` : '';
358
- console.log(`\n${data.count} AST nodes${pattern ? ` matching "${pattern}"` : ''}${kindLabel}:\n`);
376
+ process.stdout.write(
377
+ `\n${data.count} AST nodes${pattern ? ` matching "${pattern}"` : ''}${kindLabel}:\n\n`,
378
+ );
359
379
 
360
380
  for (const r of data.results) {
361
381
  const icon = KIND_ICONS[r.kind] || '?';
362
382
  const parentInfo = r.parent ? ` (in ${r.parent.name})` : '';
363
- console.log(` ${icon} ${r.name} -- ${r.file}:${r.line}${parentInfo}`);
383
+ process.stdout.write(` ${icon} ${r.name} -- ${r.file}:${r.line}${parentInfo}\n`);
364
384
  }
365
385
 
366
386
  if (data._pagination?.hasMore) {
367
- console.log(
368
- `\n ... ${data._pagination.total - data._pagination.offset - data._pagination.returned} more (use --offset ${data._pagination.offset + data._pagination.limit})`,
387
+ process.stdout.write(
388
+ `\n ... ${data._pagination.total - data._pagination.offset - data._pagination.returned} more (use --offset ${data._pagination.offset + data._pagination.limit})\n`,
369
389
  );
370
390
  }
371
- console.log();
391
+ process.stdout.write('\n');
372
392
  }
@@ -4,7 +4,9 @@ import { normalizeFileFilter } from '../db/query-builder.js';
4
4
  import { bfsTransitiveCallers } from '../domain/analysis/impact.js';
5
5
  import { explainData } from '../domain/queries.js';
6
6
  import { loadConfig } from '../infrastructure/config.js';
7
+ import { debug } from '../infrastructure/logger.js';
7
8
  import { isTestFile } from '../infrastructure/test-filter.js';
9
+ import { toErrorMessage } from '../shared/errors.js';
8
10
  import type { BetterSqlite3Database, CodegraphConfig } from '../types.js';
9
11
  import { RULE_DEFS } from './manifesto.js';
10
12
 
@@ -41,8 +43,8 @@ function resolveThresholds(
41
43
  };
42
44
  }
43
45
  return resolved;
44
- } catch {
45
- // Fall back to defaults if config loading fails
46
+ } catch (e) {
47
+ debug(`resolveThresholds: config loading failed, using defaults: ${toErrorMessage(e)}`);
46
48
  const resolved: Record<string, ThresholdEntry> = {};
47
49
  for (const def of FUNCTION_RULES) {
48
50
  resolved[def.name] = {
@@ -110,8 +112,8 @@ function readPhase44(db: BetterSqlite3Database, nodeId: number): Phase44Fields {
110
112
  sideEffects: row.side_effects ?? null,
111
113
  };
112
114
  }
113
- } catch {
114
- /* columns don't exist yet */
115
+ } catch (e) {
116
+ debug(`readPhase44: columns may not exist yet: ${toErrorMessage(e)}`);
115
117
  }
116
118
  return { riskScore: null, complexityNotes: null, sideEffects: null };
117
119
  }
@@ -413,8 +415,8 @@ function buildHealth(
413
415
  commentLines: row.comment_lines || 0,
414
416
  thresholdBreaches: checkBreaches(row as unknown as Record<string, unknown>, thresholds),
415
417
  };
416
- } catch {
417
- /* table may not exist */
418
+ } catch (e) {
419
+ debug(`readHealth: function_complexity table may not exist: ${toErrorMessage(e)}`);
418
420
  return defaultHealth();
419
421
  }
420
422
  }