@monoes/monograph 1.5.3 → 1.5.4

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 (500) hide show
  1. package/.monodesign/hook.cache.json +1 -1
  2. package/__tests__/integration/call-attribution.test.ts +116 -0
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/coverage-summary.json +399 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +881 -0
  8. package/coverage/prettify.css +1 -0
  9. package/coverage/prettify.js +2 -0
  10. package/coverage/sort-arrow-sprite.png +0 -0
  11. package/coverage/sorter.js +210 -0
  12. package/coverage/src/analysis/benchmark.ts.html +481 -0
  13. package/coverage/src/analysis/changed-files.ts.html +304 -0
  14. package/coverage/src/analysis/changed-workspaces.ts.html +367 -0
  15. package/coverage/src/analysis/churn-cache.ts.html +622 -0
  16. package/coverage/src/analysis/churn-trend.ts.html +256 -0
  17. package/coverage/src/analysis/churn.ts.html +874 -0
  18. package/coverage/src/analysis/cluster-quality.ts.html +511 -0
  19. package/coverage/src/analysis/combined.ts.html +208 -0
  20. package/coverage/src/analysis/corpus-health.ts.html +208 -0
  21. package/coverage/src/analysis/coverage-gaps.ts.html +823 -0
  22. package/coverage/src/analysis/dep-classification.ts.html +586 -0
  23. package/coverage/src/analysis/dependency-health.ts.html +523 -0
  24. package/coverage/src/analysis/duplicate-exports.ts.html +415 -0
  25. package/coverage/src/analysis/fallow-suppression.ts.html +379 -0
  26. package/coverage/src/analysis/feature-flags-config.ts.html +226 -0
  27. package/coverage/src/analysis/feature-flags.ts.html +754 -0
  28. package/coverage/src/analysis/file-classifier.ts.html +346 -0
  29. package/coverage/src/analysis/git-changed-files.ts.html +271 -0
  30. package/coverage/src/analysis/health-baseline.ts.html +325 -0
  31. package/coverage/src/analysis/index.html +641 -0
  32. package/coverage/src/analysis/issue-filters.ts.html +253 -0
  33. package/coverage/src/analysis/list.ts.html +307 -0
  34. package/coverage/src/analysis/node-similarity.ts.html +430 -0
  35. package/coverage/src/analysis/private-type-leaks.ts.html +442 -0
  36. package/coverage/src/analysis/progress.ts.html +214 -0
  37. package/coverage/src/analysis/project-state.ts.html +370 -0
  38. package/coverage/src/analysis/regression-config.ts.html +193 -0
  39. package/coverage/src/analysis/rule-overrides.ts.html +328 -0
  40. package/coverage/src/analysis/scripts.ts.html +370 -0
  41. package/coverage/src/analysis/shape-extractor.ts.html +691 -0
  42. package/coverage/src/analysis/since-duration.ts.html +211 -0
  43. package/coverage/src/analysis/suppression-context.ts.html +370 -0
  44. package/coverage/src/analysis/suppressions.ts.html +325 -0
  45. package/coverage/src/analysis/surprise-scorer.ts.html +307 -0
  46. package/coverage/src/analysis/targets.ts.html +796 -0
  47. package/coverage/src/analysis/unused-class-members.ts.html +322 -0
  48. package/coverage/src/analysis/workspace-filter.ts.html +352 -0
  49. package/coverage/src/analyze/boundary-analysis.ts.html +472 -0
  50. package/coverage/src/analyze/index.html +161 -0
  51. package/coverage/src/analyze/predicates/file.ts.html +343 -0
  52. package/coverage/src/analyze/predicates/import.ts.html +355 -0
  53. package/coverage/src/analyze/predicates/index.html +146 -0
  54. package/coverage/src/analyze/predicates/lifecycle.ts.html +250 -0
  55. package/coverage/src/analyze/unused-deps.ts.html +295 -0
  56. package/coverage/src/analyze/unused-exports.ts.html +520 -0
  57. package/coverage/src/analyze/unused-files.ts.html +223 -0
  58. package/coverage/src/api/index.html +131 -0
  59. package/coverage/src/api/production-override.ts.html +274 -0
  60. package/coverage/src/api/programmatic.ts.html +439 -0
  61. package/coverage/src/audit.ts.html +787 -0
  62. package/coverage/src/cache/augment-cache.ts.html +274 -0
  63. package/coverage/src/cache/extraction-cache.ts.html +508 -0
  64. package/coverage/src/cache/index.html +131 -0
  65. package/coverage/src/check/filtering.ts.html +319 -0
  66. package/coverage/src/check/index.html +161 -0
  67. package/coverage/src/check/index.ts.html +382 -0
  68. package/coverage/src/check/output.ts.html +322 -0
  69. package/coverage/src/check/rules.ts.html +268 -0
  70. package/coverage/src/ci-template.ts.html +337 -0
  71. package/coverage/src/claude-cli.ts.html +262 -0
  72. package/coverage/src/cli/ai-context.ts.html +664 -0
  73. package/coverage/src/cli/augment.ts.html +427 -0
  74. package/coverage/src/cli/check-update.ts.html +205 -0
  75. package/coverage/src/cli/doctor.ts.html +631 -0
  76. package/coverage/src/cli/eval-server.ts.html +463 -0
  77. package/coverage/src/cli/hooks-install.ts.html +691 -0
  78. package/coverage/src/cli/index.html +221 -0
  79. package/coverage/src/cli/setup.ts.html +676 -0
  80. package/coverage/src/cli/skill-gen.ts.html +1126 -0
  81. package/coverage/src/commands/flags.ts.html +301 -0
  82. package/coverage/src/commands/index.html +116 -0
  83. package/coverage/src/config/boundary-config.ts.html +634 -0
  84. package/coverage/src/config/cli-schema.ts.html +328 -0
  85. package/coverage/src/config/config-parsing.ts.html +391 -0
  86. package/coverage/src/config/duplicates-config.ts.html +271 -0
  87. package/coverage/src/config/external-plugin.ts.html +316 -0
  88. package/coverage/src/config/health-config.ts.html +211 -0
  89. package/coverage/src/config/ignore-exports-used-in-file.ts.html +223 -0
  90. package/coverage/src/config/index.html +341 -0
  91. package/coverage/src/config/migration-types.ts.html +304 -0
  92. package/coverage/src/config/output-format.ts.html +157 -0
  93. package/coverage/src/config/plugins/builtin.ts.html +1318 -0
  94. package/coverage/src/config/plugins/index.html +146 -0
  95. package/coverage/src/config/plugins/registry.ts.html +316 -0
  96. package/coverage/src/config/plugins/types.ts.html +127 -0
  97. package/coverage/src/config/resolution.ts.html +391 -0
  98. package/coverage/src/config/rules.ts.html +295 -0
  99. package/coverage/src/config/schema-gen.ts.html +430 -0
  100. package/coverage/src/config/types.ts.html +532 -0
  101. package/coverage/src/config/used-class-members.ts.html +244 -0
  102. package/coverage/src/config/validate.ts.html +409 -0
  103. package/coverage/src/config/workspace.ts.html +496 -0
  104. package/coverage/src/coverage/cloud-client.ts.html +418 -0
  105. package/coverage/src/coverage/index.html +146 -0
  106. package/coverage/src/coverage/upload-inventory.ts.html +337 -0
  107. package/coverage/src/coverage/upload-source-maps.ts.html +376 -0
  108. package/coverage/src/discover/entry-points.ts.html +238 -0
  109. package/coverage/src/discover/index.html +131 -0
  110. package/coverage/src/discover/infrastructure-entries.ts.html +610 -0
  111. package/coverage/src/duplicates/detect/concatenation.ts.html +196 -0
  112. package/coverage/src/duplicates/detect/extraction.ts.html +406 -0
  113. package/coverage/src/duplicates/detect/filtering.ts.html +421 -0
  114. package/coverage/src/duplicates/detect/index.html +206 -0
  115. package/coverage/src/duplicates/detect/lcp.ts.html +166 -0
  116. package/coverage/src/duplicates/detect/ranking.ts.html +133 -0
  117. package/coverage/src/duplicates/detect/statistics.ts.html +292 -0
  118. package/coverage/src/duplicates/detect/suffix-array.ts.html +349 -0
  119. package/coverage/src/duplicates/families.ts.html +370 -0
  120. package/coverage/src/duplicates/index.html +161 -0
  121. package/coverage/src/duplicates/normalize.ts.html +259 -0
  122. package/coverage/src/duplicates/shingle-filter.ts.html +196 -0
  123. package/coverage/src/duplicates/token-types.ts.html +271 -0
  124. package/coverage/src/duplicates/tokenize/index.html +116 -0
  125. package/coverage/src/duplicates/tokenize/index.ts.html +397 -0
  126. package/coverage/src/error/fallow-error.ts.html +367 -0
  127. package/coverage/src/error/index.html +116 -0
  128. package/coverage/src/error.ts.html +226 -0
  129. package/coverage/src/export/adjacency-matrix.ts.html +466 -0
  130. package/coverage/src/export/analysis-json.ts.html +346 -0
  131. package/coverage/src/export/badge.ts.html +400 -0
  132. package/coverage/src/export/canvas.ts.html +211 -0
  133. package/coverage/src/export/codeclimate.ts.html +688 -0
  134. package/coverage/src/export/compact-health.ts.html +442 -0
  135. package/coverage/src/export/compact.ts.html +334 -0
  136. package/coverage/src/export/csv.ts.html +184 -0
  137. package/coverage/src/export/cypher.ts.html +184 -0
  138. package/coverage/src/export/dot.ts.html +241 -0
  139. package/coverage/src/export/gexf.ts.html +262 -0
  140. package/coverage/src/export/graphml.ts.html +235 -0
  141. package/coverage/src/export/html.ts.html +1729 -0
  142. package/coverage/src/export/index.html +401 -0
  143. package/coverage/src/export/index.ts.html +112 -0
  144. package/coverage/src/export/json.ts.html +193 -0
  145. package/coverage/src/export/markdown.ts.html +955 -0
  146. package/coverage/src/export/mermaid.ts.html +385 -0
  147. package/coverage/src/export/obsidian.ts.html +217 -0
  148. package/coverage/src/export/sarif.ts.html +820 -0
  149. package/coverage/src/export/svg.ts.html +289 -0
  150. package/coverage/src/fix/deps.ts.html +295 -0
  151. package/coverage/src/fix/enum-members.ts.html +394 -0
  152. package/coverage/src/fix/export-surgery.ts.html +676 -0
  153. package/coverage/src/fix/exports.ts.html +364 -0
  154. package/coverage/src/fix/index.html +176 -0
  155. package/coverage/src/fix/orchestrator.ts.html +286 -0
  156. package/coverage/src/graph/analyzer.ts.html +292 -0
  157. package/coverage/src/graph/author-analytics.ts.html +736 -0
  158. package/coverage/src/graph/baseline.ts.html +1381 -0
  159. package/coverage/src/graph/clone-families.ts.html +481 -0
  160. package/coverage/src/graph/codeowners-extended.ts.html +427 -0
  161. package/coverage/src/graph/codeowners-gitlab.ts.html +535 -0
  162. package/coverage/src/graph/codeowners.ts.html +736 -0
  163. package/coverage/src/graph/complexity.ts.html +685 -0
  164. package/coverage/src/graph/cross-reference-findings.ts.html +340 -0
  165. package/coverage/src/graph/cross-reference.ts.html +502 -0
  166. package/coverage/src/graph/cycle-detection.ts.html +1018 -0
  167. package/coverage/src/graph/cycles.ts.html +751 -0
  168. package/coverage/src/graph/dead-code.ts.html +469 -0
  169. package/coverage/src/graph/dependency-closure.ts.html +463 -0
  170. package/coverage/src/graph/dfs.ts.html +439 -0
  171. package/coverage/src/graph/diff.ts.html +361 -0
  172. package/coverage/src/graph/duplication-stats.ts.html +322 -0
  173. package/coverage/src/graph/edge-filter.ts.html +436 -0
  174. package/coverage/src/graph/explain.ts.html +484 -0
  175. package/coverage/src/graph/external-style-usage.ts.html +328 -0
  176. package/coverage/src/graph/health-score.ts.html +622 -0
  177. package/coverage/src/graph/hotspots.ts.html +742 -0
  178. package/coverage/src/graph/import-chain.ts.html +529 -0
  179. package/coverage/src/graph/index.html +746 -0
  180. package/coverage/src/graph/loader.ts.html +217 -0
  181. package/coverage/src/graph/maintainability.ts.html +553 -0
  182. package/coverage/src/graph/merge.ts.html +607 -0
  183. package/coverage/src/graph/mirrored-dirs.ts.html +691 -0
  184. package/coverage/src/graph/narrowing.ts.html +361 -0
  185. package/coverage/src/graph/node-search.ts.html +688 -0
  186. package/coverage/src/graph/node-types.ts.html +286 -0
  187. package/coverage/src/graph/normalize-id.ts.html +499 -0
  188. package/coverage/src/graph/pagerank.ts.html +580 -0
  189. package/coverage/src/graph/prune.ts.html +370 -0
  190. package/coverage/src/graph/re-exports/index.html +116 -0
  191. package/coverage/src/graph/re-exports/propagate.ts.html +370 -0
  192. package/coverage/src/graph/reachability.ts.html +382 -0
  193. package/coverage/src/graph/regex-search.ts.html +781 -0
  194. package/coverage/src/graph/regression.ts.html +682 -0
  195. package/coverage/src/graph/resolve/discover-types.ts.html +220 -0
  196. package/coverage/src/graph/resolve/dynamic-imports.ts.html +391 -0
  197. package/coverage/src/graph/resolve/fallbacks.ts.html +322 -0
  198. package/coverage/src/graph/resolve/index.html +281 -0
  199. package/coverage/src/graph/resolve/index.ts.html +289 -0
  200. package/coverage/src/graph/resolve/path-info.ts.html +208 -0
  201. package/coverage/src/graph/resolve/re-exports.ts.html +127 -0
  202. package/coverage/src/graph/resolve/react-native.ts.html +166 -0
  203. package/coverage/src/graph/resolve/require-imports.ts.html +139 -0
  204. package/coverage/src/graph/resolve/specifier.ts.html +445 -0
  205. package/coverage/src/graph/resolve/static-imports.ts.html +127 -0
  206. package/coverage/src/graph/resolve/types.ts.html +409 -0
  207. package/coverage/src/graph/resolve/upgrades.ts.html +181 -0
  208. package/coverage/src/graph/ripple-impact.ts.html +478 -0
  209. package/coverage/src/graph/risk-profile.ts.html +622 -0
  210. package/coverage/src/graph/statistics.ts.html +580 -0
  211. package/coverage/src/graph/stats.ts.html +556 -0
  212. package/coverage/src/graph/subgraph.ts.html +571 -0
  213. package/coverage/src/graph/topo-sort.ts.html +598 -0
  214. package/coverage/src/graph/wcc.ts.html +646 -0
  215. package/coverage/src/groups/contract-bridge.ts.html +394 -0
  216. package/coverage/src/groups/contract-registry.ts.html +874 -0
  217. package/coverage/src/groups/group-config.ts.html +409 -0
  218. package/coverage/src/groups/group-search.ts.html +502 -0
  219. package/coverage/src/groups/group-sync.ts.html +331 -0
  220. package/coverage/src/groups/grpc-extractor.ts.html +277 -0
  221. package/coverage/src/groups/index.html +221 -0
  222. package/coverage/src/groups/service-boundary.ts.html +442 -0
  223. package/coverage/src/groups/topic-extractor.ts.html +355 -0
  224. package/coverage/src/health/analysis-counts-snapshot.ts.html +256 -0
  225. package/coverage/src/health/analysis-counts.ts.html +295 -0
  226. package/coverage/src/health/complexity-findings.ts.html +430 -0
  227. package/coverage/src/health/distribution-thresholds.ts.html +247 -0
  228. package/coverage/src/health/grouping.ts.html +499 -0
  229. package/coverage/src/health/health-grouping.ts.html +343 -0
  230. package/coverage/src/health/health-report-types.ts.html +580 -0
  231. package/coverage/src/health/health-report.ts.html +328 -0
  232. package/coverage/src/health/health-score.ts.html +280 -0
  233. package/coverage/src/health/hotspot-utils.ts.html +250 -0
  234. package/coverage/src/health/hotspots.ts.html +397 -0
  235. package/coverage/src/health/index.html +551 -0
  236. package/coverage/src/health/large-functions.ts.html +187 -0
  237. package/coverage/src/health/ownership-metrics.ts.html +268 -0
  238. package/coverage/src/health/ownership-risk.ts.html +379 -0
  239. package/coverage/src/health/ownership.ts.html +607 -0
  240. package/coverage/src/health/quality-gate.ts.html +256 -0
  241. package/coverage/src/health/risk-profile.ts.html +202 -0
  242. package/coverage/src/health/runtime-coverage-report.ts.html +388 -0
  243. package/coverage/src/health/runtime-coverage.ts.html +298 -0
  244. package/coverage/src/health/scores.ts.html +301 -0
  245. package/coverage/src/health/scoring-types.ts.html +229 -0
  246. package/coverage/src/health/target-thresholds.ts.html +361 -0
  247. package/coverage/src/health/target-types.ts.html +247 -0
  248. package/coverage/src/health/targets.ts.html +538 -0
  249. package/coverage/src/health/timings.ts.html +343 -0
  250. package/coverage/src/health/trend-types.ts.html +256 -0
  251. package/coverage/src/health/trends.ts.html +343 -0
  252. package/coverage/src/health/vital-signs-compute.ts.html +409 -0
  253. package/coverage/src/health/vital-signs-snapshot.ts.html +301 -0
  254. package/coverage/src/health/vital-signs.ts.html +388 -0
  255. package/coverage/src/index.html +191 -0
  256. package/coverage/src/index.ts.html +418 -0
  257. package/coverage/src/ingest/git-clone.ts.html +823 -0
  258. package/coverage/src/ingest/index.html +161 -0
  259. package/coverage/src/ingest/query-memory.ts.html +481 -0
  260. package/coverage/src/ingest/transcribe.ts.html +847 -0
  261. package/coverage/src/ingest/url-ingest.ts.html +346 -0
  262. package/coverage/src/init/agent-hooks.ts.html +343 -0
  263. package/coverage/src/init/git-hooks.ts.html +298 -0
  264. package/coverage/src/init/index.html +146 -0
  265. package/coverage/src/init/project-detection.ts.html +400 -0
  266. package/coverage/src/license/index.html +116 -0
  267. package/coverage/src/license/manager.ts.html +445 -0
  268. package/coverage/src/lsp/code-actions.ts.html +466 -0
  269. package/coverage/src/lsp/code-lens.ts.html +253 -0
  270. package/coverage/src/lsp/diagnostics-ext.ts.html +535 -0
  271. package/coverage/src/lsp/diagnostics-push.ts.html +394 -0
  272. package/coverage/src/lsp/diagnostics.ts.html +379 -0
  273. package/coverage/src/lsp/hover.ts.html +277 -0
  274. package/coverage/src/lsp/index.html +206 -0
  275. package/coverage/src/lsp/server.ts.html +847 -0
  276. package/coverage/src/mcp/index.html +146 -0
  277. package/coverage/src/mcp/params.ts.html +688 -0
  278. package/coverage/src/mcp/resources.ts.html +457 -0
  279. package/coverage/src/mcp/tool-builders.ts.html +694 -0
  280. package/coverage/src/mcp-resources/communities-resource.ts.html +301 -0
  281. package/coverage/src/mcp-resources/graph-resource.ts.html +127 -0
  282. package/coverage/src/mcp-resources/index.html +251 -0
  283. package/coverage/src/mcp-resources/index.ts.html +112 -0
  284. package/coverage/src/mcp-resources/named-cluster-resource.ts.html +190 -0
  285. package/coverage/src/mcp-resources/named-process-resource.ts.html +163 -0
  286. package/coverage/src/mcp-resources/processes-resource.ts.html +286 -0
  287. package/coverage/src/mcp-resources/repo-context-resource.ts.html +193 -0
  288. package/coverage/src/mcp-resources/repos-resource.ts.html +148 -0
  289. package/coverage/src/mcp-resources/schema-resource.ts.html +223 -0
  290. package/coverage/src/mcp-resources/wiki-resource.ts.html +154 -0
  291. package/coverage/src/mcp-tools/api-impact.ts.html +628 -0
  292. package/coverage/src/mcp-tools/context.ts.html +418 -0
  293. package/coverage/src/mcp-tools/cypher.ts.html +172 -0
  294. package/coverage/src/mcp-tools/detect-changes.ts.html +433 -0
  295. package/coverage/src/mcp-tools/explain.ts.html +280 -0
  296. package/coverage/src/mcp-tools/graph-query.ts.html +469 -0
  297. package/coverage/src/mcp-tools/group-list.ts.html +349 -0
  298. package/coverage/src/mcp-tools/group-query.ts.html +163 -0
  299. package/coverage/src/mcp-tools/group-sync.ts.html +181 -0
  300. package/coverage/src/mcp-tools/impact.ts.html +691 -0
  301. package/coverage/src/mcp-tools/index.html +431 -0
  302. package/coverage/src/mcp-tools/index.ts.html +148 -0
  303. package/coverage/src/mcp-tools/list-repos.ts.html +199 -0
  304. package/coverage/src/mcp-tools/neighbors.ts.html +325 -0
  305. package/coverage/src/mcp-tools/query.ts.html +343 -0
  306. package/coverage/src/mcp-tools/rename.ts.html +439 -0
  307. package/coverage/src/mcp-tools/route-map.ts.html +379 -0
  308. package/coverage/src/mcp-tools/serve.ts.html +208 -0
  309. package/coverage/src/mcp-tools/shape-check.ts.html +631 -0
  310. package/coverage/src/mcp-tools/staleness.ts.html +124 -0
  311. package/coverage/src/mcp-tools/tool-map.ts.html +283 -0
  312. package/coverage/src/mcp-tools/wiki-build.ts.html +196 -0
  313. package/coverage/src/mcp-tools/wiki.ts.html +169 -0
  314. package/coverage/src/migrate/index.html +161 -0
  315. package/coverage/src/migrate/jscpd.ts.html +319 -0
  316. package/coverage/src/migrate/jsonc-gen.ts.html +199 -0
  317. package/coverage/src/migrate/knip.ts.html +463 -0
  318. package/coverage/src/migrate/toml-gen.ts.html +229 -0
  319. package/coverage/src/parsers/c.ts.html +178 -0
  320. package/coverage/src/parsers/cpp.ts.html +172 -0
  321. package/coverage/src/parsers/csharp.ts.html +214 -0
  322. package/coverage/src/parsers/dart.ts.html +202 -0
  323. package/coverage/src/parsers/extractor.ts.html +691 -0
  324. package/coverage/src/parsers/go.ts.html +166 -0
  325. package/coverage/src/parsers/index.html +386 -0
  326. package/coverage/src/parsers/java.ts.html +166 -0
  327. package/coverage/src/parsers/kotlin.ts.html +193 -0
  328. package/coverage/src/parsers/language-config.ts.html +169 -0
  329. package/coverage/src/parsers/language-parsers.ts.html +445 -0
  330. package/coverage/src/parsers/loader.ts.html +532 -0
  331. package/coverage/src/parsers/named-bindings.ts.html +274 -0
  332. package/coverage/src/parsers/php.ts.html +196 -0
  333. package/coverage/src/parsers/python.ts.html +163 -0
  334. package/coverage/src/parsers/ruby.ts.html +202 -0
  335. package/coverage/src/parsers/rust.ts.html +160 -0
  336. package/coverage/src/parsers/swift.ts.html +169 -0
  337. package/coverage/src/parsers/typescript.ts.html +208 -0
  338. package/coverage/src/parsers/vue.ts.html +322 -0
  339. package/coverage/src/pipeline/cluster-only.ts.html +277 -0
  340. package/coverage/src/pipeline/effort.ts.html +235 -0
  341. package/coverage/src/pipeline/index.html +191 -0
  342. package/coverage/src/pipeline/orchestrator.ts.html +844 -0
  343. package/coverage/src/pipeline/phases/boundary.ts.html +538 -0
  344. package/coverage/src/pipeline/phases/bridge-adapters/electron-ipc.ts.html +358 -0
  345. package/coverage/src/pipeline/phases/bridge-adapters/index.html +176 -0
  346. package/coverage/src/pipeline/phases/bridge-adapters/registry.ts.html +127 -0
  347. package/coverage/src/pipeline/phases/bridge-adapters/tauri.ts.html +394 -0
  348. package/coverage/src/pipeline/phases/bridge-adapters/types.ts.html +178 -0
  349. package/coverage/src/pipeline/phases/bridge-adapters/wails.ts.html +184 -0
  350. package/coverage/src/pipeline/phases/bridge-resolver.ts.html +316 -0
  351. package/coverage/src/pipeline/phases/call-site-extractors.ts.html +592 -0
  352. package/coverage/src/pipeline/phases/communities.ts.html +634 -0
  353. package/coverage/src/pipeline/phases/contextual-proximity.ts.html +349 -0
  354. package/coverage/src/pipeline/phases/cross-file.ts.html +433 -0
  355. package/coverage/src/pipeline/phases/docs-parse.ts.html +1006 -0
  356. package/coverage/src/pipeline/phases/evidence.ts.html +112 -0
  357. package/coverage/src/pipeline/phases/field-access.ts.html +232 -0
  358. package/coverage/src/pipeline/phases/framework-detect.ts.html +412 -0
  359. package/coverage/src/pipeline/phases/god-nodes.ts.html +574 -0
  360. package/coverage/src/pipeline/phases/import-resolver.ts.html +427 -0
  361. package/coverage/src/pipeline/phases/index.html +626 -0
  362. package/coverage/src/pipeline/phases/leiden.ts.html +316 -0
  363. package/coverage/src/pipeline/phases/llm-extract.ts.html +577 -0
  364. package/coverage/src/pipeline/phases/markdown.ts.html +448 -0
  365. package/coverage/src/pipeline/phases/middleware-extractor.ts.html +466 -0
  366. package/coverage/src/pipeline/phases/module-resolution.ts.html +967 -0
  367. package/coverage/src/pipeline/phases/mro.ts.html +253 -0
  368. package/coverage/src/pipeline/phases/orm.ts.html +1594 -0
  369. package/coverage/src/pipeline/phases/parse.ts.html +751 -0
  370. package/coverage/src/pipeline/phases/pdf-parse.ts.html +484 -0
  371. package/coverage/src/pipeline/phases/processes.ts.html +832 -0
  372. package/coverage/src/pipeline/phases/re-export-propagation.ts.html +565 -0
  373. package/coverage/src/pipeline/phases/reachability.ts.html +553 -0
  374. package/coverage/src/pipeline/phases/routes.ts.html +1135 -0
  375. package/coverage/src/pipeline/phases/scan.ts.html +484 -0
  376. package/coverage/src/pipeline/phases/scope-resolution.ts.html +1147 -0
  377. package/coverage/src/pipeline/phases/structure.ts.html +289 -0
  378. package/coverage/src/pipeline/phases/suggest.ts.html +361 -0
  379. package/coverage/src/pipeline/phases/surprises.ts.html +334 -0
  380. package/coverage/src/pipeline/phases/tools.ts.html +625 -0
  381. package/coverage/src/pipeline/phases/variables-phase.ts.html +196 -0
  382. package/coverage/src/pipeline/phases/variables.ts.html +286 -0
  383. package/coverage/src/pipeline/phases/wildcard-phase.ts.html +235 -0
  384. package/coverage/src/pipeline/phases/wildcard-synthesis.ts.html +727 -0
  385. package/coverage/src/pipeline/progress.ts.html +259 -0
  386. package/coverage/src/pipeline/runner.ts.html +469 -0
  387. package/coverage/src/pipeline/types.ts.html +208 -0
  388. package/coverage/src/query/cypher-parser.ts.html +1435 -0
  389. package/coverage/src/query/index.html +116 -0
  390. package/coverage/src/registry/index.html +116 -0
  391. package/coverage/src/registry/repo-registry.ts.html +460 -0
  392. package/coverage/src/regression/baseline-deltas.ts.html +730 -0
  393. package/coverage/src/regression/baseline.ts.html +313 -0
  394. package/coverage/src/regression/counts.ts.html +346 -0
  395. package/coverage/src/regression/index.html +176 -0
  396. package/coverage/src/regression/outcome.ts.html +319 -0
  397. package/coverage/src/regression/tolerance.ts.html +190 -0
  398. package/coverage/src/report/attributed-duplication.ts.html +421 -0
  399. package/coverage/src/report/badge-svg.ts.html +385 -0
  400. package/coverage/src/report/cross-ref-human.ts.html +235 -0
  401. package/coverage/src/report/duplication-grouping.ts.html +307 -0
  402. package/coverage/src/report/grouping.ts.html +496 -0
  403. package/coverage/src/report/human-check.ts.html +958 -0
  404. package/coverage/src/report/human-dupes.ts.html +511 -0
  405. package/coverage/src/report/human-health.ts.html +670 -0
  406. package/coverage/src/report/human.ts.html +445 -0
  407. package/coverage/src/report/index.html +341 -0
  408. package/coverage/src/report/json-actions.ts.html +358 -0
  409. package/coverage/src/report/json-builders.ts.html +295 -0
  410. package/coverage/src/report/json-schema.ts.html +310 -0
  411. package/coverage/src/report/number-format.ts.html +358 -0
  412. package/coverage/src/report/output-grouped.ts.html +391 -0
  413. package/coverage/src/report/pipeline-perf.ts.html +253 -0
  414. package/coverage/src/report/trace-human.ts.html +466 -0
  415. package/coverage/src/reporting/graph-report.ts.html +1378 -0
  416. package/coverage/src/reporting/index.html +116 -0
  417. package/coverage/src/results/fallow-results.ts.html +541 -0
  418. package/coverage/src/results/index.html +131 -0
  419. package/coverage/src/results/types.ts.html +532 -0
  420. package/coverage/src/scope-resolution/evidence-weights.ts.html +208 -0
  421. package/coverage/src/scope-resolution/index.html +116 -0
  422. package/coverage/src/search/ast-chunker.ts.html +703 -0
  423. package/coverage/src/search/diacritic.ts.html +106 -0
  424. package/coverage/src/search/hybrid-query.ts.html +280 -0
  425. package/coverage/src/search/index.html +161 -0
  426. package/coverage/src/search/rrf.ts.html +238 -0
  427. package/coverage/src/security/index.html +131 -0
  428. package/coverage/src/security/safe-fetch.ts.html +424 -0
  429. package/coverage/src/security/sensitive-files.ts.html +157 -0
  430. package/coverage/src/skills/index.html +116 -0
  431. package/coverage/src/skills/platform-skills.ts.html +784 -0
  432. package/coverage/src/staleness/git-staleness.ts.html +283 -0
  433. package/coverage/src/staleness/index.html +116 -0
  434. package/coverage/src/storage/db.ts.html +415 -0
  435. package/coverage/src/storage/edge-store.ts.html +400 -0
  436. package/coverage/src/storage/file-cache.ts.html +319 -0
  437. package/coverage/src/storage/fts-store.ts.html +1240 -0
  438. package/coverage/src/storage/index.html +221 -0
  439. package/coverage/src/storage/node-store.ts.html +712 -0
  440. package/coverage/src/storage/schema.ts.html +598 -0
  441. package/coverage/src/storage/suppressions.ts.html +520 -0
  442. package/coverage/src/storage/unlinked-refs.ts.html +436 -0
  443. package/coverage/src/types.ts.html +676 -0
  444. package/coverage/src/validation/extraction-validator.ts.html +247 -0
  445. package/coverage/src/validation/index.html +116 -0
  446. package/coverage/src/watch/index.html +116 -0
  447. package/coverage/src/watch/watcher.ts.html +499 -0
  448. package/coverage/src/web/analyze-api.ts.html +523 -0
  449. package/coverage/src/web/api.ts.html +1837 -0
  450. package/coverage/src/web/async-jobs.ts.html +412 -0
  451. package/coverage/src/web/index.html +191 -0
  452. package/coverage/src/web/mcp-http.ts.html +766 -0
  453. package/coverage/src/web/react-ui.ts.html +319 -0
  454. package/coverage/src/web/server.ts.html +355 -0
  455. package/coverage/src/wiki/gist-publisher.ts.html +313 -0
  456. package/coverage/src/wiki/index.html +176 -0
  457. package/coverage/src/wiki/prompt-builder.ts.html +190 -0
  458. package/coverage/src/wiki/providers.ts.html +487 -0
  459. package/coverage/src/wiki/wiki-generator.ts.html +736 -0
  460. package/coverage/src/wiki/wiki-store.ts.html +232 -0
  461. package/dist/src/cli/eval-server.d.ts.map +1 -1
  462. package/dist/src/cli/eval-server.js +8 -1
  463. package/dist/src/cli/eval-server.js.map +1 -1
  464. package/dist/src/cli/skill-gen.js.map +1 -1
  465. package/dist/src/graph/regex-search.d.ts.map +1 -1
  466. package/dist/src/graph/regex-search.js +13 -13
  467. package/dist/src/graph/regex-search.js.map +1 -1
  468. package/dist/src/graph/statistics.d.ts +0 -4
  469. package/dist/src/graph/statistics.d.ts.map +1 -1
  470. package/dist/src/graph/statistics.js +0 -7
  471. package/dist/src/graph/statistics.js.map +1 -1
  472. package/dist/src/pipeline/phases/call-site-extractors.d.ts +8 -0
  473. package/dist/src/pipeline/phases/call-site-extractors.d.ts.map +1 -1
  474. package/dist/src/pipeline/phases/call-site-extractors.js +9 -9
  475. package/dist/src/pipeline/phases/call-site-extractors.js.map +1 -1
  476. package/dist/src/pipeline/phases/module-resolution.js +2 -2
  477. package/dist/src/pipeline/phases/module-resolution.js.map +1 -1
  478. package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
  479. package/dist/src/pipeline/phases/scope-resolution.js +76 -1
  480. package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
  481. package/dist/src/registry/repo-registry.d.ts.map +1 -1
  482. package/dist/src/registry/repo-registry.js +1 -2
  483. package/dist/src/registry/repo-registry.js.map +1 -1
  484. package/dist/tsconfig.tsbuildinfo +1 -1
  485. package/package.json +4 -3
  486. package/src/cli/eval-server.ts +8 -1
  487. package/src/cli/skill-gen.ts +16 -16
  488. package/src/graph/regex-search.ts +13 -14
  489. package/src/graph/statistics.ts +0 -8
  490. package/src/pipeline/phases/call-site-extractors.ts +17 -9
  491. package/src/pipeline/phases/module-resolution.ts +2 -2
  492. package/src/pipeline/phases/scope-resolution.ts +89 -1
  493. package/src/registry/repo-registry.ts +1 -2
  494. package/vitest.config.ts +7 -0
  495. package/src/analysis/cycles.ts +0 -268
  496. package/src/analysis/trace.ts +0 -300
  497. package/src/analysis/worker-pool.ts +0 -81
  498. package/src/graph/clone-detector.ts +0 -163
  499. package/src/graph/shingle-filter.ts +0 -149
  500. package/src/watch/watch-runner.ts +0 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monograph",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "type": "module",
