@monoes/monograph 1.2.0 → 1.2.2

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 (469) hide show
  1. package/.monomind/loops/mastermind-review-1782162423447.json +16 -0
  2. package/__tests__/mcp-tools/cypher.test.ts +10 -6
  3. package/__tests__/mcp-tools/detect-changes.test.ts +14 -11
  4. package/__tests__/pipeline/phases/wildcard-synthesis.test.ts +4 -4
  5. package/__tests__/search/embed-batch-config.test.ts +7 -7
  6. package/__tests__/search/hybrid-query.test.ts +3 -1
  7. package/__tests__/web/api.test.ts +1 -1
  8. package/dist/src/analysis/churn-cache.d.ts.map +1 -1
  9. package/dist/src/analysis/churn-cache.js +19 -6
  10. package/dist/src/analysis/churn-cache.js.map +1 -1
  11. package/dist/src/analysis/churn-trend.d.ts.map +1 -1
  12. package/dist/src/analysis/churn-trend.js +50 -6
  13. package/dist/src/analysis/churn-trend.js.map +1 -1
  14. package/dist/src/analysis/cluster-quality.d.ts +3 -0
  15. package/dist/src/analysis/cluster-quality.d.ts.map +1 -1
  16. package/dist/src/analysis/cluster-quality.js +18 -12
  17. package/dist/src/analysis/cluster-quality.js.map +1 -1
  18. package/dist/src/analysis/coverage-gaps.d.ts +3 -0
  19. package/dist/src/analysis/coverage-gaps.d.ts.map +1 -1
  20. package/dist/src/analysis/coverage-gaps.js +40 -1
  21. package/dist/src/analysis/coverage-gaps.js.map +1 -1
  22. package/dist/src/analysis/dep-classification.d.ts +2 -0
  23. package/dist/src/analysis/dep-classification.d.ts.map +1 -1
  24. package/dist/src/analysis/dep-classification.js +30 -0
  25. package/dist/src/analysis/dep-classification.js.map +1 -1
  26. package/dist/src/analysis/dependency-health.d.ts +4 -0
  27. package/dist/src/analysis/dependency-health.d.ts.map +1 -1
  28. package/dist/src/analysis/dependency-health.js +28 -0
  29. package/dist/src/analysis/dependency-health.js.map +1 -1
  30. package/dist/src/analysis/duplicate-exports.d.ts +9 -5
  31. package/dist/src/analysis/duplicate-exports.d.ts.map +1 -1
  32. package/dist/src/analysis/duplicate-exports.js +26 -2
  33. package/dist/src/analysis/duplicate-exports.js.map +1 -1
  34. package/dist/src/analysis/feature-flags.d.ts.map +1 -1
  35. package/dist/src/analysis/feature-flags.js +36 -17
  36. package/dist/src/analysis/feature-flags.js.map +1 -1
  37. package/dist/src/analysis/node-similarity.d.ts +19 -0
  38. package/dist/src/analysis/node-similarity.d.ts.map +1 -1
  39. package/dist/src/analysis/node-similarity.js +34 -3
  40. package/dist/src/analysis/node-similarity.js.map +1 -1
  41. package/dist/src/analysis/private-type-leaks.d.ts +4 -0
  42. package/dist/src/analysis/private-type-leaks.d.ts.map +1 -1
  43. package/dist/src/analysis/private-type-leaks.js +43 -2
  44. package/dist/src/analysis/private-type-leaks.js.map +1 -1
  45. package/dist/src/analysis/project-state.d.ts +2 -0
  46. package/dist/src/analysis/project-state.d.ts.map +1 -1
  47. package/dist/src/analysis/project-state.js +18 -7
  48. package/dist/src/analysis/project-state.js.map +1 -1
  49. package/dist/src/analysis/suppressions.d.ts.map +1 -1
  50. package/dist/src/analysis/suppressions.js +3 -1
  51. package/dist/src/analysis/suppressions.js.map +1 -1
  52. package/dist/src/analysis/trace.d.ts +5 -3
  53. package/dist/src/analysis/trace.d.ts.map +1 -1
  54. package/dist/src/analysis/trace.js +59 -53
  55. package/dist/src/analysis/trace.js.map +1 -1
  56. package/dist/src/analysis/unused-class-members.d.ts.map +1 -1
  57. package/dist/src/analysis/unused-class-members.js +7 -1
  58. package/dist/src/analysis/unused-class-members.js.map +1 -1
  59. package/dist/src/analysis/worker-pool.d.ts +3 -0
  60. package/dist/src/analysis/worker-pool.d.ts.map +1 -1
  61. package/dist/src/analysis/worker-pool.js +30 -8
  62. package/dist/src/analysis/worker-pool.js.map +1 -1
  63. package/dist/src/cli/augment.d.ts.map +1 -1
  64. package/dist/src/cli/augment.js +10 -3
  65. package/dist/src/cli/augment.js.map +1 -1
  66. package/dist/src/cli/skill-gen.js +3 -0
  67. package/dist/src/cli/skill-gen.js.map +1 -1
  68. package/dist/src/export/sarif.js +5 -5
  69. package/dist/src/export/sarif.js.map +1 -1
  70. package/dist/src/graph/analyzer.d.ts.map +1 -1
  71. package/dist/src/graph/analyzer.js +17 -2
  72. package/dist/src/graph/analyzer.js.map +1 -1
  73. package/dist/src/graph/author-analytics.d.ts +2 -0
  74. package/dist/src/graph/author-analytics.d.ts.map +1 -1
  75. package/dist/src/graph/author-analytics.js +39 -5
  76. package/dist/src/graph/author-analytics.js.map +1 -1
  77. package/dist/src/graph/baseline.d.ts +5 -0
  78. package/dist/src/graph/baseline.d.ts.map +1 -1
  79. package/dist/src/graph/baseline.js +65 -18
  80. package/dist/src/graph/baseline.js.map +1 -1
  81. package/dist/src/graph/clone-detector.d.ts.map +1 -1
  82. package/dist/src/graph/clone-detector.js +19 -33
  83. package/dist/src/graph/clone-detector.js.map +1 -1
  84. package/dist/src/graph/clone-families.d.ts +2 -0
  85. package/dist/src/graph/clone-families.d.ts.map +1 -1
  86. package/dist/src/graph/clone-families.js +38 -10
  87. package/dist/src/graph/clone-families.js.map +1 -1
  88. package/dist/src/graph/codeowners-extended.d.ts +17 -0
  89. package/dist/src/graph/codeowners-extended.d.ts.map +1 -1
  90. package/dist/src/graph/codeowners-extended.js +42 -0
  91. package/dist/src/graph/codeowners-extended.js.map +1 -1
  92. package/dist/src/graph/codeowners-gitlab.d.ts +21 -0
  93. package/dist/src/graph/codeowners-gitlab.d.ts.map +1 -1
  94. package/dist/src/graph/codeowners-gitlab.js +37 -0
  95. package/dist/src/graph/codeowners-gitlab.js.map +1 -1
  96. package/dist/src/graph/codeowners.d.ts +10 -0
  97. package/dist/src/graph/codeowners.d.ts.map +1 -1
  98. package/dist/src/graph/codeowners.js +24 -2
  99. package/dist/src/graph/codeowners.js.map +1 -1
  100. package/dist/src/graph/complexity.d.ts +10 -0
  101. package/dist/src/graph/complexity.d.ts.map +1 -1
  102. package/dist/src/graph/complexity.js +93 -43
  103. package/dist/src/graph/complexity.js.map +1 -1
  104. package/dist/src/graph/cross-reference-findings.d.ts.map +1 -1
  105. package/dist/src/graph/cross-reference-findings.js +19 -2
  106. package/dist/src/graph/cross-reference-findings.js.map +1 -1
  107. package/dist/src/graph/cross-reference.d.ts +4 -0
  108. package/dist/src/graph/cross-reference.d.ts.map +1 -1
  109. package/dist/src/graph/cross-reference.js +23 -0
  110. package/dist/src/graph/cross-reference.js.map +1 -1
  111. package/dist/src/graph/cycle-detection.d.ts +2 -0
  112. package/dist/src/graph/cycle-detection.d.ts.map +1 -1
  113. package/dist/src/graph/cycle-detection.js +26 -3
  114. package/dist/src/graph/cycle-detection.js.map +1 -1
  115. package/dist/src/graph/cycles.d.ts +11 -3
  116. package/dist/src/graph/cycles.d.ts.map +1 -1
  117. package/dist/src/graph/cycles.js +121 -83
  118. package/dist/src/graph/cycles.js.map +1 -1
  119. package/dist/src/graph/dead-code.d.ts +23 -0
  120. package/dist/src/graph/dead-code.d.ts.map +1 -1
  121. package/dist/src/graph/dead-code.js +53 -13
  122. package/dist/src/graph/dead-code.js.map +1 -1
  123. package/dist/src/graph/dependency-closure.d.ts.map +1 -1
  124. package/dist/src/graph/dependency-closure.js +20 -4
  125. package/dist/src/graph/dependency-closure.js.map +1 -1
  126. package/dist/src/graph/dfs.d.ts +14 -0
  127. package/dist/src/graph/dfs.d.ts.map +1 -1
  128. package/dist/src/graph/dfs.js +61 -0
  129. package/dist/src/graph/dfs.js.map +1 -1
  130. package/dist/src/graph/duplication-stats.d.ts.map +1 -1
  131. package/dist/src/graph/duplication-stats.js +19 -3
  132. package/dist/src/graph/duplication-stats.js.map +1 -1
  133. package/dist/src/graph/edge-filter.d.ts +2 -0
  134. package/dist/src/graph/edge-filter.d.ts.map +1 -1
  135. package/dist/src/graph/edge-filter.js +26 -12
  136. package/dist/src/graph/edge-filter.js.map +1 -1
  137. package/dist/src/graph/health-score.d.ts +8 -0
  138. package/dist/src/graph/health-score.d.ts.map +1 -1
  139. package/dist/src/graph/health-score.js +48 -17
  140. package/dist/src/graph/health-score.js.map +1 -1
  141. package/dist/src/graph/hotspots.d.ts +7 -0
  142. package/dist/src/graph/hotspots.d.ts.map +1 -1
  143. package/dist/src/graph/hotspots.js +50 -5
  144. package/dist/src/graph/hotspots.js.map +1 -1
  145. package/dist/src/graph/import-chain.d.ts +17 -1
  146. package/dist/src/graph/import-chain.d.ts.map +1 -1
  147. package/dist/src/graph/import-chain.js +68 -19
  148. package/dist/src/graph/import-chain.js.map +1 -1
  149. package/dist/src/graph/loader.d.ts.map +1 -1
  150. package/dist/src/graph/loader.js +9 -10
  151. package/dist/src/graph/loader.js.map +1 -1
  152. package/dist/src/graph/maintainability.d.ts +8 -0
  153. package/dist/src/graph/maintainability.d.ts.map +1 -1
  154. package/dist/src/graph/maintainability.js +65 -10
  155. package/dist/src/graph/maintainability.js.map +1 -1
  156. package/dist/src/graph/mirrored-dirs.d.ts +4 -0
  157. package/dist/src/graph/mirrored-dirs.d.ts.map +1 -1
  158. package/dist/src/graph/mirrored-dirs.js +48 -13
  159. package/dist/src/graph/mirrored-dirs.js.map +1 -1
  160. package/dist/src/graph/narrowing.d.ts +15 -0
  161. package/dist/src/graph/narrowing.d.ts.map +1 -1
  162. package/dist/src/graph/narrowing.js +33 -0
  163. package/dist/src/graph/narrowing.js.map +1 -1
  164. package/dist/src/graph/node-search.d.ts +3 -0
  165. package/dist/src/graph/node-search.d.ts.map +1 -1
  166. package/dist/src/graph/node-search.js +37 -4
  167. package/dist/src/graph/node-search.js.map +1 -1
  168. package/dist/src/graph/normalize-id.d.ts +4 -0
  169. package/dist/src/graph/normalize-id.d.ts.map +1 -1
  170. package/dist/src/graph/normalize-id.js +25 -2
  171. package/dist/src/graph/normalize-id.js.map +1 -1
  172. package/dist/src/graph/pagerank.d.ts +7 -0
  173. package/dist/src/graph/pagerank.d.ts.map +1 -1
  174. package/dist/src/graph/pagerank.js +49 -2
  175. package/dist/src/graph/pagerank.js.map +1 -1
  176. package/dist/src/graph/prune.d.ts +31 -0
  177. package/dist/src/graph/prune.d.ts.map +1 -1
  178. package/dist/src/graph/prune.js +65 -0
  179. package/dist/src/graph/prune.js.map +1 -1
  180. package/dist/src/graph/regex-search.d.ts +10 -2
  181. package/dist/src/graph/regex-search.d.ts.map +1 -1
  182. package/dist/src/graph/regex-search.js +33 -4
  183. package/dist/src/graph/regex-search.js.map +1 -1
  184. package/dist/src/graph/regression.d.ts +5 -0
  185. package/dist/src/graph/regression.d.ts.map +1 -1
  186. package/dist/src/graph/regression.js +45 -23
  187. package/dist/src/graph/regression.js.map +1 -1
  188. package/dist/src/graph/ripple-impact.d.ts +27 -0
  189. package/dist/src/graph/ripple-impact.d.ts.map +1 -1
  190. package/dist/src/graph/ripple-impact.js +55 -7
  191. package/dist/src/graph/ripple-impact.js.map +1 -1
  192. package/dist/src/graph/risk-profile.d.ts +7 -0
  193. package/dist/src/graph/risk-profile.d.ts.map +1 -1
  194. package/dist/src/graph/risk-profile.js +82 -48
  195. package/dist/src/graph/risk-profile.js.map +1 -1
  196. package/dist/src/graph/shingle-filter.d.ts.map +1 -1
  197. package/dist/src/graph/shingle-filter.js +25 -26
  198. package/dist/src/graph/shingle-filter.js.map +1 -1
  199. package/dist/src/graph/statistics.d.ts +19 -0
  200. package/dist/src/graph/statistics.d.ts.map +1 -1
  201. package/dist/src/graph/statistics.js +53 -40
  202. package/dist/src/graph/statistics.js.map +1 -1
  203. package/dist/src/graph/stats.d.ts +2 -0
  204. package/dist/src/graph/stats.d.ts.map +1 -1
  205. package/dist/src/graph/stats.js +54 -8
  206. package/dist/src/graph/stats.js.map +1 -1
  207. package/dist/src/graph/subgraph.d.ts +5 -0
  208. package/dist/src/graph/subgraph.d.ts.map +1 -1
  209. package/dist/src/graph/subgraph.js +73 -9
  210. package/dist/src/graph/subgraph.js.map +1 -1
  211. package/dist/src/graph/topo-sort.d.ts +15 -0
  212. package/dist/src/graph/topo-sort.d.ts.map +1 -1
  213. package/dist/src/graph/topo-sort.js +83 -10
  214. package/dist/src/graph/topo-sort.js.map +1 -1
  215. package/dist/src/graph/wcc.d.ts +29 -0
  216. package/dist/src/graph/wcc.d.ts.map +1 -1
  217. package/dist/src/graph/wcc.js +122 -39
  218. package/dist/src/graph/wcc.js.map +1 -1
  219. package/dist/src/index.js +1 -1
  220. package/dist/src/index.js.map +1 -1
  221. package/dist/src/mcp-tools/api-impact.d.ts.map +1 -1
  222. package/dist/src/mcp-tools/api-impact.js +13 -6
  223. package/dist/src/mcp-tools/api-impact.js.map +1 -1
  224. package/dist/src/mcp-tools/context.d.ts.map +1 -1
  225. package/dist/src/mcp-tools/context.js +19 -29
  226. package/dist/src/mcp-tools/context.js.map +1 -1
  227. package/dist/src/mcp-tools/cypher.d.ts +11 -0
  228. package/dist/src/mcp-tools/cypher.d.ts.map +1 -1
  229. package/dist/src/mcp-tools/cypher.js +13 -1
  230. package/dist/src/mcp-tools/cypher.js.map +1 -1
  231. package/dist/src/mcp-tools/detect-changes.d.ts.map +1 -1
  232. package/dist/src/mcp-tools/detect-changes.js +35 -20
  233. package/dist/src/mcp-tools/detect-changes.js.map +1 -1
  234. package/dist/src/mcp-tools/explain.d.ts.map +1 -1
  235. package/dist/src/mcp-tools/explain.js +4 -1
  236. package/dist/src/mcp-tools/explain.js.map +1 -1
  237. package/dist/src/mcp-tools/graph-query.d.ts +2 -0
  238. package/dist/src/mcp-tools/graph-query.d.ts.map +1 -1
  239. package/dist/src/mcp-tools/graph-query.js +20 -3
  240. package/dist/src/mcp-tools/graph-query.js.map +1 -1
  241. package/dist/src/mcp-tools/group-list.d.ts +1 -0
  242. package/dist/src/mcp-tools/group-list.d.ts.map +1 -1
  243. package/dist/src/mcp-tools/group-list.js +7 -9
  244. package/dist/src/mcp-tools/group-list.js.map +1 -1
  245. package/dist/src/mcp-tools/impact.d.ts.map +1 -1
  246. package/dist/src/mcp-tools/impact.js +20 -59
  247. package/dist/src/mcp-tools/impact.js.map +1 -1
  248. package/dist/src/mcp-tools/neighbors.d.ts.map +1 -1
  249. package/dist/src/mcp-tools/neighbors.js +24 -33
  250. package/dist/src/mcp-tools/neighbors.js.map +1 -1
  251. package/dist/src/mcp-tools/query.d.ts +2 -0
  252. package/dist/src/mcp-tools/query.d.ts.map +1 -1
  253. package/dist/src/mcp-tools/query.js +1 -0
  254. package/dist/src/mcp-tools/query.js.map +1 -1
  255. package/dist/src/mcp-tools/rename.d.ts.map +1 -1
  256. package/dist/src/mcp-tools/rename.js +12 -7
  257. package/dist/src/mcp-tools/rename.js.map +1 -1
  258. package/dist/src/mcp-tools/route-map.d.ts.map +1 -1
  259. package/dist/src/mcp-tools/route-map.js +19 -23
  260. package/dist/src/mcp-tools/route-map.js.map +1 -1
  261. package/dist/src/mcp-tools/shape-check.d.ts.map +1 -1
  262. package/dist/src/mcp-tools/shape-check.js +17 -13
  263. package/dist/src/mcp-tools/shape-check.js.map +1 -1
  264. package/dist/src/parsers/loader.js +6 -5
  265. package/dist/src/parsers/loader.js.map +1 -1
  266. package/dist/src/pipeline/cluster-only.d.ts.map +1 -1
  267. package/dist/src/pipeline/cluster-only.js +8 -4
  268. package/dist/src/pipeline/cluster-only.js.map +1 -1
  269. package/dist/src/pipeline/orchestrator.d.ts +2 -0
  270. package/dist/src/pipeline/orchestrator.d.ts.map +1 -1
  271. package/dist/src/pipeline/orchestrator.js +19 -0
  272. package/dist/src/pipeline/orchestrator.js.map +1 -1
  273. package/dist/src/pipeline/phases/boundary.d.ts +2 -0
  274. package/dist/src/pipeline/phases/boundary.d.ts.map +1 -1
  275. package/dist/src/pipeline/phases/boundary.js +31 -2
  276. package/dist/src/pipeline/phases/boundary.js.map +1 -1
  277. package/dist/src/pipeline/phases/communities.d.ts +14 -0
  278. package/dist/src/pipeline/phases/communities.d.ts.map +1 -1
  279. package/dist/src/pipeline/phases/communities.js +58 -5
  280. package/dist/src/pipeline/phases/communities.js.map +1 -1
  281. package/dist/src/pipeline/phases/contextual-proximity.d.ts.map +1 -1
  282. package/dist/src/pipeline/phases/contextual-proximity.js +21 -14
  283. package/dist/src/pipeline/phases/contextual-proximity.js.map +1 -1
  284. package/dist/src/pipeline/phases/cross-file.d.ts.map +1 -1
  285. package/dist/src/pipeline/phases/cross-file.js +35 -14
  286. package/dist/src/pipeline/phases/cross-file.js.map +1 -1
  287. package/dist/src/pipeline/phases/field-access.d.ts.map +1 -1
  288. package/dist/src/pipeline/phases/field-access.js +9 -4
  289. package/dist/src/pipeline/phases/field-access.js.map +1 -1
  290. package/dist/src/pipeline/phases/god-nodes.d.ts +4 -0
  291. package/dist/src/pipeline/phases/god-nodes.d.ts.map +1 -1
  292. package/dist/src/pipeline/phases/god-nodes.js +43 -9
  293. package/dist/src/pipeline/phases/god-nodes.js.map +1 -1
  294. package/dist/src/pipeline/phases/import-resolver.d.ts +11 -0
  295. package/dist/src/pipeline/phases/import-resolver.d.ts.map +1 -1
  296. package/dist/src/pipeline/phases/import-resolver.js +34 -7
  297. package/dist/src/pipeline/phases/import-resolver.js.map +1 -1
  298. package/dist/src/pipeline/phases/leiden.d.ts.map +1 -1
  299. package/dist/src/pipeline/phases/leiden.js +27 -19
  300. package/dist/src/pipeline/phases/leiden.js.map +1 -1
  301. package/dist/src/pipeline/phases/markdown.d.ts.map +1 -1
  302. package/dist/src/pipeline/phases/markdown.js +10 -5
  303. package/dist/src/pipeline/phases/markdown.js.map +1 -1
  304. package/dist/src/pipeline/phases/pdf-parse.d.ts.map +1 -1
  305. package/dist/src/pipeline/phases/pdf-parse.js +18 -3
  306. package/dist/src/pipeline/phases/pdf-parse.js.map +1 -1
  307. package/dist/src/pipeline/phases/processes.d.ts.map +1 -1
  308. package/dist/src/pipeline/phases/processes.js +26 -9
  309. package/dist/src/pipeline/phases/processes.js.map +1 -1
  310. package/dist/src/pipeline/phases/re-export-propagation.d.ts.map +1 -1
  311. package/dist/src/pipeline/phases/re-export-propagation.js +33 -14
  312. package/dist/src/pipeline/phases/re-export-propagation.js.map +1 -1
  313. package/dist/src/pipeline/phases/reachability.d.ts.map +1 -1
  314. package/dist/src/pipeline/phases/reachability.js +42 -26
  315. package/dist/src/pipeline/phases/reachability.js.map +1 -1
  316. package/dist/src/pipeline/phases/routes.d.ts.map +1 -1
  317. package/dist/src/pipeline/phases/routes.js +30 -21
  318. package/dist/src/pipeline/phases/routes.js.map +1 -1
  319. package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
  320. package/dist/src/pipeline/phases/scope-resolution.js +93 -63
  321. package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
  322. package/dist/src/pipeline/runner.d.ts +4 -0
  323. package/dist/src/pipeline/runner.d.ts.map +1 -1
  324. package/dist/src/pipeline/runner.js +30 -11
  325. package/dist/src/pipeline/runner.js.map +1 -1
  326. package/dist/src/query/cypher-parser.d.ts +20 -0
  327. package/dist/src/query/cypher-parser.d.ts.map +1 -1
  328. package/dist/src/query/cypher-parser.js +73 -0
  329. package/dist/src/query/cypher-parser.js.map +1 -1
  330. package/dist/src/search/embed-batch.d.ts.map +1 -1
  331. package/dist/src/search/embed-batch.js +4 -1
  332. package/dist/src/search/embed-batch.js.map +1 -1
  333. package/dist/src/search/hybrid-query.d.ts +4 -0
  334. package/dist/src/search/hybrid-query.d.ts.map +1 -1
  335. package/dist/src/search/hybrid-query.js +5 -1
  336. package/dist/src/search/hybrid-query.js.map +1 -1
  337. package/dist/src/staleness/git-staleness.js +3 -3
  338. package/dist/src/staleness/git-staleness.js.map +1 -1
  339. package/dist/src/storage/db.d.ts.map +1 -1
  340. package/dist/src/storage/db.js +12 -0
  341. package/dist/src/storage/db.js.map +1 -1
  342. package/dist/src/storage/edge-store.d.ts +2 -0
  343. package/dist/src/storage/edge-store.d.ts.map +1 -1
  344. package/dist/src/storage/edge-store.js +26 -1
  345. package/dist/src/storage/edge-store.js.map +1 -1
  346. package/dist/src/storage/embedding-store.d.ts +16 -0
  347. package/dist/src/storage/embedding-store.d.ts.map +1 -1
  348. package/dist/src/storage/embedding-store.js +26 -2
  349. package/dist/src/storage/embedding-store.js.map +1 -1
  350. package/dist/src/storage/file-cache.d.ts +5 -0
  351. package/dist/src/storage/file-cache.d.ts.map +1 -1
  352. package/dist/src/storage/file-cache.js +35 -9
  353. package/dist/src/storage/file-cache.js.map +1 -1
  354. package/dist/src/storage/fts-store.d.ts +4 -0
  355. package/dist/src/storage/fts-store.d.ts.map +1 -1
  356. package/dist/src/storage/fts-store.js +8 -3
  357. package/dist/src/storage/fts-store.js.map +1 -1
  358. package/dist/src/storage/node-store.d.ts +2 -0
  359. package/dist/src/storage/node-store.d.ts.map +1 -1
  360. package/dist/src/storage/node-store.js +28 -1
  361. package/dist/src/storage/node-store.js.map +1 -1
  362. package/dist/src/wiki/wiki-generator.d.ts.map +1 -1
  363. package/dist/src/wiki/wiki-generator.js +5 -1
  364. package/dist/src/wiki/wiki-generator.js.map +1 -1
  365. package/dist/tsconfig.tsbuildinfo +1 -1
  366. package/package.json +1 -1
  367. package/src/__tests__/pipeline/runner.parallel.test.ts +2 -2
  368. package/src/analysis/churn-cache.ts +18 -7
  369. package/src/analysis/churn-trend.ts +35 -6
  370. package/src/analysis/cluster-quality.ts +20 -11
  371. package/src/analysis/coverage-gaps.ts +44 -1
  372. package/src/analysis/dep-classification.ts +34 -0
  373. package/src/analysis/dependency-health.ts +25 -0
  374. package/src/analysis/duplicate-exports.ts +40 -4
  375. package/src/analysis/feature-flags.ts +37 -17
  376. package/src/analysis/node-similarity.ts +45 -4
  377. package/src/analysis/private-type-leaks.ts +49 -2
  378. package/src/analysis/project-state.ts +19 -9
  379. package/src/analysis/suppressions.ts +4 -1
  380. package/src/analysis/trace.ts +73 -54
  381. package/src/analysis/unused-class-members.ts +8 -1
  382. package/src/analysis/worker-pool.ts +40 -8
  383. package/src/cli/augment.ts +10 -3
  384. package/src/cli/skill-gen.ts +3 -0
  385. package/src/export/sarif.ts +5 -5
  386. package/src/graph/analyzer.ts +26 -2
  387. package/src/graph/author-analytics.ts +45 -5
  388. package/src/graph/baseline.ts +78 -24
  389. package/src/graph/clone-detector.ts +22 -36
  390. package/src/graph/clone-families.ts +34 -10
  391. package/src/graph/codeowners-extended.ts +54 -0
  392. package/src/graph/codeowners-gitlab.ts +46 -0
  393. package/src/graph/codeowners.ts +33 -2
  394. package/src/graph/complexity.ts +102 -47
  395. package/src/graph/cross-reference-findings.ts +23 -2
  396. package/src/graph/cross-reference.ts +26 -0
  397. package/src/graph/cycle-detection.ts +22 -3
  398. package/src/graph/cycles.ts +155 -96
  399. package/src/graph/dead-code.ts +76 -17
  400. package/src/graph/dependency-closure.ts +24 -7
  401. package/src/graph/dfs.ts +78 -0
  402. package/src/graph/duplication-stats.ts +17 -6
  403. package/src/graph/edge-filter.ts +26 -12
  404. package/src/graph/health-score.ts +47 -17
  405. package/src/graph/hotspots.ts +57 -8
  406. package/src/graph/import-chain.ts +89 -19
  407. package/src/graph/loader.ts +10 -7
  408. package/src/graph/maintainability.ts +72 -22
  409. package/src/graph/mirrored-dirs.ts +45 -14
  410. package/src/graph/narrowing.ts +44 -0
  411. package/src/graph/node-search.ts +50 -4
  412. package/src/graph/normalize-id.ts +26 -2
  413. package/src/graph/pagerank.ts +70 -2
  414. package/src/graph/prune.ts +76 -0
  415. package/src/graph/regex-search.ts +38 -4
  416. package/src/graph/regression.ts +48 -23
  417. package/src/graph/ripple-impact.ts +71 -11
  418. package/src/graph/risk-profile.ts +98 -53
  419. package/src/graph/shingle-filter.ts +33 -27
  420. package/src/graph/statistics.ts +73 -45
  421. package/src/graph/stats.ts +50 -16
  422. package/src/graph/subgraph.ts +97 -25
  423. package/src/graph/topo-sort.ts +101 -16
  424. package/src/graph/wcc.ts +146 -37
  425. package/src/index.ts +1 -1
  426. package/src/mcp-tools/api-impact.ts +13 -6
  427. package/src/mcp-tools/context.ts +31 -40
  428. package/src/mcp-tools/cypher.ts +14 -1
  429. package/src/mcp-tools/detect-changes.ts +38 -23
  430. package/src/mcp-tools/explain.ts +4 -1
  431. package/src/mcp-tools/graph-query.ts +24 -3
  432. package/src/mcp-tools/group-list.ts +9 -11
  433. package/src/mcp-tools/impact.ts +26 -66
  434. package/src/mcp-tools/neighbors.ts +34 -39
  435. package/src/mcp-tools/query.ts +3 -0
  436. package/src/mcp-tools/rename.ts +9 -8
  437. package/src/mcp-tools/route-map.ts +21 -23
  438. package/src/mcp-tools/shape-check.ts +18 -14
  439. package/src/parsers/loader.ts +5 -5
  440. package/src/pipeline/cluster-only.ts +8 -4
  441. package/src/pipeline/orchestrator.ts +22 -0
  442. package/src/pipeline/phases/boundary.ts +40 -2
  443. package/src/pipeline/phases/communities.ts +66 -5
  444. package/src/pipeline/phases/contextual-proximity.ts +15 -14
  445. package/src/pipeline/phases/cross-file.ts +35 -14
  446. package/src/pipeline/phases/field-access.ts +11 -4
  447. package/src/pipeline/phases/god-nodes.ts +47 -10
  448. package/src/pipeline/phases/import-resolver.ts +37 -5
  449. package/src/pipeline/phases/leiden.ts +25 -18
  450. package/src/pipeline/phases/markdown.ts +12 -7
  451. package/src/pipeline/phases/pdf-parse.ts +19 -2
  452. package/src/pipeline/phases/processes.ts +27 -15
  453. package/src/pipeline/phases/re-export-propagation.ts +40 -18
  454. package/src/pipeline/phases/reachability.ts +39 -24
  455. package/src/pipeline/phases/routes.ts +32 -21
  456. package/src/pipeline/phases/scope-resolution.ts +110 -71
  457. package/src/pipeline/runner.ts +28 -11
  458. package/src/query/cypher-parser.ts +83 -0
  459. package/src/search/embed-batch.ts +4 -1
  460. package/src/search/hybrid-query.ts +11 -1
  461. package/src/staleness/git-staleness.ts +3 -3
  462. package/src/storage/db.ts +11 -0
  463. package/src/storage/edge-store.ts +34 -1
  464. package/src/storage/embedding-store.ts +38 -6
  465. package/src/storage/file-cache.ts +35 -21
  466. package/src/storage/fts-store.ts +12 -3
  467. package/src/storage/node-store.ts +39 -1
  468. package/src/wiki/wiki-generator.ts +5 -1
  469. package/src/coverage/runtime.ts +0 -249
