@monoes/monograph 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/mcp-tools/cypher.test.ts +10 -6
- package/__tests__/mcp-tools/detect-changes.test.ts +14 -11
- package/__tests__/pipeline/phases/wildcard-synthesis.test.ts +4 -4
- package/__tests__/search/embed-batch-config.test.ts +7 -7
- package/__tests__/search/hybrid-query.test.ts +3 -1
- package/__tests__/web/api.test.ts +1 -1
- package/dist/src/analysis/churn-cache.d.ts.map +1 -1
- package/dist/src/analysis/churn-cache.js +19 -6
- package/dist/src/analysis/churn-cache.js.map +1 -1
- package/dist/src/analysis/churn-trend.d.ts.map +1 -1
- package/dist/src/analysis/churn-trend.js +50 -6
- package/dist/src/analysis/churn-trend.js.map +1 -1
- package/dist/src/analysis/cluster-quality.d.ts +3 -0
- package/dist/src/analysis/cluster-quality.d.ts.map +1 -1
- package/dist/src/analysis/cluster-quality.js +18 -12
- package/dist/src/analysis/cluster-quality.js.map +1 -1
- package/dist/src/analysis/coverage-gaps.d.ts +3 -0
- package/dist/src/analysis/coverage-gaps.d.ts.map +1 -1
- package/dist/src/analysis/coverage-gaps.js +40 -1
- package/dist/src/analysis/coverage-gaps.js.map +1 -1
- package/dist/src/analysis/dep-classification.d.ts +2 -0
- package/dist/src/analysis/dep-classification.d.ts.map +1 -1
- package/dist/src/analysis/dep-classification.js +30 -0
- package/dist/src/analysis/dep-classification.js.map +1 -1
- package/dist/src/analysis/dependency-health.d.ts +4 -0
- package/dist/src/analysis/dependency-health.d.ts.map +1 -1
- package/dist/src/analysis/dependency-health.js +28 -0
- package/dist/src/analysis/dependency-health.js.map +1 -1
- package/dist/src/analysis/duplicate-exports.d.ts +9 -5
- package/dist/src/analysis/duplicate-exports.d.ts.map +1 -1
- package/dist/src/analysis/duplicate-exports.js +26 -2
- package/dist/src/analysis/duplicate-exports.js.map +1 -1
- package/dist/src/analysis/feature-flags.d.ts.map +1 -1
- package/dist/src/analysis/feature-flags.js +36 -17
- package/dist/src/analysis/feature-flags.js.map +1 -1
- package/dist/src/analysis/node-similarity.d.ts +19 -0
- package/dist/src/analysis/node-similarity.d.ts.map +1 -1
- package/dist/src/analysis/node-similarity.js +34 -3
- package/dist/src/analysis/node-similarity.js.map +1 -1
- package/dist/src/analysis/private-type-leaks.d.ts +4 -0
- package/dist/src/analysis/private-type-leaks.d.ts.map +1 -1
- package/dist/src/analysis/private-type-leaks.js +43 -2
- package/dist/src/analysis/private-type-leaks.js.map +1 -1
- package/dist/src/analysis/project-state.d.ts +2 -0
- package/dist/src/analysis/project-state.d.ts.map +1 -1
- package/dist/src/analysis/project-state.js +18 -7
- package/dist/src/analysis/project-state.js.map +1 -1
- package/dist/src/analysis/suppressions.d.ts.map +1 -1
- package/dist/src/analysis/suppressions.js +3 -1
- package/dist/src/analysis/suppressions.js.map +1 -1
- package/dist/src/analysis/trace.d.ts +5 -3
- package/dist/src/analysis/trace.d.ts.map +1 -1
- package/dist/src/analysis/trace.js +59 -53
- package/dist/src/analysis/trace.js.map +1 -1
- package/dist/src/analysis/unused-class-members.d.ts.map +1 -1
- package/dist/src/analysis/unused-class-members.js +7 -1
- package/dist/src/analysis/unused-class-members.js.map +1 -1
- package/dist/src/analysis/worker-pool.d.ts +3 -0
- package/dist/src/analysis/worker-pool.d.ts.map +1 -1
- package/dist/src/analysis/worker-pool.js +30 -8
- package/dist/src/analysis/worker-pool.js.map +1 -1
- package/dist/src/cli/augment.d.ts.map +1 -1
- package/dist/src/cli/augment.js +10 -3
- package/dist/src/cli/augment.js.map +1 -1
- package/dist/src/export/sarif.js +5 -5
- package/dist/src/export/sarif.js.map +1 -1
- package/dist/src/graph/analyzer.d.ts.map +1 -1
- package/dist/src/graph/analyzer.js +17 -2
- package/dist/src/graph/analyzer.js.map +1 -1
- package/dist/src/graph/author-analytics.d.ts +2 -0
- package/dist/src/graph/author-analytics.d.ts.map +1 -1
- package/dist/src/graph/author-analytics.js +39 -5
- package/dist/src/graph/author-analytics.js.map +1 -1
- package/dist/src/graph/baseline.d.ts +5 -0
- package/dist/src/graph/baseline.d.ts.map +1 -1
- package/dist/src/graph/baseline.js +65 -18
- package/dist/src/graph/baseline.js.map +1 -1
- package/dist/src/graph/clone-detector.d.ts.map +1 -1
- package/dist/src/graph/clone-detector.js +19 -33
- package/dist/src/graph/clone-detector.js.map +1 -1
- package/dist/src/graph/clone-families.d.ts +2 -0
- package/dist/src/graph/clone-families.d.ts.map +1 -1
- package/dist/src/graph/clone-families.js +38 -10
- package/dist/src/graph/clone-families.js.map +1 -1
- package/dist/src/graph/codeowners-extended.d.ts +17 -0
- package/dist/src/graph/codeowners-extended.d.ts.map +1 -1
- package/dist/src/graph/codeowners-extended.js +42 -0
- package/dist/src/graph/codeowners-extended.js.map +1 -1
- package/dist/src/graph/codeowners-gitlab.d.ts +21 -0
- package/dist/src/graph/codeowners-gitlab.d.ts.map +1 -1
- package/dist/src/graph/codeowners-gitlab.js +37 -0
- package/dist/src/graph/codeowners-gitlab.js.map +1 -1
- package/dist/src/graph/codeowners.d.ts +10 -0
- package/dist/src/graph/codeowners.d.ts.map +1 -1
- package/dist/src/graph/codeowners.js +24 -2
- package/dist/src/graph/codeowners.js.map +1 -1
- package/dist/src/graph/complexity.d.ts +10 -0
- package/dist/src/graph/complexity.d.ts.map +1 -1
- package/dist/src/graph/complexity.js +93 -43
- package/dist/src/graph/complexity.js.map +1 -1
- package/dist/src/graph/cross-reference-findings.d.ts.map +1 -1
- package/dist/src/graph/cross-reference-findings.js +19 -2
- package/dist/src/graph/cross-reference-findings.js.map +1 -1
- package/dist/src/graph/cross-reference.d.ts +4 -0
- package/dist/src/graph/cross-reference.d.ts.map +1 -1
- package/dist/src/graph/cross-reference.js +23 -0
- package/dist/src/graph/cross-reference.js.map +1 -1
- package/dist/src/graph/cycle-detection.d.ts +2 -0
- package/dist/src/graph/cycle-detection.d.ts.map +1 -1
- package/dist/src/graph/cycle-detection.js +26 -3
- package/dist/src/graph/cycle-detection.js.map +1 -1
- package/dist/src/graph/cycles.d.ts +11 -3
- package/dist/src/graph/cycles.d.ts.map +1 -1
- package/dist/src/graph/cycles.js +121 -83
- package/dist/src/graph/cycles.js.map +1 -1
- package/dist/src/graph/dead-code.d.ts +23 -0
- package/dist/src/graph/dead-code.d.ts.map +1 -1
- package/dist/src/graph/dead-code.js +53 -13
- package/dist/src/graph/dead-code.js.map +1 -1
- package/dist/src/graph/dependency-closure.d.ts.map +1 -1
- package/dist/src/graph/dependency-closure.js +20 -4
- package/dist/src/graph/dependency-closure.js.map +1 -1
- package/dist/src/graph/dfs.d.ts +14 -0
- package/dist/src/graph/dfs.d.ts.map +1 -1
- package/dist/src/graph/dfs.js +61 -0
- package/dist/src/graph/dfs.js.map +1 -1
- package/dist/src/graph/duplication-stats.d.ts.map +1 -1
- package/dist/src/graph/duplication-stats.js +19 -3
- package/dist/src/graph/duplication-stats.js.map +1 -1
- package/dist/src/graph/edge-filter.d.ts +2 -0
- package/dist/src/graph/edge-filter.d.ts.map +1 -1
- package/dist/src/graph/edge-filter.js +26 -12
- package/dist/src/graph/edge-filter.js.map +1 -1
- package/dist/src/graph/health-score.d.ts +8 -0
- package/dist/src/graph/health-score.d.ts.map +1 -1
- package/dist/src/graph/health-score.js +48 -17
- package/dist/src/graph/health-score.js.map +1 -1
- package/dist/src/graph/hotspots.d.ts +7 -0
- package/dist/src/graph/hotspots.d.ts.map +1 -1
- package/dist/src/graph/hotspots.js +50 -5
- package/dist/src/graph/hotspots.js.map +1 -1
- package/dist/src/graph/import-chain.d.ts +17 -1
- package/dist/src/graph/import-chain.d.ts.map +1 -1
- package/dist/src/graph/import-chain.js +68 -19
- package/dist/src/graph/import-chain.js.map +1 -1
- package/dist/src/graph/loader.d.ts.map +1 -1
- package/dist/src/graph/loader.js +9 -10
- package/dist/src/graph/loader.js.map +1 -1
- package/dist/src/graph/maintainability.d.ts +8 -0
- package/dist/src/graph/maintainability.d.ts.map +1 -1
- package/dist/src/graph/maintainability.js +65 -10
- package/dist/src/graph/maintainability.js.map +1 -1
- package/dist/src/graph/mirrored-dirs.d.ts +4 -0
- package/dist/src/graph/mirrored-dirs.d.ts.map +1 -1
- package/dist/src/graph/mirrored-dirs.js +48 -13
- package/dist/src/graph/mirrored-dirs.js.map +1 -1
- package/dist/src/graph/narrowing.d.ts +15 -0
- package/dist/src/graph/narrowing.d.ts.map +1 -1
- package/dist/src/graph/narrowing.js +33 -0
- package/dist/src/graph/narrowing.js.map +1 -1
- package/dist/src/graph/node-search.d.ts +3 -0
- package/dist/src/graph/node-search.d.ts.map +1 -1
- package/dist/src/graph/node-search.js +37 -4
- package/dist/src/graph/node-search.js.map +1 -1
- package/dist/src/graph/normalize-id.d.ts +4 -0
- package/dist/src/graph/normalize-id.d.ts.map +1 -1
- package/dist/src/graph/normalize-id.js +25 -2
- package/dist/src/graph/normalize-id.js.map +1 -1
- package/dist/src/graph/pagerank.d.ts +7 -0
- package/dist/src/graph/pagerank.d.ts.map +1 -1
- package/dist/src/graph/pagerank.js +49 -2
- package/dist/src/graph/pagerank.js.map +1 -1
- package/dist/src/graph/prune.d.ts +31 -0
- package/dist/src/graph/prune.d.ts.map +1 -1
- package/dist/src/graph/prune.js +65 -0
- package/dist/src/graph/prune.js.map +1 -1
- package/dist/src/graph/regex-search.d.ts +10 -2
- package/dist/src/graph/regex-search.d.ts.map +1 -1
- package/dist/src/graph/regex-search.js +33 -4
- package/dist/src/graph/regex-search.js.map +1 -1
- package/dist/src/graph/regression.d.ts +5 -0
- package/dist/src/graph/regression.d.ts.map +1 -1
- package/dist/src/graph/regression.js +45 -23
- package/dist/src/graph/regression.js.map +1 -1
- package/dist/src/graph/ripple-impact.d.ts +27 -0
- package/dist/src/graph/ripple-impact.d.ts.map +1 -1
- package/dist/src/graph/ripple-impact.js +55 -7
- package/dist/src/graph/ripple-impact.js.map +1 -1
- package/dist/src/graph/risk-profile.d.ts +7 -0
- package/dist/src/graph/risk-profile.d.ts.map +1 -1
- package/dist/src/graph/risk-profile.js +82 -48
- package/dist/src/graph/risk-profile.js.map +1 -1
- package/dist/src/graph/shingle-filter.d.ts.map +1 -1
- package/dist/src/graph/shingle-filter.js +25 -26
- package/dist/src/graph/shingle-filter.js.map +1 -1
- package/dist/src/graph/statistics.d.ts +19 -0
- package/dist/src/graph/statistics.d.ts.map +1 -1
- package/dist/src/graph/statistics.js +53 -40
- package/dist/src/graph/statistics.js.map +1 -1
- package/dist/src/graph/stats.d.ts +2 -0
- package/dist/src/graph/stats.d.ts.map +1 -1
- package/dist/src/graph/stats.js +54 -8
- package/dist/src/graph/stats.js.map +1 -1
- package/dist/src/graph/subgraph.d.ts +5 -0
- package/dist/src/graph/subgraph.d.ts.map +1 -1
- package/dist/src/graph/subgraph.js +73 -9
- package/dist/src/graph/subgraph.js.map +1 -1
- package/dist/src/graph/topo-sort.d.ts +15 -0
- package/dist/src/graph/topo-sort.d.ts.map +1 -1
- package/dist/src/graph/topo-sort.js +83 -10
- package/dist/src/graph/topo-sort.js.map +1 -1
- package/dist/src/graph/wcc.d.ts +29 -0
- package/dist/src/graph/wcc.d.ts.map +1 -1
- package/dist/src/graph/wcc.js +122 -39
- package/dist/src/graph/wcc.js.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp-tools/api-impact.d.ts.map +1 -1
- package/dist/src/mcp-tools/api-impact.js +13 -6
- package/dist/src/mcp-tools/api-impact.js.map +1 -1
- package/dist/src/mcp-tools/context.d.ts.map +1 -1
- package/dist/src/mcp-tools/context.js +19 -29
- package/dist/src/mcp-tools/context.js.map +1 -1
- package/dist/src/mcp-tools/cypher.d.ts +11 -0
- package/dist/src/mcp-tools/cypher.d.ts.map +1 -1
- package/dist/src/mcp-tools/cypher.js +13 -1
- package/dist/src/mcp-tools/cypher.js.map +1 -1
- package/dist/src/mcp-tools/detect-changes.d.ts.map +1 -1
- package/dist/src/mcp-tools/detect-changes.js +35 -20
- package/dist/src/mcp-tools/detect-changes.js.map +1 -1
- package/dist/src/mcp-tools/explain.d.ts.map +1 -1
- package/dist/src/mcp-tools/explain.js +4 -1
- package/dist/src/mcp-tools/explain.js.map +1 -1
- package/dist/src/mcp-tools/graph-query.d.ts +2 -0
- package/dist/src/mcp-tools/graph-query.d.ts.map +1 -1
- package/dist/src/mcp-tools/graph-query.js +20 -3
- package/dist/src/mcp-tools/graph-query.js.map +1 -1
- package/dist/src/mcp-tools/group-list.d.ts +1 -0
- package/dist/src/mcp-tools/group-list.d.ts.map +1 -1
- package/dist/src/mcp-tools/group-list.js +7 -9
- package/dist/src/mcp-tools/group-list.js.map +1 -1
- package/dist/src/mcp-tools/impact.d.ts.map +1 -1
- package/dist/src/mcp-tools/impact.js +20 -59
- package/dist/src/mcp-tools/impact.js.map +1 -1
- package/dist/src/mcp-tools/neighbors.d.ts.map +1 -1
- package/dist/src/mcp-tools/neighbors.js +24 -33
- package/dist/src/mcp-tools/neighbors.js.map +1 -1
- package/dist/src/mcp-tools/query.d.ts +2 -0
- package/dist/src/mcp-tools/query.d.ts.map +1 -1
- package/dist/src/mcp-tools/query.js +1 -0
- package/dist/src/mcp-tools/query.js.map +1 -1
- package/dist/src/mcp-tools/rename.d.ts.map +1 -1
- package/dist/src/mcp-tools/rename.js +12 -7
- package/dist/src/mcp-tools/rename.js.map +1 -1
- package/dist/src/mcp-tools/route-map.d.ts.map +1 -1
- package/dist/src/mcp-tools/route-map.js +19 -23
- package/dist/src/mcp-tools/route-map.js.map +1 -1
- package/dist/src/mcp-tools/shape-check.d.ts.map +1 -1
- package/dist/src/mcp-tools/shape-check.js +17 -13
- package/dist/src/mcp-tools/shape-check.js.map +1 -1
- package/dist/src/parsers/loader.js +6 -5
- package/dist/src/parsers/loader.js.map +1 -1
- package/dist/src/pipeline/cluster-only.d.ts.map +1 -1
- package/dist/src/pipeline/cluster-only.js +8 -4
- package/dist/src/pipeline/cluster-only.js.map +1 -1
- package/dist/src/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/src/pipeline/orchestrator.js +19 -0
- package/dist/src/pipeline/orchestrator.js.map +1 -1
- package/dist/src/pipeline/phases/boundary.d.ts +2 -0
- package/dist/src/pipeline/phases/boundary.d.ts.map +1 -1
- package/dist/src/pipeline/phases/boundary.js +31 -2
- package/dist/src/pipeline/phases/boundary.js.map +1 -1
- package/dist/src/pipeline/phases/communities.d.ts +14 -0
- package/dist/src/pipeline/phases/communities.d.ts.map +1 -1
- package/dist/src/pipeline/phases/communities.js +58 -5
- package/dist/src/pipeline/phases/communities.js.map +1 -1
- package/dist/src/pipeline/phases/contextual-proximity.d.ts.map +1 -1
- package/dist/src/pipeline/phases/contextual-proximity.js +21 -14
- package/dist/src/pipeline/phases/contextual-proximity.js.map +1 -1
- package/dist/src/pipeline/phases/cross-file.d.ts.map +1 -1
- package/dist/src/pipeline/phases/cross-file.js +35 -14
- package/dist/src/pipeline/phases/cross-file.js.map +1 -1
- package/dist/src/pipeline/phases/field-access.d.ts.map +1 -1
- package/dist/src/pipeline/phases/field-access.js +9 -4
- package/dist/src/pipeline/phases/field-access.js.map +1 -1
- package/dist/src/pipeline/phases/god-nodes.d.ts +4 -0
- package/dist/src/pipeline/phases/god-nodes.d.ts.map +1 -1
- package/dist/src/pipeline/phases/god-nodes.js +43 -9
- package/dist/src/pipeline/phases/god-nodes.js.map +1 -1
- package/dist/src/pipeline/phases/import-resolver.d.ts +11 -0
- package/dist/src/pipeline/phases/import-resolver.d.ts.map +1 -1
- package/dist/src/pipeline/phases/import-resolver.js +34 -7
- package/dist/src/pipeline/phases/import-resolver.js.map +1 -1
- package/dist/src/pipeline/phases/leiden.d.ts.map +1 -1
- package/dist/src/pipeline/phases/leiden.js +27 -19
- package/dist/src/pipeline/phases/leiden.js.map +1 -1
- package/dist/src/pipeline/phases/markdown.d.ts.map +1 -1
- package/dist/src/pipeline/phases/markdown.js +10 -5
- package/dist/src/pipeline/phases/markdown.js.map +1 -1
- package/dist/src/pipeline/phases/pdf-parse.d.ts.map +1 -1
- package/dist/src/pipeline/phases/pdf-parse.js +18 -3
- package/dist/src/pipeline/phases/pdf-parse.js.map +1 -1
- package/dist/src/pipeline/phases/processes.d.ts.map +1 -1
- package/dist/src/pipeline/phases/processes.js +26 -9
- package/dist/src/pipeline/phases/processes.js.map +1 -1
- package/dist/src/pipeline/phases/re-export-propagation.d.ts.map +1 -1
- package/dist/src/pipeline/phases/re-export-propagation.js +33 -14
- package/dist/src/pipeline/phases/re-export-propagation.js.map +1 -1
- package/dist/src/pipeline/phases/reachability.d.ts.map +1 -1
- package/dist/src/pipeline/phases/reachability.js +42 -26
- package/dist/src/pipeline/phases/reachability.js.map +1 -1
- package/dist/src/pipeline/phases/routes.d.ts.map +1 -1
- package/dist/src/pipeline/phases/routes.js +30 -21
- package/dist/src/pipeline/phases/routes.js.map +1 -1
- package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
- package/dist/src/pipeline/phases/scope-resolution.js +93 -63
- package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
- package/dist/src/pipeline/runner.d.ts +4 -0
- package/dist/src/pipeline/runner.d.ts.map +1 -1
- package/dist/src/pipeline/runner.js +30 -11
- package/dist/src/pipeline/runner.js.map +1 -1
- package/dist/src/query/cypher-parser.d.ts +20 -0
- package/dist/src/query/cypher-parser.d.ts.map +1 -1
- package/dist/src/query/cypher-parser.js +73 -0
- package/dist/src/query/cypher-parser.js.map +1 -1
- package/dist/src/search/embed-batch.d.ts.map +1 -1
- package/dist/src/search/embed-batch.js +4 -1
- package/dist/src/search/embed-batch.js.map +1 -1
- package/dist/src/search/hybrid-query.d.ts +4 -0
- package/dist/src/search/hybrid-query.d.ts.map +1 -1
- package/dist/src/search/hybrid-query.js +5 -1
- package/dist/src/search/hybrid-query.js.map +1 -1
- package/dist/src/staleness/git-staleness.js +3 -3
- package/dist/src/staleness/git-staleness.js.map +1 -1
- package/dist/src/storage/db.d.ts.map +1 -1
- package/dist/src/storage/db.js +12 -0
- package/dist/src/storage/db.js.map +1 -1
- package/dist/src/storage/edge-store.d.ts +2 -0
- package/dist/src/storage/edge-store.d.ts.map +1 -1
- package/dist/src/storage/edge-store.js +26 -1
- package/dist/src/storage/edge-store.js.map +1 -1
- package/dist/src/storage/embedding-store.d.ts +16 -0
- package/dist/src/storage/embedding-store.d.ts.map +1 -1
- package/dist/src/storage/embedding-store.js +26 -2
- package/dist/src/storage/embedding-store.js.map +1 -1
- package/dist/src/storage/file-cache.d.ts +5 -0
- package/dist/src/storage/file-cache.d.ts.map +1 -1
- package/dist/src/storage/file-cache.js +35 -9
- package/dist/src/storage/file-cache.js.map +1 -1
- package/dist/src/storage/fts-store.d.ts +4 -0
- package/dist/src/storage/fts-store.d.ts.map +1 -1
- package/dist/src/storage/fts-store.js +8 -3
- package/dist/src/storage/fts-store.js.map +1 -1
- package/dist/src/storage/node-store.d.ts +2 -0
- package/dist/src/storage/node-store.d.ts.map +1 -1
- package/dist/src/storage/node-store.js +28 -1
- package/dist/src/storage/node-store.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/__tests__/pipeline/runner.parallel.test.ts +2 -2
- package/src/analysis/churn-cache.ts +18 -7
- package/src/analysis/churn-trend.ts +35 -6
- package/src/analysis/cluster-quality.ts +20 -11
- package/src/analysis/coverage-gaps.ts +44 -1
- package/src/analysis/dep-classification.ts +34 -0
- package/src/analysis/dependency-health.ts +25 -0
- package/src/analysis/duplicate-exports.ts +40 -4
- package/src/analysis/feature-flags.ts +37 -17
- package/src/analysis/node-similarity.ts +45 -4
- package/src/analysis/private-type-leaks.ts +49 -2
- package/src/analysis/project-state.ts +19 -9
- package/src/analysis/suppressions.ts +4 -1
- package/src/analysis/trace.ts +73 -54
- package/src/analysis/unused-class-members.ts +8 -1
- package/src/analysis/worker-pool.ts +40 -8
- package/src/cli/augment.ts +10 -3
- package/src/export/sarif.ts +5 -5
- package/src/graph/analyzer.ts +26 -2
- package/src/graph/author-analytics.ts +45 -5
- package/src/graph/baseline.ts +78 -24
- package/src/graph/clone-detector.ts +22 -36
- package/src/graph/clone-families.ts +34 -10
- package/src/graph/codeowners-extended.ts +54 -0
- package/src/graph/codeowners-gitlab.ts +46 -0
- package/src/graph/codeowners.ts +33 -2
- package/src/graph/complexity.ts +102 -47
- package/src/graph/cross-reference-findings.ts +23 -2
- package/src/graph/cross-reference.ts +26 -0
- package/src/graph/cycle-detection.ts +22 -3
- package/src/graph/cycles.ts +155 -96
- package/src/graph/dead-code.ts +76 -17
- package/src/graph/dependency-closure.ts +24 -7
- package/src/graph/dfs.ts +78 -0
- package/src/graph/duplication-stats.ts +17 -6
- package/src/graph/edge-filter.ts +26 -12
- package/src/graph/health-score.ts +47 -17
- package/src/graph/hotspots.ts +57 -8
- package/src/graph/import-chain.ts +89 -19
- package/src/graph/loader.ts +10 -7
- package/src/graph/maintainability.ts +72 -22
- package/src/graph/mirrored-dirs.ts +45 -14
- package/src/graph/narrowing.ts +44 -0
- package/src/graph/node-search.ts +49 -4
- package/src/graph/normalize-id.ts +26 -2
- package/src/graph/pagerank.ts +69 -2
- package/src/graph/prune.ts +76 -0
- package/src/graph/regex-search.ts +38 -4
- package/src/graph/regression.ts +48 -23
- package/src/graph/ripple-impact.ts +71 -11
- package/src/graph/risk-profile.ts +98 -53
- package/src/graph/shingle-filter.ts +33 -27
- package/src/graph/statistics.ts +73 -45
- package/src/graph/stats.ts +50 -16
- package/src/graph/subgraph.ts +96 -25
- package/src/graph/topo-sort.ts +101 -16
- package/src/graph/wcc.ts +146 -37
- package/src/index.ts +1 -1
- package/src/mcp-tools/api-impact.ts +13 -6
- package/src/mcp-tools/context.ts +31 -40
- package/src/mcp-tools/cypher.ts +14 -1
- package/src/mcp-tools/detect-changes.ts +38 -23
- package/src/mcp-tools/explain.ts +4 -1
- package/src/mcp-tools/graph-query.ts +24 -3
- package/src/mcp-tools/group-list.ts +9 -11
- package/src/mcp-tools/impact.ts +26 -66
- package/src/mcp-tools/neighbors.ts +34 -39
- package/src/mcp-tools/query.ts +3 -0
- package/src/mcp-tools/rename.ts +9 -8
- package/src/mcp-tools/route-map.ts +21 -23
- package/src/mcp-tools/shape-check.ts +18 -14
- package/src/parsers/loader.ts +5 -5
- package/src/pipeline/cluster-only.ts +8 -4
- package/src/pipeline/orchestrator.ts +22 -0
- package/src/pipeline/phases/boundary.ts +40 -2
- package/src/pipeline/phases/communities.ts +66 -5
- package/src/pipeline/phases/contextual-proximity.ts +15 -14
- package/src/pipeline/phases/cross-file.ts +35 -14
- package/src/pipeline/phases/field-access.ts +11 -4
- package/src/pipeline/phases/god-nodes.ts +47 -10
- package/src/pipeline/phases/import-resolver.ts +37 -5
- package/src/pipeline/phases/leiden.ts +25 -18
- package/src/pipeline/phases/markdown.ts +12 -7
- package/src/pipeline/phases/pdf-parse.ts +19 -2
- package/src/pipeline/phases/processes.ts +27 -15
- package/src/pipeline/phases/re-export-propagation.ts +40 -18
- package/src/pipeline/phases/reachability.ts +39 -24
- package/src/pipeline/phases/routes.ts +32 -21
- package/src/pipeline/phases/scope-resolution.ts +110 -71
- package/src/pipeline/runner.ts +28 -11
- package/src/query/cypher-parser.ts +83 -0
- package/src/search/embed-batch.ts +4 -1
- package/src/search/hybrid-query.ts +11 -1
- package/src/staleness/git-staleness.ts +3 -3
- package/src/storage/db.ts +11 -0
- package/src/storage/edge-store.ts +34 -1
- package/src/storage/embedding-store.ts +38 -6
- package/src/storage/file-cache.ts +35 -21
- package/src/storage/fts-store.ts +12 -3
- package/src/storage/node-store.ts +39 -1
- 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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
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 =
|
|
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
|
})
|
package/src/analysis/trace.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
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
|
-
|
|
165
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
package/src/cli/augment.ts
CHANGED
|
@@ -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
|
-
|
|
99
|
-
|
|
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
|
}
|
package/src/export/sarif.ts
CHANGED
|
@@ -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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
69
|
+
helpUri: 'https://github.com/monoes/monomind/blob/main/docs/rules/hotspot.md',
|
|
70
70
|
},
|
|
71
71
|
];
|
|
72
72
|
|
package/src/graph/analyzer.ts
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
+
}
|