5
5
  "description": "Native TypeScript code intelligence engine for monomind",
6
6
  "main": "dist/src/index.js",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsc",
23
- "test": "vitest run"
23
+ "test": "vitest run",
24
+ "test:coverage": "vitest run --coverage"
24
25
  },
25
26
  "dependencies": {
26
27
  "better-sqlite3": "^12.0.0",
@@ -56,4 +57,4 @@
56
57
  "typescript": "^7.0.2",
57
58
  "vitest": "^4.1.4"
58
59
  }
59
- }
60
+ }
@@ -9,6 +9,7 @@
9
9
  * createEvalServer('/path/to/repo', 4848)
10
10
  */
11
11
 
12
+ import { createRequire } from 'node:module';
12
13
  import type { Server } from 'http';
13
14
  import type { Application } from 'express';
14
15
  import { openDb, closeDb } from '../storage/db.js';
@@ -49,7 +50,13 @@ export function createEvalServer(repoPath: string, _port?: number): EvalServerHa
49
50
  function getApp(): Application {
50
51
  if (_app) return _app;
51
52
 
52
- // eslint-disable-next-line @typescript-eslint/no-require-imports
53
+ // createRequire, not a bare require(): this package is ESM
54
+ // ("type": "module"), where `require` is undefined at runtime — the
55
+ // eslint-disable that used to sit here silenced the lint but the call
56
+ // still threw "require is not defined" the moment getApp() ran.
57
+ // getApp() is synchronous, so `await import()` is not an option; this is
58
+ // the correct ESM escape hatch for a deliberately deferred load.
59
+ const require = createRequire(import.meta.url);
53
60
  const express = require('express') as typeof import('express');
54
61
  const app = express();
55
62
  app.use(express.json());
@@ -60,29 +60,29 @@ export async function generateSkillFiles(
60
60
  const db = openDb(dbPath);
61
61
 
62
62
  try {
63
- const communities = queryCommunities(db);
63
+ const communities = queryCommunities(db);
64
64
 
65
- if (communities.length === 0) {
66
- return { filesWritten: [], communityCount: 0 };
67
- }
65
+ if (communities.length === 0) {
66
+ return { filesWritten: [], communityCount: 0 };
67
+ }
68
68
 
69
- await fs.mkdir(skillsDir, { recursive: true });
69
+ await fs.mkdir(skillsDir, { recursive: true });
70
70
 
71
- const filesWritten: string[] = [];
71
+ const filesWritten: string[] = [];
72
72
 
73
- for (const community of communities) {
74
- const members = queryMembers(db, community.community_id);
75
- const crossConnections = queryCrossConnections(db, community.community_id);
73
+ for (const community of communities) {
74
+ const members = queryMembers(db, community.community_id);
75
+ const crossConnections = queryCrossConnections(db, community.community_id);
76
76
 
77
- const content = renderSkillMarkdown(community, members, crossConnections, repoPath);
77
+ const content = renderSkillMarkdown(community, members, crossConnections, repoPath);
78
78
 
79
- const kebab = toKebabName(community.label || `community-${community.community_id}`);
80
- const filePath = path.join(skillsDir, `${kebab}.md`);
81
- await fs.writeFile(filePath, content, 'utf-8');
82
- filesWritten.push(filePath);
83
- }
79
+ const kebab = toKebabName(community.label || `community-${community.community_id}`);
80
+ const filePath = path.join(skillsDir, `${kebab}.md`);
81
+ await fs.writeFile(filePath, content, 'utf-8');
82
+ filesWritten.push(filePath);
83
+ }
84
84
 
85
- return { filesWritten, communityCount: communities.length };
85
+ return { filesWritten, communityCount: communities.length };
86
86
  } finally {
87
87
  closeDb(db);
88
88
  }
@@ -48,20 +48,6 @@ export function regexSearchNodes(
48
48
  const results: RegexNodeMatch[] = [];
49
49
 
50
50
  for (const r of rows) {
51
- const node: MonographNode = {
52
- id: r.id,
53
- label: r.label as MonographNode['label'],
54
- name: r.name,
55
- normLabel: r.norm_label,
56
- filePath: r.file_path ?? undefined,
57
- startLine: r.start_line ?? undefined,
58
- endLine: r.end_line ?? undefined,
59
- communityId: r.community_id ?? undefined,
60
- isExported: r.is_exported === 1,
61
- language: r.language ?? undefined,
62
- properties: r.properties ? (JSON.parse(r.properties) as Record<string, unknown>) : undefined,
63
- };
64
-
65
51
  for (const field of fields) {
66
52
  const value =
67
53
  field === 'name' ? r.name :
@@ -70,6 +56,19 @@ export function regexSearchNodes(
70
56
  r.label;
71
57
 
72
58
  if (value && re.test(value)) {
59
+ const node: MonographNode = {
60
+ id: r.id,
61
+ label: r.label as MonographNode['label'],
62
+ name: r.name,
63
+ normLabel: r.norm_label,
64
+ filePath: r.file_path ?? undefined,
65
+ startLine: r.start_line ?? undefined,
66
+ endLine: r.end_line ?? undefined,
67
+ communityId: r.community_id ?? undefined,
68
+ isExported: r.is_exported === 1,
69
+ language: r.language ?? undefined,
70
+ properties: r.properties ? (JSON.parse(r.properties) as Record<string, unknown>) : undefined,
71
+ };
73
72
  results.push({ node, field });
74
73
  break;
75
74
  }
@@ -27,14 +27,6 @@ function getGraph(db: MonographDb): Graph {
27
27
  return graph;
28
28
  }
29
29
 
30
- /**
31
- * Evict the cached graph for a given DB instance (call after writes).
32
- */
33
- export function invalidateGraphCache(db: MonographDb): void {
34
- const key = (db as unknown as { name: string }).name ?? '__default__';
35
- _graphCache.delete(key);
36
- }
37
-
38
30
  /**
39
31
  * Graph density: ratio of actual edges to maximum possible directed edges.
40
32
  * For a directed graph with n nodes: max = n * (n - 1).
@@ -7,6 +7,14 @@ export interface CallSite {
7
7
  form: 'method' | 'direct' | 'dynamic';
8
8
  receiverName?: string;
9
9
  methodName?: string;
10
+ /**
11
+ * Byte offset of the match in the source, used by scope resolution to find
12
+ * the function enclosing this call. Without it every CALLS edge is
13
+ * attributed to the containing File, which makes a function look "used" by
14
+ * its own file just for being declared there — see the dead-code masking
15
+ * described in graph/dead-code.ts.
16
+ */
17
+ offset?: number;
10
18
  }
11
19
 
12
20
  // ── Language keyword sets ────────────────────────────────────────────────────
@@ -71,13 +79,13 @@ function extractSimpleCallSites(
71
79
  const methodPattern = /(\w+)\.(\w+)\s*\(/g;
72
80
  let m: RegExpExecArray | null;
73
81
  while ((m = methodPattern.exec(source)) !== null) {
74
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
82
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
75
83
  }
76
84
  const directPattern = new RegExp(`(?<![.[\\w])(${directCharClass})\\s*\\(`, 'g');
77
85
  while ((m = directPattern.exec(source)) !== null) {
78
86
  const name = m[1]!;
79
87
  if (keywords.has(name)) continue;
80
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct' });
88
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct' });
81
89
  }
82
90
  return sites;
83
91
  }
@@ -112,44 +120,44 @@ export function extractCallSites(
112
120
  const methodPattern = /(\w+)\.(\w+)\s*(?:<[^>]*>\s*)?\(/g;
113
121
  let m: RegExpExecArray | null;
114
122
  while ((m = methodPattern.exec(source)) !== null) {
115
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
123
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
116
124
  }
117
125
 
118
126
  const chainedPattern = /[)\]]\s*\.(\w+)\s*(?:<[^>]*>\s*)?\(/g;
119
127
  while ((m = chainedPattern.exec(source)) !== null) {
120
128
  const name = m[1];
121
129
  if (JS_KEYWORDS.has(name)) continue;
122
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `?.${name}`, form: 'method', methodName: name });
130
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `?.${name}`, form: 'method', methodName: name });
123
131
  }
124
132
 
125
133
  const directPattern = /(?<![.[\w])([A-Za-z_$][\w$]*)\s*(?:<[^>]*>\s*)?\(/g;
126
134
  while ((m = directPattern.exec(source)) !== null) {
127
135
  const name = m[1];
128
136
  if (JS_KEYWORDS.has(name)) continue;
129
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct', methodName: name });
137
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct', methodName: name });
130
138
  }
131
139
 
132
140
  const newPattern = /\bnew\s+([A-Z][\w$]*)\s*(?:<[^>]*>\s*)?\(/g;
133
141
  while ((m = newPattern.exec(source)) !== null) {
134
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `new ${m[1]}`, form: 'direct', methodName: m[1] });
142
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `new ${m[1]}`, form: 'direct', methodName: m[1] });
135
143
  }
136
144
 
137
145
  const dynamicPattern = /\w+\s*\[[\w'"` ]+\]\s*\(/g;
138
146
  while ((m = dynamicPattern.exec(source)) !== null) {
139
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: m[0], form: 'dynamic' });
147
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: m[0], form: 'dynamic' });
140
148
  }
