@monoes/monograph 1.2.0 → 1.2.1

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 (460) hide show
  1. package/__tests__/mcp-tools/cypher.test.ts +10 -6
  2. package/__tests__/mcp-tools/detect-changes.test.ts +14 -11
  3. package/__tests__/pipeline/phases/wildcard-synthesis.test.ts +4 -4
  4. package/__tests__/search/embed-batch-config.test.ts +7 -7
  5. package/__tests__/search/hybrid-query.test.ts +3 -1
  6. package/__tests__/web/api.test.ts +1 -1
  7. package/dist/src/analysis/churn-cache.d.ts.map +1 -1
  8. package/dist/src/analysis/churn-cache.js +19 -6
  9. package/dist/src/analysis/churn-cache.js.map +1 -1
  10. package/dist/src/analysis/churn-trend.d.ts.map +1 -1
  11. package/dist/src/analysis/churn-trend.js +50 -6
  12. package/dist/src/analysis/churn-trend.js.map +1 -1
  13. package/dist/src/analysis/cluster-quality.d.ts +3 -0
  14. package/dist/src/analysis/cluster-quality.d.ts.map +1 -1
  15. package/dist/src/analysis/cluster-quality.js +18 -12
  16. package/dist/src/analysis/cluster-quality.js.map +1 -1
  17. package/dist/src/analysis/coverage-gaps.d.ts +3 -0
  18. package/dist/src/analysis/coverage-gaps.d.ts.map +1 -1
  19. package/dist/src/analysis/coverage-gaps.js +40 -1
  20. package/dist/src/analysis/coverage-gaps.js.map +1 -1
  21. package/dist/src/analysis/dep-classification.d.ts +2 -0
  22. package/dist/src/analysis/dep-classification.d.ts.map +1 -1
  23. package/dist/src/analysis/dep-classification.js +30 -0
  24. package/dist/src/analysis/dep-classification.js.map +1 -1
  25. package/dist/src/analysis/dependency-health.d.ts +4 -0
  26. package/dist/src/analysis/dependency-health.d.ts.map +1 -1
  27. package/dist/src/analysis/dependency-health.js +28 -0
  28. package/dist/src/analysis/dependency-health.js.map +1 -1
  29. package/dist/src/analysis/duplicate-exports.d.ts +9 -5
  30. package/dist/src/analysis/duplicate-exports.d.ts.map +1 -1
  31. package/dist/src/analysis/duplicate-exports.js +26 -2
  32. package/dist/src/analysis/duplicate-exports.js.map +1 -1
  33. package/dist/src/analysis/feature-flags.d.ts.map +1 -1
  34. package/dist/src/analysis/feature-flags.js +36 -17
  35. package/dist/src/analysis/feature-flags.js.map +1 -1
  36. package/dist/src/analysis/node-similarity.d.ts +19 -0
  37. package/dist/src/analysis/node-similarity.d.ts.map +1 -1
  38. package/dist/src/analysis/node-similarity.js +34 -3
  39. package/dist/src/analysis/node-similarity.js.map +1 -1
  40. package/dist/src/analysis/private-type-leaks.d.ts +4 -0
  41. package/dist/src/analysis/private-type-leaks.d.ts.map +1 -1
  42. package/dist/src/analysis/private-type-leaks.js +43 -2
  43. package/dist/src/analysis/private-type-leaks.js.map +1 -1
  44. package/dist/src/analysis/project-state.d.ts +2 -0
  45. package/dist/src/analysis/project-state.d.ts.map +1 -1
  46. package/dist/src/analysis/project-state.js +18 -7
  47. package/dist/src/analysis/project-state.js.map +1 -1
  48. package/dist/src/analysis/suppressions.d.ts.map +1 -1
  49. package/dist/src/analysis/suppressions.js +3 -1
  50. package/dist/src/analysis/suppressions.js.map +1 -1
  51. package/dist/src/analysis/trace.d.ts +5 -3
  52. package/dist/src/analysis/trace.d.ts.map +1 -1
  53. package/dist/src/analysis/trace.js +59 -53
  54. package/dist/src/analysis/trace.js.map +1 -1
  55. package/dist/src/analysis/unused-class-members.d.ts.map +1 -1
  56. package/dist/src/analysis/unused-class-members.js +7 -1
  57. package/dist/src/analysis/unused-class-members.js.map +1 -1
  58. package/dist/src/analysis/worker-pool.d.ts +3 -0
  59. package/dist/src/analysis/worker-pool.d.ts.map +1 -1
  60. package/dist/src/analysis/worker-pool.js +30 -8
  61. package/dist/src/analysis/worker-pool.js.map +1 -1
  62. package/dist/src/cli/augment.d.ts.map +1 -1
  63. package/dist/src/cli/augment.js +10 -3
  64. package/dist/src/cli/augment.js.map +1 -1
  65. package/dist/src/export/sarif.js +5 -5
  66. package/dist/src/export/sarif.js.map +1 -1
  67. package/dist/src/graph/analyzer.d.ts.map +1 -1
  68. package/dist/src/graph/analyzer.js +17 -2
  69. package/dist/src/graph/analyzer.js.map +1 -1
  70. package/dist/src/graph/author-analytics.d.ts +2 -0
  71. package/dist/src/graph/author-analytics.d.ts.map +1 -1
  72. package/dist/src/graph/author-analytics.js +39 -5
  73. package/dist/src/graph/author-analytics.js.map +1 -1
  74. package/dist/src/graph/baseline.d.ts +5 -0
  75. package/dist/src/graph/baseline.d.ts.map +1 -1
  76. package/dist/src/graph/baseline.js +65 -18
  77. package/dist/src/graph/baseline.js.map +1 -1
  78. package/dist/src/graph/clone-detector.d.ts.map +1 -1
  79. package/dist/src/graph/clone-detector.js +19 -33
  80. package/dist/src/graph/clone-detector.js.map +1 -1
  81. package/dist/src/graph/clone-families.d.ts +2 -0
  82. package/dist/src/graph/clone-families.d.ts.map +1 -1
  83. package/dist/src/graph/clone-families.js +38 -10
  84. package/dist/src/graph/clone-families.js.map +1 -1
  85. package/dist/src/graph/codeowners-extended.d.ts +17 -0
  86. package/dist/src/graph/codeowners-extended.d.ts.map +1 -1
  87. package/dist/src/graph/codeowners-extended.js +42 -0
  88. package/dist/src/graph/codeowners-extended.js.map +1 -1
  89. package/dist/src/graph/codeowners-gitlab.d.ts +21 -0
  90. package/dist/src/graph/codeowners-gitlab.d.ts.map +1 -1
  91. package/dist/src/graph/codeowners-gitlab.js +37 -0
  92. package/dist/src/graph/codeowners-gitlab.js.map +1 -1
  93. package/dist/src/graph/codeowners.d.ts +10 -0
  94. package/dist/src/graph/codeowners.d.ts.map +1 -1
  95. package/dist/src/graph/codeowners.js +24 -2
  96. package/dist/src/graph/codeowners.js.map +1 -1
  97. package/dist/src/graph/complexity.d.ts +10 -0
  98. package/dist/src/graph/complexity.d.ts.map +1 -1
  99. package/dist/src/graph/complexity.js +93 -43
  100. package/dist/src/graph/complexity.js.map +1 -1
  101. package/dist/src/graph/cross-reference-findings.d.ts.map +1 -1
  102. package/dist/src/graph/cross-reference-findings.js +19 -2
  103. package/dist/src/graph/cross-reference-findings.js.map +1 -1
  104. package/dist/src/graph/cross-reference.d.ts +4 -0
  105. package/dist/src/graph/cross-reference.d.ts.map +1 -1
  106. package/dist/src/graph/cross-reference.js +23 -0
  107. package/dist/src/graph/cross-reference.js.map +1 -1
  108. package/dist/src/graph/cycle-detection.d.ts +2 -0
  109. package/dist/src/graph/cycle-detection.d.ts.map +1 -1
  110. package/dist/src/graph/cycle-detection.js +26 -3
  111. package/dist/src/graph/cycle-detection.js.map +1 -1
  112. package/dist/src/graph/cycles.d.ts +11 -3
  113. package/dist/src/graph/cycles.d.ts.map +1 -1
  114. package/dist/src/graph/cycles.js +121 -83
  115. package/dist/src/graph/cycles.js.map +1 -1
  116. package/dist/src/graph/dead-code.d.ts +23 -0
  117. package/dist/src/graph/dead-code.d.ts.map +1 -1
  118. package/dist/src/graph/dead-code.js +53 -13
  119. package/dist/src/graph/dead-code.js.map +1 -1
  120. package/dist/src/graph/dependency-closure.d.ts.map +1 -1
  121. package/dist/src/graph/dependency-closure.js +20 -4
  122. package/dist/src/graph/dependency-closure.js.map +1 -1
  123. package/dist/src/graph/dfs.d.ts +14 -0
  124. package/dist/src/graph/dfs.d.ts.map +1 -1
  125. package/dist/src/graph/dfs.js +61 -0
  126. package/dist/src/graph/dfs.js.map +1 -1
  127. package/dist/src/graph/duplication-stats.d.ts.map +1 -1
  128. package/dist/src/graph/duplication-stats.js +19 -3
  129. package/dist/src/graph/duplication-stats.js.map +1 -1
  130. package/dist/src/graph/edge-filter.d.ts +2 -0
  131. package/dist/src/graph/edge-filter.d.ts.map +1 -1
  132. package/dist/src/graph/edge-filter.js +26 -12
  133. package/dist/src/graph/edge-filter.js.map +1 -1
  134. package/dist/src/graph/health-score.d.ts +8 -0
  135. package/dist/src/graph/health-score.d.ts.map +1 -1
  136. package/dist/src/graph/health-score.js +48 -17
  137. package/dist/src/graph/health-score.js.map +1 -1
  138. package/dist/src/graph/hotspots.d.ts +7 -0
  139. package/dist/src/graph/hotspots.d.ts.map +1 -1
  140. package/dist/src/graph/hotspots.js +50 -5
  141. package/dist/src/graph/hotspots.js.map +1 -1
  142. package/dist/src/graph/import-chain.d.ts +17 -1
  143. package/dist/src/graph/import-chain.d.ts.map +1 -1
  144. package/dist/src/graph/import-chain.js +68 -19
  145. package/dist/src/graph/import-chain.js.map +1 -1
  146. package/dist/src/graph/loader.d.ts.map +1 -1
  147. package/dist/src/graph/loader.js +9 -10
  148. package/dist/src/graph/loader.js.map +1 -1
  149. package/dist/src/graph/maintainability.d.ts +8 -0
  150. package/dist/src/graph/maintainability.d.ts.map +1 -1
  151. package/dist/src/graph/maintainability.js +65 -10
  152. package/dist/src/graph/maintainability.js.map +1 -1
  153. package/dist/src/graph/mirrored-dirs.d.ts +4 -0
  154. package/dist/src/graph/mirrored-dirs.d.ts.map +1 -1
  155. package/dist/src/graph/mirrored-dirs.js +48 -13
  156. package/dist/src/graph/mirrored-dirs.js.map +1 -1
  157. package/dist/src/graph/narrowing.d.ts +15 -0
  158. package/dist/src/graph/narrowing.d.ts.map +1 -1
  159. package/dist/src/graph/narrowing.js +33 -0
  160. package/dist/src/graph/narrowing.js.map +1 -1
  161. package/dist/src/graph/node-search.d.ts +3 -0
  162. package/dist/src/graph/node-search.d.ts.map +1 -1
  163. package/dist/src/graph/node-search.js +37 -4
  164. package/dist/src/graph/node-search.js.map +1 -1
  165. package/dist/src/graph/normalize-id.d.ts +4 -0
  166. package/dist/src/graph/normalize-id.d.ts.map +1 -1
  167. package/dist/src/graph/normalize-id.js +25 -2
  168. package/dist/src/graph/normalize-id.js.map +1 -1
  169. package/dist/src/graph/pagerank.d.ts +7 -0
  170. package/dist/src/graph/pagerank.d.ts.map +1 -1
  171. package/dist/src/graph/pagerank.js +49 -2
  172. package/dist/src/graph/pagerank.js.map +1 -1
  173. package/dist/src/graph/prune.d.ts +31 -0
  174. package/dist/src/graph/prune.d.ts.map +1 -1
  175. package/dist/src/graph/prune.js +65 -0
  176. package/dist/src/graph/prune.js.map +1 -1
  177. package/dist/src/graph/regex-search.d.ts +10 -2
  178. package/dist/src/graph/regex-search.d.ts.map +1 -1
  179. package/dist/src/graph/regex-search.js +33 -4
  180. package/dist/src/graph/regex-search.js.map +1 -1
  181. package/dist/src/graph/regression.d.ts +5 -0
  182. package/dist/src/graph/regression.d.ts.map +1 -1
  183. package/dist/src/graph/regression.js +45 -23
  184. package/dist/src/graph/regression.js.map +1 -1
  185. package/dist/src/graph/ripple-impact.d.ts +27 -0
  186. package/dist/src/graph/ripple-impact.d.ts.map +1 -1
  187. package/dist/src/graph/ripple-impact.js +55 -7
  188. package/dist/src/graph/ripple-impact.js.map +1 -1
  189. package/dist/src/graph/risk-profile.d.ts +7 -0
  190. package/dist/src/graph/risk-profile.d.ts.map +1 -1
  191. package/dist/src/graph/risk-profile.js +82 -48
  192. package/dist/src/graph/risk-profile.js.map +1 -1
  193. package/dist/src/graph/shingle-filter.d.ts.map +1 -1
  194. package/dist/src/graph/shingle-filter.js +25 -26
  195. package/dist/src/graph/shingle-filter.js.map +1 -1
  196. package/dist/src/graph/statistics.d.ts +19 -0
  197. package/dist/src/graph/statistics.d.ts.map +1 -1
  198. package/dist/src/graph/statistics.js +53 -40
  199. package/dist/src/graph/statistics.js.map +1 -1
  200. package/dist/src/graph/stats.d.ts +2 -0
  201. package/dist/src/graph/stats.d.ts.map +1 -1
  202. package/dist/src/graph/stats.js +54 -8
  203. package/dist/src/graph/stats.js.map +1 -1
  204. package/dist/src/graph/subgraph.d.ts +5 -0
  205. package/dist/src/graph/subgraph.d.ts.map +1 -1
  206. package/dist/src/graph/subgraph.js +73 -9
  207. package/dist/src/graph/subgraph.js.map +1 -1
  208. package/dist/src/graph/topo-sort.d.ts +15 -0
  209. package/dist/src/graph/topo-sort.d.ts.map +1 -1
  210. package/dist/src/graph/topo-sort.js +83 -10
  211. package/dist/src/graph/topo-sort.js.map +1 -1
  212. package/dist/src/graph/wcc.d.ts +29 -0
  213. package/dist/src/graph/wcc.d.ts.map +1 -1
  214. package/dist/src/graph/wcc.js +122 -39
  215. package/dist/src/graph/wcc.js.map +1 -1
  216. package/dist/src/index.js +1 -1
  217. package/dist/src/index.js.map +1 -1
  218. package/dist/src/mcp-tools/api-impact.d.ts.map +1 -1
  219. package/dist/src/mcp-tools/api-impact.js +13 -6
  220. package/dist/src/mcp-tools/api-impact.js.map +1 -1
  221. package/dist/src/mcp-tools/context.d.ts.map +1 -1
  222. package/dist/src/mcp-tools/context.js +19 -29
  223. package/dist/src/mcp-tools/context.js.map +1 -1
  224. package/dist/src/mcp-tools/cypher.d.ts +11 -0
  225. package/dist/src/mcp-tools/cypher.d.ts.map +1 -1
  226. package/dist/src/mcp-tools/cypher.js +13 -1
  227. package/dist/src/mcp-tools/cypher.js.map +1 -1
  228. package/dist/src/mcp-tools/detect-changes.d.ts.map +1 -1
  229. package/dist/src/mcp-tools/detect-changes.js +35 -20
  230. package/dist/src/mcp-tools/detect-changes.js.map +1 -1
  231. package/dist/src/mcp-tools/explain.d.ts.map +1 -1
  232. package/dist/src/mcp-tools/explain.js +4 -1
  233. package/dist/src/mcp-tools/explain.js.map +1 -1
  234. package/dist/src/mcp-tools/graph-query.d.ts +2 -0
  235. package/dist/src/mcp-tools/graph-query.d.ts.map +1 -1
  236. package/dist/src/mcp-tools/graph-query.js +20 -3
  237. package/dist/src/mcp-tools/graph-query.js.map +1 -1
  238. package/dist/src/mcp-tools/group-list.d.ts +1 -0
  239. package/dist/src/mcp-tools/group-list.d.ts.map +1 -1
  240. package/dist/src/mcp-tools/group-list.js +7 -9
  241. package/dist/src/mcp-tools/group-list.js.map +1 -1
  242. package/dist/src/mcp-tools/impact.d.ts.map +1 -1
  243. package/dist/src/mcp-tools/impact.js +20 -59
  244. package/dist/src/mcp-tools/impact.js.map +1 -1
  245. package/dist/src/mcp-tools/neighbors.d.ts.map +1 -1
  246. package/dist/src/mcp-tools/neighbors.js +24 -33
  247. package/dist/src/mcp-tools/neighbors.js.map +1 -1
  248. package/dist/src/mcp-tools/query.d.ts +2 -0
  249. package/dist/src/mcp-tools/query.d.ts.map +1 -1
  250. package/dist/src/mcp-tools/query.js +1 -0
  251. package/dist/src/mcp-tools/query.js.map +1 -1
  252. package/dist/src/mcp-tools/rename.d.ts.map +1 -1
  253. package/dist/src/mcp-tools/rename.js +12 -7
  254. package/dist/src/mcp-tools/rename.js.map +1 -1
  255. package/dist/src/mcp-tools/route-map.d.ts.map +1 -1
  256. package/dist/src/mcp-tools/route-map.js +19 -23
  257. package/dist/src/mcp-tools/route-map.js.map +1 -1
  258. package/dist/src/mcp-tools/shape-check.d.ts.map +1 -1
  259. package/dist/src/mcp-tools/shape-check.js +17 -13
  260. package/dist/src/mcp-tools/shape-check.js.map +1 -1
  261. package/dist/src/parsers/loader.js +6 -5
  262. package/dist/src/parsers/loader.js.map +1 -1
  263. package/dist/src/pipeline/cluster-only.d.ts.map +1 -1
  264. package/dist/src/pipeline/cluster-only.js +8 -4
  265. package/dist/src/pipeline/cluster-only.js.map +1 -1
  266. package/dist/src/pipeline/orchestrator.d.ts.map +1 -1
  267. package/dist/src/pipeline/orchestrator.js +19 -0
  268. package/dist/src/pipeline/orchestrator.js.map +1 -1
  269. package/dist/src/pipeline/phases/boundary.d.ts +2 -0
  270. package/dist/src/pipeline/phases/boundary.d.ts.map +1 -1
  271. package/dist/src/pipeline/phases/boundary.js +31 -2
  272. package/dist/src/pipeline/phases/boundary.js.map +1 -1
  273. package/dist/src/pipeline/phases/communities.d.ts +14 -0
  274. package/dist/src/pipeline/phases/communities.d.ts.map +1 -1
  275. package/dist/src/pipeline/phases/communities.js +58 -5
  276. package/dist/src/pipeline/phases/communities.js.map +1 -1
  277. package/dist/src/pipeline/phases/contextual-proximity.d.ts.map +1 -1
  278. package/dist/src/pipeline/phases/contextual-proximity.js +21 -14
  279. package/dist/src/pipeline/phases/contextual-proximity.js.map +1 -1
  280. package/dist/src/pipeline/phases/cross-file.d.ts.map +1 -1
  281. package/dist/src/pipeline/phases/cross-file.js +35 -14
  282. package/dist/src/pipeline/phases/cross-file.js.map +1 -1
  283. package/dist/src/pipeline/phases/field-access.d.ts.map +1 -1
  284. package/dist/src/pipeline/phases/field-access.js +9 -4
  285. package/dist/src/pipeline/phases/field-access.js.map +1 -1
  286. package/dist/src/pipeline/phases/god-nodes.d.ts +4 -0
  287. package/dist/src/pipeline/phases/god-nodes.d.ts.map +1 -1
  288. package/dist/src/pipeline/phases/god-nodes.js +43 -9
  289. package/dist/src/pipeline/phases/god-nodes.js.map +1 -1
  290. package/dist/src/pipeline/phases/import-resolver.d.ts +11 -0
  291. package/dist/src/pipeline/phases/import-resolver.d.ts.map +1 -1
  292. package/dist/src/pipeline/phases/import-resolver.js +34 -7
  293. package/dist/src/pipeline/phases/import-resolver.js.map +1 -1
  294. package/dist/src/pipeline/phases/leiden.d.ts.map +1 -1
  295. package/dist/src/pipeline/phases/leiden.js +27 -19
  296. package/dist/src/pipeline/phases/leiden.js.map +1 -1
  297. package/dist/src/pipeline/phases/markdown.d.ts.map +1 -1
  298. package/dist/src/pipeline/phases/markdown.js +10 -5
  299. package/dist/src/pipeline/phases/markdown.js.map +1 -1
  300. package/dist/src/pipeline/phases/pdf-parse.d.ts.map +1 -1
  301. package/dist/src/pipeline/phases/pdf-parse.js +18 -3
  302. package/dist/src/pipeline/phases/pdf-parse.js.map +1 -1
  303. package/dist/src/pipeline/phases/processes.d.ts.map +1 -1
  304. package/dist/src/pipeline/phases/processes.js +26 -9
  305. package/dist/src/pipeline/phases/processes.js.map +1 -1
  306. package/dist/src/pipeline/phases/re-export-propagation.d.ts.map +1 -1
  307. package/dist/src/pipeline/phases/re-export-propagation.js +33 -14
  308. package/dist/src/pipeline/phases/re-export-propagation.js.map +1 -1
  309. package/dist/src/pipeline/phases/reachability.d.ts.map +1 -1
  310. package/dist/src/pipeline/phases/reachability.js +42 -26
  311. package/dist/src/pipeline/phases/reachability.js.map +1 -1
  312. package/dist/src/pipeline/phases/routes.d.ts.map +1 -1
  313. package/dist/src/pipeline/phases/routes.js +30 -21
  314. package/dist/src/pipeline/phases/routes.js.map +1 -1
  315. package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
  316. package/dist/src/pipeline/phases/scope-resolution.js +93 -63
  317. package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
  318. package/dist/src/pipeline/runner.d.ts +4 -0
  319. package/dist/src/pipeline/runner.d.ts.map +1 -1
  320. package/dist/src/pipeline/runner.js +30 -11
  321. package/dist/src/pipeline/runner.js.map +1 -1
  322. package/dist/src/query/cypher-parser.d.ts +20 -0
  323. package/dist/src/query/cypher-parser.d.ts.map +1 -1
  324. package/dist/src/query/cypher-parser.js +73 -0
  325. package/dist/src/query/cypher-parser.js.map +1 -1
  326. package/dist/src/search/embed-batch.d.ts.map +1 -1
  327. package/dist/src/search/embed-batch.js +4 -1
  328. package/dist/src/search/embed-batch.js.map +1 -1
  329. package/dist/src/search/hybrid-query.d.ts +4 -0
  330. package/dist/src/search/hybrid-query.d.ts.map +1 -1
  331. package/dist/src/search/hybrid-query.js +5 -1
  332. package/dist/src/search/hybrid-query.js.map +1 -1
  333. package/dist/src/staleness/git-staleness.js +3 -3
  334. package/dist/src/staleness/git-staleness.js.map +1 -1
  335. package/dist/src/storage/db.d.ts.map +1 -1
  336. package/dist/src/storage/db.js +12 -0
  337. package/dist/src/storage/db.js.map +1 -1
  338. package/dist/src/storage/edge-store.d.ts +2 -0
  339. package/dist/src/storage/edge-store.d.ts.map +1 -1
  340. package/dist/src/storage/edge-store.js +26 -1
  341. package/dist/src/storage/edge-store.js.map +1 -1
  342. package/dist/src/storage/embedding-store.d.ts +16 -0
  343. package/dist/src/storage/embedding-store.d.ts.map +1 -1
  344. package/dist/src/storage/embedding-store.js +26 -2
  345. package/dist/src/storage/embedding-store.js.map +1 -1
  346. package/dist/src/storage/file-cache.d.ts +5 -0
  347. package/dist/src/storage/file-cache.d.ts.map +1 -1
  348. package/dist/src/storage/file-cache.js +35 -9
  349. package/dist/src/storage/file-cache.js.map +1 -1
  350. package/dist/src/storage/fts-store.d.ts +4 -0
  351. package/dist/src/storage/fts-store.d.ts.map +1 -1
  352. package/dist/src/storage/fts-store.js +8 -3
  353. package/dist/src/storage/fts-store.js.map +1 -1
  354. package/dist/src/storage/node-store.d.ts +2 -0
  355. package/dist/src/storage/node-store.d.ts.map +1 -1
  356. package/dist/src/storage/node-store.js +28 -1
  357. package/dist/src/storage/node-store.js.map +1 -1
  358. package/dist/tsconfig.tsbuildinfo +1 -1
  359. package/package.json +1 -1
  360. package/src/__tests__/pipeline/runner.parallel.test.ts +2 -2
  361. package/src/analysis/churn-cache.ts +18 -7
  362. package/src/analysis/churn-trend.ts +35 -6
  363. package/src/analysis/cluster-quality.ts +20 -11
  364. package/src/analysis/coverage-gaps.ts +44 -1
  365. package/src/analysis/dep-classification.ts +34 -0
  366. package/src/analysis/dependency-health.ts +25 -0
  367. package/src/analysis/duplicate-exports.ts +40 -4
  368. package/src/analysis/feature-flags.ts +37 -17
  369. package/src/analysis/node-similarity.ts +45 -4
  370. package/src/analysis/private-type-leaks.ts +49 -2
  371. package/src/analysis/project-state.ts +19 -9
  372. package/src/analysis/suppressions.ts +4 -1
  373. package/src/analysis/trace.ts +73 -54
  374. package/src/analysis/unused-class-members.ts +8 -1
  375. package/src/analysis/worker-pool.ts +40 -8
  376. package/src/cli/augment.ts +10 -3
  377. package/src/export/sarif.ts +5 -5
  378. package/src/graph/analyzer.ts +26 -2
  379. package/src/graph/author-analytics.ts +45 -5
  380. package/src/graph/baseline.ts +78 -24
  381. package/src/graph/clone-detector.ts +22 -36
  382. package/src/graph/clone-families.ts +34 -10
  383. package/src/graph/codeowners-extended.ts +54 -0
  384. package/src/graph/codeowners-gitlab.ts +46 -0
  385. package/src/graph/codeowners.ts +33 -2
  386. package/src/graph/complexity.ts +102 -47
  387. package/src/graph/cross-reference-findings.ts +23 -2
  388. package/src/graph/cross-reference.ts +26 -0
  389. package/src/graph/cycle-detection.ts +22 -3
  390. package/src/graph/cycles.ts +155 -96
  391. package/src/graph/dead-code.ts +76 -17
  392. package/src/graph/dependency-closure.ts +24 -7
  393. package/src/graph/dfs.ts +78 -0
  394. package/src/graph/duplication-stats.ts +17 -6
  395. package/src/graph/edge-filter.ts +26 -12
  396. package/src/graph/health-score.ts +47 -17
  397. package/src/graph/hotspots.ts +57 -8
  398. package/src/graph/import-chain.ts +89 -19
  399. package/src/graph/loader.ts +10 -7
  400. package/src/graph/maintainability.ts +72 -22
  401. package/src/graph/mirrored-dirs.ts +45 -14
  402. package/src/graph/narrowing.ts +44 -0
  403. package/src/graph/node-search.ts +49 -4
  404. package/src/graph/normalize-id.ts +26 -2
  405. package/src/graph/pagerank.ts +69 -2
  406. package/src/graph/prune.ts +76 -0
  407. package/src/graph/regex-search.ts +38 -4
  408. package/src/graph/regression.ts +48 -23
  409. package/src/graph/ripple-impact.ts +71 -11
  410. package/src/graph/risk-profile.ts +98 -53
  411. package/src/graph/shingle-filter.ts +33 -27
  412. package/src/graph/statistics.ts +73 -45
  413. package/src/graph/stats.ts +50 -16
  414. package/src/graph/subgraph.ts +96 -25
  415. package/src/graph/topo-sort.ts +101 -16
  416. package/src/graph/wcc.ts +146 -37
  417. package/src/index.ts +1 -1
  418. package/src/mcp-tools/api-impact.ts +13 -6
  419. package/src/mcp-tools/context.ts +31 -40
  420. package/src/mcp-tools/cypher.ts +14 -1
  421. package/src/mcp-tools/detect-changes.ts +38 -23
  422. package/src/mcp-tools/explain.ts +4 -1
  423. package/src/mcp-tools/graph-query.ts +24 -3
  424. package/src/mcp-tools/group-list.ts +9 -11
  425. package/src/mcp-tools/impact.ts +26 -66
  426. package/src/mcp-tools/neighbors.ts +34 -39
  427. package/src/mcp-tools/query.ts +3 -0
  428. package/src/mcp-tools/rename.ts +9 -8
  429. package/src/mcp-tools/route-map.ts +21 -23
  430. package/src/mcp-tools/shape-check.ts +18 -14
  431. package/src/parsers/loader.ts +5 -5
  432. package/src/pipeline/cluster-only.ts +8 -4
  433. package/src/pipeline/orchestrator.ts +22 -0
  434. package/src/pipeline/phases/boundary.ts +40 -2
  435. package/src/pipeline/phases/communities.ts +66 -5
  436. package/src/pipeline/phases/contextual-proximity.ts +15 -14
  437. package/src/pipeline/phases/cross-file.ts +35 -14
  438. package/src/pipeline/phases/field-access.ts +11 -4
  439. package/src/pipeline/phases/god-nodes.ts +47 -10
  440. package/src/pipeline/phases/import-resolver.ts +37 -5
  441. package/src/pipeline/phases/leiden.ts +25 -18
  442. package/src/pipeline/phases/markdown.ts +12 -7
  443. package/src/pipeline/phases/pdf-parse.ts +19 -2
  444. package/src/pipeline/phases/processes.ts +27 -15
  445. package/src/pipeline/phases/re-export-propagation.ts +40 -18
  446. package/src/pipeline/phases/reachability.ts +39 -24
  447. package/src/pipeline/phases/routes.ts +32 -21
  448. package/src/pipeline/phases/scope-resolution.ts +110 -71
  449. package/src/pipeline/runner.ts +28 -11
  450. package/src/query/cypher-parser.ts +83 -0
  451. package/src/search/embed-batch.ts +4 -1
  452. package/src/search/hybrid-query.ts +11 -1
  453. package/src/staleness/git-staleness.ts +3 -3
  454. package/src/storage/db.ts +11 -0
  455. package/src/storage/edge-store.ts +34 -1
  456. package/src/storage/embedding-store.ts +38 -6
  457. package/src/storage/file-cache.ts +35 -21
  458. package/src/storage/fts-store.ts +12 -3
  459. package/src/storage/node-store.ts +39 -1
  460. package/src/coverage/runtime.ts +0 -249
