@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
|
@@ -73,17 +73,30 @@ export function classifyReachability(
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// ──
|
|
76
|
+
// ── Preload forward adjacency map for BFS (one query vs N per BFS node) ────────
|
|
77
|
+
const forwardEdges = new Map<string, string[]>();
|
|
78
|
+
const fwdRows = db.prepare(
|
|
79
|
+
`SELECT source_id, target_id FROM edges WHERE relation IN ('IMPORTS', 'RE_EXPORTS')`,
|
|
80
|
+
).all() as { source_id: string; target_id: string }[];
|
|
81
|
+
for (const row of fwdRows) {
|
|
82
|
+
let targets = forwardEdges.get(row.source_id);
|
|
83
|
+
if (!targets) {
|
|
84
|
+
targets = [];
|
|
85
|
+
forwardEdges.set(row.source_id, targets);
|
|
86
|
+
}
|
|
87
|
+
targets.push(row.target_id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── BFS from entry points — uses preloaded adjacency (no per-node DB query) ──
|
|
77
91
|
const bfs = (startIds: Set<string>, role: ReachabilityRole) => {
|
|
78
92
|
const queue = Array.from(startIds);
|
|
79
93
|
const visited = new Set(startIds);
|
|
80
94
|
while (queue.length > 0) {
|
|
81
95
|
const nodeId = queue.shift()!;
|
|
82
|
-
//
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for (const { target_id } of imported) {
|
|
96
|
+
// O(1) adjacency lookup instead of per-node DB query
|
|
97
|
+
const targets = forwardEdges.get(nodeId);
|
|
98
|
+
if (!targets) continue;
|
|
99
|
+
for (const target_id of targets) {
|
|
87
100
|
if (!visited.has(target_id)) {
|
|
88
101
|
visited.add(target_id);
|
|
89
102
|
// Set role only if not already set to a higher-priority role
|
|
@@ -102,24 +115,26 @@ export function classifyReachability(
|
|
|
102
115
|
bfs(testEntryIds, 'test');
|
|
103
116
|
bfs(runtimeEntryIds, 'runtime');
|
|
104
117
|
|
|
105
|
-
// ── Persist roles and count
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
// ── Persist roles and count — batch UPDATE in transaction ────────────────────
|
|
119
|
+
const updateStmt = db.prepare(`UPDATE nodes SET properties = ? WHERE id = ?`);
|
|
120
|
+
const persistAll = db.transaction(() => {
|
|
121
|
+
let runtime = 0, test = 0, support = 0, unreachable = 0;
|
|
122
|
+
for (const node of allFileNodes) {
|
|
123
|
+
const role = roleMap.get(node.id) ?? 'unreachable';
|
|
124
|
+
let props: Record<string, unknown> = {};
|
|
125
|
+
try {
|
|
126
|
+
if (node.properties) props = JSON.parse(node.properties);
|
|
127
|
+
} catch { /* ignore parse errors */ }
|
|
128
|
+
props.reachabilityRole = role;
|
|
129
|
+
updateStmt.run(JSON.stringify(props), node.id);
|
|
130
|
+
if (role === 'runtime') runtime++;
|
|
131
|
+
else if (role === 'test') test++;
|
|
132
|
+
else if (role === 'support') support++;
|
|
133
|
+
else unreachable++;
|
|
134
|
+
}
|
|
135
|
+
return { runtime, test, support, unreachable };
|
|
136
|
+
});
|
|
137
|
+
return persistAll();
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
/**
|
|
@@ -46,6 +46,23 @@ export const routesPhase: PipelinePhase<RoutesOutput> = {
|
|
|
46
46
|
const routeNodes: MonographNode[] = [];
|
|
47
47
|
const handlesEdges: MonographEdge[] = [];
|
|
48
48
|
|
|
49
|
+
// ── Preload handler index: name:filePath → nodeId (one DB query vs N per route) ──
|
|
50
|
+
// Keyed as `${name}\0${filePath}` for O(1) lookup during route extraction.
|
|
51
|
+
const handlerIndex = new Map<string, string>();
|
|
52
|
+
if (ctx.db) {
|
|
53
|
+
const handlerRows = ctx.db
|
|
54
|
+
.prepare(`SELECT id, name, file_path FROM nodes WHERE file_path IS NOT NULL`)
|
|
55
|
+
.all() as { id: string; name: string; file_path: string }[];
|
|
56
|
+
for (const row of handlerRows) {
|
|
57
|
+
handlerIndex.set(`${row.name}\0${row.file_path}`, row.id);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** O(1) handler lookup using the preloaded index */
|
|
62
|
+
function lookupHandler(handlerName: string, filePath: string): string | undefined {
|
|
63
|
+
return handlerIndex.get(`${handlerName}\0${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
for (const fileNode of fileNodes) {
|
|
50
67
|
const relPath = fileNode.filePath ?? '';
|
|
51
68
|
const ext = extname(relPath).toLowerCase();
|
|
@@ -84,20 +101,18 @@ export const routesPhase: PipelinePhase<RoutesOutput> = {
|
|
|
84
101
|
if (source) {
|
|
85
102
|
const handlerName = extractDefaultExportName(source);
|
|
86
103
|
if (handlerName) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (row) {
|
|
91
|
-
const edgeId = makeId(routeNodeId, row.id, 'handles_route');
|
|
104
|
+
const targetId = lookupHandler(handlerName, relPath);
|
|
105
|
+
if (targetId) {
|
|
106
|
+
const edgeId = makeId(routeNodeId, targetId, 'handles_route');
|
|
92
107
|
handlesEdges.push({
|
|
93
108
|
id: edgeId,
|
|
94
109
|
sourceId: routeNodeId,
|
|
95
|
-
targetId
|
|
110
|
+
targetId,
|
|
96
111
|
relation: 'HANDLES_ROUTE',
|
|
97
112
|
confidence: 'EXTRACTED',
|
|
98
113
|
confidenceScore: 0.9,
|
|
99
114
|
});
|
|
100
|
-
entry.handlerNodeId =
|
|
115
|
+
entry.handlerNodeId = targetId;
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
}
|
|
@@ -131,20 +146,18 @@ export const routesPhase: PipelinePhase<RoutesOutput> = {
|
|
|
131
146
|
const entry: RouteEntry = { method: e.method, path: e.path, filePath: relPath, routeNodeId, middlewareChain: [] };
|
|
132
147
|
|
|
133
148
|
if (ctx.db && e.handlerName) {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (row) {
|
|
138
|
-
const edgeId = makeId(routeNodeId, row.id, 'handles_route');
|
|
149
|
+
const targetId = lookupHandler(e.handlerName, relPath);
|
|
150
|
+
if (targetId) {
|
|
151
|
+
const edgeId = makeId(routeNodeId, targetId, 'handles_route');
|
|
139
152
|
handlesEdges.push({
|
|
140
153
|
id: edgeId,
|
|
141
154
|
sourceId: routeNodeId,
|
|
142
|
-
targetId
|
|
155
|
+
targetId,
|
|
143
156
|
relation: 'HANDLES_ROUTE',
|
|
144
157
|
confidence: 'EXTRACTED',
|
|
145
158
|
confidenceScore: 0.9,
|
|
146
159
|
});
|
|
147
|
-
entry.handlerNodeId =
|
|
160
|
+
entry.handlerNodeId = targetId;
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
163
|
|
|
@@ -172,20 +185,18 @@ export const routesPhase: PipelinePhase<RoutesOutput> = {
|
|
|
172
185
|
const entry: RouteEntry = { method: e.method, path: e.path, filePath: relPath, routeNodeId, middlewareChain: [] };
|
|
173
186
|
|
|
174
187
|
if (ctx.db && e.handlerName) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (row) {
|
|
179
|
-
const edgeId = makeId(routeNodeId, row.id, 'handles_route');
|
|
188
|
+
const targetId = lookupHandler(e.handlerName, relPath);
|
|
189
|
+
if (targetId) {
|
|
190
|
+
const edgeId = makeId(routeNodeId, targetId, 'handles_route');
|
|
180
191
|
handlesEdges.push({
|
|
181
192
|
id: edgeId,
|
|
182
193
|
sourceId: routeNodeId,
|
|
183
|
-
targetId
|
|
194
|
+
targetId,
|
|
184
195
|
relation: 'HANDLES_ROUTE',
|
|
185
196
|
confidence: 'EXTRACTED',
|
|
186
197
|
confidenceScore: 0.9,
|
|
187
198
|
});
|
|
188
|
-
entry.handlerNodeId =
|
|
199
|
+
entry.handlerNodeId = targetId;
|
|
189
200
|
}
|
|
190
201
|
}
|
|
191
202
|
|
|
@@ -210,68 +210,119 @@ function extractCallSites(
|
|
|
210
210
|
return sites;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
// ── Stage 3: Build import
|
|
213
|
+
// ── Stage 3: Build import maps from DB (batch) ────────────────────────────────
|
|
214
214
|
|
|
215
|
-
/**
|
|
216
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Load all IMPORTS edges once and return a map of fileNodeId → (name → filePath).
|
|
217
|
+
* Eliminates the N+1 pattern of querying per-file inside the main loop.
|
|
218
|
+
*/
|
|
219
|
+
function buildAllImportMaps(
|
|
217
220
|
ctx: PipelineContext,
|
|
218
|
-
fileNodeId: string,
|
|
219
221
|
symbolNodes: MonographNode[],
|
|
220
|
-
): Map<string, string
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
): Map<string, Map<string, string>> {
|
|
223
|
+
const result = new Map<string, Map<string, string>>();
|
|
224
|
+
if (!ctx.db) return result;
|
|
225
|
+
|
|
226
|
+
// Build a lookup: fileNodeId → filePath from symbolNodes
|
|
227
|
+
const idToFilePath = new Map<string, string>();
|
|
228
|
+
for (const n of symbolNodes) {
|
|
229
|
+
if (n.label === 'File' && n.filePath) {
|
|
230
|
+
idToFilePath.set(n.id, n.filePath);
|
|
231
|
+
// Also register the alternate makeId form
|
|
232
|
+
const altId = makeId(n.filePath.replace(/\//g, '_'), 'file');
|
|
233
|
+
if (altId !== n.id) idToFilePath.set(altId, n.filePath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
224
236
|
|
|
225
|
-
//
|
|
237
|
+
// Load ALL IMPORTS edges in one query
|
|
226
238
|
const importsRows = ctx.db
|
|
227
|
-
.prepare(`SELECT target_id FROM edges WHERE
|
|
228
|
-
.all(
|
|
239
|
+
.prepare(`SELECT source_id, target_id FROM edges WHERE relation = 'IMPORTS'`)
|
|
240
|
+
.all() as { source_id: string; target_id: string }[];
|
|
229
241
|
|
|
230
242
|
for (const row of importsRows) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
),
|
|
238
|
-
);
|
|
239
|
-
if (fileNode?.filePath) {
|
|
240
|
-
// Extract the module name from the last path segment without extension
|
|
241
|
-
const parts = fileNode.filePath.split('/');
|
|
242
|
-
const baseName = parts[parts.length - 1].replace(/\.\w+$/, '');
|
|
243
|
-
importMap.set(baseName, fileNode.filePath);
|
|
244
|
-
importMap.set(fileNode.filePath, fileNode.filePath);
|
|
243
|
+
const targetPath = idToFilePath.get(row.target_id);
|
|
244
|
+
if (!targetPath) continue;
|
|
245
|
+
let fileMap = result.get(row.source_id);
|
|
246
|
+
if (!fileMap) {
|
|
247
|
+
fileMap = new Map();
|
|
248
|
+
result.set(row.source_id, fileMap);
|
|
245
249
|
}
|
|
250
|
+
const parts = targetPath.split('/');
|
|
251
|
+
const baseName = parts[parts.length - 1].replace(/\.\w+$/, '');
|
|
252
|
+
fileMap.set(baseName, targetPath);
|
|
253
|
+
fileMap.set(targetPath, targetPath);
|
|
246
254
|
}
|
|
247
255
|
|
|
248
|
-
return
|
|
256
|
+
return result;
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
|
|
259
|
+
/** Returns the pre-built import map for a given file node (empty map if not found). */
|
|
260
|
+
function getImportMap(
|
|
261
|
+
allImportMaps: Map<string, Map<string, string>>,
|
|
262
|
+
fileNodeId: string,
|
|
263
|
+
): Map<string, string> {
|
|
264
|
+
return allImportMaps.get(fileNodeId) ?? new Map();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ── Stage 4 helpers: preloaded function/method index ──────────────────────────
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Load all Function/Method/Constructor nodes once.
|
|
271
|
+
* Returns two indices:
|
|
272
|
+
* - byFilePath: filePath → name → id[] (for resolveTarget candidate lookup)
|
|
273
|
+
* - nameCounts: name → total occurrences across all files (for ambiguity check)
|
|
274
|
+
*/
|
|
275
|
+
function buildFunctionIndex(ctx: PipelineContext): {
|
|
276
|
+
byFilePath: Map<string, Map<string, string[]>>;
|
|
277
|
+
nameCounts: Map<string, number>;
|
|
278
|
+
} {
|
|
279
|
+
const byFilePath = new Map<string, Map<string, string[]>>();
|
|
280
|
+
const nameCounts = new Map<string, number>();
|
|
281
|
+
|
|
282
|
+
if (!ctx.db) return { byFilePath, nameCounts };
|
|
283
|
+
|
|
284
|
+
const rows = ctx.db
|
|
285
|
+
.prepare(`SELECT id, name, file_path FROM nodes WHERE label IN ('Function', 'Method', 'Constructor') AND file_path IS NOT NULL`)
|
|
286
|
+
.all() as { id: string; name: string; file_path: string }[];
|
|
287
|
+
|
|
288
|
+
for (const row of rows) {
|
|
289
|
+
// byFilePath index
|
|
290
|
+
let fileMap = byFilePath.get(row.file_path);
|
|
291
|
+
if (!fileMap) {
|
|
292
|
+
fileMap = new Map();
|
|
293
|
+
byFilePath.set(row.file_path, fileMap);
|
|
294
|
+
}
|
|
295
|
+
let ids = fileMap.get(row.name);
|
|
296
|
+
if (!ids) {
|
|
297
|
+
ids = [];
|
|
298
|
+
fileMap.set(row.name, ids);
|
|
299
|
+
}
|
|
300
|
+
ids.push(row.id);
|
|
301
|
+
|
|
302
|
+
// global count for ambiguity detection
|
|
303
|
+
nameCounts.set(row.name, (nameCounts.get(row.name) ?? 0) + 1);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return { byFilePath, nameCounts };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Stage 4 + 5: Resolve target node (index-based, no per-call DB query) ──────
|
|
252
310
|
|
|
253
311
|
function resolveTarget(
|
|
254
|
-
ctx: PipelineContext,
|
|
255
312
|
site: CallSite,
|
|
256
313
|
callerFilePath: string,
|
|
257
314
|
importMap: Map<string, string>,
|
|
315
|
+
fnIndex: Map<string, Map<string, string[]>>,
|
|
258
316
|
): { targetId: string } | null {
|
|
259
|
-
if (!ctx.db) return null;
|
|
260
|
-
|
|
261
317
|
const methodName = site.methodName;
|
|
262
318
|
if (!methodName) return null;
|
|
263
319
|
|
|
264
|
-
let candidateFilePaths: string[]
|
|
320
|
+
let candidateFilePaths: string[];
|
|
265
321
|
|
|
266
322
|
if (site.form === 'method' && site.receiverName) {
|
|
267
323
|
// Stage 3: Infer receiver type — look up receiver in import map
|
|
268
324
|
const receiverPath = importMap.get(site.receiverName);
|
|
269
|
-
|
|
270
|
-
candidateFilePaths = [receiverPath];
|
|
271
|
-
} else {
|
|
272
|
-
// Receiver not imported — look in same file only
|
|
273
|
-
candidateFilePaths = [callerFilePath];
|
|
274
|
-
}
|
|
325
|
+
candidateFilePaths = receiverPath ? [receiverPath] : [callerFilePath];
|
|
275
326
|
} else if (site.form === 'direct') {
|
|
276
327
|
// Direct call: same file first, then all imported files
|
|
277
328
|
candidateFilePaths = [callerFilePath, ...importMap.values()];
|
|
@@ -279,22 +330,13 @@ function resolveTarget(
|
|
|
279
330
|
return null;
|
|
280
331
|
}
|
|
281
332
|
|
|
282
|
-
// Stage 5:
|
|
333
|
+
// Stage 5: O(1) lookup in preloaded index instead of per-call DB query
|
|
283
334
|
for (const fp of candidateFilePaths) {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
WHERE name = ?
|
|
288
|
-
AND label IN ('Function', 'Method', 'Constructor')
|
|
289
|
-
AND file_path = ?
|
|
290
|
-
LIMIT 2
|
|
291
|
-
`)
|
|
292
|
-
.all(methodName, fp) as { id: string }[];
|
|
293
|
-
|
|
294
|
-
if (rows.length === 1) {
|
|
295
|
-
return { targetId: rows[0].id };
|
|
335
|
+
const ids = fnIndex.get(fp)?.get(methodName);
|
|
336
|
+
if (ids && ids.length === 1) {
|
|
337
|
+
return { targetId: ids[0] };
|
|
296
338
|
}
|
|
297
|
-
//
|
|
339
|
+
// ids.length > 1 → ambiguous within this file — skip to next candidate
|
|
298
340
|
}
|
|
299
341
|
|
|
300
342
|
return null;
|
|
@@ -361,6 +403,8 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
|
|
|
361
403
|
let skippedDynamic = 0;
|
|
362
404
|
let ambiguous = 0;
|
|
363
405
|
|
|
406
|
+
// ── Batch preload: one query per table instead of one per file/call-site ──
|
|
407
|
+
|
|
364
408
|
// Build a map from file_path → file node id using the parse output
|
|
365
409
|
const fileNodesByPath = new Map<string, MonographNode>();
|
|
366
410
|
for (const node of symbolNodes) {
|
|
@@ -388,6 +432,12 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
|
|
|
388
432
|
}
|
|
389
433
|
}
|
|
390
434
|
|
|
435
|
+
// Preload all IMPORTS edges → per-file import maps (one DB query total)
|
|
436
|
+
const allImportMaps = buildAllImportMaps(ctx, symbolNodes);
|
|
437
|
+
|
|
438
|
+
// Preload all Function/Method/Constructor nodes into indices (one DB query total)
|
|
439
|
+
const { byFilePath: fnIndex, nameCounts } = buildFunctionIndex(ctx);
|
|
440
|
+
|
|
391
441
|
for (const [filePath, fileNode] of fileNodesByPath) {
|
|
392
442
|
const ext = extname(filePath).toLowerCase();
|
|
393
443
|
if (!isSupportedExt(ext)) continue;
|
|
@@ -403,8 +453,8 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
|
|
|
403
453
|
// Stage 1+2: Extract call sites
|
|
404
454
|
const callSites = extractCallSites(source, filePath, fileNode.id, ext);
|
|
405
455
|
|
|
406
|
-
// Stage 3:
|
|
407
|
-
const importMap =
|
|
456
|
+
// Stage 3: Get pre-built import map for this file (O(1))
|
|
457
|
+
const importMap = getImportMap(allImportMaps, fileNode.id);
|
|
408
458
|
|
|
409
459
|
for (const site of callSites) {
|
|
410
460
|
if (site.form === 'dynamic') {
|
|
@@ -412,29 +462,18 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
|
|
|
412
462
|
continue;
|
|
413
463
|
}
|
|
414
464
|
|
|
415
|
-
// Stage 4+5: Resolve target
|
|
416
|
-
const resolved = resolveTarget(
|
|
465
|
+
// Stage 4+5: Resolve target using preloaded indices (no DB query)
|
|
466
|
+
const resolved = resolveTarget(site, filePath, importMap, fnIndex);
|
|
417
467
|
|
|
418
468
|
if (!resolved) {
|
|
419
|
-
// Could not resolve —
|
|
420
|
-
// (method calls with unresolvable receivers are simply skipped)
|
|
469
|
+
// Could not resolve — method calls with unresolvable receivers are simply skipped
|
|
421
470
|
continue;
|
|
422
471
|
}
|
|
423
472
|
|
|
424
|
-
//
|
|
425
|
-
if (
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
SELECT COUNT(*) as cnt FROM nodes
|
|
429
|
-
WHERE name = ?
|
|
430
|
-
AND label IN ('Function', 'Method', 'Constructor')
|
|
431
|
-
`)
|
|
432
|
-
.get(site.methodName) as { cnt: number };
|
|
433
|
-
if (totalMatches.cnt > 1) {
|
|
434
|
-
ambiguous++;
|
|
435
|
-
// Still emit if we found exactly one in the candidate file — this is per-file ambiguity
|
|
436
|
-
// The resolve already picked one candidate, so proceed
|
|
437
|
-
}
|
|
473
|
+
// Ambiguity check using preloaded name counts (no DB query)
|
|
474
|
+
if (site.methodName && (nameCounts.get(site.methodName) ?? 0) > 1) {
|
|
475
|
+
ambiguous++;
|
|
476
|
+
// Still emit — we found exactly one candidate in the caller's context
|
|
438
477
|
}
|
|
439
478
|
|
|
440
479
|
// Stage 6: Emit edge (source is the file node of the caller)
|
package/src/pipeline/runner.ts
CHANGED
|
@@ -3,36 +3,48 @@ import { MonographError } from '../types.js';
|
|
|
3
3
|
|
|
4
4
|
export class PipelineRunner {
|
|
5
5
|
private readonly phases: PipelinePhase<unknown>[];
|
|
6
|
+
/** Topo-sorted phase names, computed once in constructor for O(1) phase map lookups. */
|
|
7
|
+
private readonly sortedNames: string[];
|
|
8
|
+
/** O(1) phase lookup by name. */
|
|
9
|
+
private readonly phaseMap: Map<string, PipelinePhase<unknown>>;
|
|
6
10
|
|
|
7
11
|
constructor(phases: PipelinePhase<unknown>[]) {
|
|
8
12
|
this.phases = phases;
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
this.sortedNames = topoSort(phases);
|
|
14
|
+
this.phaseMap = new Map(phases.map(p => [p.name, p]));
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
async run(ctx: PipelineContext): Promise<Map<string, unknown>> {
|
|
14
18
|
const outputs = new Map<string, unknown>();
|
|
15
|
-
const phaseMap = new Map(this.phases.map(p => [p.name, p]));
|
|
16
19
|
|
|
17
20
|
// Lazily-created promise per phase — ensures dep promises exist before they are awaited
|
|
18
21
|
const promises = new Map<string, Promise<void>>();
|
|
19
22
|
|
|
20
23
|
const getOrCreatePromise = (name: string): Promise<void> => {
|
|
21
24
|
if (promises.has(name)) return promises.get(name)!;
|
|
22
|
-
const phase = phaseMap.get(name)!;
|
|
25
|
+
const phase = this.phaseMap.get(name)!;
|
|
23
26
|
const p = (async () => {
|
|
24
27
|
// Wait for all dep phases to finish
|
|
25
28
|
await Promise.all(phase.deps.map(dep => getOrCreatePromise(dep)));
|
|
26
29
|
ctx.onProgress?.({ phase: phase.name });
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
try {
|
|
31
|
+
const output = await phase.execute(ctx, outputs);
|
|
32
|
+
outputs.set(phase.name, output);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
35
|
+
throw new MonographError(`Phase '${phase.name}' failed: ${msg}`);
|
|
36
|
+
}
|
|
29
37
|
})();
|
|
30
38
|
promises.set(name, p);
|
|
31
39
|
return p;
|
|
32
40
|
};
|
|
33
41
|
|
|
34
|
-
//
|
|
35
|
-
|
|
42
|
+
// Use allSettled so every in-flight phase completes before we return.
|
|
43
|
+
// This ensures the DB is not closed while phases are still writing to it,
|
|
44
|
+
// which would otherwise cause unhandled rejections that hang the process.
|
|
45
|
+
const results = await Promise.allSettled(this.sortedNames.map(name => getOrCreatePromise(name)));
|
|
46
|
+
const failed = results.find(r => r.status === 'rejected') as PromiseRejectedResult | undefined;
|
|
47
|
+
if (failed) throw failed.reason;
|
|
36
48
|
return outputs;
|
|
37
49
|
}
|
|
38
50
|
}
|
|
@@ -94,14 +106,19 @@ export async function runIncrementalAst(
|
|
|
94
106
|
if (preserveInferred) {
|
|
95
107
|
if (changedFiles.length > 0) {
|
|
96
108
|
const placeholders = changedFiles.map(() => '?').join(',');
|
|
109
|
+
// Use a CTE to resolve file_path→id once, then delete edges referencing those ids.
|
|
110
|
+
// This avoids passing changedFiles twice (N*2 params) and lets SQLite reuse the scan.
|
|
97
111
|
db.prepare(`
|
|
112
|
+
WITH changed_ids AS (
|
|
113
|
+
SELECT id FROM nodes WHERE file_path IN (${placeholders})
|
|
114
|
+
)
|
|
98
115
|
DELETE FROM edges
|
|
99
116
|
WHERE confidence = 'EXTRACTED'
|
|
100
117
|
AND (
|
|
101
|
-
source_id IN (SELECT id FROM
|
|
102
|
-
OR target_id IN (SELECT id FROM
|
|
118
|
+
source_id IN (SELECT id FROM changed_ids)
|
|
119
|
+
OR target_id IN (SELECT id FROM changed_ids)
|
|
103
120
|
)
|
|
104
|
-
`).run(...changedFiles
|
|
121
|
+
`).run(...changedFiles);
|
|
105
122
|
} else {
|
|
106
123
|
db.prepare(`DELETE FROM edges WHERE confidence = 'EXTRACTED'`).run();
|
|
107
124
|
}
|
|
@@ -365,3 +365,86 @@ export function executeCypherQuery(db: Database.Database, query: string): Cypher
|
|
|
365
365
|
};
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
|
+
|
|
369
|
+
// ── Structured text formatter ──────────────────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Format a CypherResult as structured text for LLM consumption.
|
|
373
|
+
*
|
|
374
|
+
* Rows that contain *_filePath and *_startLine fields are rendered with
|
|
375
|
+
* "file:line" navigation hints so LLMs can jump directly to the symbol.
|
|
376
|
+
* Plain key=value pairs are rendered for all other fields.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```
|
|
380
|
+
* monograph_cypher result (3 rows, 2ms)
|
|
381
|
+
*
|
|
382
|
+
* Row 1:
|
|
383
|
+
* n.name = buildAsync
|
|
384
|
+
* n.filePath = src/server.ts:42
|
|
385
|
+
*
|
|
386
|
+
* Row 2:
|
|
387
|
+
* ...
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
export function formatCypherResult(result: CypherResult): string {
|
|
391
|
+
if (result.error) {
|
|
392
|
+
return `monograph_cypher error: ${result.error}`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (result.rows.length === 0) {
|
|
396
|
+
return `monograph_cypher result (0 rows, ${result.queryTime}ms)\n\nNo results found.`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const lines: string[] = [
|
|
400
|
+
`monograph_cypher result (${result.rows.length} row${result.rows.length === 1 ? '' : 's'}, ${result.queryTime}ms)`,
|
|
401
|
+
'',
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
for (let i = 0; i < result.rows.length; i++) {
|
|
405
|
+
lines.push(`Row ${i + 1}:`);
|
|
406
|
+
const row = result.rows[i];
|
|
407
|
+
|
|
408
|
+
// Group fields by alias prefix (e.g., "n_name", "b_filePath")
|
|
409
|
+
// and reconstruct "alias.field = value" display, with file:line merging
|
|
410
|
+
const seen = new Set<string>();
|
|
411
|
+
|
|
412
|
+
for (const key of Object.keys(row)) {
|
|
413
|
+
if (seen.has(key)) continue;
|
|
414
|
+
seen.add(key);
|
|
415
|
+
|
|
416
|
+
// key format is aliasName_fieldName (e.g. n_filePath, n_startLine)
|
|
417
|
+
const sepIdx = key.indexOf('_');
|
|
418
|
+
if (sepIdx === -1) {
|
|
419
|
+
lines.push(` ${key} = ${row[key] ?? 'null'}`);
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const alias = key.slice(0, sepIdx);
|
|
424
|
+
const field = key.slice(sepIdx + 1);
|
|
425
|
+
|
|
426
|
+
// Merge filePath + startLine into a single file:line hint
|
|
427
|
+
if (field === 'filePath') {
|
|
428
|
+
const startLineKey = `${alias}_startLine`;
|
|
429
|
+
const startLine = row[startLineKey];
|
|
430
|
+
seen.add(startLineKey);
|
|
431
|
+
const filePath = row[key];
|
|
432
|
+
if (filePath != null && startLine != null) {
|
|
433
|
+
lines.push(` ${alias}.filePath = ${filePath}:${startLine}`);
|
|
434
|
+
} else if (filePath != null) {
|
|
435
|
+
lines.push(` ${alias}.filePath = ${filePath}`);
|
|
436
|
+
}
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Skip startLine — already consumed by filePath branch above
|
|
441
|
+
if (field === 'startLine') continue;
|
|
442
|
+
|
|
443
|
+
lines.push(` ${alias}.${field} = ${row[key] ?? 'null'}`);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (i < result.rows.length - 1) lines.push('');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return lines.join('\n');
|
|
450
|
+
}
|
|
@@ -6,7 +6,7 @@ import type Database from 'better-sqlite3';
|
|
|
6
6
|
import type { EmbedderFn } from './embedder.js';
|
|
7
7
|
import type { EmbedDeviceConfig } from './device-config.js';
|
|
8
8
|
import type { HttpEmbedderConfig } from './http-embedder.js';
|
|
9
|
-
import { upsertEmbedding, countEmbeddings } from '../storage/embedding-store.js';
|
|
9
|
+
import { upsertEmbedding, ensureEmbeddingSchema, countEmbeddings } from '../storage/embedding-store.js';
|
|
10
10
|
import { embedText } from './embedder.js';
|
|
11
11
|
|
|
12
12
|
const BATCH_SIZE = 32;
|
|
@@ -110,6 +110,9 @@ export async function embedAll(
|
|
|
110
110
|
const toEmbed = filteredRows.filter((r) => !existingIds.has(r.id));
|
|
111
111
|
skipped = filteredRows.length - toEmbed.length;
|
|
112
112
|
|
|
113
|
+
// Ensure schema migration runs once before any upsert — not per-row.
|
|
114
|
+
if (toEmbed.length > 0) ensureEmbeddingSchema(db);
|
|
115
|
+
|
|
113
116
|
// Process in batches
|
|
114
117
|
for (let i = 0; i < toEmbed.length; i += BATCH_SIZE) {
|
|
115
118
|
const batch = toEmbed.slice(i, i + BATCH_SIZE);
|