@optave/codegraph 3.12.0 → 3.15.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 (524) hide show
  1. package/README.md +83 -46
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +38 -40
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/b2.d.ts +7 -0
  6. package/dist/ast-analysis/rules/b2.d.ts.map +1 -0
  7. package/dist/ast-analysis/rules/b2.js +240 -0
  8. package/dist/ast-analysis/rules/b2.js.map +1 -0
  9. package/dist/ast-analysis/rules/b3.d.ts +6 -0
  10. package/dist/ast-analysis/rules/b3.d.ts.map +1 -0
  11. package/dist/ast-analysis/rules/b3.js +105 -0
  12. package/dist/ast-analysis/rules/b3.js.map +1 -0
  13. package/dist/ast-analysis/rules/b4.d.ts +9 -0
  14. package/dist/ast-analysis/rules/b4.d.ts.map +1 -0
  15. package/dist/ast-analysis/rules/b4.js +361 -0
  16. package/dist/ast-analysis/rules/b4.js.map +1 -0
  17. package/dist/ast-analysis/rules/b5.d.ts +4 -0
  18. package/dist/ast-analysis/rules/b5.d.ts.map +1 -0
  19. package/dist/ast-analysis/rules/b5.js +52 -0
  20. package/dist/ast-analysis/rules/b5.js.map +1 -0
  21. package/dist/ast-analysis/rules/c.d.ts +4 -0
  22. package/dist/ast-analysis/rules/c.d.ts.map +1 -0
  23. package/dist/ast-analysis/rules/c.js +143 -0
  24. package/dist/ast-analysis/rules/c.js.map +1 -0
  25. package/dist/ast-analysis/rules/index.d.ts.map +1 -1
  26. package/dist/ast-analysis/rules/index.js +34 -0
  27. package/dist/ast-analysis/rules/index.js.map +1 -1
  28. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  29. package/dist/ast-analysis/rules/javascript.js +3 -0
  30. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  31. package/dist/ast-analysis/shared.d.ts.map +1 -1
  32. package/dist/ast-analysis/shared.js +2 -0
  33. package/dist/ast-analysis/shared.js.map +1 -1
  34. package/dist/ast-analysis/visitor-utils.d.ts +1 -0
  35. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  36. package/dist/ast-analysis/visitor-utils.js +5 -0
  37. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  38. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  39. package/dist/ast-analysis/visitor.js +60 -47
  40. package/dist/ast-analysis/visitor.js.map +1 -1
  41. package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
  42. package/dist/ast-analysis/visitors/cfg-visitor.js +126 -76
  43. package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
  44. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  45. package/dist/ast-analysis/visitors/complexity-visitor.js +27 -15
  46. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  47. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  48. package/dist/ast-analysis/visitors/dataflow-visitor.js +54 -21
  49. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  50. package/dist/cli/commands/audit.d.ts.map +1 -1
  51. package/dist/cli/commands/audit.js +2 -1
  52. package/dist/cli/commands/audit.js.map +1 -1
  53. package/dist/cli/commands/batch.d.ts.map +1 -1
  54. package/dist/cli/commands/batch.js +1 -0
  55. package/dist/cli/commands/batch.js.map +1 -1
  56. package/dist/cli/commands/build.d.ts.map +1 -1
  57. package/dist/cli/commands/build.js +6 -1
  58. package/dist/cli/commands/build.js.map +1 -1
  59. package/dist/cli/commands/config.d.ts +3 -0
  60. package/dist/cli/commands/config.d.ts.map +1 -0
  61. package/dist/cli/commands/config.js +275 -0
  62. package/dist/cli/commands/config.js.map +1 -0
  63. package/dist/cli/commands/roles.d.ts.map +1 -1
  64. package/dist/cli/commands/roles.js +6 -1
  65. package/dist/cli/commands/roles.js.map +1 -1
  66. package/dist/cli/commands/triage.js +1 -1
  67. package/dist/cli/commands/triage.js.map +1 -1
  68. package/dist/cli/index.d.ts.map +1 -1
  69. package/dist/cli/index.js +10 -0
  70. package/dist/cli/index.js.map +1 -1
  71. package/dist/cli/shared/options.d.ts +2 -1
  72. package/dist/cli/shared/options.d.ts.map +1 -1
  73. package/dist/cli/shared/options.js +11 -1
  74. package/dist/cli/shared/options.js.map +1 -1
  75. package/dist/cli/types.d.ts +2 -0
  76. package/dist/cli/types.d.ts.map +1 -1
  77. package/dist/db/better-sqlite3.d.ts +2 -1
  78. package/dist/db/better-sqlite3.d.ts.map +1 -1
  79. package/dist/db/better-sqlite3.js.map +1 -1
  80. package/dist/db/connection.d.ts +7 -1
  81. package/dist/db/connection.d.ts.map +1 -1
  82. package/dist/db/connection.js +20 -5
  83. package/dist/db/connection.js.map +1 -1
  84. package/dist/db/index.d.ts +1 -1
  85. package/dist/db/index.d.ts.map +1 -1
  86. package/dist/db/index.js +1 -1
  87. package/dist/db/index.js.map +1 -1
  88. package/dist/db/migrations.d.ts.map +1 -1
  89. package/dist/db/migrations.js +69 -1
  90. package/dist/db/migrations.js.map +1 -1
  91. package/dist/db/repository/build-stmts.d.ts.map +1 -1
  92. package/dist/db/repository/build-stmts.js +18 -0
  93. package/dist/db/repository/build-stmts.js.map +1 -1
  94. package/dist/db/repository/dataflow.d.ts +5 -0
  95. package/dist/db/repository/dataflow.d.ts.map +1 -1
  96. package/dist/db/repository/dataflow.js +14 -0
  97. package/dist/db/repository/dataflow.js.map +1 -1
  98. package/dist/db/repository/index.d.ts +1 -1
  99. package/dist/db/repository/index.d.ts.map +1 -1
  100. package/dist/db/repository/index.js +1 -1
  101. package/dist/db/repository/index.js.map +1 -1
  102. package/dist/db/repository/native-repository.d.ts.map +1 -1
  103. package/dist/db/repository/native-repository.js +47 -34
  104. package/dist/db/repository/native-repository.js.map +1 -1
  105. package/dist/domain/analysis/context.d.ts +2 -2
  106. package/dist/domain/analysis/dependencies.d.ts +2 -2
  107. package/dist/domain/analysis/diff-impact.d.ts +2 -2
  108. package/dist/domain/analysis/fn-impact.d.ts +3 -1
  109. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  110. package/dist/domain/analysis/fn-impact.js +4 -0
  111. package/dist/domain/analysis/fn-impact.js.map +1 -1
  112. package/dist/domain/analysis/implementations.d.ts +2 -2
  113. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  114. package/dist/domain/analysis/module-map.js +32 -5
  115. package/dist/domain/analysis/module-map.js.map +1 -1
  116. package/dist/domain/analysis/roles.d.ts +7 -1
  117. package/dist/domain/analysis/roles.d.ts.map +1 -1
  118. package/dist/domain/analysis/roles.js +16 -0
  119. package/dist/domain/analysis/roles.js.map +1 -1
  120. package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
  121. package/dist/domain/graph/builder/call-resolver.d.ts +29 -13
  122. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  123. package/dist/domain/graph/builder/call-resolver.js +125 -205
  124. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  125. package/dist/domain/graph/builder/cha.d.ts +9 -1
  126. package/dist/domain/graph/builder/cha.d.ts.map +1 -1
  127. package/dist/domain/graph/builder/cha.js +17 -2
  128. package/dist/domain/graph/builder/cha.js.map +1 -1
  129. package/dist/domain/graph/builder/context.d.ts +1 -0
  130. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  131. package/dist/domain/graph/builder/context.js.map +1 -1
  132. package/dist/domain/graph/builder/helpers.d.ts +24 -1
  133. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  134. package/dist/domain/graph/builder/helpers.js +174 -65
  135. package/dist/domain/graph/builder/helpers.js.map +1 -1
  136. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  137. package/dist/domain/graph/builder/incremental.js +166 -97
  138. package/dist/domain/graph/builder/incremental.js.map +1 -1
  139. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  140. package/dist/domain/graph/builder/pipeline.js +46 -5
  141. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  142. package/dist/domain/graph/builder/stages/build-edges.d.ts +0 -2
  143. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  144. package/dist/domain/graph/builder/stages/build-edges.js +554 -538
  145. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  146. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  147. package/dist/domain/graph/builder/stages/collect-files.js +10 -7
  148. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  149. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  150. package/dist/domain/graph/builder/stages/detect-changes.js +3 -2
  151. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  152. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  153. package/dist/domain/graph/builder/stages/finalize.js +4 -0
  154. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  155. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  156. package/dist/domain/graph/builder/stages/native-orchestrator.js +952 -343
  157. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  158. package/dist/domain/graph/builder/stages/resolve-imports.js +1 -1
  159. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  160. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
  161. package/dist/domain/graph/resolver/points-to.js +105 -57
  162. package/dist/domain/graph/resolver/points-to.js.map +1 -1
  163. package/dist/domain/graph/resolver/strategy.d.ts +61 -0
  164. package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
  165. package/dist/domain/graph/resolver/strategy.js +222 -0
  166. package/dist/domain/graph/resolver/strategy.js.map +1 -0
  167. package/dist/domain/graph/watcher.d.ts.map +1 -1
  168. package/dist/domain/graph/watcher.js +16 -9
  169. package/dist/domain/graph/watcher.js.map +1 -1
  170. package/dist/domain/parser.d.ts +16 -5
  171. package/dist/domain/parser.d.ts.map +1 -1
  172. package/dist/domain/parser.js +58 -17
  173. package/dist/domain/parser.js.map +1 -1
  174. package/dist/domain/queries.d.ts +1 -1
  175. package/dist/domain/queries.d.ts.map +1 -1
  176. package/dist/domain/queries.js +1 -1
  177. package/dist/domain/queries.js.map +1 -1
  178. package/dist/domain/wasm-worker-entry.js +13 -2
  179. package/dist/domain/wasm-worker-entry.js.map +1 -1
  180. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  181. package/dist/domain/wasm-worker-pool.js +26 -5
  182. package/dist/domain/wasm-worker-pool.js.map +1 -1
  183. package/dist/domain/wasm-worker-protocol.d.ts +8 -0
  184. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  185. package/dist/extractors/cpp.d.ts.map +1 -1
  186. package/dist/extractors/cpp.js +42 -1
  187. package/dist/extractors/cpp.js.map +1 -1
  188. package/dist/extractors/cuda.d.ts.map +1 -1
  189. package/dist/extractors/cuda.js +42 -1
  190. package/dist/extractors/cuda.js.map +1 -1
  191. package/dist/extractors/dart.js +48 -3
  192. package/dist/extractors/dart.js.map +1 -1
  193. package/dist/extractors/groovy.js +62 -3
  194. package/dist/extractors/groovy.js.map +1 -1
  195. package/dist/extractors/helpers.d.ts +15 -2
  196. package/dist/extractors/helpers.d.ts.map +1 -1
  197. package/dist/extractors/helpers.js +45 -1
  198. package/dist/extractors/helpers.js.map +1 -1
  199. package/dist/extractors/java.d.ts.map +1 -1
  200. package/dist/extractors/java.js +85 -8
  201. package/dist/extractors/java.js.map +1 -1
  202. package/dist/extractors/javascript.d.ts.map +1 -1
  203. package/dist/extractors/javascript.js +686 -169
  204. package/dist/extractors/javascript.js.map +1 -1
  205. package/dist/extractors/kotlin.js +58 -3
  206. package/dist/extractors/kotlin.js.map +1 -1
  207. package/dist/extractors/objc.js +25 -2
  208. package/dist/extractors/objc.js.map +1 -1
  209. package/dist/extractors/scala.js +62 -2
  210. package/dist/extractors/scala.js.map +1 -1
  211. package/dist/extractors/swift.js +52 -3
  212. package/dist/extractors/swift.js.map +1 -1
  213. package/dist/features/audit.js +26 -23
  214. package/dist/features/audit.js.map +1 -1
  215. package/dist/features/boundaries.d.ts.map +1 -1
  216. package/dist/features/boundaries.js +12 -9
  217. package/dist/features/boundaries.js.map +1 -1
  218. package/dist/features/cfg.d.ts.map +1 -1
  219. package/dist/features/cfg.js +25 -18
  220. package/dist/features/cfg.js.map +1 -1
  221. package/dist/features/check.d.ts.map +1 -1
  222. package/dist/features/check.js +18 -5
  223. package/dist/features/check.js.map +1 -1
  224. package/dist/features/communities.d.ts +4 -2
  225. package/dist/features/communities.d.ts.map +1 -1
  226. package/dist/features/communities.js +6 -4
  227. package/dist/features/communities.js.map +1 -1
  228. package/dist/features/dataflow.d.ts +60 -0
  229. package/dist/features/dataflow.d.ts.map +1 -1
  230. package/dist/features/dataflow.js +530 -6
  231. package/dist/features/dataflow.js.map +1 -1
  232. package/dist/features/manifesto.d.ts.map +1 -1
  233. package/dist/features/manifesto.js +59 -72
  234. package/dist/features/manifesto.js.map +1 -1
  235. package/dist/features/sequence.d.ts.map +1 -1
  236. package/dist/features/sequence.js +27 -22
  237. package/dist/features/sequence.js.map +1 -1
  238. package/dist/features/snapshot.d.ts.map +1 -1
  239. package/dist/features/snapshot.js +36 -28
  240. package/dist/features/snapshot.js.map +1 -1
  241. package/dist/features/structure-query.d.ts +1 -1
  242. package/dist/features/structure-query.d.ts.map +1 -1
  243. package/dist/features/structure-query.js +6 -6
  244. package/dist/features/structure-query.js.map +1 -1
  245. package/dist/features/structure.d.ts.map +1 -1
  246. package/dist/features/structure.js +150 -62
  247. package/dist/features/structure.js.map +1 -1
  248. package/dist/features/triage.d.ts.map +1 -1
  249. package/dist/features/triage.js +18 -11
  250. package/dist/features/triage.js.map +1 -1
  251. package/dist/graph/algorithms/bfs.d.ts +1 -1
  252. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  253. package/dist/graph/algorithms/bfs.js +14 -13
  254. package/dist/graph/algorithms/bfs.js.map +1 -1
  255. package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
  256. package/dist/graph/algorithms/tarjan.js +5 -0
  257. package/dist/graph/algorithms/tarjan.js.map +1 -1
  258. package/dist/graph/builders/dependency.js +28 -22
  259. package/dist/graph/builders/dependency.js.map +1 -1
  260. package/dist/graph/classifiers/roles.d.ts +10 -1
  261. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  262. package/dist/graph/classifiers/roles.js +60 -6
  263. package/dist/graph/classifiers/roles.js.map +1 -1
  264. package/dist/index.d.ts +1 -1
  265. package/dist/index.d.ts.map +1 -1
  266. package/dist/index.js +1 -1
  267. package/dist/index.js.map +1 -1
  268. package/dist/infrastructure/config.d.ts +87 -4
  269. package/dist/infrastructure/config.d.ts.map +1 -1
  270. package/dist/infrastructure/config.js +424 -22
  271. package/dist/infrastructure/config.js.map +1 -1
  272. package/dist/infrastructure/registry.d.ts +27 -7
  273. package/dist/infrastructure/registry.d.ts.map +1 -1
  274. package/dist/infrastructure/registry.js +79 -5
  275. package/dist/infrastructure/registry.js.map +1 -1
  276. package/dist/infrastructure/update-check.d.ts.map +1 -1
  277. package/dist/infrastructure/update-check.js +49 -31
  278. package/dist/infrastructure/update-check.js.map +1 -1
  279. package/dist/mcp/server.d.ts +2 -10
  280. package/dist/mcp/server.d.ts.map +1 -1
  281. package/dist/mcp/server.js.map +1 -1
  282. package/dist/mcp/tools/ast-query.d.ts +1 -1
  283. package/dist/mcp/tools/ast-query.d.ts.map +1 -1
  284. package/dist/mcp/tools/audit.d.ts +1 -1
  285. package/dist/mcp/tools/audit.d.ts.map +1 -1
  286. package/dist/mcp/tools/batch-query.d.ts +1 -1
  287. package/dist/mcp/tools/batch-query.d.ts.map +1 -1
  288. package/dist/mcp/tools/branch-compare.d.ts +1 -1
  289. package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
  290. package/dist/mcp/tools/brief.d.ts +1 -1
  291. package/dist/mcp/tools/brief.d.ts.map +1 -1
  292. package/dist/mcp/tools/cfg.d.ts +1 -1
  293. package/dist/mcp/tools/cfg.d.ts.map +1 -1
  294. package/dist/mcp/tools/check.d.ts +1 -1
  295. package/dist/mcp/tools/check.d.ts.map +1 -1
  296. package/dist/mcp/tools/co-changes.d.ts +1 -1
  297. package/dist/mcp/tools/co-changes.d.ts.map +1 -1
  298. package/dist/mcp/tools/code-owners.d.ts +1 -1
  299. package/dist/mcp/tools/code-owners.d.ts.map +1 -1
  300. package/dist/mcp/tools/communities.d.ts +1 -1
  301. package/dist/mcp/tools/communities.d.ts.map +1 -1
  302. package/dist/mcp/tools/complexity.d.ts +1 -1
  303. package/dist/mcp/tools/complexity.d.ts.map +1 -1
  304. package/dist/mcp/tools/context.d.ts +1 -1
  305. package/dist/mcp/tools/context.d.ts.map +1 -1
  306. package/dist/mcp/tools/dataflow.d.ts +1 -1
  307. package/dist/mcp/tools/dataflow.d.ts.map +1 -1
  308. package/dist/mcp/tools/diff-impact.d.ts +1 -1
  309. package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
  310. package/dist/mcp/tools/execution-flow.d.ts +1 -1
  311. package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
  312. package/dist/mcp/tools/export-graph.d.ts +1 -1
  313. package/dist/mcp/tools/export-graph.d.ts.map +1 -1
  314. package/dist/mcp/tools/file-deps.d.ts +1 -1
  315. package/dist/mcp/tools/file-deps.d.ts.map +1 -1
  316. package/dist/mcp/tools/file-exports.d.ts +1 -1
  317. package/dist/mcp/tools/file-exports.d.ts.map +1 -1
  318. package/dist/mcp/tools/find-cycles.d.ts +1 -1
  319. package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
  320. package/dist/mcp/tools/fn-impact.d.ts +1 -1
  321. package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
  322. package/dist/mcp/tools/impact-analysis.d.ts +1 -1
  323. package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
  324. package/dist/mcp/tools/implementations.d.ts +1 -1
  325. package/dist/mcp/tools/implementations.d.ts.map +1 -1
  326. package/dist/mcp/tools/index.d.ts +2 -5
  327. package/dist/mcp/tools/index.d.ts.map +1 -1
  328. package/dist/mcp/tools/index.js.map +1 -1
  329. package/dist/mcp/tools/interfaces.d.ts +1 -1
  330. package/dist/mcp/tools/interfaces.d.ts.map +1 -1
  331. package/dist/mcp/tools/list-functions.d.ts +1 -1
  332. package/dist/mcp/tools/list-functions.d.ts.map +1 -1
  333. package/dist/mcp/tools/list-repos.d.ts +1 -1
  334. package/dist/mcp/tools/list-repos.d.ts.map +1 -1
  335. package/dist/mcp/tools/module-map.d.ts +1 -1
  336. package/dist/mcp/tools/module-map.d.ts.map +1 -1
  337. package/dist/mcp/tools/node-roles.d.ts +1 -1
  338. package/dist/mcp/tools/node-roles.d.ts.map +1 -1
  339. package/dist/mcp/tools/path.d.ts +1 -1
  340. package/dist/mcp/tools/path.d.ts.map +1 -1
  341. package/dist/mcp/tools/query.d.ts +1 -1
  342. package/dist/mcp/tools/query.d.ts.map +1 -1
  343. package/dist/mcp/tools/semantic-search.d.ts +1 -1
  344. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  345. package/dist/mcp/tools/sequence.d.ts +1 -1
  346. package/dist/mcp/tools/sequence.d.ts.map +1 -1
  347. package/dist/mcp/tools/structure.d.ts +1 -1
  348. package/dist/mcp/tools/structure.d.ts.map +1 -1
  349. package/dist/mcp/tools/symbol-children.d.ts +1 -1
  350. package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
  351. package/dist/mcp/tools/triage.d.ts +1 -1
  352. package/dist/mcp/tools/triage.d.ts.map +1 -1
  353. package/dist/mcp/tools/where.d.ts +1 -1
  354. package/dist/mcp/tools/where.d.ts.map +1 -1
  355. package/dist/mcp/types.d.ts +19 -0
  356. package/dist/mcp/types.d.ts.map +1 -0
  357. package/dist/mcp/types.js +6 -0
  358. package/dist/mcp/types.js.map +1 -0
  359. package/dist/presentation/queries-cli/index.d.ts +1 -1
  360. package/dist/presentation/queries-cli/index.d.ts.map +1 -1
  361. package/dist/presentation/queries-cli/index.js +1 -1
  362. package/dist/presentation/queries-cli/index.js.map +1 -1
  363. package/dist/presentation/queries-cli/overview.d.ts +1 -0
  364. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  365. package/dist/presentation/queries-cli/overview.js +20 -1
  366. package/dist/presentation/queries-cli/overview.js.map +1 -1
  367. package/dist/presentation/queries-cli.d.ts +1 -1
  368. package/dist/presentation/queries-cli.d.ts.map +1 -1
  369. package/dist/presentation/queries-cli.js +1 -1
  370. package/dist/presentation/queries-cli.js.map +1 -1
  371. package/dist/presentation/structure.d.ts +1 -1
  372. package/dist/presentation/structure.d.ts.map +1 -1
  373. package/dist/presentation/structure.js +2 -2
  374. package/dist/presentation/structure.js.map +1 -1
  375. package/dist/presentation/viewer.d.ts.map +1 -1
  376. package/dist/presentation/viewer.js +45 -32
  377. package/dist/presentation/viewer.js.map +1 -1
  378. package/dist/shared/constants.d.ts +21 -0
  379. package/dist/shared/constants.d.ts.map +1 -1
  380. package/dist/shared/constants.js +25 -0
  381. package/dist/shared/constants.js.map +1 -1
  382. package/dist/shared/normalize.d.ts.map +1 -1
  383. package/dist/shared/normalize.js +12 -22
  384. package/dist/shared/normalize.js.map +1 -1
  385. package/dist/shared/paginate.d.ts +4 -17
  386. package/dist/shared/paginate.d.ts.map +1 -1
  387. package/dist/shared/paginate.js.map +1 -1
  388. package/dist/types.d.ts +113 -1
  389. package/dist/types.d.ts.map +1 -1
  390. package/grammars/tree-sitter-erlang.wasm +0 -0
  391. package/grammars/tree-sitter-gleam.wasm +0 -0
  392. package/package.json +7 -8
  393. package/src/ast-analysis/engine.ts +43 -63
  394. package/src/ast-analysis/rules/b2.ts +263 -0
  395. package/src/ast-analysis/rules/b3.ts +127 -0
  396. package/src/ast-analysis/rules/b4.ts +378 -0
  397. package/src/ast-analysis/rules/b5.ts +65 -0
  398. package/src/ast-analysis/rules/c.ts +157 -0
  399. package/src/ast-analysis/rules/index.ts +34 -0
  400. package/src/ast-analysis/rules/javascript.ts +3 -0
  401. package/src/ast-analysis/shared.ts +2 -0
  402. package/src/ast-analysis/visitor-utils.ts +5 -0
  403. package/src/ast-analysis/visitor.ts +82 -52
  404. package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
  405. package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
  406. package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
  407. package/src/cli/commands/audit.ts +2 -1
  408. package/src/cli/commands/batch.ts +1 -0
  409. package/src/cli/commands/build.ts +6 -1
  410. package/src/cli/commands/config.ts +353 -0
  411. package/src/cli/commands/roles.ts +6 -1
  412. package/src/cli/commands/triage.ts +1 -1
  413. package/src/cli/index.ts +10 -0
  414. package/src/cli/shared/options.ts +11 -1
  415. package/src/cli/types.ts +2 -0
  416. package/src/db/better-sqlite3.ts +5 -4
  417. package/src/db/connection.ts +23 -5
  418. package/src/db/index.ts +1 -0
  419. package/src/db/migrations.ts +69 -1
  420. package/src/db/repository/build-stmts.ts +30 -0
  421. package/src/db/repository/dataflow.ts +16 -0
  422. package/src/db/repository/index.ts +1 -1
  423. package/src/db/repository/native-repository.ts +56 -40
  424. package/src/domain/analysis/fn-impact.ts +4 -0
  425. package/src/domain/analysis/module-map.ts +38 -6
  426. package/src/domain/analysis/roles.ts +23 -0
  427. package/src/domain/graph/builder/call-resolver.ts +156 -218
  428. package/src/domain/graph/builder/cha.ts +18 -1
  429. package/src/domain/graph/builder/context.ts +1 -0
  430. package/src/domain/graph/builder/helpers.ts +205 -67
  431. package/src/domain/graph/builder/incremental.ts +249 -119
  432. package/src/domain/graph/builder/pipeline.ts +59 -6
  433. package/src/domain/graph/builder/stages/build-edges.ts +783 -652
  434. package/src/domain/graph/builder/stages/collect-files.ts +12 -6
  435. package/src/domain/graph/builder/stages/detect-changes.ts +4 -2
  436. package/src/domain/graph/builder/stages/finalize.ts +4 -0
  437. package/src/domain/graph/builder/stages/native-orchestrator.ts +1214 -398
  438. package/src/domain/graph/builder/stages/resolve-imports.ts +1 -1
  439. package/src/domain/graph/resolver/points-to.ts +182 -59
  440. package/src/domain/graph/resolver/strategy.ts +265 -0
  441. package/src/domain/graph/watcher.ts +19 -9
  442. package/src/domain/parser.ts +57 -16
  443. package/src/domain/queries.ts +1 -1
  444. package/src/domain/wasm-worker-entry.ts +13 -2
  445. package/src/domain/wasm-worker-pool.ts +29 -4
  446. package/src/domain/wasm-worker-protocol.ts +5 -0
  447. package/src/extractors/cpp.ts +44 -1
  448. package/src/extractors/cuda.ts +44 -1
  449. package/src/extractors/dart.ts +48 -3
  450. package/src/extractors/groovy.ts +62 -2
  451. package/src/extractors/helpers.ts +48 -2
  452. package/src/extractors/java.ts +88 -8
  453. package/src/extractors/javascript.ts +693 -167
  454. package/src/extractors/kotlin.ts +57 -3
  455. package/src/extractors/objc.ts +25 -1
  456. package/src/extractors/scala.ts +63 -1
  457. package/src/extractors/swift.ts +46 -3
  458. package/src/features/audit.ts +43 -34
  459. package/src/features/boundaries.ts +17 -9
  460. package/src/features/cfg.ts +31 -22
  461. package/src/features/check.ts +21 -5
  462. package/src/features/communities.ts +28 -19
  463. package/src/features/dataflow.ts +755 -6
  464. package/src/features/manifesto.ts +76 -75
  465. package/src/features/sequence.ts +29 -23
  466. package/src/features/snapshot.ts +36 -25
  467. package/src/features/structure-query.ts +7 -7
  468. package/src/features/structure.ts +185 -55
  469. package/src/features/triage.ts +28 -15
  470. package/src/graph/algorithms/bfs.ts +13 -12
  471. package/src/graph/algorithms/tarjan.ts +5 -0
  472. package/src/graph/builders/dependency.ts +35 -23
  473. package/src/graph/classifiers/roles.ts +74 -7
  474. package/src/index.ts +5 -1
  475. package/src/infrastructure/config.ts +511 -23
  476. package/src/infrastructure/registry.ts +117 -12
  477. package/src/infrastructure/update-check.ts +55 -33
  478. package/src/mcp/server.ts +2 -8
  479. package/src/mcp/tools/ast-query.ts +1 -1
  480. package/src/mcp/tools/audit.ts +1 -1
  481. package/src/mcp/tools/batch-query.ts +1 -1
  482. package/src/mcp/tools/branch-compare.ts +1 -1
  483. package/src/mcp/tools/brief.ts +1 -1
  484. package/src/mcp/tools/cfg.ts +1 -1
  485. package/src/mcp/tools/check.ts +1 -1
  486. package/src/mcp/tools/co-changes.ts +1 -1
  487. package/src/mcp/tools/code-owners.ts +1 -1
  488. package/src/mcp/tools/communities.ts +1 -1
  489. package/src/mcp/tools/complexity.ts +1 -1
  490. package/src/mcp/tools/context.ts +1 -1
  491. package/src/mcp/tools/dataflow.ts +1 -1
  492. package/src/mcp/tools/diff-impact.ts +1 -1
  493. package/src/mcp/tools/execution-flow.ts +1 -1
  494. package/src/mcp/tools/export-graph.ts +1 -1
  495. package/src/mcp/tools/file-deps.ts +1 -1
  496. package/src/mcp/tools/file-exports.ts +1 -1
  497. package/src/mcp/tools/find-cycles.ts +1 -1
  498. package/src/mcp/tools/fn-impact.ts +1 -1
  499. package/src/mcp/tools/impact-analysis.ts +1 -1
  500. package/src/mcp/tools/implementations.ts +1 -1
  501. package/src/mcp/tools/index.ts +2 -5
  502. package/src/mcp/tools/interfaces.ts +1 -1
  503. package/src/mcp/tools/list-functions.ts +1 -1
  504. package/src/mcp/tools/list-repos.ts +1 -1
  505. package/src/mcp/tools/module-map.ts +1 -1
  506. package/src/mcp/tools/node-roles.ts +1 -1
  507. package/src/mcp/tools/path.ts +1 -1
  508. package/src/mcp/tools/query.ts +1 -1
  509. package/src/mcp/tools/semantic-search.ts +1 -1
  510. package/src/mcp/tools/sequence.ts +1 -1
  511. package/src/mcp/tools/structure.ts +1 -1
  512. package/src/mcp/tools/symbol-children.ts +1 -1
  513. package/src/mcp/tools/triage.ts +1 -1
  514. package/src/mcp/tools/where.ts +1 -1
  515. package/src/mcp/types.ts +21 -0
  516. package/src/presentation/queries-cli/index.ts +1 -1
  517. package/src/presentation/queries-cli/overview.ts +35 -1
  518. package/src/presentation/queries-cli.ts +1 -0
  519. package/src/presentation/structure.ts +3 -3
  520. package/src/presentation/viewer.ts +98 -87
  521. package/src/shared/constants.ts +26 -0
  522. package/src/shared/normalize.ts +13 -22
  523. package/src/shared/paginate.ts +4 -18
  524. package/src/types.ts +127 -1