package/src/graph/wcc.ts CHANGED
@@ -20,59 +20,168 @@ export function weaklyConnectedComponents(db: MonographDb): string[][] {
20
20
  if (nodeRows.length === 0) return [];
21
21
 
22
22
  const nodes = nodeRows.map(r => r.id);
23
+ const { parent, rank } = buildUnionFind(nodes);
23
24
 
24
- // Union-Find
25
+ for (const { source_id: src, target_id: tgt } of edgeRows) {
26
+ if (src === tgt) continue;
27
+ if (!parent.has(src) || !parent.has(tgt)) continue;
28
+ ufUnion(parent, rank, src, tgt);
29
+ }
30
+
31
+ return groupByRoot(nodes, parent);
32
+ }
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Summary stats — avoids name resolution and component array materialisation
36
+ // ---------------------------------------------------------------------------
37
+
38
+ export interface WccStats {
39
+ /** Total number of weakly connected components */
40
+ componentCount: number;
41
+ /** Size of the largest component */
42
+ largestSize: number;
43
+ /** Size of the smallest component */
44
+ smallestSize: number;
45
+ /** Mean component size */
46
+ meanSize: number;
47
+ /** Number of isolated nodes (component size = 1) */
48
+ isolatedNodeCount: number;
49
+ }
50
+
51
+ /**
52
+ * Lightweight summary statistics for all WCCs.
53
+ * Does not materialise component arrays — returns aggregate numbers only.
54
+ */
55
+ export function wccStats(db: MonographDb): WccStats {
56
+ const components = weaklyConnectedComponents(db);
57
+ if (components.length === 0) {
58
+ return { componentCount: 0, largestSize: 0, smallestSize: 0, meanSize: 0, isolatedNodeCount: 0 };
59
+ }
60
+
61
+ const sizes = components.map(c => c.length);
62
+ const total = sizes.reduce((a, b) => a + b, 0);
63
+ return {
64
+ componentCount: sizes.length,
65
+ largestSize: sizes[0], // already sorted descending
66
+ smallestSize: sizes[sizes.length - 1],
67
+ meanSize: Math.round((total / sizes.length) * 100) / 100,
68
+ isolatedNodeCount: sizes.filter(s => s === 1).length,
69
+ };
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Structured text formatter for LLM consumption
74
+ // ---------------------------------------------------------------------------
75
+
76
+ /**
77
+ * Format WCC results as structured text for LLM consumption.
78
+ *
79
+ * Resolves node IDs to names and file paths for the top-N largest components.
80
+ * Small/isolated components are summarised in aggregate to avoid token waste.
81
+ *
82
+ * @param db - The MonographDb instance (for name resolution)
83
+ * @param components - Result of weaklyConnectedComponents()
84
+ * @param topN - Number of largest components to detail (default: 5)
85
+ * @returns Structured text string suitable for LLM context injection
86
+ */
87
+ export function formatWcc(db: MonographDb, components: string[][], topN = 5): string {
88
+ if (components.length === 0) {
89
+ return 'Weakly connected components: no nodes found.';
90
+ }
91
+
92
+ const isolatedCount = components.filter(c => c.length === 1).length;
93
+ const nonTrivial = components.filter(c => c.length > 1);
94
+ const display = nonTrivial.slice(0, topN);
95
+
96
+ // Batch-resolve all node IDs in the displayed components
97
+ const allIds = display.flat();
98
+ const CHUNK = 200;
99
+ const nodeInfo = new Map<string, { name: string; filePath: string | null }>();
100
+
101
+ for (let i = 0; i < allIds.length; i += CHUNK) {
102
+ const chunk = allIds.slice(i, i + CHUNK);
103
+ const ph = chunk.map(() => '?').join(',');
104
+ const rows = db
105
+ .prepare(`SELECT id, name, file_path FROM nodes WHERE id IN (${ph})`)
106
+ .all(...chunk) as { id: string; name: string; file_path: string | null }[];
107
+ for (const row of rows) nodeInfo.set(row.id, { name: row.name ?? row.id, filePath: row.file_path });
108
+ }
109
+
110
+ const lines: string[] = [
111
+ `Weakly connected components: ${components.length} total` +
112
+ (isolatedCount > 0 ? ` (${isolatedCount} isolated node${isolatedCount === 1 ? '' : 's'})` : ''),
113
+ '',
114
+ ];
115
+
116
+ for (let ci = 0; ci < display.length; ci++) {
117
+ const comp = display[ci];
118
+ lines.push(`Component ${ci + 1} (${comp.length} nodes):`);
119
+ for (const id of comp) {
120
+ const info = nodeInfo.get(id);
121
+ const name = info?.name ?? id;
122
+ const fp = info?.filePath;
123
+ lines.push(` ${name}${fp ? ` — ${fp}` : ''}`);
124
+ }
125
+ if (ci < display.length - 1) lines.push('');
126
+ }
127
+
128
+ if (nonTrivial.length > topN) {
129
+ lines.push('');
130
+ lines.push(`... (${nonTrivial.length - topN} more non-trivial component${nonTrivial.length - topN === 1 ? '' : 's'} omitted)`);
131
+ }
132
+
133
+ return lines.join('\n');
134
+ }
135
+
136
+ // ---------------------------------------------------------------------------
137
+ // Internal union-find helpers
138
+ // ---------------------------------------------------------------------------
139
+
140
+ function buildUnionFind(nodes: string[]): { parent: Map<string, string>; rank: Map<string, number> } {
25
141
  const parent = new Map<string, string>();
26
142
  const rank = new Map<string, number>();
27
-
28
143
  for (const n of nodes) {
29
144
  parent.set(n, n);
30
145
  rank.set(n, 0);
31
146
  }
147
+ return { parent, rank };
148
+ }
32
149
 
33
- function find(x: string): string {
34
- let root = x;
35
- while (parent.get(root) !== root) root = parent.get(root)!;
36
- // Path compression
37
- let cur = x;
38
- while (cur !== root) {
39
- const next = parent.get(cur)!;
40
- parent.set(cur, root);
41
- cur = next;
42
- }
43
- return root;
44
- }
45
-
46
- function union(a: string, b: string): void {
47
- const ra = find(a);
48
- const rb = find(b);
49
- if (ra === rb) return;
50
- const rankA = rank.get(ra) ?? 0;
51
- const rankB = rank.get(rb) ?? 0;
52
- if (rankA < rankB) {
53
- parent.set(ra, rb);
54
- } else if (rankA > rankB) {
55
- parent.set(rb, ra);
56
- } else {
57
- parent.set(rb, ra);
58
- rank.set(ra, rankA + 1);
59
- }
150
+ function ufFind(parent: Map<string, string>, x: string): string {
151
+ let root = x;
152
+ while (parent.get(root) !== root) root = parent.get(root)!;
153
+ // Path compression
154
+ let cur = x;
155
+ while (cur !== root) {
156
+ const next = parent.get(cur)!;
157
+ parent.set(cur, root);
158
+ cur = next;
60
159
  }
160
+ return root;
161
+ }
61
162
 
62
- for (const { source_id: src, target_id: tgt } of edgeRows) {
63
- if (src === tgt) continue;
64
- if (!parent.has(src) || !parent.has(tgt)) continue;
65
- union(src, tgt);
163
+ function ufUnion(parent: Map<string, string>, rank: Map<string, number>, a: string, b: string): void {
164
+ const ra = ufFind(parent, a);
165
+ const rb = ufFind(parent, b);
166
+ if (ra === rb) return;
167
+ const rankA = rank.get(ra) ?? 0;
168
+ const rankB = rank.get(rb) ?? 0;
169
+ if (rankA < rankB) {
170
+ parent.set(ra, rb);
171
+ } else if (rankA > rankB) {
172
+ parent.set(rb, ra);
173
+ } else {
174
+ parent.set(rb, ra);
175
+ rank.set(ra, rankA + 1);
66
176
  }
177
+ }
67
178
 
68
- // Group nodes by root
179
+ function groupByRoot(nodes: string[], parent: Map<string, string>): string[][] {
69
180
  const components = new Map<string, string[]>();
70
181
  for (const n of nodes) {
71
- const root = find(n);
182
+ const root = ufFind(parent, n);
72
183
  if (!components.has(root)) components.set(root, []);
73
184
  components.get(root)!.push(n);
74
185
  }
75
-
76
- // Return sorted by size descending
77
186
  return [...components.values()].sort((a, b) => b.length - a.length);
78
187
  }
package/src/index.ts CHANGED
@@ -32,7 +32,7 @@ export * from './groups/group-sync.js';
32
32
  export * from './mcp-tools/group-query.js';
33
33
  export * from './mcp-tools/group-list.js';
34
34
  export * from './mcp-tools/group-sync.js';
35
- // wiki/wiki-store.js re-exported via mcp-tools/wiki.js to avoid WikiPage duplicate
35
+ // wiki/wiki-store.js intentionally not re-exported: its WikiPage conflicts with mcp-tools/wiki.ts's WikiPage
36
36
  export * from './wiki/prompt-builder.js';
37
37
  export * from './wiki/wiki-generator.js';
38
38
  export * from './wiki/gist-publisher.js';
@@ -134,15 +134,22 @@ export function getMonographApiImpact(
134
134
  // 3. Forward BFS on CALLS edges from handler
135
135
  const bfsResults = forwardBfs(handler.id, db, MAX_DEPTH);
136
136
 
137
- // 4. Resolve node details for each callee
137
+ // 4. Resolve node details for all callees in a single batch query (avoids N+1).
138
138
  const callees: Array<{ depth: number; node: MonographNode }> = [];
139
- for (const { depth, nodeId } of bfsResults) {
140
- const nodeRow = db
141
- .prepare('SELECT * FROM nodes WHERE id = ?')
142
- .get(nodeId) as Record<string, unknown> | undefined;
143
- if (nodeRow) {
139
+ if (bfsResults.length > 0) {
140
+ const calleeIds = bfsResults.map((b) => b.nodeId);
141
+ const depthByNodeId = new Map(bfsResults.map((b) => [b.nodeId, b.depth]));
142
+ const placeholders = calleeIds.map(() => '?').join(',');
143
+ const nodeRows = db
144
+ .prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)
145
+ .all(...calleeIds) as Record<string, unknown>[];
146
+ for (const nodeRow of nodeRows) {
147
+ const nodeId = nodeRow.id as string;
148
+ const depth = depthByNodeId.get(nodeId) ?? 0;
144
149
  callees.push({ depth, node: rowToNode(nodeRow) });
145
150
  }
151
+ // Restore BFS order (sort by depth ascending)
152
+ callees.sort((a, b) => a.depth - b.depth);
146
153
  }
147
154
 
148
155
  // 5. Find processes that include the handler or any callee via STEP_IN_PROCESS
@@ -31,6 +31,31 @@ export interface MonographContextResult {
31
31
  inProcesses: Array<{ id: string; name: string }>;
32
32
  }
33
33
 
34
+ // ── Shared helper: query related nodes by edge relation and direction ──────────
35
+
36
+ function queryRelated(
37
+ db: Database.Database,
38
+ nodeId: string,
39
+ relation: string,
40
+ inbound: boolean,
41
+ limit = 50,
42
+ ): MonographNode[] {
43
+ // inbound=true → this node is the target; source nodes are the result
44
+ // inbound=false → this node is the source; target nodes are the result
45
+ const [filterCol, joinCol] = inbound
46
+ ? ['target_id', 'source_id']
47
+ : ['source_id', 'target_id'];
48
+
49
+ const rows = db
50
+ .prepare(
51
+ `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.${joinCol}
52
+ WHERE e.${filterCol} = ? AND e.relation = ? LIMIT ?`,
53
+ )
54
+ .all(nodeId, relation, limit) as Record<string, unknown>[];
55
+
56
+ return rows.map(rowToNode);
57
+ }
58
+
34
59
  // ── Implementation ─────────────────────────────────────────────────────────────
35
60
 
36
61
  export function getMonographContext(
@@ -58,37 +83,11 @@ export function getMonographContext(
58
83
  const node = rowToNode(nodeRow);
59
84
  const nodeId = node.id;
60
85
 
61
- // 2. Callers: nodes that CALL this node (inbound CALLS edges)
62
- const callerRows = db
63
- .prepare(
64
- `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.source_id
65
- WHERE e.target_id = ? AND e.relation = 'CALLS' LIMIT ?`,
66
- )
67
- .all(nodeId, LIMIT) as Record<string, unknown>[];
68
-
69
- // 3. Callees: nodes this node CALLS (outbound CALLS edges)
70
- const calleeRows = db
71
- .prepare(
72
- `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id
73
- WHERE e.source_id = ? AND e.relation = 'CALLS' LIMIT ?`,
74
- )
75
- .all(nodeId, LIMIT) as Record<string, unknown>[];
76
-
77
- // 4. Imports: what this node imports (outbound IMPORTS edges)
78
- const importRows = db
79
- .prepare(
80
- `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id
81
- WHERE e.source_id = ? AND e.relation = 'IMPORTS' LIMIT ?`,
82
- )
83
- .all(nodeId, LIMIT) as Record<string, unknown>[];
84
-
85
- // 5. ImportedBy: what imports this node (inbound IMPORTS edges)
86
- const importedByRows = db
87
- .prepare(
88
- `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.source_id
89
- WHERE e.target_id = ? AND e.relation = 'IMPORTS' LIMIT ?`,
90
- )
91
- .all(nodeId, LIMIT) as Record<string, unknown>[];
86
+ // 2–5. Callers / callees / imports / importedBy via shared helper
87
+ const callers = queryRelated(db, nodeId, 'CALLS', true, LIMIT);
88
+ const callees = queryRelated(db, nodeId, 'CALLS', false, LIMIT);
89
+ const imports = queryRelated(db, nodeId, 'IMPORTS', false, LIMIT);
90
+ const importedBy = queryRelated(db, nodeId, 'IMPORTS', true, LIMIT);
92
91
 
93
92
  // 6. Community: from node's community_id field
94
93
  let community: { id: number; label?: string } | null = null;
@@ -108,13 +107,5 @@ export function getMonographContext(
108
107
  )
109
108
  .all(nodeId, LIMIT) as Array<{ id: string; name: string }>;
110
109
 
111
- return {
112
- node,
113
- callers: callerRows.map(rowToNode),
114
- callees: calleeRows.map(rowToNode),
115
- imports: importRows.map(rowToNode),
116
- importedBy: importedByRows.map(rowToNode),
117
- community,
118
- inProcesses: processRows,
119
- };
110
+ return { node, callers, callees, imports, importedBy, community, inProcesses: processRows };
120
111
  }
@@ -1,8 +1,9 @@
1
1
  import type Database from 'better-sqlite3';
2
- import { executeCypherQuery } from '../query/cypher-parser.js';
2
+ import { executeCypherQuery, formatCypherResult } from '../query/cypher-parser.js';
3
3
  import type { CypherResult } from '../query/cypher-parser.js';
4
4
 
5
5
  export type { CypherResult };
6
+ export { formatCypherResult };
6
7
 
7
8
  /**
8
9
  * Execute a restricted read-only Cypher-style query against the Monograph graph.
@@ -14,3 +15,15 @@ export type { CypherResult };
14
15
  export function getMonographCypher(db: Database.Database, query: string): CypherResult {
15
16
  return executeCypherQuery(db, query);
16
17
  }
18
+
19
+ /**
20
+ * Execute a Cypher-style query and return structured text for LLM consumption.
21
+ * Rows with filePath + startLine fields are rendered as "file:line" navigation hints.
22
+ *
23
+ * @param db - Open better-sqlite3 database handle
24
+ * @param query - Cypher MATCH query string
25
+ * @returns Formatted string suitable for direct injection into LLM context
26
+ */
27
+ export function getMonographCypherText(db: Database.Database, query: string): string {
28
+ return formatCypherResult(executeCypherQuery(db, query));
29
+ }
@@ -13,6 +13,9 @@ export interface MonographDetectChangesResult {
13
13
 
14
14
  // ── Implementation ─────────────────────────────────────────────────────────────
15
15
 
16
+ const MAX_FILES = 200; // cap on changed-file count to prevent unbounded IN clauses
17
+ const MAX_STDOUT = 1_048_576; // 1 MiB stdout cap for git output
18
+
16
19
  export function detectMonographChanges(
17
20
  db: Database.Database,
18
21
  input: { baseBranch?: string; includeTests?: boolean },
@@ -33,15 +36,17 @@ export function detectMonographChanges(
33
36
  const result = spawnSync('git', ['diff', '--name-only', `${baseBranch}...HEAD`], {
34
37
  cwd: repoPath,
35
38
  encoding: 'utf-8',
39
+ maxBuffer: MAX_STDOUT,
36
40
  });
37
41
  if (result.error) throw result.error;
38
42
  if (result.status !== 0) throw new Error(result.stderr ?? `git exited ${result.status}`);
39
- const output = result.stdout.trim();
43
+ const output = (result.stdout ?? '').slice(0, MAX_STDOUT).trim();
40
44
 
41
45
  changedFiles = output
42
46
  .split('\n')
43
47
  .map(f => f.trim())
44
- .filter(f => f.length > 0);
48
+ .filter(f => f.length > 0)
49
+ .slice(0, MAX_FILES); // cap to prevent unbounded IN clauses
45
50
  } catch (err) {
46
51
  const message = err instanceof Error ? err.message : String(err);
47
52
  return { changedFiles: [], affectedSymbols: [], affectedProcesses: [], error: message };
@@ -56,22 +61,25 @@ export function detectMonographChanges(
56
61
  return { changedFiles: [], affectedSymbols: [], affectedProcesses: [] };
57
62
  }
58
63
 
59
- // Query DB for symbols in changed files
64
+ // Query DB for symbols in changed files (batch if needed)
60
65
  let affectedSymbols: Array<{ name: string; filePath: string; label: string }> = [];
61
66
  let affectedSymbolIds: string[] = [];
62
67
 
63
68
  try {
64
- const placeholders = changedFiles.map(() => '?').join(',');
65
- const rows = db
66
- .prepare(`SELECT id, name, file_path, label FROM nodes WHERE file_path IN (${placeholders})`)
67
- .all(...changedFiles) as Array<{ id: string; name: string; file_path: string; label: string }>;
68
-
69
- affectedSymbols = rows.map(r => ({
70
- name: r.name,
71
- filePath: r.file_path,
72
- label: r.label,
73
- }));
74
- affectedSymbolIds = rows.map(r => r.id);
69
+ // Process in batches of 50 to keep SQLite IN clause size bounded
70
+ const BATCH = 50;
71
+ const allRows: Array<{ id: string; name: string; file_path: string; label: string }> = [];
72
+ for (let i = 0; i < changedFiles.length; i += BATCH) {
73
+ const batch = changedFiles.slice(i, i + BATCH);
74
+ const placeholders = batch.map(() => '?').join(',');
75
+ const rows = db
76
+ .prepare(`SELECT id, name, file_path, label FROM nodes WHERE file_path IN (${placeholders})`)
77
+ .all(...batch) as Array<{ id: string; name: string; file_path: string; label: string }>;
78
+ allRows.push(...rows);
79
+ }
80
+
81
+ affectedSymbols = allRows.map(r => ({ name: r.name, filePath: r.file_path, label: r.label }));
82
+ affectedSymbolIds = allRows.map(r => r.id);
75
83
  } catch (err) {
76
84
  const message = err instanceof Error ? err.message : String(err);
77
85
  return { changedFiles, affectedSymbols: [], affectedProcesses: [], error: message };
@@ -83,15 +91,22 @@ export function detectMonographChanges(
83
91
 
84
92
  if (affectedSymbolIds.length > 0) {
85
93
  try {
86
- const placeholders = affectedSymbolIds.map(() => '?').join(',');
87
- const processRows = db
88
- .prepare(
89
- `SELECT DISTINCT n.id, n.name
90
- FROM nodes n JOIN edges e ON n.id = e.source_id
91
- WHERE e.relation = 'STEP_IN_PROCESS' AND e.target_id IN (${placeholders})`,
92
- )
93
- .all(...affectedSymbolIds) as Array<{ id: string; name: string }>;
94
- affectedProcesses = processRows;
94
+ // Also batch symbol IDs to keep IN clause bounded
95
+ const BATCH = 50;
96
+ const processSet = new Map<string, string>();
97
+ for (let i = 0; i < affectedSymbolIds.length; i += BATCH) {
98
+ const batch = affectedSymbolIds.slice(i, i + BATCH);
99
+ const placeholders = batch.map(() => '?').join(',');
100
+ const processRows = db
101
+ .prepare(
102
+ `SELECT DISTINCT n.id, n.name
103
+ FROM nodes n JOIN edges e ON n.id = e.source_id
104
+ WHERE e.relation = 'STEP_IN_PROCESS' AND e.target_id IN (${placeholders})`,
105
+ )
106
+ .all(...batch) as Array<{ id: string; name: string }>;
107
+ for (const row of processRows) processSet.set(row.id, row.name);
108
+ }
109
+ affectedProcesses = Array.from(processSet.entries()).map(([id, name]) => ({ id, name }));
95
110
  } catch {
96
111
  // Non-fatal: return what we have
97
112
  }
@@ -40,7 +40,10 @@ export function explainNode(db: Database.Database, name: string): ExplainResult
40
40
  ).all(node.id) as { name: string; relation: string }[];
41
41
 
42
42
  const connectionCount = outRows.length + inRows.length;
43
- const fileLine = node.filePath ? ` defined in \`${node.filePath}\`` : '';
43
+ const fileRef = node.filePath
44
+ ? (node.startLine != null ? `${node.filePath}:${node.startLine}` : node.filePath)
45
+ : null;
46
+ const fileLine = fileRef ? ` defined in \`${fileRef}\`` : '';
44
47
 
45
48
  let explanation = `**${node.name}** is a ${node.label}${fileLine}.`;
46
49
 
@@ -20,6 +20,8 @@ function rowToNode(row: Record<string, unknown>): MonographNode {
20
20
  export interface GraphQueryInput {
21
21
  query: string;
22
22
  mode?: 'bfs' | 'dfs';
23
+ /** Direction of edge traversal. 'both' includes incoming edges (callers) and outgoing (callees). Default: 'out' */
24
+ direction?: 'out' | 'in' | 'both';
23
25
  tokenBudget?: number;
24
26
  depth?: number;
25
27
  }
@@ -40,6 +42,7 @@ export function queryGraph(
40
42
  input: GraphQueryInput,
41
43
  ): GraphQueryResult {
42
44
  const mode = input.mode ?? 'bfs';
45
+ const direction = input.direction ?? 'out';
43
46
  const tokenBudget = input.tokenBudget ?? 2000;
44
47
  const maxDepth = input.depth ?? 3;
45
48
 
@@ -64,15 +67,33 @@ export function queryGraph(
64
67
  if (!truncated) {
65
68
  // BFS or DFS expansion
66
69
  const frontier: Array<{ id: string; depth: number }> = seeds.map(s => ({ id: s.id, depth: 0 }));
67
- const edgeStmt = db.prepare(
70
+
71
+ // Prepare edge traversal statements for each direction.
72
+ // 'out' follows outgoing edges (what this node calls/imports/uses).
73
+ // 'in' follows incoming edges (what calls/imports/uses this node).
74
+ // 'both' follows both directions — needed for full context (callers + callees).
75
+ const outEdgeStmt = db.prepare(
68
76
  `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id WHERE e.source_id = ? LIMIT 20`
69
77
  );
78
+ const inEdgeStmt = db.prepare(
79
+ `SELECT n.* FROM nodes n JOIN edges e ON n.id = e.source_id WHERE e.target_id = ? LIMIT 20`
80
+ );
81
+ const getNeighbors = (id: string): Record<string, unknown>[] => {
82
+ if (direction === 'out') return outEdgeStmt.all(id) as Record<string, unknown>[];
83
+ if (direction === 'in') return inEdgeStmt.all(id) as Record<string, unknown>[];
84
+ // 'both': union of outgoing and incoming, deduplicated by id
85
+ const outRows = outEdgeStmt.all(id) as Record<string, unknown>[];
86
+ const inRows = inEdgeStmt.all(id) as Record<string, unknown>[];
87
+ const seen = new Set(outRows.map(r => r['id'] as string));
88
+ return [...outRows, ...inRows.filter(r => !seen.has(r['id'] as string))];
89
+ };
90
+
70
91
 
71
92
  if (mode === 'bfs') {
72
93
  while (frontier.length > 0 && !truncated) {
73
94
  const { id, depth } = frontier.shift()!;
74
95
  if (depth >= maxDepth) continue;
75
- const neighbors = edgeStmt.all(id) as Record<string, unknown>[];
96
+ const neighbors = getNeighbors(id);
76
97
  for (const row of neighbors) {
77
98
  const node = rowToNode(row);
78
99
  if (visited.has(node.id)) continue;
@@ -89,7 +110,7 @@ export function queryGraph(
89
110
  while (stack.length > 0 && !truncated) {
90
111
  const { id, depth } = stack.pop()!;
91
112
  if (depth >= maxDepth) continue;
92
- const neighbors = edgeStmt.all(id) as Record<string, unknown>[];
113
+ const neighbors = getNeighbors(id);
93
114
  for (let i = neighbors.length - 1; i >= 0; i--) {
94
115
  const node = rowToNode(neighbors[i]!);
95
116
  if (visited.has(node.id)) continue;
@@ -14,6 +14,7 @@ export interface GroupRepoInfo {
14
14
  path: string;
15
15
  indexedAt: string | null;
16
16
  nodeCount: number;
17
+ error?: string;
17
18
  }
18
19
 
19
20
  export interface GroupListResult {
@@ -29,13 +30,12 @@ function getRepoDbPath(repoPath: string): string {
29
30
  return join(repoPath, '.monomind', 'monograph.db');
30
31
  }
31
32
 
32
- function readRepStats(
33
- repoName: string,
33
+ function readRepoStats(
34
34
  repoPath: string,
35
- ): { indexedAt: string | null; nodeCount: number } {
35
+ ): { indexedAt: string | null; nodeCount: number; error?: string } {
36
36
  const dbPath = getRepoDbPath(repoPath);
37
37
  if (!existsSync(dbPath)) {
38
- return { indexedAt: null, nodeCount: 0 };
38
+ return { indexedAt: null, nodeCount: 0, error: 'index not found' };
39
39
  }
40
40
 
41
41
  let db: Database.Database | null = null;
@@ -48,13 +48,10 @@ function readRepStats(
48
48
 
49
49
  const countRow = db.prepare(`SELECT COUNT(*) as cnt FROM nodes`).get() as { cnt: number };
50
50
 
51
- return {
52
- indexedAt: metaRow?.value ?? null,
53
- nodeCount: countRow.cnt,
54
- };
51
+ return { indexedAt: metaRow?.value ?? null, nodeCount: countRow.cnt };
55
52
  } catch (err) {
56
- console.warn(`[group-list] Error reading repo "${repoName}": ${err}`);
57
- return { indexedAt: null, nodeCount: 0 };
53
+ const message = err instanceof Error ? err.message : String(err);
54
+ return { indexedAt: null, nodeCount: 0, error: message };
58
55
  } finally {
59
56
  db?.close();
60
57
  }
@@ -70,12 +67,13 @@ export async function getGroupList(configPath?: string): Promise<GroupListResult
70
67
  const config = parseGroupConfig(resolvedPath);
71
68
 
72
69
  const repos: GroupRepoInfo[] = config.repos.map((repo) => {
73
- const { indexedAt, nodeCount } = readRepStats(repo.name, repo.path);
70
+ const { indexedAt, nodeCount, error } = readRepoStats(repo.path);
74
71
  return {
75
72
  name: repo.name,
76
73
  path: repo.path,
77
74
  indexedAt,
78
75
  nodeCount,
76
+ ...(error ? { error } : {}),
79
77
  };
80
78
  });
81
79