141
149
  } else if (PY_EXTS.has(ext)) {
142
150
  const methodPattern = /(\w+)\.(\w+)\s*\(/g;
143
151
  let m: RegExpExecArray | null;
144
152
  while ((m = methodPattern.exec(source)) !== null) {
145
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
153
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: `${m[1]}.${m[2]}`, form: 'method', receiverName: m[1], methodName: m[2] });
146
154
  }
147
155
 
148
156
  const directPattern = /(?<![.[\w])([A-Za-z_][\w]*)\s*\(/g;
149
157
  while ((m = directPattern.exec(source)) !== null) {
150
158
  const name = m[1];
151
159
  if (PY_KEYWORDS.has(name)) continue;
152
- sites.push({ callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct', methodName: name });
160
+ sites.push({ offset: m.index, callerFileNodeId: fileNodeId, callerFilePath: filePath, calleeRaw: name, form: 'direct', methodName: name });
153
161
  }
154
162
  }
155
163
 
@@ -1,6 +1,6 @@
1
1
  // ── Module resolution for import specifiers ──────────────────────────────────
2
2
 
3
- import { readFileSync, existsSync } from 'fs';
3
+ import { readFileSync, existsSync, readdirSync } from 'fs';
4
4
  import { join, extname, dirname, resolve as resolvePath } from 'path';
5
5
  import type { MonographNode } from '../../types.js';
6
6
  import { TS_JS_EXTS, CJS_MJS_EXTS } from './call-site-extractors.js';
@@ -40,7 +40,7 @@ export function buildWorkspacePackageMap(repoPath: string): Map<string, string>
40
40
  const scanDirs = (base: string, depth: number) => {
41
41
  if (depth > 2) return;
42
42
  let entries: string[];
43
- try { entries = require('fs').readdirSync(base); } catch { return; }
43
+ try { entries = readdirSync(base); } catch { return; }
44
44
  for (const e of entries) {
45
45
  const full = join(base, e);
46
46
  const pkgJson = join(full, 'package.json');
@@ -56,6 +56,86 @@ function buildFunctionIndex(ctx: PipelineContext): {
56
56
  return { byFilePath, nameCounts };
57
57
  }
58
58
 
59
+ /** A callable node with a known line range, used to attribute a call to its caller. */
60
+ interface EnclosingSymbol {
61
+ id: string;
62
+ startLine: number;
63
+ endLine: number;
64
+ }
65
+
66
+ /**
67
+ * Line ranges of every callable, grouped by file, innermost-first.
68
+ *
69
+ * Call edges are attributed to the enclosing function rather than the file.
70
+ * Attributing them to the file makes a function appear "used" by its own file
71
+ * purely because it is declared there, which silently disabled dead-export
72
+ * detection: `detectDeadCodeNodes` rejects any candidate with an inbound CALLS
73
+ * edge, and that self-edge always existed.
74
+ */
75
+ function buildEnclosingIndex(ctx: PipelineContext): Map<string, EnclosingSymbol[]> {
76
+ const byFile = new Map<string, EnclosingSymbol[]>();
77
+ if (!ctx.db) return byFile;
78
+
79
+ const rows = ctx.db
80
+ .prepare(
81
+ `SELECT id, file_path, start_line, end_line FROM nodes
82
+ WHERE label IN ('Function', 'Method', 'Constructor')
83
+ AND file_path IS NOT NULL AND start_line IS NOT NULL AND end_line IS NOT NULL`
84
+ )
85
+ .all() as { id: string; file_path: string; start_line: number; end_line: number }[];
86
+
87
+ for (const row of rows) {
88
+ let list = byFile.get(row.file_path);
89
+ if (!list) { list = []; byFile.set(row.file_path, list); }
90
+ list.push({ id: row.id, startLine: row.start_line, endLine: row.end_line });
91
+ }
92
+
93
+ // Narrowest range first, so the first containing match is the innermost
94
+ // scope — a call inside a closure belongs to the closure, not the function
95
+ // that happens to wrap it.
96
+ for (const list of byFile.values()) {
97
+ list.sort((a, b) => (a.endLine - a.startLine) - (b.endLine - b.startLine));
98
+ }
99
+
100
+ return byFile;
101
+ }
102
+
103
+ /** Line-start offsets, for turning a match offset into a 1-based line number. */
104
+ function buildLineOffsets(source: string): number[] {
105
+ const offsets = [0];
106
+ for (let i = 0; i < source.length; i++) {
107
+ if (source.charCodeAt(i) === 10) offsets.push(i + 1);
108
+ }
109
+ return offsets;
110
+ }
111
+
112
+ function lineAtOffset(lineOffsets: number[], offset: number): number {
113
+ let lo = 0, hi = lineOffsets.length - 1;
114
+ while (lo < hi) {
115
+ const mid = (lo + hi + 1) >> 1;
116
+ if (lineOffsets[mid]! <= offset) lo = mid; else hi = mid - 1;
117
+ }
118
+ return lo + 1;
119
+ }
120
+
121
+ /**
122
+ * The innermost callable containing `offset`, or null when the call sits at
123
+ * module top level — which is genuinely file-scoped, so the file node stays
124
+ * the correct source for those.
125
+ */
126
+ function findEnclosingSymbolId(
127
+ enclosing: EnclosingSymbol[] | undefined,
128
+ lineOffsets: number[],
129
+ offset: number | undefined,
130
+ ): string | null {
131
+ if (!enclosing || enclosing.length === 0 || offset === undefined) return null;
132
+ const line = lineAtOffset(lineOffsets, offset);
133
+ for (const sym of enclosing) {
134
+ if (line >= sym.startLine && line <= sym.endLine) return sym.id;
135
+ }
136
+ return null;
137
+ }
138
+
59
139
  // ── Target resolution ────────────────────────────────────────────────────────
60
140
 
61
141
  function pickBestId(ids: string[], site: CallSite): string | null {
@@ -226,6 +306,7 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
226
306
 
227
307
  const allImportMaps = buildAllImportMapsFromSource(ctx.repoPath, fileNodesByPath, fileContents);
228
308
  const { byFilePath: fnIndex, nameCounts } = buildFunctionIndex(ctx);
309
+ const enclosingIndex = buildEnclosingIndex(ctx);
229
310
 
230
311
  const edgeStmts = ctx.db ? prepareEdgeStmts(ctx.db) : null;
231
312
  const resolveAllCalls = ctx.db?.transaction(() => {
@@ -243,6 +324,8 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
243
324
  }
244
325
 
245
326
  const callSites = extractCallSites(source, filePath, fileNode.id, ext);
327
+ const lineOffsets = buildLineOffsets(source);
328
+ const enclosing = enclosingIndex.get(filePath);
246
329
  const importMap = getImportMap(allImportMaps, fileNode.id);
247
330
  const ctorMap = (TS_JS_EXTS.has(ext) || CJS_MJS_EXTS.has(ext)) ? extractConstructorAssignments(source) : undefined;
248
331
  const importedFiles = [...new Set(importMap.values())];
@@ -260,7 +343,12 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
260
343
  ambiguous++;
261
344
  }
262
345
 
263
- const result = edgeStmts ? emitEdge(edgeStmts, site.callerFileNodeId, resolved.targetId) : 'skipped';
346
+ // Attribute the call to the function it sits in; fall back to the
347
+ // file node for genuine module-top-level calls.
348
+ const sourceId =
349
+ findEnclosingSymbolId(enclosing, lineOffsets, site.offset) ?? site.callerFileNodeId;
350
+
351
+ const result = edgeStmts ? emitEdge(edgeStmts, sourceId, resolved.targetId) : 'skipped';
264
352
  if (result === 'inserted' || result === 'upgraded') {
265
353
  resolvedEdges++;
266
354
  }
@@ -4,7 +4,7 @@
4
4
  * Persists at ~/.monograph/registry.json and tracks all repos indexed by monograph.
5
5
  */
6
6
 
7
- import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'fs';
7
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, renameSync } from 'fs';
8
8
  import { join, basename, dirname } from 'path';
9
9
  import { homedir } from 'os';
10
10
 
@@ -67,7 +67,6 @@ export function saveRegistry(registry: RepoRegistry): void {
67
67
  const tmpPath = registryPath + '.tmp';
68
68
  writeFileSync(tmpPath, JSON.stringify(registry, null, 2), 'utf8');
69
69
  // Rename is atomic on POSIX systems
70
- const { renameSync } = require('fs') as typeof import('fs');
71
70
  renameSync(tmpPath, registryPath);
72
71
  }
73
72
 
package/vitest.config.ts CHANGED
@@ -6,8 +6,15 @@ export default defineConfig({
6
6
  include: ['__tests__/**/*.test.ts', 'src/__tests__/**/*.test.ts'],
7
7
  exclude: ['node_modules', 'dist', '**/._*'],
8
8
  globals: true,
9
+ // Off by default to keep the edit/test loop fast; run
10
+ // `npm run test:coverage` (or pass --coverage) to measure.
9
11
  coverage: {
10
12
  enabled: false,
13
+ provider: 'v8',
14
+ reporter: ['text-summary', 'json-summary', 'html'],
15
+ reportsDirectory: './coverage',
16
+ include: ['src/**/*.ts'],
17
+ exclude: ['src/**/*.test.ts', 'src/__tests__/**', 'src/**/*.d.ts'],
11
18
  },
12
19
  },
13
20
  });
@@ -1,268 +0,0 @@
1
- import type { MonographDb } from '../storage/db.js';
2
-
3
- export interface DependencyCycle {
4
- files: string[]; // file paths forming the cycle, in order
5
- length: number;
6
- isCrossCommunity: boolean; // true if files span multiple communities
7
- edgeRelations: string[]; // relations used in the cycle edges
8
- }
9
-
10
- export interface CycleDetectionResult {
11
- cycles: DependencyCycle[];
12
- totalCycles: number;
13
- filesInCycles: number;
14
- longestCycle: number;
15
- crossCommunityCycles: number;
16
- }
17
-
18
- interface NodeRow {
19
- id: string;
20
- file_path: string | null;
21
- community_id: number | null;
22
- }
23
-
24
- interface EdgeRow {
25
- source_id: string;
26
- target_id: string;
27
- relation: string;
28
- }
29
-
30
- /**
31
- * Iterative Tarjan's SCC algorithm to avoid stack overflow on large graphs.
32
- * Returns arrays of node IDs, each array is one SCC.
33
- */
34
- function tarjanSCC(adjacency: Map<string, string[]>, nodes: string[]): string[][] {
35
- const index: Map<string, number> = new Map();
36
- const lowlink: Map<string, number> = new Map();
37
- const onStack: Set<string> = new Set();
38
- const stack: string[] = [];
39
- const sccs: string[][] = [];
40
- let counter = 0;
41
-
42
- // Iterative DFS using explicit call stack
43
- for (const startNode of nodes) {
44
- if (index.has(startNode)) continue;
45
-
46
- // Each frame: [nodeId, neighborIndex, parent]
47
- type Frame = { node: string; neighborIdx: number };
48
- const callStack: Frame[] = [{ node: startNode, neighborIdx: 0 }];
49
-
50
- index.set(startNode, counter);
51
- lowlink.set(startNode, counter);
52
- counter++;
53
- stack.push(startNode);
54
- onStack.add(startNode);
55
-
56
- while (callStack.length > 0) {
57
- const frame = callStack[callStack.length - 1];
58
- const { node } = frame;
59
- const neighbors = adjacency.get(node) ?? [];
60
-
61
- if (frame.neighborIdx < neighbors.length) {
62
- const neighbor = neighbors[frame.neighborIdx];
63
- frame.neighborIdx++;
64
-
65
- if (!index.has(neighbor)) {
66
- index.set(neighbor, counter);
67
- lowlink.set(neighbor, counter);
68
- counter++;
69
- stack.push(neighbor);
70
- onStack.add(neighbor);
71
- callStack.push({ node: neighbor, neighborIdx: 0 });
72
- } else if (onStack.has(neighbor)) {
73
- const curLow = lowlink.get(node)!;
74
- const neighborIdx = index.get(neighbor)!;
75
- if (neighborIdx < curLow) {
76
- lowlink.set(node, neighborIdx);
77
- }
78
- }
79
- } else {
80
- // Done with this node — pop and propagate lowlink to parent
81
- callStack.pop();
82
- if (callStack.length > 0) {
83
- const parent = callStack[callStack.length - 1].node;
84
- const parentLow = lowlink.get(parent)!;
85
- const nodeLow = lowlink.get(node)!;
86
- if (nodeLow < parentLow) {
87
- lowlink.set(parent, nodeLow);
88
- }
89
- }
90
-
91
- // If this is an SCC root, pop the stack
92
- if (lowlink.get(node) === index.get(node)) {
93
- const scc: string[] = [];
94
- let w: string;
95
- do {
96
- w = stack.pop()!;
97
- onStack.delete(w);
98
- scc.push(w);
99
- } while (w !== node);
100
- sccs.push(scc);
101
- }
102
- }
103
- }
104
- }
105
-
106
- return sccs;
107
- }
108
-
109
- /**
110
- * Enumerate elementary cycles within a given SCC using DFS.
111
- * Caps at maxCycles to avoid exponential blowup.
112
- */
113
- function findCyclesInSCC(
114
- scc: string[],
115
- adjacency: Map<string, string[]>,
116
- maxCycles: number
117
- ): string[][] {
118
- const sccSet = new Set(scc);
119
- const cycles: string[][] = [];
120
- const seen = new Set<string>();
121
-
122
- for (const startNode of scc) {
123
- if (cycles.length >= maxCycles) break;
124
-
125
- const path: string[] = [startNode];
126
- const onPath = new Set<string>([startNode]);
127
-
128
- const dfs = (node: string): void => {
129
- if (cycles.length >= maxCycles) return;
130
- const neighbors = adjacency.get(node) ?? [];
131
- for (const neighbor of neighbors) {
132
- if (!sccSet.has(neighbor)) continue;
133
- if (neighbor === startNode && path.length > 1) {
134
- // Found a cycle — normalize via smallest-index-first rotation
135
- const canonical = canonicalRotation([...path]);
136
- const key = canonical.join('\0');
137
- if (!seen.has(key)) {
138
- seen.add(key);
139
- cycles.push([...path, startNode]);
140
- }
141
- } else if (!onPath.has(neighbor)) {
142
- path.push(neighbor);
143
- onPath.add(neighbor);
144
- dfs(neighbor);
145
- path.pop();
146
- onPath.delete(neighbor);
147
- }
148
- }
149
- };
150
-
151
- dfs(startNode);
152
- }
153
-
154
- return cycles;
155
- }
156
-
157
- function canonicalRotation(cycle: string[]): string[] {
158
- // Find index of lexicographically smallest element
159
- let minIdx = 0;
160
- for (let i = 1; i < cycle.length; i++) {
161
- if (cycle[i] < cycle[minIdx]) minIdx = i;
162
- }
163
- return [...cycle.slice(minIdx), ...cycle.slice(0, minIdx)];
164
- }
165
-
166
- export function detectCycles(db: MonographDb): CycleDetectionResult {
167
- // Load all File nodes
168
- const nodeRows = db.prepare(
169
- `SELECT id, file_path, community_id FROM nodes WHERE label = 'File'`
170
- ).all() as NodeRow[];
171
-
172
- const nodeMap = new Map<string, NodeRow>();
173
- for (const row of nodeRows) {
174
- nodeMap.set(row.id, row);
175
- }
176
-
177
- // Load all IMPORTS and RE_EXPORTS edges between File nodes
178
- const edgeRows = db.prepare(
179
- `SELECT e.source_id, e.target_id, e.relation
180
- FROM edges e
181
- WHERE e.relation IN ('IMPORTS', 'RE_EXPORTS')
182
- AND e.source_id IN (SELECT id FROM nodes WHERE label = 'File')
183
- AND e.target_id IN (SELECT id FROM nodes WHERE label = 'File')`
184
- ).all() as EdgeRow[];
185
-
186
- // Build adjacency list
187
- const adjacency = new Map<string, string[]>();
188
- const edgeRelationMap = new Map<string, string>(); // "src\0tgt" -> relation
189
-
190
- for (const row of nodeRows) {
191
- adjacency.set(row.id, []);
192
- }
193
-
194
- for (const edge of edgeRows) {
195
- const neighbors = adjacency.get(edge.source_id);
196
- if (neighbors) {
197
- neighbors.push(edge.target_id);
198
- }
199
- edgeRelationMap.set(`${edge.source_id}\0${edge.target_id}`, edge.relation);
200
- }
201
-
202
- // Run Tarjan's SCC
203
- const nodeIds = nodeRows.map(n => n.id);
204
- const sccs = tarjanSCC(adjacency, nodeIds);
205
-
206
- // SCCs with size > 1 are multi-node cycles; size-1 SCCs are cycles only if self-referencing
207
- const cycleSCCs = sccs.filter(scc =>
208
- scc.length > 1 || (scc.length === 1 && adjacency.get(scc[0])?.includes(scc[0])),
209
- );
210
-
211
- const cycles: DependencyCycle[] = [];
212
- const filesInCyclesSet = new Set<string>();
213
-
214
- for (const scc of cycleSCCs) {
215
- const elementaryCycles = scc.length === 1
216
- ? [[scc[0], scc[0]]]
217
- : findCyclesInSCC(scc, adjacency, 20);
218
- for (const cyclePath of elementaryCycles) {
219
- // cyclePath is [...nodes, startNode] — last element = first
220
- const cycleNodes = cyclePath.slice(0, -1);
221
-
222
- for (const nodeId of cycleNodes) {
223
- filesInCyclesSet.add(nodeId);
224
- }
225
-
226
- // Collect file paths
227
- const files = cycleNodes.map(id => nodeMap.get(id)?.file_path ?? id);
228
-
229
- // isCrossCommunity: any two nodes with different community_id
230
- const communityIds = cycleNodes
231
- .map(id => nodeMap.get(id)?.community_id)
232
- .filter((c): c is number => c !== null && c !== undefined);
233
- const uniqueCommunities = new Set(communityIds);
234
- const isCrossCommunity = uniqueCommunities.size > 1;
235
-
236
- // Collect edge relations used in this cycle
237
- const edgeRelations: string[] = [];
238
- for (let i = 0; i < cyclePath.length - 1; i++) {
239
- const key = `${cyclePath[i]}\0${cyclePath[i + 1]}`;
240
- const rel = edgeRelationMap.get(key);
241
- if (rel && !edgeRelations.includes(rel)) {
242
- edgeRelations.push(rel);
243
- }
244
- }
245
-
246
- cycles.push({
247
- files,
248
- length: cycleNodes.length,
249
- isCrossCommunity,
250
- edgeRelations,
251
- });
252
- }
253
- }
254
-
255
- // Sort by length ascending
256
- cycles.sort((a, b) => a.length - b.length);
257
-
258
- const longestCycle = cycles.length > 0 ? cycles.reduce((a, c) => Math.max(a, c.length), 0) : 0;
259
- const crossCommunityCycles = cycles.filter(c => c.isCrossCommunity).length;
260
-
261
- return {
262
- cycles,
263
- totalCycles: cycles.length,
264
- filesInCycles: filesInCyclesSet.size,
265
- longestCycle,
266
- crossCommunityCycles,
267
- };
268
- }