@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
package/src/graph/loader.ts
CHANGED
|
@@ -18,24 +18,27 @@ export function loadGraphFromEdges(edges: MonographEdge[]): Graph {
|
|
|
18
18
|
|
|
19
19
|
export function loadGraphFromDb(db: import('../storage/db.js').MonographDb): Graph {
|
|
20
20
|
const nodes = db.prepare('SELECT id FROM nodes').all() as { id: string }[];
|
|
21
|
-
|
|
21
|
+
// Explicitly select only the columns used here — avoids fetching reason/evidence blobs
|
|
22
|
+
const edges = db.prepare(
|
|
23
|
+
'SELECT id, source_id, target_id, relation, confidence, confidence_score FROM edges'
|
|
24
|
+
).all() as {
|
|
22
25
|
id: string; source_id: string; target_id: string; relation: string;
|
|
23
26
|
confidence: string; confidence_score: number;
|
|
24
27
|
}[];
|
|
25
28
|
|
|
26
29
|
const graph = new Graph({ multi: true, type: 'directed' });
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
// Seed known nodes first; edges below may reference node ids not in the nodes table
|
|
31
|
+
// (defensive: addNode is a no-op guard here, has-check avoided to reduce overhead)
|
|
32
|
+
for (const n of nodes) graph.mergeNode(n.id);
|
|
30
33
|
for (const e of edges) {
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
graph.mergeNode(e.source_id);
|
|
35
|
+
graph.mergeNode(e.target_id);
|
|
33
36
|
try {
|
|
34
37
|
graph.addEdge(e.source_id, e.target_id, {
|
|
35
38
|
id: e.id, relation: e.relation,
|
|
36
39
|
confidence: e.confidence, confidenceScore: e.confidence_score,
|
|
37
40
|
});
|
|
38
|
-
} catch { /* skip */ }
|
|
41
|
+
} catch { /* skip duplicate edges */ }
|
|
39
42
|
}
|
|
40
43
|
return graph;
|
|
41
44
|
}
|
|
@@ -46,35 +46,36 @@ export function computeMaintainabilityIndex(db: MonographDb): MaintainabilityRep
|
|
|
46
46
|
return { results: [], averageMi: 100, lowMiCount: 0, criticalCount: 0 };
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// ── Batch degree lookup: one query replaces 2×N individual queries ────────
|
|
50
|
+
// Combine in-degree and out-degree via UNION ALL + GROUP BY in a single pass.
|
|
51
|
+
const degreeMap = new Map<string, number>();
|
|
52
|
+
for (const { node_id, deg } of db.prepare(`
|
|
53
|
+
SELECT node_id, SUM(cnt) as deg FROM (
|
|
54
|
+
SELECT target_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY target_id
|
|
55
|
+
UNION ALL
|
|
56
|
+
SELECT source_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY source_id
|
|
57
|
+
) GROUP BY node_id
|
|
58
|
+
`).all() as { node_id: string; deg: number }[]) {
|
|
59
|
+
degreeMap.set(node_id, deg);
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
const results: MaintainabilityResult[] = [];
|
|
63
|
+
const propUpdates: Array<{ id: string; props: string }> = [];
|
|
50
64
|
|
|
51
65
|
for (const node of nodes) {
|
|
52
66
|
const loc = Math.max(1, node.end_line - node.start_line + 1);
|
|
53
|
-
|
|
54
|
-
// Count in/out degree from edges table
|
|
55
|
-
const inDegreeRow = db.prepare(
|
|
56
|
-
'SELECT COUNT(*) as c FROM edges WHERE target_id = ?'
|
|
57
|
-
).get(node.id) as { c: number };
|
|
58
|
-
const outDegreeRow = db.prepare(
|
|
59
|
-
'SELECT COUNT(*) as c FROM edges WHERE source_id = ?'
|
|
60
|
-
).get(node.id) as { c: number };
|
|
61
|
-
|
|
62
|
-
const degree = inDegreeRow.c + outDegreeRow.c;
|
|
67
|
+
const degree = degreeMap.get(node.id) ?? 0;
|
|
63
68
|
const hvProxy = degree * Math.log2(Math.max(degree, 2));
|
|
64
69
|
|
|
65
70
|
// Maintainability Index formula
|
|
66
71
|
const rawMi = 171 - 5.2 * Math.log(hvProxy + 1) - 0.23 * (loc / 10) - 16.2 * Math.log(Math.max(1, loc));
|
|
67
72
|
const mi = Math.max(0, Math.min(100, rawMi));
|
|
68
|
-
|
|
69
73
|
const grade = gradeFromMi(mi);
|
|
70
74
|
|
|
71
|
-
//
|
|
72
|
-
const props = node.properties ? JSON.parse(node.properties) : {};
|
|
75
|
+
// Stage property update — batched below
|
|
76
|
+
const props: Record<string, unknown> = node.properties ? JSON.parse(node.properties) : {};
|
|
73
77
|
props.maintainabilityIndex = mi;
|
|
74
|
-
|
|
75
|
-
JSON.stringify(props),
|
|
76
|
-
node.id
|
|
77
|
-
);
|
|
78
|
+
propUpdates.push({ id: node.id, props: JSON.stringify(props) });
|
|
78
79
|
|
|
79
80
|
results.push({
|
|
80
81
|
nodeId: node.id,
|
|
@@ -87,6 +88,12 @@ export function computeMaintainabilityIndex(db: MonographDb): MaintainabilityRep
|
|
|
87
88
|
});
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
// ── Batch UPDATE all property writes in one transaction ───────────────────
|
|
92
|
+
const updateStmt = db.prepare('UPDATE nodes SET properties = ? WHERE id = ?');
|
|
93
|
+
db.transaction((updates: typeof propUpdates) => {
|
|
94
|
+
for (const { id, props } of updates) updateStmt.run(props, id);
|
|
95
|
+
})(propUpdates);
|
|
96
|
+
|
|
90
97
|
// Sort by MI ascending (worst first)
|
|
91
98
|
results.sort((a, b) => a.mi - b.mi);
|
|
92
99
|
|
|
@@ -94,13 +101,56 @@ export function computeMaintainabilityIndex(db: MonographDb): MaintainabilityRep
|
|
|
94
101
|
? results.reduce((sum, r) => sum + r.mi, 0) / results.length
|
|
95
102
|
: 100;
|
|
96
103
|
|
|
97
|
-
const lowMiCount = results.filter(r => r.mi < 65).length;
|
|
98
|
-
const criticalCount = results.filter(r => r.mi < 25).length;
|
|
99
|
-
|
|
100
104
|
return {
|
|
101
105
|
results,
|
|
102
106
|
averageMi: Math.round(averageMi * 100) / 100,
|
|
103
|
-
lowMiCount,
|
|
104
|
-
criticalCount,
|
|
107
|
+
lowMiCount: results.filter(r => r.mi < 65).length,
|
|
108
|
+
criticalCount: results.filter(r => r.mi < 25).length,
|
|
105
109
|
};
|
|
106
110
|
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format a MaintainabilityReport as structured text with file:line hints for LLM navigation.
|
|
114
|
+
*
|
|
115
|
+
* @param report - MaintainabilityReport from computeMaintainabilityIndex()
|
|
116
|
+
* @param topN - number of worst files to list (default 10)
|
|
117
|
+
* @returns structured text suitable for LLM consumption
|
|
118
|
+
*/
|
|
119
|
+
export function formatMaintainability(report: MaintainabilityReport, topN = 10): string {
|
|
120
|
+
const { results, averageMi, lowMiCount, criticalCount } = report;
|
|
121
|
+
|
|
122
|
+
if (results.length === 0) {
|
|
123
|
+
return 'maintainability: no Function/Method/Symbol nodes with line info found\n';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const lines: string[] = [
|
|
127
|
+
`maintainability: ${results.length} nodes analysed`,
|
|
128
|
+
` avg_mi: ${averageMi} low(mi<65): ${lowMiCount} critical(mi<25): ${criticalCount}`,
|
|
129
|
+
'',
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
// results already sorted by MI ascending (worst first)
|
|
133
|
+
const worst = results.slice(0, topN);
|
|
134
|
+
if (worst.length > 0) {
|
|
135
|
+
lines.push(`top_${topN}_worst_mi:`);
|
|
136
|
+
for (const r of worst) {
|
|
137
|
+
const loc = r.filePath ? `${r.filePath}:1` : `<unknown>:1`;
|
|
138
|
+
lines.push(` - ${r.name} grade:${r.grade} mi:${r.mi}`);
|
|
139
|
+
lines.push(` file: ${loc}`);
|
|
140
|
+
lines.push(` loc: ${r.linesOfCode} halstead_vol: ${r.halsteadVolume}`);
|
|
141
|
+
}
|
|
142
|
+
lines.push('');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const critical = results.filter(r => r.mi < 25);
|
|
146
|
+
if (critical.length > 0) {
|
|
147
|
+
lines.push(`critical_nodes(mi<25): ${critical.length}`);
|
|
148
|
+
for (const r of critical.slice(0, 5)) {
|
|
149
|
+
const loc = r.filePath ? `${r.filePath}:1` : `<unknown>:1`;
|
|
150
|
+
lines.push(` - ${r.name} file: ${loc} mi:${r.mi} grade:${r.grade}`);
|
|
151
|
+
}
|
|
152
|
+
if (critical.length > 5) lines.push(` ... and ${critical.length - 5} more`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return lines.join('\n');
|
|
156
|
+
}
|
|
@@ -126,18 +126,30 @@ export function detectMirroredFamilies(
|
|
|
126
126
|
const fa = families[i];
|
|
127
127
|
const fb = families[j];
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
const
|
|
129
|
+
// Single pass: extract dir and basename together to avoid two separate .map() iterations
|
|
130
|
+
let firstDirA: string | undefined;
|
|
131
|
+
let singleDirA = true;
|
|
132
|
+
let firstDirB: string | undefined;
|
|
133
|
+
let singleDirB = true;
|
|
134
|
+
const namesA = new Set<string>();
|
|
135
|
+
const namesB = new Set<string>();
|
|
136
|
+
for (const f of fa.files) {
|
|
137
|
+
const d = dirOf(f);
|
|
138
|
+
if (firstDirA === undefined) firstDirA = d;
|
|
139
|
+
else if (d !== firstDirA) { singleDirA = false; }
|
|
140
|
+
namesA.add(baseName(f));
|
|
141
|
+
}
|
|
142
|
+
for (const f of fb.files) {
|
|
143
|
+
const d = dirOf(f);
|
|
144
|
+
if (firstDirB === undefined) firstDirB = d;
|
|
145
|
+
else if (d !== firstDirB) { singleDirB = false; }
|
|
146
|
+
namesB.add(baseName(f));
|
|
147
|
+
}
|
|
136
148
|
|
|
137
|
-
if (
|
|
149
|
+
if (!singleDirA || !singleDirB) continue;
|
|
138
150
|
|
|
139
|
-
const
|
|
140
|
-
const
|
|
151
|
+
const dirA = firstDirA!;
|
|
152
|
+
const dirB = firstDirB!;
|
|
141
153
|
|
|
142
154
|
const shared: string[] = [];
|
|
143
155
|
for (const n of namesA) {
|
|
@@ -164,8 +176,27 @@ export function detectMirroredFamilies(
|
|
|
164
176
|
|
|
165
177
|
mirrored.sort((a, b) => b.similarity - a.similarity);
|
|
166
178
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
};
|
|
179
|
+
const remaining: CloneFamily[] = [];
|
|
180
|
+
for (const i of remainingIndices) remaining.push(families[i]);
|
|
181
|
+
|
|
182
|
+
return { mirrored, remaining };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Format a MirroredDirsReport as structured text for LLM consumption.
|
|
187
|
+
*/
|
|
188
|
+
export function formatMirroredDirs(report: MirroredDirsReport): string {
|
|
189
|
+
if (report.pairs.length === 0) {
|
|
190
|
+
return `No mirrored directories found (${report.totalDirsAnalyzed} dirs analyzed).`;
|
|
191
|
+
}
|
|
192
|
+
const lines: string[] = [
|
|
193
|
+
`Mirrored directories: ${report.pairs.length} pair(s) of ${report.totalDirsAnalyzed} analyzed`,
|
|
194
|
+
'',
|
|
195
|
+
];
|
|
196
|
+
for (const p of report.pairs) {
|
|
197
|
+
lines.push(
|
|
198
|
+
` ${p.dirA} <-> ${p.dirB} similarity=${(p.similarity * 100).toFixed(0)}% shared=${p.sharedFileNames.length} uniqueA=${p.uniqueToA} uniqueB=${p.uniqueToB}`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return lines.join('\n');
|
|
171
202
|
}
|
package/src/graph/narrowing.ts
CHANGED
|
@@ -46,3 +46,47 @@ export function markMemberExportsReferenced(
|
|
|
46
46
|
}
|
|
47
47
|
return result;
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
export interface NarrowingReport {
|
|
51
|
+
filePath: string;
|
|
52
|
+
totalExports: number;
|
|
53
|
+
referencedExports: string[];
|
|
54
|
+
unusedExports: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Given a file's exports and the accessed members from all import sites,
|
|
59
|
+
* return a report of which exports are unused.
|
|
60
|
+
*/
|
|
61
|
+
export function filterUnusedExports(
|
|
62
|
+
filePath: string,
|
|
63
|
+
exports: string[],
|
|
64
|
+
accessed: AccessedMembers,
|
|
65
|
+
): NarrowingReport {
|
|
66
|
+
const referenced = markMemberExportsReferenced(exports, accessed);
|
|
67
|
+
const unusedExports: string[] = [];
|
|
68
|
+
for (const exp of exports) {
|
|
69
|
+
if (!referenced.has(exp)) unusedExports.push(exp);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
filePath,
|
|
73
|
+
totalExports: exports.length,
|
|
74
|
+
referencedExports: Array.from(referenced),
|
|
75
|
+
unusedExports,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format narrowing reports as structured text for LLM dead-import diagnostics.
|
|
81
|
+
*/
|
|
82
|
+
export function formatNarrowingReport(reports: NarrowingReport[]): string {
|
|
83
|
+
const withUnused = reports.filter(r => r.unusedExports.length > 0);
|
|
84
|
+
if (withUnused.length === 0) return 'No unused exports found.';
|
|
85
|
+
|
|
86
|
+
const lines: string[] = [`Unused exports in ${withUnused.length} file(s):`, ''];
|
|
87
|
+
for (const r of withUnused) {
|
|
88
|
+
lines.push(` ${r.filePath} (${r.unusedExports.length}/${r.totalExports} unused)`);
|
|
89
|
+
for (const exp of r.unusedExports) lines.push(` - ${exp}`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
package/src/graph/node-search.ts
CHANGED
|
@@ -50,11 +50,39 @@ function rowToNode(r: {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// ── Prepared statement cache ───────────────────────────────────────────────────
|
|
54
|
+
// Keyed by a fingerprint of which conditions are active (not by param values),
|
|
55
|
+
// so the same query shape reuses the compiled statement across calls.
|
|
56
|
+
|
|
57
|
+
// WeakMap ensures the cache is garbage-collected when the DB object is released.
|
|
58
|
+
const stmtCache = new WeakMap<object, Map<string, ReturnType<MonographDb['prepare']>>>();
|
|
59
|
+
|
|
60
|
+
function getCachedStmt(
|
|
61
|
+
db: MonographDb,
|
|
62
|
+
key: string,
|
|
63
|
+
buildSql: () => string,
|
|
64
|
+
): ReturnType<MonographDb['prepare']> {
|
|
65
|
+
let dbCache = stmtCache.get(db);
|
|
66
|
+
if (!dbCache) {
|
|
67
|
+
dbCache = new Map();
|
|
68
|
+
stmtCache.set(db, dbCache);
|
|
69
|
+
}
|
|
70
|
+
let stmt = dbCache.get(key);
|
|
71
|
+
if (!stmt) {
|
|
72
|
+
stmt = db.prepare(buildSql());
|
|
73
|
+
dbCache.set(key, stmt);
|
|
74
|
+
}
|
|
75
|
+
return stmt;
|
|
76
|
+
}
|
|
77
|
+
|
|
53
78
|
// ── DB-backed search ───────────────────────────────────────────────────────────
|
|
54
79
|
|
|
55
80
|
/**
|
|
56
81
|
* Search nodes by structured property criteria.
|
|
57
82
|
* All supplied criteria are combined with AND.
|
|
83
|
+
*
|
|
84
|
+
* Prepared statements are cached per-DB keyed by the active condition set so
|
|
85
|
+
* repeated calls with the same filter shape reuse the compiled statement.
|
|
58
86
|
*/
|
|
59
87
|
export function searchNodesByProperty(
|
|
60
88
|
db: MonographDb,
|
|
@@ -62,15 +90,19 @@ export function searchNodesByProperty(
|
|
|
62
90
|
): MonographNode[] {
|
|
63
91
|
const conditions: string[] = [];
|
|
64
92
|
const params: unknown[] = [];
|
|
93
|
+
// fingerprint encodes which conditions are active (not their values)
|
|
94
|
+
const fingerprint: string[] = [];
|
|
65
95
|
|
|
66
96
|
if (options.label) {
|
|
67
97
|
conditions.push('label = ?');
|
|
68
98
|
params.push(options.label);
|
|
99
|
+
fingerprint.push('lbl');
|
|
69
100
|
}
|
|
70
101
|
|
|
71
102
|
if (options.language) {
|
|
72
103
|
conditions.push('LOWER(language) = LOWER(?)');
|
|
73
104
|
params.push(options.language);
|
|
105
|
+
fingerprint.push('lang');
|
|
74
106
|
}
|
|
75
107
|
|
|
76
108
|
if (options.fileExtension !== undefined) {
|
|
@@ -80,30 +112,43 @@ export function searchNodesByProperty(
|
|
|
80
112
|
: `.${options.fileExtension}`;
|
|
81
113
|
conditions.push("file_path LIKE ?");
|
|
82
114
|
params.push(`%${ext}`);
|
|
115
|
+
fingerprint.push('ext');
|
|
83
116
|
}
|
|
84
117
|
|
|
85
118
|
if (options.filePath !== undefined) {
|
|
86
119
|
conditions.push("LOWER(file_path) LIKE LOWER(?)");
|
|
87
120
|
params.push(`%${options.filePath}%`);
|
|
121
|
+
fingerprint.push('fp');
|
|
88
122
|
}
|
|
89
123
|
|
|
90
124
|
if (options.isExported !== undefined) {
|
|
91
125
|
conditions.push('is_exported = ?');
|
|
92
126
|
params.push(options.isExported ? 1 : 0);
|
|
127
|
+
fingerprint.push('exp');
|
|
93
128
|
}
|
|
94
129
|
|
|
95
130
|
if (options.communityId !== undefined) {
|
|
96
131
|
conditions.push('community_id = ?');
|
|
97
132
|
params.push(options.communityId);
|
|
133
|
+
fingerprint.push('com');
|
|
98
134
|
}
|
|
99
135
|
|
|
136
|
+
const hasLimit = options.limit !== undefined;
|
|
137
|
+
if (hasLimit) fingerprint.push('lim');
|
|
138
|
+
|
|
100
139
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
101
|
-
const limitClause =
|
|
102
|
-
const
|
|
140
|
+
const limitClause = hasLimit ? `LIMIT ${Number(options.limit)}` : '';
|
|
141
|
+
const cacheKey = fingerprint.join(':') || 'all';
|
|
142
|
+
|
|
143
|
+
const stmt = getCachedStmt(
|
|
144
|
+
db,
|
|
145
|
+
cacheKey,
|
|
146
|
+
() => `SELECT id, label, name, norm_label, file_path, start_line, end_line,
|
|
103
147
|
community_id, is_exported, language, properties
|
|
104
|
-
FROM nodes ${where} ${limitClause}
|
|
148
|
+
FROM nodes ${where} ${limitClause}`,
|
|
149
|
+
);
|
|
105
150
|
|
|
106
|
-
const rows =
|
|
151
|
+
const rows = stmt.all(...params) as Parameters<typeof rowToNode>[0][];
|
|
107
152
|
return rows.map(rowToNode);
|
|
108
153
|
}
|
|
109
154
|
|
|
@@ -87,8 +87,9 @@ export function reconcileEdges(
|
|
|
87
87
|
let remapped = false;
|
|
88
88
|
|
|
89
89
|
if (!nodeIds.has(src)) {
|
|
90
|
+
// Cache normalizeId result to avoid calling it twice for the same string
|
|
90
91
|
const canonical = normToId.get(normalizeId(src));
|
|
91
|
-
if (canonical) {
|
|
92
|
+
if (canonical !== undefined) {
|
|
92
93
|
src = canonical;
|
|
93
94
|
remapped = true;
|
|
94
95
|
}
|
|
@@ -96,7 +97,7 @@ export function reconcileEdges(
|
|
|
96
97
|
|
|
97
98
|
if (!nodeIds.has(tgt)) {
|
|
98
99
|
const canonical = normToId.get(normalizeId(tgt));
|
|
99
|
-
if (canonical) {
|
|
100
|
+
if (canonical !== undefined) {
|
|
100
101
|
tgt = canonical;
|
|
101
102
|
remapped = true;
|
|
102
103
|
}
|
|
@@ -112,3 +113,26 @@ export function reconcileEdges(
|
|
|
112
113
|
|
|
113
114
|
return { resolved, dangling, remappedCount };
|
|
114
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Format a ReconciliationResult as structured text for LLM consumption.
|
|
119
|
+
*/
|
|
120
|
+
export function formatReconciliationResult(result: ReconciliationResult): string {
|
|
121
|
+
const total = result.resolved.length + result.dangling.length;
|
|
122
|
+
const lines: string[] = [
|
|
123
|
+
`Edge reconciliation: ${result.resolved.length}/${total} resolved, ${result.remappedCount} remapped, ${result.dangling.length} dangling`,
|
|
124
|
+
];
|
|
125
|
+
if (result.dangling.length > 0) {
|
|
126
|
+
lines.push('');
|
|
127
|
+
lines.push('Dangling edges (unresolvable endpoints):');
|
|
128
|
+
const limit = Math.min(result.dangling.length, 20);
|
|
129
|
+
for (let i = 0; i < limit; i++) {
|
|
130
|
+
const e = result.dangling[i]!;
|
|
131
|
+
lines.push(` ${e.source} -> ${e.target}`);
|
|
132
|
+
}
|
|
133
|
+
if (result.dangling.length > limit) {
|
|
134
|
+
lines.push(` ... and ${result.dangling.length - limit} more`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return lines.join('\n');
|
|
138
|
+
}
|
package/src/graph/pagerank.ts
CHANGED
|
@@ -9,6 +9,59 @@ export interface PageRankOptions {
|
|
|
9
9
|
tolerance?: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Per-DB statement cache — avoids recompiling SQL on every pageRank() call.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
interface StmtCache {
|
|
16
|
+
selectNodes: ReturnType<MonographDb['prepare']>;
|
|
17
|
+
selectEdges: ReturnType<MonographDb['prepare']>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const _stmtCache = new Map<string, StmtCache>();
|
|
21
|
+
|
|
22
|
+
function getStmts(db: MonographDb): StmtCache {
|
|
23
|
+
const key = (db as unknown as { name: string }).name ?? '__default__';
|
|
24
|
+
let cache = _stmtCache.get(key);
|
|
25
|
+
if (!cache) {
|
|
26
|
+
cache = {
|
|
27
|
+
selectNodes: db.prepare('SELECT id FROM nodes'),
|
|
28
|
+
selectEdges: db.prepare('SELECT source_id, target_id FROM edges'),
|
|
29
|
+
};
|
|
30
|
+
_stmtCache.set(key, cache);
|
|
31
|
+
}
|
|
32
|
+
return cache;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Result cache — avoids re-running power iteration when the graph hasn't
|
|
37
|
+
// changed. Keyed by (dbName, nodeCount, edgeCount) with a 5-second TTL.
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
const PAGERANK_CACHE_TTL_MS = 5_000;
|
|
40
|
+
|
|
41
|
+
interface PageRankCacheEntry {
|
|
42
|
+
result: Map<string, number>;
|
|
43
|
+
expiresAt: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const _resultCache = new Map<string, PageRankCacheEntry>();
|
|
47
|
+
|
|
48
|
+
function resultCacheKey(db: MonographDb, nodeCount: number, edgeCount: number): string {
|
|
49
|
+
const name = (db as unknown as { name: string }).name ?? '__default__';
|
|
50
|
+
return `${name}:${nodeCount}:${edgeCount}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Evict cached statements and results for a given DB instance (call after writes).
|
|
55
|
+
*/
|
|
56
|
+
export function invalidatePageRankCache(db: MonographDb): void {
|
|
57
|
+
const key = (db as unknown as { name: string }).name ?? '__default__';
|
|
58
|
+
_stmtCache.delete(key);
|
|
59
|
+
// Purge all result-cache entries for this DB (they have the name as prefix)
|
|
60
|
+
for (const k of _resultCache.keys()) {
|
|
61
|
+
if (k.startsWith(`${key}:`)) _resultCache.delete(k);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
12
65
|
/**
|
|
13
66
|
* Compute PageRank scores for all nodes using power iteration.
|
|
14
67
|
*
|
|
@@ -16,6 +69,9 @@ export interface PageRankOptions {
|
|
|
16
69
|
* After convergence the scores still sum to ~1 (standard normalized PageRank).
|
|
17
70
|
* Dangling nodes (out-degree 0) distribute their rank equally to all nodes.
|
|
18
71
|
*
|
|
72
|
+
* Results are cached for 5 seconds when the graph's node+edge counts are
|
|
73
|
+
* unchanged, making repeated calls (e.g. during context preloading) free.
|
|
74
|
+
*
|
|
19
75
|
* @param db - The MonographDb instance
|
|
20
76
|
* @param options - Optional tuning parameters
|
|
21
77
|
* @returns Map of nodeId → PageRank score
|
|
@@ -23,14 +79,21 @@ export interface PageRankOptions {
|
|
|
23
79
|
export function pageRank(db: MonographDb, options: PageRankOptions = {}): Map<string, number> {
|
|
24
80
|
const { dampingFactor = 0.85, maxIterations = 100, tolerance = 1e-6 } = options;
|
|
25
81
|
|
|
26
|
-
const
|
|
27
|
-
const
|
|
82
|
+
const stmts = getStmts(db);
|
|
83
|
+
const nodeRows = stmts.selectNodes.all() as { id: string }[];
|
|
84
|
+
const edgeRows = stmts.selectEdges.all() as {
|
|
28
85
|
source_id: string;
|
|
29
86
|
target_id: string;
|
|
30
87
|
}[];
|
|
31
88
|
|
|
32
89
|
if (nodeRows.length === 0) return new Map();
|
|
33
90
|
|
|
91
|
+
// Check result cache before running power iteration
|
|
92
|
+
const cacheKey = resultCacheKey(db, nodeRows.length, edgeRows.length);
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const cached = _resultCache.get(cacheKey);
|
|
95
|
+
if (cached && now < cached.expiresAt) return cached.result;
|
|
96
|
+
|
|
34
97
|
const nodes = nodeRows.map(r => r.id);
|
|
35
98
|
const n = nodes.length;
|
|
36
99
|
const nodeIndex = new Map<string, number>();
|
|
@@ -84,5 +147,9 @@ export function pageRank(db: MonographDb, options: PageRankOptions = {}): Map<st
|
|
|
84
147
|
for (let i = 0; i < n; i++) {
|
|
85
148
|
result.set(nodes[i], scores[i]);
|
|
86
149
|
}
|
|
150
|
+
|
|
151
|
+
// Store in result cache
|
|
152
|
+
_resultCache.set(cacheKey, { result, expiresAt: now + PAGERANK_CACHE_TTL_MS });
|
|
153
|
+
|
|
87
154
|
return result;
|
|
88
155
|
}
|
package/src/graph/prune.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { MonographDb } from '../storage/db.js';
|
|
2
2
|
|
|
3
|
+
export interface PruneResult {
|
|
4
|
+
danglingEdgesRemoved: number;
|
|
5
|
+
orphanNodesRemoved: number;
|
|
6
|
+
staleFileNodesRemoved: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* Remove edges whose source or target node does not exist in the nodes table.
|
|
5
11
|
*
|
|
@@ -17,3 +23,73 @@ export function pruneDanglingEdges(db: MonographDb): number {
|
|
|
17
23
|
|
|
18
24
|
return result.changes;
|
|
19
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Remove non-File nodes that have no incoming or outgoing edges.
|
|
29
|
+
* Leaves File nodes in place so they can still be queried even if isolated.
|
|
30
|
+
*
|
|
31
|
+
* @param db - The MonographDb instance
|
|
32
|
+
* @returns Number of orphan nodes removed
|
|
33
|
+
*/
|
|
34
|
+
export function pruneOrphanNodes(db: MonographDb): number {
|
|
35
|
+
const result = db.prepare(`
|
|
36
|
+
DELETE FROM nodes
|
|
37
|
+
WHERE label != 'File'
|
|
38
|
+
AND NOT EXISTS (SELECT 1 FROM edges WHERE source_id = nodes.id OR target_id = nodes.id)
|
|
39
|
+
`).run();
|
|
40
|
+
return result.changes;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Remove File nodes whose file_path no longer exists on disk.
|
|
45
|
+
* Also cascades to remove any edges connected to those nodes (via pruneDanglingEdges).
|
|
46
|
+
*
|
|
47
|
+
* @param db - The MonographDb instance
|
|
48
|
+
* @param existingPaths - Set of file paths that currently exist on disk
|
|
49
|
+
* @returns Number of stale File nodes removed (call pruneDanglingEdges after to clean edges)
|
|
50
|
+
*/
|
|
51
|
+
export function pruneStaleFileNodes(db: MonographDb, existingPaths: Set<string>): number {
|
|
52
|
+
const staleRows = db.prepare(
|
|
53
|
+
`SELECT id, file_path FROM nodes WHERE label = 'File' AND file_path IS NOT NULL`,
|
|
54
|
+
).all() as { id: string; file_path: string }[];
|
|
55
|
+
|
|
56
|
+
let removed = 0;
|
|
57
|
+
const deleteStmt = db.prepare(`DELETE FROM nodes WHERE id = ?`);
|
|
58
|
+
const tx = db.transaction(() => {
|
|
59
|
+
for (const row of staleRows) {
|
|
60
|
+
if (!existingPaths.has(row.file_path)) {
|
|
61
|
+
deleteStmt.run(row.id);
|
|
62
|
+
removed++;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
tx();
|
|
67
|
+
return removed;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Run a full prune pass: dangling edges → orphan nodes → stale file nodes.
|
|
72
|
+
* Optionally pass existingPaths to also remove deleted-file nodes.
|
|
73
|
+
*/
|
|
74
|
+
export function pruneAll(db: MonographDb, existingPaths?: Set<string>): PruneResult {
|
|
75
|
+
const danglingEdgesRemoved = pruneDanglingEdges(db);
|
|
76
|
+
const orphanNodesRemoved = pruneOrphanNodes(db);
|
|
77
|
+
const staleFileNodesRemoved = existingPaths ? pruneStaleFileNodes(db, existingPaths) : 0;
|
|
78
|
+
// A second dangling-edge pass cleans up any edges left by stale file node removal
|
|
79
|
+
if (staleFileNodesRemoved > 0) pruneDanglingEdges(db);
|
|
80
|
+
return { danglingEdgesRemoved, orphanNodesRemoved, staleFileNodesRemoved };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Format a PruneResult as structured text for LLM consumption.
|
|
85
|
+
*/
|
|
86
|
+
export function formatPruneResult(result: PruneResult): string {
|
|
87
|
+
const total = result.danglingEdgesRemoved + result.orphanNodesRemoved + result.staleFileNodesRemoved;
|
|
88
|
+
if (total === 0) return 'Prune pass: nothing to remove — graph is clean.';
|
|
89
|
+
return [
|
|
90
|
+
`Prune pass removed ${total} artifact(s):`,
|
|
91
|
+
` dangling edges: ${result.danglingEdgesRemoved}`,
|
|
92
|
+
` orphan nodes: ${result.orphanNodesRemoved}`,
|
|
93
|
+
` stale file nodes: ${result.staleFileNodesRemoved}`,
|
|
94
|
+
].join('\n');
|
|
95
|
+
}
|