@monoes/monograph 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.monomind/loops/mastermind-review-1782162423447.json +16 -0
- 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/cli/skill-gen.js +3 -0
- package/dist/src/cli/skill-gen.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 +2 -0
- 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/src/wiki/wiki-generator.d.ts.map +1 -1
- package/dist/src/wiki/wiki-generator.js +5 -1
- package/dist/src/wiki/wiki-generator.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/cli/skill-gen.ts +3 -0
- 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 +50 -4
- package/src/graph/normalize-id.ts +26 -2
- package/src/graph/pagerank.ts +70 -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 +97 -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/wiki/wiki-generator.ts +5 -1
- package/src/coverage/runtime.ts +0 -249
package/src/graph/wcc.ts
CHANGED
|
@@ -20,59 +20,168 @@ export function weaklyConnectedComponents(db: MonographDb): string[][] {
|
|
|
20
20
|
if (nodeRows.length === 0) return [];
|
|
21
21
|
|
|
22
22
|
const nodes = nodeRows.map(r => r.id);
|
|
23
|
+
const { parent, rank } = buildUnionFind(nodes);
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
for (const { source_id: src, target_id: tgt } of edgeRows) {
|
|
26
|
+
if (src === tgt) continue;
|
|
27
|
+
if (!parent.has(src) || !parent.has(tgt)) continue;
|
|
28
|
+
ufUnion(parent, rank, src, tgt);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return groupByRoot(nodes, parent);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Summary stats — avoids name resolution and component array materialisation
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export interface WccStats {
|
|
39
|
+
/** Total number of weakly connected components */
|
|
40
|
+
componentCount: number;
|
|
41
|
+
/** Size of the largest component */
|
|
42
|
+
largestSize: number;
|
|
43
|
+
/** Size of the smallest component */
|
|
44
|
+
smallestSize: number;
|
|
45
|
+
/** Mean component size */
|
|
46
|
+
meanSize: number;
|
|
47
|
+
/** Number of isolated nodes (component size = 1) */
|
|
48
|
+
isolatedNodeCount: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Lightweight summary statistics for all WCCs.
|
|
53
|
+
* Does not materialise component arrays — returns aggregate numbers only.
|
|
54
|
+
*/
|
|
55
|
+
export function wccStats(db: MonographDb): WccStats {
|
|
56
|
+
const components = weaklyConnectedComponents(db);
|
|
57
|
+
if (components.length === 0) {
|
|
58
|
+
return { componentCount: 0, largestSize: 0, smallestSize: 0, meanSize: 0, isolatedNodeCount: 0 };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sizes = components.map(c => c.length);
|
|
62
|
+
const total = sizes.reduce((a, b) => a + b, 0);
|
|
63
|
+
return {
|
|
64
|
+
componentCount: sizes.length,
|
|
65
|
+
largestSize: sizes[0], // already sorted descending
|
|
66
|
+
smallestSize: sizes[sizes.length - 1],
|
|
67
|
+
meanSize: Math.round((total / sizes.length) * 100) / 100,
|
|
68
|
+
isolatedNodeCount: sizes.filter(s => s === 1).length,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Structured text formatter for LLM consumption
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Format WCC results as structured text for LLM consumption.
|
|
78
|
+
*
|
|
79
|
+
* Resolves node IDs to names and file paths for the top-N largest components.
|
|
80
|
+
* Small/isolated components are summarised in aggregate to avoid token waste.
|
|
81
|
+
*
|
|
82
|
+
* @param db - The MonographDb instance (for name resolution)
|
|
83
|
+
* @param components - Result of weaklyConnectedComponents()
|
|
84
|
+
* @param topN - Number of largest components to detail (default: 5)
|
|
85
|
+
* @returns Structured text string suitable for LLM context injection
|
|
86
|
+
*/
|
|
87
|
+
export function formatWcc(db: MonographDb, components: string[][], topN = 5): string {
|
|
88
|
+
if (components.length === 0) {
|
|
89
|
+
return 'Weakly connected components: no nodes found.';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const isolatedCount = components.filter(c => c.length === 1).length;
|
|
93
|
+
const nonTrivial = components.filter(c => c.length > 1);
|
|
94
|
+
const display = nonTrivial.slice(0, topN);
|
|
95
|
+
|
|
96
|
+
// Batch-resolve all node IDs in the displayed components
|
|
97
|
+
const allIds = display.flat();
|
|
98
|
+
const CHUNK = 200;
|
|
99
|
+
const nodeInfo = new Map<string, { name: string; filePath: string | null }>();
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < allIds.length; i += CHUNK) {
|
|
102
|
+
const chunk = allIds.slice(i, i + CHUNK);
|
|
103
|
+
const ph = chunk.map(() => '?').join(',');
|
|
104
|
+
const rows = db
|
|
105
|
+
.prepare(`SELECT id, name, file_path FROM nodes WHERE id IN (${ph})`)
|
|
106
|
+
.all(...chunk) as { id: string; name: string; file_path: string | null }[];
|
|
107
|
+
for (const row of rows) nodeInfo.set(row.id, { name: row.name ?? row.id, filePath: row.file_path });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const lines: string[] = [
|
|
111
|
+
`Weakly connected components: ${components.length} total` +
|
|
112
|
+
(isolatedCount > 0 ? ` (${isolatedCount} isolated node${isolatedCount === 1 ? '' : 's'})` : ''),
|
|
113
|
+
'',
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
for (let ci = 0; ci < display.length; ci++) {
|
|
117
|
+
const comp = display[ci];
|
|
118
|
+
lines.push(`Component ${ci + 1} (${comp.length} nodes):`);
|
|
119
|
+
for (const id of comp) {
|
|
120
|
+
const info = nodeInfo.get(id);
|
|
121
|
+
const name = info?.name ?? id;
|
|
122
|
+
const fp = info?.filePath;
|
|
123
|
+
lines.push(` ${name}${fp ? ` — ${fp}` : ''}`);
|
|
124
|
+
}
|
|
125
|
+
if (ci < display.length - 1) lines.push('');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (nonTrivial.length > topN) {
|
|
129
|
+
lines.push('');
|
|
130
|
+
lines.push(`... (${nonTrivial.length - topN} more non-trivial component${nonTrivial.length - topN === 1 ? '' : 's'} omitted)`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return lines.join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Internal union-find helpers
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
function buildUnionFind(nodes: string[]): { parent: Map<string, string>; rank: Map<string, number> } {
|
|
25
141
|
const parent = new Map<string, string>();
|
|
26
142
|
const rank = new Map<string, number>();
|
|
27
|
-
|
|
28
143
|
for (const n of nodes) {
|
|
29
144
|
parent.set(n, n);
|
|
30
145
|
rank.set(n, 0);
|
|
31
146
|
}
|
|
147
|
+
return { parent, rank };
|
|
148
|
+
}
|
|
32
149
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
return root;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function union(a: string, b: string): void {
|
|
47
|
-
const ra = find(a);
|
|
48
|
-
const rb = find(b);
|
|
49
|
-
if (ra === rb) return;
|
|
50
|
-
const rankA = rank.get(ra) ?? 0;
|
|
51
|
-
const rankB = rank.get(rb) ?? 0;
|
|
52
|
-
if (rankA < rankB) {
|
|
53
|
-
parent.set(ra, rb);
|
|
54
|
-
} else if (rankA > rankB) {
|
|
55
|
-
parent.set(rb, ra);
|
|
56
|
-
} else {
|
|
57
|
-
parent.set(rb, ra);
|
|
58
|
-
rank.set(ra, rankA + 1);
|
|
59
|
-
}
|
|
150
|
+
function ufFind(parent: Map<string, string>, x: string): string {
|
|
151
|
+
let root = x;
|
|
152
|
+
while (parent.get(root) !== root) root = parent.get(root)!;
|
|
153
|
+
// Path compression
|
|
154
|
+
let cur = x;
|
|
155
|
+
while (cur !== root) {
|
|
156
|
+
const next = parent.get(cur)!;
|
|
157
|
+
parent.set(cur, root);
|
|
158
|
+
cur = next;
|
|
60
159
|
}
|
|
160
|
+
return root;
|
|
161
|
+
}
|
|
61
162
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
163
|
+
function ufUnion(parent: Map<string, string>, rank: Map<string, number>, a: string, b: string): void {
|
|
164
|
+
const ra = ufFind(parent, a);
|
|
165
|
+
const rb = ufFind(parent, b);
|
|
166
|
+
if (ra === rb) return;
|
|
167
|
+
const rankA = rank.get(ra) ?? 0;
|
|
168
|
+
const rankB = rank.get(rb) ?? 0;
|
|
169
|
+
if (rankA < rankB) {
|
|
170
|
+
parent.set(ra, rb);
|
|
171
|
+
} else if (rankA > rankB) {
|
|
172
|
+
parent.set(rb, ra);
|
|
173
|
+
} else {
|
|
174
|
+
parent.set(rb, ra);
|
|
175
|
+
rank.set(ra, rankA + 1);
|
|
66
176
|
}
|
|
177
|
+
}
|
|
67
178
|
|
|
68
|
-
|
|
179
|
+
function groupByRoot(nodes: string[], parent: Map<string, string>): string[][] {
|
|
69
180
|
const components = new Map<string, string[]>();
|
|
70
181
|
for (const n of nodes) {
|
|
71
|
-
const root =
|
|
182
|
+
const root = ufFind(parent, n);
|
|
72
183
|
if (!components.has(root)) components.set(root, []);
|
|
73
184
|
components.get(root)!.push(n);
|
|
74
185
|
}
|
|
75
|
-
|
|
76
|
-
// Return sorted by size descending
|
|
77
186
|
return [...components.values()].sort((a, b) => b.length - a.length);
|
|
78
187
|
}
|
package/src/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ export * from './groups/group-sync.js';
|
|
|
32
32
|
export * from './mcp-tools/group-query.js';
|
|
33
33
|
export * from './mcp-tools/group-list.js';
|
|
34
34
|
export * from './mcp-tools/group-sync.js';
|
|
35
|
-
// wiki/wiki-store.js re-exported
|
|
35
|
+
// wiki/wiki-store.js intentionally not re-exported: its WikiPage conflicts with mcp-tools/wiki.ts's WikiPage
|
|
36
36
|
export * from './wiki/prompt-builder.js';
|
|
37
37
|
export * from './wiki/wiki-generator.js';
|
|
38
38
|
export * from './wiki/gist-publisher.js';
|
|
@@ -134,15 +134,22 @@ export function getMonographApiImpact(
|
|
|
134
134
|
// 3. Forward BFS on CALLS edges from handler
|
|
135
135
|
const bfsResults = forwardBfs(handler.id, db, MAX_DEPTH);
|
|
136
136
|
|
|
137
|
-
// 4. Resolve node details for
|
|
137
|
+
// 4. Resolve node details for all callees in a single batch query (avoids N+1).
|
|
138
138
|
const callees: Array<{ depth: number; node: MonographNode }> = [];
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
if (bfsResults.length > 0) {
|
|
140
|
+
const calleeIds = bfsResults.map((b) => b.nodeId);
|
|
141
|
+
const depthByNodeId = new Map(bfsResults.map((b) => [b.nodeId, b.depth]));
|
|
142
|
+
const placeholders = calleeIds.map(() => '?').join(',');
|
|
143
|
+
const nodeRows = db
|
|
144
|
+
.prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)
|
|
145
|
+
.all(...calleeIds) as Record<string, unknown>[];
|
|
146
|
+
for (const nodeRow of nodeRows) {
|
|
147
|
+
const nodeId = nodeRow.id as string;
|
|
148
|
+
const depth = depthByNodeId.get(nodeId) ?? 0;
|
|
144
149
|
callees.push({ depth, node: rowToNode(nodeRow) });
|
|
145
150
|
}
|
|
151
|
+
// Restore BFS order (sort by depth ascending)
|
|
152
|
+
callees.sort((a, b) => a.depth - b.depth);
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
// 5. Find processes that include the handler or any callee via STEP_IN_PROCESS
|
package/src/mcp-tools/context.ts
CHANGED
|
@@ -31,6 +31,31 @@ export interface MonographContextResult {
|
|
|
31
31
|
inProcesses: Array<{ id: string; name: string }>;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// ── Shared helper: query related nodes by edge relation and direction ──────────
|
|
35
|
+
|
|
36
|
+
function queryRelated(
|
|
37
|
+
db: Database.Database,
|
|
38
|
+
nodeId: string,
|
|
39
|
+
relation: string,
|
|
40
|
+
inbound: boolean,
|
|
41
|
+
limit = 50,
|
|
42
|
+
): MonographNode[] {
|
|
43
|
+
// inbound=true → this node is the target; source nodes are the result
|
|
44
|
+
// inbound=false → this node is the source; target nodes are the result
|
|
45
|
+
const [filterCol, joinCol] = inbound
|
|
46
|
+
? ['target_id', 'source_id']
|
|
47
|
+
: ['source_id', 'target_id'];
|
|
48
|
+
|
|
49
|
+
const rows = db
|
|
50
|
+
.prepare(
|
|
51
|
+
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.${joinCol}
|
|
52
|
+
WHERE e.${filterCol} = ? AND e.relation = ? LIMIT ?`,
|
|
53
|
+
)
|
|
54
|
+
.all(nodeId, relation, limit) as Record<string, unknown>[];
|
|
55
|
+
|
|
56
|
+
return rows.map(rowToNode);
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
// ── Implementation ─────────────────────────────────────────────────────────────
|
|
35
60
|
|
|
36
61
|
export function getMonographContext(
|
|
@@ -58,37 +83,11 @@ export function getMonographContext(
|
|
|
58
83
|
const node = rowToNode(nodeRow);
|
|
59
84
|
const nodeId = node.id;
|
|
60
85
|
|
|
61
|
-
// 2. Callers
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
.all(nodeId, LIMIT) as Record<string, unknown>[];
|
|
68
|
-
|
|
69
|
-
// 3. Callees: nodes this node CALLS (outbound CALLS edges)
|
|
70
|
-
const calleeRows = db
|
|
71
|
-
.prepare(
|
|
72
|
-
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id
|
|
73
|
-
WHERE e.source_id = ? AND e.relation = 'CALLS' LIMIT ?`,
|
|
74
|
-
)
|
|
75
|
-
.all(nodeId, LIMIT) as Record<string, unknown>[];
|
|
76
|
-
|
|
77
|
-
// 4. Imports: what this node imports (outbound IMPORTS edges)
|
|
78
|
-
const importRows = db
|
|
79
|
-
.prepare(
|
|
80
|
-
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id
|
|
81
|
-
WHERE e.source_id = ? AND e.relation = 'IMPORTS' LIMIT ?`,
|
|
82
|
-
)
|
|
83
|
-
.all(nodeId, LIMIT) as Record<string, unknown>[];
|
|
84
|
-
|
|
85
|
-
// 5. ImportedBy: what imports this node (inbound IMPORTS edges)
|
|
86
|
-
const importedByRows = db
|
|
87
|
-
.prepare(
|
|
88
|
-
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.source_id
|
|
89
|
-
WHERE e.target_id = ? AND e.relation = 'IMPORTS' LIMIT ?`,
|
|
90
|
-
)
|
|
91
|
-
.all(nodeId, LIMIT) as Record<string, unknown>[];
|
|
86
|
+
// 2–5. Callers / callees / imports / importedBy via shared helper
|
|
87
|
+
const callers = queryRelated(db, nodeId, 'CALLS', true, LIMIT);
|
|
88
|
+
const callees = queryRelated(db, nodeId, 'CALLS', false, LIMIT);
|
|
89
|
+
const imports = queryRelated(db, nodeId, 'IMPORTS', false, LIMIT);
|
|
90
|
+
const importedBy = queryRelated(db, nodeId, 'IMPORTS', true, LIMIT);
|
|
92
91
|
|
|
93
92
|
// 6. Community: from node's community_id field
|
|
94
93
|
let community: { id: number; label?: string } | null = null;
|
|
@@ -108,13 +107,5 @@ export function getMonographContext(
|
|
|
108
107
|
)
|
|
109
108
|
.all(nodeId, LIMIT) as Array<{ id: string; name: string }>;
|
|
110
109
|
|
|
111
|
-
return {
|
|
112
|
-
node,
|
|
113
|
-
callers: callerRows.map(rowToNode),
|
|
114
|
-
callees: calleeRows.map(rowToNode),
|
|
115
|
-
imports: importRows.map(rowToNode),
|
|
116
|
-
importedBy: importedByRows.map(rowToNode),
|
|
117
|
-
community,
|
|
118
|
-
inProcesses: processRows,
|
|
119
|
-
};
|
|
110
|
+
return { node, callers, callees, imports, importedBy, community, inProcesses: processRows };
|
|
120
111
|
}
|
package/src/mcp-tools/cypher.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type Database from 'better-sqlite3';
|
|
2
|
-
import { executeCypherQuery } from '../query/cypher-parser.js';
|
|
2
|
+
import { executeCypherQuery, formatCypherResult } from '../query/cypher-parser.js';
|
|
3
3
|
import type { CypherResult } from '../query/cypher-parser.js';
|
|
4
4
|
|
|
5
5
|
export type { CypherResult };
|
|
6
|
+
export { formatCypherResult };
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Execute a restricted read-only Cypher-style query against the Monograph graph.
|
|
@@ -14,3 +15,15 @@ export type { CypherResult };
|
|
|
14
15
|
export function getMonographCypher(db: Database.Database, query: string): CypherResult {
|
|
15
16
|
return executeCypherQuery(db, query);
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Execute a Cypher-style query and return structured text for LLM consumption.
|
|
21
|
+
* Rows with filePath + startLine fields are rendered as "file:line" navigation hints.
|
|
22
|
+
*
|
|
23
|
+
* @param db - Open better-sqlite3 database handle
|
|
24
|
+
* @param query - Cypher MATCH query string
|
|
25
|
+
* @returns Formatted string suitable for direct injection into LLM context
|
|
26
|
+
*/
|
|
27
|
+
export function getMonographCypherText(db: Database.Database, query: string): string {
|
|
28
|
+
return formatCypherResult(executeCypherQuery(db, query));
|
|
29
|
+
}
|
|
@@ -13,6 +13,9 @@ export interface MonographDetectChangesResult {
|
|
|
13
13
|
|
|
14
14
|
// ── Implementation ─────────────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
|
+
const MAX_FILES = 200; // cap on changed-file count to prevent unbounded IN clauses
|
|
17
|
+
const MAX_STDOUT = 1_048_576; // 1 MiB stdout cap for git output
|
|
18
|
+
|
|
16
19
|
export function detectMonographChanges(
|
|
17
20
|
db: Database.Database,
|
|
18
21
|
input: { baseBranch?: string; includeTests?: boolean },
|
|
@@ -33,15 +36,17 @@ export function detectMonographChanges(
|
|
|
33
36
|
const result = spawnSync('git', ['diff', '--name-only', `${baseBranch}...HEAD`], {
|
|
34
37
|
cwd: repoPath,
|
|
35
38
|
encoding: 'utf-8',
|
|
39
|
+
maxBuffer: MAX_STDOUT,
|
|
36
40
|
});
|
|
37
41
|
if (result.error) throw result.error;
|
|
38
42
|
if (result.status !== 0) throw new Error(result.stderr ?? `git exited ${result.status}`);
|
|
39
|
-
const output = result.stdout.trim();
|
|
43
|
+
const output = (result.stdout ?? '').slice(0, MAX_STDOUT).trim();
|
|
40
44
|
|
|
41
45
|
changedFiles = output
|
|
42
46
|
.split('\n')
|
|
43
47
|
.map(f => f.trim())
|
|
44
|
-
.filter(f => f.length > 0)
|
|
48
|
+
.filter(f => f.length > 0)
|
|
49
|
+
.slice(0, MAX_FILES); // cap to prevent unbounded IN clauses
|
|
45
50
|
} catch (err) {
|
|
46
51
|
const message = err instanceof Error ? err.message : String(err);
|
|
47
52
|
return { changedFiles: [], affectedSymbols: [], affectedProcesses: [], error: message };
|
|
@@ -56,22 +61,25 @@ export function detectMonographChanges(
|
|
|
56
61
|
return { changedFiles: [], affectedSymbols: [], affectedProcesses: [] };
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
// Query DB for symbols in changed files
|
|
64
|
+
// Query DB for symbols in changed files (batch if needed)
|
|
60
65
|
let affectedSymbols: Array<{ name: string; filePath: string; label: string }> = [];
|
|
61
66
|
let affectedSymbolIds: string[] = [];
|
|
62
67
|
|
|
63
68
|
try {
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
// Process in batches of 50 to keep SQLite IN clause size bounded
|
|
70
|
+
const BATCH = 50;
|
|
71
|
+
const allRows: Array<{ id: string; name: string; file_path: string; label: string }> = [];
|
|
72
|
+
for (let i = 0; i < changedFiles.length; i += BATCH) {
|
|
73
|
+
const batch = changedFiles.slice(i, i + BATCH);
|
|
74
|
+
const placeholders = batch.map(() => '?').join(',');
|
|
75
|
+
const rows = db
|
|
76
|
+
.prepare(`SELECT id, name, file_path, label FROM nodes WHERE file_path IN (${placeholders})`)
|
|
77
|
+
.all(...batch) as Array<{ id: string; name: string; file_path: string; label: string }>;
|
|
78
|
+
allRows.push(...rows);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
affectedSymbols = allRows.map(r => ({ name: r.name, filePath: r.file_path, label: r.label }));
|
|
82
|
+
affectedSymbolIds = allRows.map(r => r.id);
|
|
75
83
|
} catch (err) {
|
|
76
84
|
const message = err instanceof Error ? err.message : String(err);
|
|
77
85
|
return { changedFiles, affectedSymbols: [], affectedProcesses: [], error: message };
|
|
@@ -83,15 +91,22 @@ export function detectMonographChanges(
|
|
|
83
91
|
|
|
84
92
|
if (affectedSymbolIds.length > 0) {
|
|
85
93
|
try {
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
// Also batch symbol IDs to keep IN clause bounded
|
|
95
|
+
const BATCH = 50;
|
|
96
|
+
const processSet = new Map<string, string>();
|
|
97
|
+
for (let i = 0; i < affectedSymbolIds.length; i += BATCH) {
|
|
98
|
+
const batch = affectedSymbolIds.slice(i, i + BATCH);
|
|
99
|
+
const placeholders = batch.map(() => '?').join(',');
|
|
100
|
+
const processRows = db
|
|
101
|
+
.prepare(
|
|
102
|
+
`SELECT DISTINCT n.id, n.name
|
|
103
|
+
FROM nodes n JOIN edges e ON n.id = e.source_id
|
|
104
|
+
WHERE e.relation = 'STEP_IN_PROCESS' AND e.target_id IN (${placeholders})`,
|
|
105
|
+
)
|
|
106
|
+
.all(...batch) as Array<{ id: string; name: string }>;
|
|
107
|
+
for (const row of processRows) processSet.set(row.id, row.name);
|
|
108
|
+
}
|
|
109
|
+
affectedProcesses = Array.from(processSet.entries()).map(([id, name]) => ({ id, name }));
|
|
95
110
|
} catch {
|
|
96
111
|
// Non-fatal: return what we have
|
|
97
112
|
}
|
package/src/mcp-tools/explain.ts
CHANGED
|
@@ -40,7 +40,10 @@ export function explainNode(db: Database.Database, name: string): ExplainResult
|
|
|
40
40
|
).all(node.id) as { name: string; relation: string }[];
|
|
41
41
|
|
|
42
42
|
const connectionCount = outRows.length + inRows.length;
|
|
43
|
-
const
|
|
43
|
+
const fileRef = node.filePath
|
|
44
|
+
? (node.startLine != null ? `${node.filePath}:${node.startLine}` : node.filePath)
|
|
45
|
+
: null;
|
|
46
|
+
const fileLine = fileRef ? ` defined in \`${fileRef}\`` : '';
|
|
44
47
|
|
|
45
48
|
let explanation = `**${node.name}** is a ${node.label}${fileLine}.`;
|
|
46
49
|
|
|
@@ -20,6 +20,8 @@ function rowToNode(row: Record<string, unknown>): MonographNode {
|
|
|
20
20
|
export interface GraphQueryInput {
|
|
21
21
|
query: string;
|
|
22
22
|
mode?: 'bfs' | 'dfs';
|
|
23
|
+
/** Direction of edge traversal. 'both' includes incoming edges (callers) and outgoing (callees). Default: 'out' */
|
|
24
|
+
direction?: 'out' | 'in' | 'both';
|
|
23
25
|
tokenBudget?: number;
|
|
24
26
|
depth?: number;
|
|
25
27
|
}
|
|
@@ -40,6 +42,7 @@ export function queryGraph(
|
|
|
40
42
|
input: GraphQueryInput,
|
|
41
43
|
): GraphQueryResult {
|
|
42
44
|
const mode = input.mode ?? 'bfs';
|
|
45
|
+
const direction = input.direction ?? 'out';
|
|
43
46
|
const tokenBudget = input.tokenBudget ?? 2000;
|
|
44
47
|
const maxDepth = input.depth ?? 3;
|
|
45
48
|
|
|
@@ -64,15 +67,33 @@ export function queryGraph(
|
|
|
64
67
|
if (!truncated) {
|
|
65
68
|
// BFS or DFS expansion
|
|
66
69
|
const frontier: Array<{ id: string; depth: number }> = seeds.map(s => ({ id: s.id, depth: 0 }));
|
|
67
|
-
|
|
70
|
+
|
|
71
|
+
// Prepare edge traversal statements for each direction.
|
|
72
|
+
// 'out' follows outgoing edges (what this node calls/imports/uses).
|
|
73
|
+
// 'in' follows incoming edges (what calls/imports/uses this node).
|
|
74
|
+
// 'both' follows both directions — needed for full context (callers + callees).
|
|
75
|
+
const outEdgeStmt = db.prepare(
|
|
68
76
|
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.target_id WHERE e.source_id = ? LIMIT 20`
|
|
69
77
|
);
|
|
78
|
+
const inEdgeStmt = db.prepare(
|
|
79
|
+
`SELECT n.* FROM nodes n JOIN edges e ON n.id = e.source_id WHERE e.target_id = ? LIMIT 20`
|
|
80
|
+
);
|
|
81
|
+
const getNeighbors = (id: string): Record<string, unknown>[] => {
|
|
82
|
+
if (direction === 'out') return outEdgeStmt.all(id) as Record<string, unknown>[];
|
|
83
|
+
if (direction === 'in') return inEdgeStmt.all(id) as Record<string, unknown>[];
|
|
84
|
+
// 'both': union of outgoing and incoming, deduplicated by id
|
|
85
|
+
const outRows = outEdgeStmt.all(id) as Record<string, unknown>[];
|
|
86
|
+
const inRows = inEdgeStmt.all(id) as Record<string, unknown>[];
|
|
87
|
+
const seen = new Set(outRows.map(r => r['id'] as string));
|
|
88
|
+
return [...outRows, ...inRows.filter(r => !seen.has(r['id'] as string))];
|
|
89
|
+
};
|
|
90
|
+
|
|
70
91
|
|
|
71
92
|
if (mode === 'bfs') {
|
|
72
93
|
while (frontier.length > 0 && !truncated) {
|
|
73
94
|
const { id, depth } = frontier.shift()!;
|
|
74
95
|
if (depth >= maxDepth) continue;
|
|
75
|
-
const neighbors =
|
|
96
|
+
const neighbors = getNeighbors(id);
|
|
76
97
|
for (const row of neighbors) {
|
|
77
98
|
const node = rowToNode(row);
|
|
78
99
|
if (visited.has(node.id)) continue;
|
|
@@ -89,7 +110,7 @@ export function queryGraph(
|
|
|
89
110
|
while (stack.length > 0 && !truncated) {
|
|
90
111
|
const { id, depth } = stack.pop()!;
|
|
91
112
|
if (depth >= maxDepth) continue;
|
|
92
|
-
const neighbors =
|
|
113
|
+
const neighbors = getNeighbors(id);
|
|
93
114
|
for (let i = neighbors.length - 1; i >= 0; i--) {
|
|
94
115
|
const node = rowToNode(neighbors[i]!);
|
|
95
116
|
if (visited.has(node.id)) continue;
|
|
@@ -14,6 +14,7 @@ export interface GroupRepoInfo {
|
|
|
14
14
|
path: string;
|
|
15
15
|
indexedAt: string | null;
|
|
16
16
|
nodeCount: number;
|
|
17
|
+
error?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface GroupListResult {
|
|
@@ -29,13 +30,12 @@ function getRepoDbPath(repoPath: string): string {
|
|
|
29
30
|
return join(repoPath, '.monomind', 'monograph.db');
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function
|
|
33
|
-
repoName: string,
|
|
33
|
+
function readRepoStats(
|
|
34
34
|
repoPath: string,
|
|
35
|
-
): { indexedAt: string | null; nodeCount: number } {
|
|
35
|
+
): { indexedAt: string | null; nodeCount: number; error?: string } {
|
|
36
36
|
const dbPath = getRepoDbPath(repoPath);
|
|
37
37
|
if (!existsSync(dbPath)) {
|
|
38
|
-
return { indexedAt: null, nodeCount: 0 };
|
|
38
|
+
return { indexedAt: null, nodeCount: 0, error: 'index not found' };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
let db: Database.Database | null = null;
|
|
@@ -48,13 +48,10 @@ function readRepStats(
|
|
|
48
48
|
|
|
49
49
|
const countRow = db.prepare(`SELECT COUNT(*) as cnt FROM nodes`).get() as { cnt: number };
|
|
50
50
|
|
|
51
|
-
return {
|
|
52
|
-
indexedAt: metaRow?.value ?? null,
|
|
53
|
-
nodeCount: countRow.cnt,
|
|
54
|
-
};
|
|
51
|
+
return { indexedAt: metaRow?.value ?? null, nodeCount: countRow.cnt };
|
|
55
52
|
} catch (err) {
|
|
56
|
-
|
|
57
|
-
return { indexedAt: null, nodeCount: 0 };
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
+
return { indexedAt: null, nodeCount: 0, error: message };
|
|
58
55
|
} finally {
|
|
59
56
|
db?.close();
|
|
60
57
|
}
|
|
@@ -70,12 +67,13 @@ export async function getGroupList(configPath?: string): Promise<GroupListResult
|
|
|
70
67
|
const config = parseGroupConfig(resolvedPath);
|
|
71
68
|
|
|
72
69
|
const repos: GroupRepoInfo[] = config.repos.map((repo) => {
|
|
73
|
-
const { indexedAt, nodeCount } =
|
|
70
|
+
const { indexedAt, nodeCount, error } = readRepoStats(repo.path);
|
|
74
71
|
return {
|
|
75
72
|
name: repo.name,
|
|
76
73
|
path: repo.path,
|
|
77
74
|
indexedAt,
|
|
78
75
|
nodeCount,
|
|
76
|
+
...(error ? { error } : {}),
|
|
79
77
|
};
|
|
80
78
|
});
|
|
81
79
|
|