@@ -1,9 +1,13 @@
1
1
  import { execFileSync } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
2
3
  import fs from 'node:fs';
4
+ import os from 'node:os';
3
5
  import path from 'node:path';
4
6
  import { ConfigError, toErrorMessage } from '../shared/errors.js';
5
- import type { CodegraphConfig } from '../types.js';
7
+ import { compileGlobs, matchesAny } from '../shared/globs.js';
8
+ import type { CodegraphConfig, ConfigSource, ConsentDecision } from '../types.js';
6
9
  import { debug, warn } from './logger.js';
10
+ import { getUserConfigConsent, REGISTRY_PATH, setUserConfigConsent } from './registry.js';
7
11
 
8
12
  export type { CodegraphConfig } from '../types.js';
9
13
 
@@ -17,6 +21,7 @@ export const DEFAULTS = {
17
21
  include: [] as string[],
18
22
  exclude: [] as string[],
19
23
  ignoreDirs: [] as string[],
24
+ ignoreAdditionalDirs: [] as string[],
20
25
  extensions: [] as string[],
21
26
  aliases: {} as Record<string, string>,
22
27
  build: {
@@ -24,7 +29,9 @@ export const DEFAULTS = {
24
29
  dbPath: '.codegraph/graph.db',
25
30
  driftThreshold: 0.2,
26
31
  smallFilesThreshold: 5,
27
- typescriptResolver: false,
32
+ typescriptResolver: true,
33
+ engine: 'auto' as 'auto' | 'native' | 'wasm',
34
+ fastSkipDiag: false,
28
35
  },
29
36
  query: {
30
37
  defaultDepth: 3,
@@ -83,14 +90,21 @@ export const DEFAULTS = {
83
90
  // TODO(Phase 8.3): wire these into the points-to solver and type-propagation path
84
91
  // once config is threaded through to extractSymbols / buildPointsToMap. Currently
85
92
  // controlled by hardcoded constants in src/extractors/javascript.ts
86
- // (MAX_PROPAGATION_DEPTH, PROPAGATION_HOP_PENALTY) and in
93
+ // (MAX_PROPAGATION_DEPTH, PROPAGATION_HOP_PENALTY, INFERRED_RETURN_TYPE_CONFIDENCE) and in
87
94
  // src/domain/graph/resolver/points-to.ts (MAX_SOLVER_ITERATIONS).
88
95
  typePropagationDepth: 3,
96
+ /**
97
+ * Confidence score assigned to a return type inferred from `return new Constructor()`
98
+ * when no explicit TypeScript annotation is present.
99
+ * Mirrors `INFERRED_RETURN_TYPE_CONFIDENCE` in `src/extractors/javascript.ts`.
100
+ * @reserved — not yet wired; see TODO above.
101
+ */
102
+ typeInferenceConfidence: 0.85,
89
103
  /**
90
104
  * Maximum fixed-point iterations for the Phase 8.3 points-to solver.
91
105
  * @reserved — currently not wired to either the WASM solver
92
106
  * (`MAX_SOLVER_ITERATIONS` in `points-to.ts`) or the native Rust solver
93
- * (`MAX_SOLVER_ITERATIONS` in `edge_builder.rs`), both of which use the
107
+ * (`MAX_SOLVER_ITERATIONS` in `stages/build_edges.rs`), both of which use the
94
108
  * same hardcoded value of 50. See the TODO comment above.
95
109
  */
96
110
  pointsToMaxIterations: 50,
@@ -166,43 +180,439 @@ export const DEFAULTS = {
166
180
  },
167
181
  } satisfies CodegraphConfig;
168
182
 
183
+ // ── Per-process user-config override (set by CLI flags) ────────────────
184
+ // Set once by the preAction hook before any command runs; cleared when changed.
185
+ let _userConfigOverride: string | boolean | undefined;
186
+
187
+ /**
188
+ * Set the per-run user-config override from CLI flags.
189
+ * Called by the CLI preAction hook before any command executes.
190
+ * - false → --no-user-config
191
+ * - string → --user-config <path>
192
+ * - true → --user-config (bare, use default global file)
193
+ * - undefined → clear override, revert to consent-based resolution
194
+ */
195
+ export function setUserConfigOverride(v: string | boolean | undefined): void {
196
+ _userConfigOverride = v;
197
+ _configCache.clear();
198
+ _globalConfigCache.clear();
199
+ }
200
+
169
201
  // Per-cwd config cache — avoids re-reading the config file on every query call.
170
- // The config file rarely changes within a single process lifetime.
202
+ // Key includes the applied global path so toggled flags/consent are reflected.
171
203
  const _configCache = new Map<string, CodegraphConfig>();
204
+ // Parallel cache for the sanitized global layer — needed so loadConfigWithProvenance
205
+ // can correctly attribute global-layer keys even on a _configCache hit.
206
+ const _globalConfigCache = new Map<string, Record<string, unknown> | null>();
207
+
208
+ // ── Global config file location ─────────────────────────────────────────
209
+
210
+ /**
211
+ * Return the canonical path where a new global config file should be written.
212
+ *
213
+ * Uses the same priority logic as resolveUserConfigPath() but always returns a
214
+ * path — it does not check whether the file exists. Used by `--init` to know
215
+ * where to scaffold the file.
216
+ *
217
+ * Priority:
218
+ * 1. CODEGRAPH_USER_CONFIG env var (used as-is)
219
+ * 2. $XDG_CONFIG_HOME/codegraph/config.json
220
+ * %APPDATA%\codegraph\config.json (Windows)
221
+ * fallback: ~/.config/codegraph/config.json
222
+ */
223
+ export function getDefaultUserConfigPath(): string {
224
+ const envPath = process.env.CODEGRAPH_USER_CONFIG;
225
+ if (envPath) return envPath;
226
+
227
+ const home = os.homedir();
228
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
229
+ if (xdgConfig) return path.join(xdgConfig, 'codegraph', 'config.json');
230
+ if (process.platform === 'win32') {
231
+ const appdata = process.env.APPDATA;
232
+ return appdata
233
+ ? path.join(appdata, 'codegraph', 'config.json')
234
+ : path.join(home, '.config', 'codegraph', 'config.json');
235
+ }
236
+ return path.join(home, '.config', 'codegraph', 'config.json');
237
+ }
238
+
239
+ /**
240
+ * Resolve the absolute path to the user-level global config file.
241
+ *
242
+ * Priority:
243
+ * 1. CODEGRAPH_USER_CONFIG env var (location override only — not forced-on)
244
+ * 2. $XDG_CONFIG_HOME/codegraph/config.json (Unix/macOS)
245
+ * %APPDATA%\codegraph\config.json (Windows)
246
+ * fallback: ~/.config/codegraph/config.json
247
+ * 3. ~/.codegraph/config.json (legacy, next to registry.json)
248
+ *
249
+ * Returns the path of the first existing file, or null if none exist.
250
+ */
251
+ export function resolveUserConfigPath(): string | null {
252
+ const envPath = process.env.CODEGRAPH_USER_CONFIG;
253
+ if (envPath) {
254
+ if (fs.existsSync(envPath)) return envPath;
255
+ debug(`CODEGRAPH_USER_CONFIG points to missing file: ${envPath}`);
256
+ return null;
257
+ }
258
+
259
+ const home = os.homedir();
260
+
261
+ // XDG_CONFIG_HOME takes priority on all platforms when explicitly set.
262
+ // Falls back to %APPDATA% on Windows, or ~/.config on Unix/macOS.
263
+ let platformDefault: string;
264
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
265
+ if (xdgConfig) {
266
+ platformDefault = path.join(xdgConfig, 'codegraph', 'config.json');
267
+ } else if (process.platform === 'win32') {
268
+ const appdata = process.env.APPDATA;
269
+ platformDefault = appdata
270
+ ? path.join(appdata, 'codegraph', 'config.json')
271
+ : path.join(home, '.config', 'codegraph', 'config.json');
272
+ } else {
273
+ platformDefault = path.join(home, '.config', 'codegraph', 'config.json');
274
+ }
275
+
276
+ if (fs.existsSync(platformDefault)) return platformDefault;
277
+
278
+ const legacyPath = path.join(home, '.codegraph', 'config.json');
279
+ if (fs.existsSync(legacyPath)) return legacyPath;
280
+
281
+ return null;
282
+ }
283
+
284
+ // ── Global config file loading ──────────────────────────────────────────
285
+
286
+ interface ParsedUserConfig {
287
+ globalConfig: Record<string, unknown>;
288
+ appliesToGlobs: string[];
289
+ }
290
+
291
+ /**
292
+ * Read and parse a user-level global config file.
293
+ * Handles both plain-config and appliesTo-wrapper formats.
294
+ * Returns null on missing or malformed files (never throws).
295
+ */
296
+ function loadUserConfigFile(filePath: string): ParsedUserConfig | null {
297
+ try {
298
+ const raw = fs.readFileSync(filePath, 'utf-8');
299
+ const parsed = JSON.parse(raw) as Record<string, unknown>;
300
+ // Wrapper format: { appliesTo: [...], config: {...} }
301
+ if ('appliesTo' in parsed && typeof parsed.config === 'object' && parsed.config !== null) {
302
+ const globs = Array.isArray(parsed.appliesTo)
303
+ ? (parsed.appliesTo as unknown[]).filter((g): g is string => typeof g === 'string')
304
+ : [];
305
+ return { globalConfig: parsed.config as Record<string, unknown>, appliesToGlobs: globs };
306
+ }
307
+ // Plain config (no appliesTo wrapper)
308
+ return { globalConfig: parsed, appliesToGlobs: [] };
309
+ } catch (err) {
310
+ debug(`Failed to load user config at ${filePath}: ${toErrorMessage(err)}`);
311
+ return null;
312
+ }
313
+ }
314
+
315
+ // ── Safety sanitisation ─────────────────────────────────────────────────
316
+
317
+ /**
318
+ * Drop any unsafe keys from the global layer before merging.
319
+ * Currently: absolute build.dbPath (would make all repos share one DB).
320
+ * Relative dbPaths resolve per-repo and are allowed through unchanged.
321
+ */
322
+ function sanitizeUserLayer(raw: Record<string, unknown>): Record<string, unknown> {
323
+ const build = raw.build as Record<string, unknown> | undefined;
324
+ if (build && typeof build.dbPath === 'string' && path.isAbsolute(build.dbPath)) {
325
+ warn(
326
+ `User config: build.dbPath "${build.dbPath}" is absolute and was ignored ` +
327
+ '(an absolute dbPath would share one database across all repos).',
328
+ );
329
+ const sanitizedBuild = { ...build };
330
+ delete sanitizedBuild.dbPath;
331
+ return { ...raw, build: sanitizedBuild };
332
+ }
333
+ return raw;
334
+ }
335
+
336
+ // ── excludeTests shorthand (per-layer) ─────────────────────────────────
337
+
338
+ /**
339
+ * Hoist a top-level `excludeTests` key from a raw layer into `query.excludeTests`.
340
+ * If the layer already has `query.excludeTests`, that value wins (no-op).
341
+ * Also removes any stale `excludeTests` key that may have leaked into `merged`.
342
+ */
343
+ function applyExcludeTestsShorthand(
344
+ merged: Record<string, unknown>,
345
+ rawLayer: Record<string, unknown>,
346
+ ): Record<string, unknown> {
347
+ if ('excludeTests' in rawLayer) {
348
+ // Only hoist if this layer doesn't also set query.excludeTests
349
+ if (!(rawLayer.query && 'excludeTests' in (rawLayer.query as object))) {
350
+ (merged.query as Record<string, unknown>).excludeTests = Boolean(rawLayer.excludeTests);
351
+ }
352
+ const result = { ...merged };
353
+ delete result.excludeTests;
354
+ return result;
355
+ }
356
+ if ('excludeTests' in merged) {
357
+ const result = { ...merged };
358
+ delete result.excludeTests;
359
+ return result;
360
+ }
361
+ return merged;
362
+ }
363
+
364
+ // ── Consent resolution ──────────────────────────────────────────────────
365
+
366
+ interface ConsentResolutionResult {
367
+ applied: boolean;
368
+ globalPath: string | null;
369
+ consentDecision: ConsentDecision | undefined;
370
+ }
371
+
372
+ /**
373
+ * Resolve whether the global user config should be applied for a given repo.
374
+ * Implements the §4.1/§4.2 precedence chain from the spec.
375
+ *
376
+ * @param rootDir Absolute repo root.
377
+ * @param override Per-run override from CLI flags (_userConfigOverride).
378
+ * @param registryPath Optional registry path (for tests).
379
+ */
380
+ function resolveConsent(
381
+ rootDir: string,
382
+ override: string | boolean | undefined,
383
+ registryPath: string = REGISTRY_PATH,
384
+ ): ConsentResolutionResult {
385
+ // §4.1 step 1: --no-user-config
386
+ if (override === false) {
387
+ return { applied: false, globalPath: null, consentDecision: undefined };
388
+ }
389
+
390
+ // §4.1 steps 2–3: explicit path or bare --user-config
391
+ if (override !== undefined) {
392
+ const explicitPath = typeof override === 'string' ? override : resolveUserConfigPath();
393
+ if (explicitPath && fs.existsSync(explicitPath)) {
394
+ return { applied: true, globalPath: explicitPath, consentDecision: undefined };
395
+ }
396
+ if (typeof override === 'string') {
397
+ warn(`--user-config path "${override}" does not exist; skipping global layer.`);
398
+ }
399
+ return { applied: false, globalPath: null, consentDecision: undefined };
400
+ }
401
+
402
+ // §4.1 step 4: resolve global file — if none, NOT applied
403
+ const globalPath = resolveUserConfigPath();
404
+ if (!globalPath) {
405
+ return { applied: false, globalPath: null, consentDecision: undefined };
406
+ }
407
+
408
+ // §4.2: check per-repo decision
409
+ const consentDecision = getUserConfigConsent(rootDir, registryPath);
410
+
411
+ // §4.2 step 1: recorded disabled
412
+ if (consentDecision === 'disabled') {
413
+ return { applied: false, globalPath, consentDecision };
414
+ }
415
+
416
+ // §4.2 step 2: recorded enabled
417
+ if (consentDecision === 'enabled') {
418
+ return { applied: true, globalPath, consentDecision };
419
+ }
420
+
421
+ // §4.2 step 3: appliesTo glob match (dynamic, never persisted)
422
+ const parsed = loadUserConfigFile(globalPath);
423
+ if (parsed?.appliesToGlobs.length) {
424
+ const expanded = parsed.appliesToGlobs.map((g) =>
425
+ g.startsWith('~') ? path.join(os.homedir(), g.slice(1)) : g,
426
+ );
427
+ const regexes = compileGlobs(expanded);
428
+ const absRoot = path.resolve(rootDir);
429
+ if (matchesAny(regexes, absRoot)) {
430
+ return { applied: true, globalPath, consentDecision: undefined };
431
+ }
432
+ }
433
+
434
+ // §4.2 steps 4–5: undecided — caller decides whether to prompt
435
+ return { applied: false, globalPath, consentDecision: undefined };
436
+ }
437
+
438
+ // Last applied global path and parsed data — exposed so pipeline.ts and
439
+ // loadConfigWithProvenance can reuse the already-parsed file contents without a
440
+ // second disk read (eliminating the TOCTOU window between loadConfig and callers).
441
+ let _lastAppliedGlobalPath: string | null = null;
442
+ let _lastAppliedGlobalConfig: Record<string, unknown> | null = null;
443
+ export function getLastAppliedGlobalPath(): string | null {
444
+ return _lastAppliedGlobalPath;
445
+ }
446
+ export function getLastAppliedGlobalConfig(): Record<string, unknown> | null {
447
+ return _lastAppliedGlobalConfig;
448
+ }
449
+
450
+ // ── Build-relevant config hash ──────────────────────────────────────────
451
+
452
+ const BUILD_HASH_KEYS: ReadonlyArray<keyof CodegraphConfig> = [
453
+ 'include',
454
+ 'exclude',
455
+ 'ignoreDirs',
456
+ 'ignoreAdditionalDirs',
457
+ 'extensions',
458
+ 'aliases',
459
+ 'build',
460
+ ];
461
+
462
+ /**
463
+ * Compute a short stable hash of the build-relevant config subset.
464
+ * Used by the pipeline to detect config changes that require a full rebuild.
465
+ */
466
+ export function computeConfigHash(config: CodegraphConfig): string {
467
+ const subset: Partial<CodegraphConfig> = {};
468
+ for (const k of BUILD_HASH_KEYS) {
469
+ (subset as Record<string, unknown>)[k] = config[k];
470
+ }
471
+ return createHash('sha256').update(JSON.stringify(subset)).digest('hex').slice(0, 16);
472
+ }
473
+
474
+ // ── Interactive consent prompt ──────────────────────────────────────────
475
+
476
+ /**
477
+ * When called from the build command, check whether we should prompt the user
478
+ * for global-config consent and, if so, prompt and persist the answer.
479
+ *
480
+ * Only fires when ALL of:
481
+ * - A global config file exists
482
+ * - The repo is undecided (no recorded consent)
483
+ * - Not matched by appliesTo globs
484
+ * - process.stdin.isTTY && process.stdout.isTTY
485
+ * - CI env is not set
486
+ * - No per-run --user-config / --no-user-config flag is active
487
+ */
488
+ export async function promptForConsentIfNeeded(
489
+ rootDir: string,
490
+ registryPath: string = REGISTRY_PATH,
491
+ ): Promise<void> {
492
+ // No-op if per-run override is active
493
+ if (_userConfigOverride !== undefined) return;
494
+
495
+ const globalPath = resolveUserConfigPath();
496
+ if (!globalPath) return;
497
+
498
+ const consentDecision = getUserConfigConsent(rootDir, registryPath);
499
+ if (consentDecision !== undefined) return; // already decided
500
+
501
+ // Check appliesTo globs (dynamic consent — no prompt needed)
502
+ const parsed = loadUserConfigFile(globalPath);
503
+ if (parsed?.appliesToGlobs.length) {
504
+ const expanded = parsed.appliesToGlobs.map((g) =>
505
+ g.startsWith('~') ? path.join(os.homedir(), g.slice(1)) : g,
506
+ );
507
+ const regexes = compileGlobs(expanded);
508
+ const absRoot = path.resolve(rootDir);
509
+ if (matchesAny(regexes, absRoot)) return; // covered by appliesTo
510
+ }
511
+
512
+ // Only prompt in fully interactive sessions
513
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return;
514
+ if (process.env.CI) return;
515
+
516
+ const { createInterface } = await import('node:readline');
517
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
518
+
519
+ const answer = await new Promise<string>((resolve) => {
520
+ rl.question(
521
+ `\nA global codegraph config was found at ${globalPath}.\n` +
522
+ `Apply settings not explicitly configured in this repo to ${path.resolve(rootDir)}? [y/N]\n` +
523
+ `(remembered per-repo; change later with \`codegraph config --enable-global|--disable-global\`)\n` +
524
+ `> `,
525
+ (ans) => {
526
+ rl.close();
527
+ resolve(ans.trim().toLowerCase());
528
+ },
529
+ );
530
+ });
531
+
532
+ const decided = answer === 'y' || answer === 'yes' ? 'enabled' : 'disabled';
533
+ setUserConfigConsent(rootDir, decided, registryPath);
534
+ process.stderr.write(`Global config consent recorded: ${decided}\n`);
535
+ }
536
+
537
+ // ── Main config loader ──────────────────────────────────────────────────
538
+
539
+ /** Options for loadConfig. */
540
+ export interface LoadConfigOpts {
541
+ /** Per-run user-config override (from CLI flags or programmatic call). */
542
+ userConfig?: string | boolean;
543
+ /** Registry path override (mainly for tests). */
544
+ registryPath?: string;
545
+ }
172
546
 
173
547
  /**
174
548
  * Load project configuration from a .codegraphrc.json or similar file.
175
- * Returns merged config with defaults. Results are cached per cwd.
549
+ * Returns merged config with defaults: defaults global (if applied) → project → env → secrets.
550
+ * Results are cached per cwd + applied global path.
176
551
  */
177
- export function loadConfig(cwd?: string): CodegraphConfig {
178
- cwd = cwd || process.cwd();
179
- const cached = _configCache.get(cwd);
180
- if (cached) return structuredClone(cached);
552
+ export function loadConfig(cwd?: string, opts?: LoadConfigOpts): CodegraphConfig {
553
+ cwd = path.resolve(cwd || process.cwd());
554
+
555
+ // Determine effective override: explicit opts win over module-level variable
556
+ const override = opts?.userConfig !== undefined ? opts.userConfig : _userConfigOverride;
557
+
558
+ // Resolve consent and global path
559
+ const { applied, globalPath } = resolveConsent(cwd, override, opts?.registryPath);
560
+
561
+ // Cache key includes applied global path and override flag so toggled consent is reflected
562
+ const cacheKey = `${cwd}::${applied ? (globalPath ?? 'default') : 'none'}`;
563
+ // Always update _lastAppliedGlobalPath/_lastAppliedGlobalConfig before returning —
564
+ // on a cache hit the previous call may have been for a different repo or different
565
+ // opts, so stale values here would misbehave for programmatic callers making
566
+ // multiple buildGraph calls in the same process.
567
+ _lastAppliedGlobalPath = applied ? globalPath : null;
568
+ _lastAppliedGlobalConfig = null; // updated below if a global file is loaded
569
+ const cached = _configCache.get(cacheKey);
570
+ if (cached) {
571
+ // Restore global config so loadConfigWithProvenance gets correct provenance on cache hits.
572
+ _lastAppliedGlobalConfig = _globalConfigCache.get(cacheKey) ?? null;
573
+ return structuredClone(cached);
574
+ }
181
575
 
576
+ // ── Layer 0: DEFAULTS ─────────────────────────────────────────────
577
+ let merged = DEFAULTS as unknown as Record<string, unknown>;
578
+
579
+ // ── Layer 1: global (if applied) ──────────────────────────────────
580
+ if (applied && globalPath) {
581
+ const userFileData = loadUserConfigFile(globalPath);
582
+ if (userFileData) {
583
+ debug(`Applying global user config from ${globalPath}`);
584
+ const sanitized = sanitizeUserLayer(userFileData.globalConfig);
585
+ // Cache the sanitized global data so pipeline.ts and loadConfigWithProvenance
586
+ // can use it without a second disk read (eliminates TOCTOU window).
587
+ _lastAppliedGlobalConfig = sanitized;
588
+ merged = mergeConfig(merged, sanitized);
589
+ merged = applyExcludeTestsShorthand(merged, sanitized);
590
+ }
591
+ }
592
+
593
+ // ── Layer 2: project ──────────────────────────────────────────────
182
594
  for (const name of CONFIG_FILES) {
183
595
  const filePath = path.join(cwd, name);
184
596
  if (fs.existsSync(filePath)) {
185
597
  try {
186
598
  const raw = fs.readFileSync(filePath, 'utf-8');
187
- const config = JSON.parse(raw);
188
- debug(`Loaded config from ${filePath}`);
189
- const merged = mergeConfig(DEFAULTS as unknown as Record<string, unknown>, config);
190
- if ('excludeTests' in config && !(config.query && 'excludeTests' in config.query)) {
191
- (merged.query as Record<string, unknown>).excludeTests = Boolean(config.excludeTests);
192
- }
193
- delete merged.excludeTests;
194
- const result = resolveSecrets(applyEnvOverrides(merged as unknown as CodegraphConfig));
195
- _configCache.set(cwd, structuredClone(result));
196
- return result;
599
+ const projectConfig = JSON.parse(raw) as Record<string, unknown>;
600
+ debug(`Loaded project config from ${filePath}`);
601
+ merged = mergeConfig(merged, projectConfig);
602
+ merged = applyExcludeTestsShorthand(merged, projectConfig);
603
+ break;
197
604
  } catch (err: unknown) {
198
605
  if (err instanceof ConfigError) throw err;
199
606
  debug(`Failed to parse config ${filePath}: ${toErrorMessage(err)}`);
200
607
  }
201
608
  }
202
609
  }
203
- const defaults = resolveSecrets(applyEnvOverrides({ ...DEFAULTS }));
204
- _configCache.set(cwd, structuredClone(defaults));
205
- return defaults;
610
+
611
+ // ── Layers 3–4: env overrides + secret resolution ─────────────────
612
+ const result = resolveSecrets(applyEnvOverrides(merged as unknown as CodegraphConfig));
613
+ _configCache.set(cacheKey, structuredClone(result));
614
+ _globalConfigCache.set(cacheKey, _lastAppliedGlobalConfig);
615
+ return result;
206
616
  }
207
617
 
208
618
  /**
@@ -212,6 +622,66 @@ export function loadConfig(cwd?: string): CodegraphConfig {
212
622
  */
213
623
  export function clearConfigCache(): void {
214
624
  _configCache.clear();
625
+ _globalConfigCache.clear();
626
+ }
627
+
628
+ /**
629
+ * Load config and return it together with per-key provenance information.
630
+ * Used by `codegraph config --explain`.
631
+ *
632
+ * Calls loadConfig first so _lastAppliedGlobalConfig is populated, then uses
633
+ * that cached data for the global-layer provenance — avoiding a second disk
634
+ * read and eliminating the TOCTOU window between the two reads.
635
+ */
636
+ export function loadConfigWithProvenance(
637
+ cwd?: string,
638
+ opts?: LoadConfigOpts,
639
+ ): import('../types.js').ConfigWithProvenance {
640
+ cwd = path.resolve(cwd || process.cwd());
641
+ const override = opts?.userConfig !== undefined ? opts.userConfig : _userConfigOverride;
642
+ const { applied, globalPath, consentDecision } = resolveConsent(
643
+ cwd,
644
+ override,
645
+ opts?.registryPath,
646
+ );
647
+
648
+ // Load (or return from cache) the merged config first — this also populates
649
+ // _lastAppliedGlobalConfig with the already-parsed and sanitized global layer.
650
+ const config = loadConfig(cwd, opts);
651
+
652
+ // Build provenance by tracking which layer supplies each top-level key
653
+ const provenance: Record<string, ConfigSource> = {};
654
+
655
+ // Layer 0: defaults — everything starts as 'default'
656
+ for (const k of Object.keys(DEFAULTS)) provenance[k] = 'default';
657
+
658
+ // Layer 1: global — reuse the data loadConfig already parsed (no second disk read)
659
+ const globalRaw = applied && globalPath ? _lastAppliedGlobalConfig : null;
660
+ if (globalRaw) {
661
+ for (const k of Object.keys(globalRaw)) provenance[k] = 'user';
662
+ }
663
+
664
+ // Layer 2: project
665
+ for (const name of CONFIG_FILES) {
666
+ const filePath = path.join(cwd, name);
667
+ if (fs.existsSync(filePath)) {
668
+ try {
669
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, unknown>;
670
+ for (const k of Object.keys(raw)) provenance[k] = 'project';
671
+ break;
672
+ } catch (err) {
673
+ debug(`loadConfigWithProvenance: failed to parse ${filePath}: ${toErrorMessage(err)}`);
674
+ }
675
+ }
676
+ }
677
+
678
+ // Layer 3+: env overrides (LLM keys)
679
+ const ENV_LLM_KEYS = ['CODEGRAPH_LLM_PROVIDER', 'CODEGRAPH_LLM_API_KEY', 'CODEGRAPH_LLM_MODEL'];
680
+ if (ENV_LLM_KEYS.some((k) => process.env[k] !== undefined)) {
681
+ provenance.llm = 'env';
682
+ }
683
+
684
+ return { config, provenance, appliedGlobalPath: applied ? globalPath : null, consentDecision };
215
685
  }
216
686
 
217
687
  const ENV_LLM_MAP: Record<string, string> = {
@@ -227,6 +697,24 @@ export function applyEnvOverrides(config: CodegraphConfig): CodegraphConfig {
227
697
  process.env[envKey as keyof NodeJS.ProcessEnv];
228
698
  }
229
699
  }
700
+ // Engine selection: CODEGRAPH_ENGINE env always wins over config-file value.
701
+ if (process.env.CODEGRAPH_ENGINE !== undefined) {
702
+ const raw = process.env.CODEGRAPH_ENGINE;
703
+ const valid = ['auto', 'native', 'wasm'] as const;
704
+ if ((valid as readonly string[]).includes(raw)) {
705
+ (config.build as Record<string, unknown>).engine = raw as 'auto' | 'native' | 'wasm';
706
+ } else {
707
+ warn(
708
+ `CODEGRAPH_ENGINE="${raw}" is not a valid engine value (expected auto|native|wasm). Falling back to "auto".`,
709
+ );
710
+ (config.build as Record<string, unknown>).engine = 'auto';
711
+ }
712
+ }
713
+ // Fast-skip diagnostic flag.
714
+ if (process.env.CODEGRAPH_FAST_SKIP_DIAG !== undefined) {
715
+ (config.build as Record<string, unknown>).fastSkipDiag =
716
+ process.env.CODEGRAPH_FAST_SKIP_DIAG === '1';
717
+ }
230
718
  return config;
231
719
  }
232
720