@@ -1,5 +1,3 @@
1
- import * as path from 'node:path';
2
-
3
1
  export interface ProjectFile {
4
2
  fileId: number;
5
3
  path: string;
@@ -16,16 +14,25 @@ export interface WorkspaceEntry {
16
14
  export interface ProjectState {
17
15
  files: ProjectFile[];
18
16
  pathToId: Map<string, number>;
17
+ /** O(1) lookup by numeric fileId */
18
+ idToFile: Map<number, ProjectFile>;
19
19
  workspaces: WorkspaceEntry[];
20
20
  }
21
21
 
22
22
  export function makeProjectState(files: ProjectFile[], workspaces: WorkspaceEntry[]): ProjectState {
23
- const pathToId = new Map(files.map(f => [f.path, f.fileId]));
24
- return { files, pathToId, workspaces };
23
+ // Build both lookup maps in a single pass to avoid intermediate arrays
24
+ const pathToId = new Map<string, number>();
25
+ const idToFile = new Map<number, ProjectFile>();
26
+ for (const f of files) {
27
+ pathToId.set(f.path, f.fileId);
28
+ idToFile.set(f.fileId, f);
29
+ }
30
+ return { files, pathToId, idToFile, workspaces };
25
31
  }
26
32
 
27
33
  export function fileById(state: ProjectState, fileId: number): ProjectFile | undefined {
28
- return state.files.find(f => f.fileId === fileId);
34
+ // O(1) lookup via idToFile map instead of O(N) linear scan
35
+ return state.idToFile.get(fileId);
29
36
  }
30
37
 
31
38
  export function idForPath(state: ProjectState, filePath: string): number | undefined {
@@ -62,10 +69,13 @@ export class PackageResolver {
62
69
  private entries: Array<{ root: string; name: string }>;
63
70
 
64
71
  constructor(workspaces: WorkspaceEntry[]) {
65
- this.entries = workspaces
66
- .filter(w => w.name)
67
- .map(w => ({ root: w.root, name: w.name! }))
68
- .sort((a, b) => b.root.length - a.root.length);
72
+ // Combine filter + map into a single for-of loop to avoid 2 intermediate arrays
73
+ const named: Array<{ root: string; name: string }> = [];
74
+ for (const w of workspaces) {
75
+ if (w.name) named.push({ root: w.root, name: w.name });
76
+ }
77
+ named.sort((a, b) => b.root.length - a.root.length);
78
+ this.entries = named;
69
79
  }
70
80
 
71
81
  resolvePackage(filePath: string): string | undefined {
@@ -15,6 +15,9 @@ export const NON_CORE_KINDS: SuppressionKind[] = [
15
15
  'code-duplication',
16
16
  ];
17
17
 
18
+ // Set for O(1) membership check in findStale hot path
19
+ const NON_CORE_KINDS_SET = new Set<SuppressionKind>(NON_CORE_KINDS);
20
+
18
21
  export interface Suppression {
19
22
  path: string;
20
23
  line: number;
@@ -58,7 +61,7 @@ export function findStale(ctx: SuppressionContext): StaleSuppression[] {
58
61
  .filter((s) => {
59
62
  const key = suppressionKey(s);
60
63
  const isConsumed = ctx.consumed.has(key);
61
- const isNonCore = NON_CORE_KINDS.includes(s.kind);
64
+ const isNonCore = NON_CORE_KINDS_SET.has(s.kind);
62
65
  // Keep (return as stale) if NOT consumed AND NOT a non-core kind
63
66
  return !isConsumed && !isNonCore;
64
67
  })
@@ -49,7 +49,7 @@ export interface CloneTrace {
49
49
  groupId?: number;
50
50
  }
51
51
 
52
- interface GraphNode {
52
+ export interface GraphNode {
53
53
  id: string;
54
54
  filePath?: string;
55
55
  name: string;
@@ -68,12 +68,27 @@ interface Graph {
68
68
  edges: GraphEdge[];
69
69
  }
70
70
 
71
- export function traceExport(graph: Graph, file: string, exportName: string): ExportTrace {
71
+ /** Build an O(1) id→node index. Pass to the trace functions when calling multiple times on the same graph. */
72
+ export function buildNodeIndex(graph: Graph): Map<string, GraphNode> {
73
+ const index = new Map<string, GraphNode>();
74
+ for (const n of graph.nodes) index.set(n.id, n);
75
+ return index;
76
+ }
77
+
78
+ export function traceExport(
79
+ graph: Graph,
80
+ file: string,
81
+ exportName: string,
82
+ nodeIndex?: Map<string, GraphNode>,
83
+ ): ExportTrace {
84
+ // Build index once if not supplied — O(N) amortised when caller reuses it
85
+ const idx = nodeIndex ?? buildNodeIndex(graph);
86
+
72
87
  // Find nodes in the target file matching exportName
73
- const targetNodes = graph.nodes.filter(
74
- (n) => n.filePath === file && n.name === exportName,
75
- );
76
- const targetIds = new Set(targetNodes.map((n) => n.id));
88
+ const targetIds = new Set<string>();
89
+ for (const n of graph.nodes) {
90
+ if (n.filePath === file && n.name === exportName) targetIds.add(n.id);
91
+ }
77
92
 
78
93
  // Find CALLS or IMPORTS edges pointing to these nodes from other files
79
94
  const directReferences: ReferenceLocation[] = [];
@@ -82,7 +97,7 @@ export function traceExport(graph: Graph, file: string, exportName: string): Exp
82
97
  for (const edge of graph.edges) {
83
98
  if (!targetIds.has(edge.targetId)) continue;
84
99
 
85
- const sourceNode = graph.nodes.find((n) => n.id === edge.sourceId);
100
+ const sourceNode = idx.get(edge.sourceId);
86
101
  if (!sourceNode) continue;
87
102
 
88
103
  // Skip self-references (same file)
@@ -127,66 +142,70 @@ export function traceExport(graph: Graph, file: string, exportName: string): Exp
127
142
  };
128
143
  }
129
144
 
130
- export function traceFile(graph: Graph, file: string): FileTrace {
145
+ export function traceFile(
146
+ graph: Graph,
147
+ file: string,
148
+ nodeIndex?: Map<string, GraphNode>,
149
+ ): FileTrace {
150
+ // Build index once if not supplied
151
+ const idx = nodeIndex ?? buildNodeIndex(graph);
152
+
131
153
  // Find all nodes in this file
132
154
  const fileNodes = graph.nodes.filter((n) => n.filePath === file);
133
155
  const fileNodeIds = new Set(fileNodes.map((n) => n.id));
134
156
 
135
- // Build exports list
136
- const exports = fileNodes
137
- .filter((n) => n.isExported)
138
- .map((n) => {
139
- // Check if any external node references this node
140
- const hasReferences = graph.edges.some(
141
- (e) =>
142
- e.targetId === n.id &&
143
- (e.relation === 'CALLS' || e.relation === 'IMPORTS') &&
144
- !fileNodeIds.has(e.sourceId),
145
- );
146
- return {
147
- name: n.name,
148
- isUsed: hasReferences,
149
- line: n.startLine ?? 0,
150
- };
151
- });
152
-
153
- // Find files this file imports from (outgoing IMPORTS edges from file nodes)
157
+ // Pre-compute which target node ids have external references in one edge pass
158
+ // to avoid O(exported_nodes * edges) in the map below.
159
+ const externallyReferenced = new Set<string>();
154
160
  const importsFromSet = new Set<string>();
161
+ const importedBySet = new Set<string>();
162
+ const reExports: Array<{ name: string; fromFile: string }> = [];
163
+
155
164
  for (const edge of graph.edges) {
156
- if (!fileNodeIds.has(edge.sourceId)) continue;
157
- if (edge.relation !== 'IMPORTS') continue;
158
- const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
159
- if (targetNode?.filePath && targetNode.filePath !== file) {
160
- importsFromSet.add(targetNode.filePath);
165
+ const srcInFile = fileNodeIds.has(edge.sourceId);
166
+ const tgtInFile = fileNodeIds.has(edge.targetId);
167
+
168
+ if (edge.relation === 'IMPORTS') {
169
+ if (srcInFile) {
170
+ const targetNode = idx.get(edge.targetId);
171
+ if (targetNode?.filePath && targetNode.filePath !== file) {
172
+ importsFromSet.add(targetNode.filePath);
173
+ }
174
+ }
175
+ if (tgtInFile) {
176
+ const sourceNode = idx.get(edge.sourceId);
177
+ if (sourceNode?.filePath && sourceNode.filePath !== file) {
178
+ importedBySet.add(sourceNode.filePath);
179
+ }
180
+ }
181
+ // External IMPORTS into a file node → that node is externally referenced
182
+ if (tgtInFile && !srcInFile) {
183
+ externallyReferenced.add(edge.targetId);
184
+ }
161
185
  }
162
- }
163
186
 
164
- // Find files that import this file (incoming IMPORTS edges to file nodes)
165
- const importedBySet = new Set<string>();
166
- for (const edge of graph.edges) {
167
- if (!fileNodeIds.has(edge.targetId)) continue;
168
- if (edge.relation !== 'IMPORTS') continue;
169
- const sourceNode = graph.nodes.find((n) => n.id === edge.sourceId);
170
- if (sourceNode?.filePath && sourceNode.filePath !== file) {
171
- importedBySet.add(sourceNode.filePath);
187
+ if (edge.relation === 'CALLS' && tgtInFile && !srcInFile) {
188
+ externallyReferenced.add(edge.targetId);
172
189
  }
173
- }
174
190
 
175
- // Find re-exports (RE_EXPORTS edges going out from file nodes)
176
- const reExports: Array<{ name: string; fromFile: string }> = [];
177
- for (const edge of graph.edges) {
178
- if (!fileNodeIds.has(edge.sourceId)) continue;
179
- if (edge.relation !== 'RE_EXPORTS') continue;
180
- const sourceNode = graph.nodes.find((n) => n.id === edge.sourceId);
181
- const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
182
- if (sourceNode && targetNode?.filePath) {
183
- reExports.push({
184
- name: sourceNode.name,
185
- fromFile: targetNode.filePath,
186
- });
191
+ if (edge.relation === 'RE_EXPORTS' && srcInFile) {
192
+ const sourceNode = idx.get(edge.sourceId);
193
+ const targetNode = idx.get(edge.targetId);
194
+ if (sourceNode && targetNode?.filePath) {
195
+ reExports.push({ name: sourceNode.name, fromFile: targetNode.filePath });
196
+ }
187
197
  }
188
198
  }
189
199
 
200
+ // Build exports list using pre-computed set — O(exported_nodes) not O(exported_nodes * edges)
201
+ const exports = fileNodes
202
+ .filter((n) => n.isExported)
203
+ .map((n) => ({
204
+ name: n.name,
205
+ isUsed: externallyReferenced.has(n.id),
206
+ line: n.startLine ?? 0,
207
+ }));
208
+
190
209
  return {
191
210
  file,
192
211
  exports,
@@ -38,9 +38,16 @@ export function isClassMemberSuppressed(
38
38
  return false;
39
39
  }
40
40
 
41
+ // Cache compiled glob regexes to avoid re-compilation on repeated allowlist checks
42
+ const _globReCache = new Map<string, RegExp>();
43
+
41
44
  function memberGlobMatch(name: string, pattern: string): boolean {
42
45
  if (!pattern.includes('*')) return name === pattern;
43
- const re = new RegExp('^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$');
46
+ let re = _globReCache.get(pattern);
47
+ if (!re) {
48
+ re = new RegExp('^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$');
49
+ _globReCache.set(pattern, re);
50
+ }
44
51
  return re.test(name);
45
52
  }
46
53
 
@@ -8,29 +8,61 @@ export interface WorkerPoolOptions {
8
8
  stackSizeMb?: number;
9
9
  }
10
10
 
11
+ interface QueuedTask {
12
+ script: string;
13
+ input: unknown;
14
+ resolve: (value: unknown) => void;
15
+ reject: (reason: unknown) => void;
16
+ }
17
+
11
18
  let _globalPool: WorkerPool | null = null;
12
19
 
13
20
  export class WorkerPool {
14
21
  readonly threads: number;
15
22
  readonly stackSizeMb: number;
16
23
 
24
+ private _active = 0;
25
+ private readonly _queue: QueuedTask[] = [];
26
+
17
27
  constructor(opts: WorkerPoolOptions = {}) {
18
28
  this.threads = opts.threads ?? cpus().length;
19
29
  this.stackSizeMb = opts.stackSizeMb ?? DEFAULT_STACK_SIZE_MB;
20
30
  }
21
31
 
22
32
  run<T>(workerScript: string, input: unknown): Promise<T> {
23
- return new Promise((resolve, reject) => {
24
- const worker = new Worker(workerScript, {
25
- workerData: input,
33
+ return new Promise<T>((resolve, reject) => {
34
+ this._queue.push({ script: workerScript, input, resolve: resolve as (v: unknown) => void, reject });
35
+ this._drain();
36
+ });
37
+ }
38
+
39
+ private _drain(): void {
40
+ while (this._active < this.threads && this._queue.length > 0) {
41
+ const task = this._queue.shift()!;
42
+ this._active++;
43
+ const worker = new Worker(task.script, {
44
+ workerData: task.input,
26
45
  resourceLimits: { stackSizeMb: this.stackSizeMb },
27
46
  });
28
- worker.on('message', (v: T) => resolve(v));
29
- worker.on('error', reject);
30
- worker.on('exit', code => {
31
- if (code !== 0) reject(new Error(`Worker exited with code ${code}`));
47
+ const cleanup = () => {
48
+ this._active--;
49
+ this._drain();
50
+ };
51
+ worker.on('message', (v: unknown) => {
52
+ task.resolve(v);
53
+ cleanup();
32
54
  });
33
- });
55
+ worker.on('error', (err: unknown) => {
56
+ task.reject(err);
57
+ cleanup();
58
+ });
59
+ worker.on('exit', (code: number) => {
60
+ if (code !== 0) {
61
+ task.reject(new Error(`Worker exited with code ${code}`));
62
+ cleanup();
63
+ }
64
+ });
65
+ }
34
66
  }
35
67
  }
36
68
 
@@ -89,14 +89,21 @@ export async function augmentContext(options: AugmentContextOptions): Promise<st
89
89
  );
90
90
  }
91
91
 
92
- // Markdown format
92
+ // Markdown format — include file:line navigation hints so LLMs can jump directly to code.
93
93
  const lines: string[] = ['## Relevant Code Context', ''];
94
94
 
95
95
  for (const r of results) {
96
96
  const label = r.label ?? 'Symbol';
97
97
  const name = r.name ?? r.id;
98
- const location = r.filePath ? `${r.filePath}` : null;
99
- const heading = location ? `### ${label}: \`${name}\` (${location})` : `### ${label}: \`${name}\``;
98
+ // Build a file:line reference when available so the LLM can navigate directly.
99
+ let location: string | null = null;
100
+ if (r.filePath) {
101
+ location = r.startLine != null ? `${r.filePath}:${r.startLine}` : r.filePath;
102
+ }
103
+ const scoreHint = `score=${r.score.toFixed(3)}`;
104
+ const heading = location
105
+ ? `### ${label}: \`${name}\` — \`${location}\` (${scoreHint})`
106
+ : `### ${label}: \`${name}\` (${scoreHint})`;
100
107
  lines.push(heading);
101
108
  lines.push('');
102
109
  }
@@ -245,6 +245,9 @@ function renderSkillMarkdown(
245
245
  lines.push('---');
246
246
  lines.push(`name: ${toKebabName(label)}`);
247
247
  lines.push(`description: "Skill for the ${label} community. ${members.length} symbols."`);
248
+ lines.push(`type: Code Community`);
249
+ lines.push(`tags: [monograph, code-community]`);
250
+ lines.push(`timestamp: ${new Date().toISOString()}`);
248
251
  lines.push('---');
249
252
  lines.push('');
250
253
 
@@ -38,35 +38,35 @@ const SARIF_RULES: SarifRule[] = [
38
38
  name: 'GodNode',
39
39
  shortDescription: { text: 'High-centrality node (god node) exceeds fan-in threshold' },
40
40
  fullDescription: { text: 'A file or module has an unusually high number of incoming dependencies, making it a central point of coupling. Consider splitting this module into smaller, more focused units.' },
41
- helpUri: 'https://github.com/nokhodian/monomind/blob/main/docs/rules/god-node.md',
41
+ helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/god-node.md',
42
42
  },
43
43
  {
44
44
  id: 'monograph/unreachable-file',
45
45
  name: 'UnreachableFile',
46
46
  shortDescription: { text: 'File node unreachable from any entry point' },
47
47
  fullDescription: { text: 'A file is not reachable from any known entry point. It may be dead code that can be safely removed.' },
48
- helpUri: 'https://github.com/nokhodian/monomind/blob/main/docs/rules/unreachable-file.md',
48
+ helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/unreachable-file.md',
49
49
  },
50
50
  {
51
51
  id: 'monograph/circular-import',
52
52
  name: 'CircularImport',
53
53
  shortDescription: { text: 'Circular import detected' },
54
54
  fullDescription: { text: 'A circular import chain was detected between files. Circular imports can cause initialization order issues and make the codebase harder to understand.' },
55
- helpUri: 'https://github.com/nokhodian/monomind/blob/main/docs/rules/circular-import.md',
55
+ helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/circular-import.md',
56
56
  },
57
57
  {
58
58
  id: 'monograph/bridge-node',
59
59
  name: 'BridgeNode',
60
60
  shortDescription: { text: 'Bridge node — high cross-community coupling' },
61
61
  fullDescription: { text: 'A file acts as a bridge between multiple community clusters, creating high cross-community coupling. This may indicate architectural boundaries are not being respected.' },
62
- helpUri: 'https://github.com/nokhodian/monomind/blob/main/docs/rules/bridge-node.md',
62
+ helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/bridge-node.md',
63
63
  },
64
64
  {
65
65
  id: 'monograph/hotspot',
66
66
  name: 'Hotspot',
67
67
  shortDescription: { text: 'File is both frequently changed and highly connected' },
68
68
  fullDescription: { text: 'A file is both a churn hotspot (frequently modified) and highly connected in the dependency graph. Changes here have wide blast radius and high risk of regression.' },
69
- helpUri: 'https://github.com/nokhodian/monomind/blob/main/docs/rules/hotspot.md',
69
+ helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/hotspot.md',
70
70
  },
71
71
  ];
72
72
 
@@ -4,13 +4,37 @@ import type Graph from 'graphology';
4
4
  import type { MonographDb } from '../storage/db.js';
5
5
  import { loadGraphFromDb } from './loader.js';
6
6
 
7
+ // ── TTL graph cache ───────────────────────────────────────────────────────────
8
+ // Avoids loading the full graph from the DB multiple times in the same request
9
+ // window (e.g. when getShortestPath and getBetweennessCentrality are called
10
+ // back-to-back on the same DB instance).
11
+
12
+ const GRAPH_CACHE_TTL_MS = 30_000;
13
+
14
+ interface CachedGraph {
15
+ graph: Graph;
16
+ expiresAt: number;
17
+ }
18
+
19
+ const graphCache = new WeakMap<object, CachedGraph>();
20
+
21
+ function getOrLoadGraph(db: MonographDb): Graph {
22
+ const cached = graphCache.get(db);
23
+ if (cached && Date.now() < cached.expiresAt) return cached.graph;
24
+ const graph = loadGraphFromDb(db);
25
+ graphCache.set(db, { graph, expiresAt: Date.now() + GRAPH_CACHE_TTL_MS });
26
+ return graph;
27
+ }
28
+
29
+ // ─────────────────────────────────────────────────────────────────────────────
30
+
7
31
  export function getShortestPath(
8
32
  db: MonographDb,
9
33
  sourceId: string,
10
34
  targetId: string,
11
35
  maxDepth = 6,
12
36
  ): string[] | null {
13
- const graph = loadGraphFromDb(db);
37
+ const graph = getOrLoadGraph(db);
14
38
  if (!graph.hasNode(sourceId) || !graph.hasNode(targetId)) return null;
15
39
  try {
16
40
  const path = bidirectional(graph, sourceId, targetId);
@@ -30,7 +54,7 @@ export function getShortestPath(
30
54
  * structural bridges — refactoring them has wide blast radius.
31
55
  */
32
56
  export function getBetweennessCentrality(db: MonographDb): Map<string, number> {
33
- const graph = loadGraphFromDb(db);
57
+ const graph = getOrLoadGraph(db);
34
58
  if (graph.order === 0) return new Map();
35
59
  const scores = betweennessCentrality(graph, { normalized: true });
36
60
  return new Map(Object.entries(scores));
@@ -76,16 +76,22 @@ export function computeAuthorAnalytics(repoPath: string, db: MonographDb): Autho
76
76
  if (commitCount.size === 0) return EMPTY_REPORT;
77
77
 
78
78
  // ── File ownership ─────────────────────────────────────────────────────────
79
- // Get File nodes with degree > 2
79
+ // Get File nodes with degree > 2 — use JOIN+GROUP BY instead of correlated subquery
80
80
  const fileRows = db.prepare(`
81
81
  SELECT n.id, n.name, n.file_path
82
82
  FROM nodes n
83
+ JOIN (
84
+ SELECT id, COUNT(*) AS deg
85
+ FROM (
86
+ SELECT source_id AS id FROM edges
87
+ UNION ALL
88
+ SELECT target_id AS id FROM edges
89
+ )
90
+ GROUP BY id
91
+ HAVING COUNT(*) > 2
92
+ ) deg_tbl ON deg_tbl.id = n.id
83
93
  WHERE n.label = 'File'
84
94
  AND n.file_path IS NOT NULL
85
- AND (
86
- SELECT COUNT(*) FROM edges e
87
- WHERE e.source_id = n.id OR e.target_id = n.id
88
- ) > 2
89
95
  LIMIT 200
90
96
  `).all() as { id: string; name: string; file_path: string }[];
91
97
 
@@ -175,3 +181,37 @@ export function computeAuthorAnalytics(repoPath: string, db: MonographDb): Autho
175
181
 
176
182
  return { authors, topOwners, botAuthors, unownedFiles };
177
183
  }
184
+
185
+ /** Format AuthorAnalyticsReport as structured text for LLM navigation. */
186
+ export function formatAuthorAnalytics(report: AuthorAnalyticsReport): string {
187
+ if (report.authors.length === 0) {
188
+ return 'Author analytics: no commit history found.';
189
+ }
190
+
191
+ const lines: string[] = [
192
+ `Author analytics: ${report.authors.length} contributor(s), ${report.unownedFiles} unowned file(s).`,
193
+ '',
194
+ ];
195
+
196
+ if (report.topOwners.length > 0) {
197
+ lines.push('Top file owners (non-bot):');
198
+ for (const a of report.topOwners) {
199
+ const trend = a.churnTrend === 'accelerating' ? ' [↑ accelerating]'
200
+ : a.churnTrend === 'declining' ? ' [↓ declining]' : '';
201
+ lines.push(` ${a.author} files:${a.filesOwned} commits:${a.commitCount} recent30d:${a.recentCommits}${trend}`);
202
+ }
203
+ lines.push('');
204
+ }
205
+
206
+ if (report.botAuthors.length > 0) {
207
+ lines.push(`Bot/automation authors (${report.botAuthors.length}): ${report.botAuthors.join(', ')}`);
208
+ lines.push('');
209
+ }
210
+
211
+ if (report.unownedFiles > 0) {
212
+ lines.push(`Unowned files: ${report.unownedFiles} file(s) have no majority author (> 50% commits).`);
213
+ lines.push('Consider adding CODEOWNERS entries for these files.');
214
+ }
215
+
216
+ return lines.join('\n').trimEnd();
217
+ }