@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
|
@@ -31,6 +31,10 @@ export interface HybridResult extends RankedResult {
|
|
|
31
31
|
filePath: string | null;
|
|
32
32
|
label: string;
|
|
33
33
|
score: number;
|
|
34
|
+
/** First line of the symbol in its source file (1-based, null if unknown). */
|
|
35
|
+
startLine?: number | null;
|
|
36
|
+
/** Last line of the symbol in its source file (1-based, null if unknown). */
|
|
37
|
+
endLine?: number | null;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
/**
|
|
@@ -61,6 +65,8 @@ export async function hybridQuery(
|
|
|
61
65
|
filePath: r.filePath,
|
|
62
66
|
label: r.label,
|
|
63
67
|
score: r.rank,
|
|
68
|
+
startLine: r.startLine,
|
|
69
|
+
endLine: r.endLine,
|
|
64
70
|
}));
|
|
65
71
|
|
|
66
72
|
// ── Decide whether to add vector results ──────────────────────────────────
|
|
@@ -121,7 +127,7 @@ export async function hybridQuery(
|
|
|
121
127
|
const placeholders = unknownIds.map(() => '?').join(',');
|
|
122
128
|
const rows = db
|
|
123
129
|
.prepare(
|
|
124
|
-
`SELECT id, name, norm_label, file_path, label FROM nodes WHERE id IN (${placeholders})`,
|
|
130
|
+
`SELECT id, name, norm_label, file_path, label, start_line, end_line FROM nodes WHERE id IN (${placeholders})`,
|
|
125
131
|
)
|
|
126
132
|
.all(...unknownIds) as {
|
|
127
133
|
id: string;
|
|
@@ -129,6 +135,8 @@ export async function hybridQuery(
|
|
|
129
135
|
norm_label: string;
|
|
130
136
|
file_path: string | null;
|
|
131
137
|
label: string;
|
|
138
|
+
start_line: number | null;
|
|
139
|
+
end_line: number | null;
|
|
132
140
|
}[];
|
|
133
141
|
|
|
134
142
|
const rowMap = new Map(rows.map((r) => [r.id, r]));
|
|
@@ -140,6 +148,8 @@ export async function hybridQuery(
|
|
|
140
148
|
item.normLabel = row.norm_label;
|
|
141
149
|
item.filePath = row.file_path;
|
|
142
150
|
item.label = row.label;
|
|
151
|
+
(item as HybridResult).startLine = row.start_line;
|
|
152
|
+
(item as HybridResult).endLine = row.end_line;
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
155
|
}
|
|
@@ -24,12 +24,12 @@ export function checkStaleness(db: MonographDb, repoPath: string): StalenessRepo
|
|
|
24
24
|
return { isStale: false, indexedAt: null, indexedCommit: null, currentCommit: null, changedSince: [], staleSince: null };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// Display short SHA (7 chars) for the interface field; compare
|
|
27
|
+
// Display short SHA (7 chars) for the interface field; compare short SHAs for staleness
|
|
28
28
|
const indexedCommitShort = indexedCommitFull ? indexedCommitFull.slice(0, 7) : null;
|
|
29
29
|
const currentCommitShort = currentCommit ? currentCommit.slice(0, 7) : null;
|
|
30
30
|
|
|
31
|
-
// 3. If no stored commit or
|
|
32
|
-
if (!indexedCommitFull ||
|
|
31
|
+
// 3. If no stored commit or short SHAs match, not stale
|
|
32
|
+
if (!indexedCommitFull || indexedCommitShort === currentCommitShort) {
|
|
33
33
|
return { isStale: false, indexedAt: null, indexedCommit: indexedCommitShort, currentCommit: currentCommitShort, changedSince: [], staleSince: null };
|
|
34
34
|
}
|
|
35
35
|
|
package/src/storage/db.ts
CHANGED
|
@@ -37,6 +37,17 @@ function applyMigrations(db: MonographDb): void {
|
|
|
37
37
|
db.exec(FTS_SYNC_TRIGGERS);
|
|
38
38
|
db.exec(CREATE_EMBEDDINGS);
|
|
39
39
|
db.exec(CREATE_WIKI_PAGES);
|
|
40
|
+
|
|
41
|
+
// Schema version table — tracks incremental column additions.
|
|
42
|
+
db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY)`);
|
|
43
|
+
const row = db.prepare('SELECT MAX(version) AS v FROM schema_version').get() as { v: number | null };
|
|
44
|
+
const current = row?.v ?? 0;
|
|
45
|
+
|
|
46
|
+
if (current < 1) {
|
|
47
|
+
// v1: added `reason TEXT` to edges
|
|
48
|
+
try { db.exec('ALTER TABLE edges ADD COLUMN reason TEXT'); } catch { /* already present */ }
|
|
49
|
+
db.prepare('INSERT OR REPLACE INTO schema_version VALUES (1)').run();
|
|
50
|
+
}
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
/** Write to a .tmp file then rename for atomic replacement. */
|
|
@@ -17,15 +17,48 @@ export function insertEdge(db: Database.Database, edge: MonographEdge): void {
|
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const INSERT_EDGE_SQL = `
|
|
21
|
+
INSERT OR REPLACE INTO edges (id, source_id, target_id, relation, confidence, confidence_score, reason, evidence)
|
|
22
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
23
|
+
`;
|
|
24
|
+
|
|
20
25
|
export function insertEdges(db: Database.Database, edges: MonographEdge[]): void {
|
|
26
|
+
if (edges.length === 0) return;
|
|
27
|
+
// Prepare once and reuse across all rows — avoids N redundant prepare calls
|
|
28
|
+
const stmt = db.prepare(INSERT_EDGE_SQL);
|
|
21
29
|
const insertMany = db.transaction((rows: MonographEdge[]) => {
|
|
22
30
|
for (const e of rows) {
|
|
23
|
-
|
|
31
|
+
stmt.run(
|
|
32
|
+
e.id,
|
|
33
|
+
e.sourceId,
|
|
34
|
+
e.targetId,
|
|
35
|
+
e.relation,
|
|
36
|
+
e.confidence,
|
|
37
|
+
e.confidenceScore,
|
|
38
|
+
e.reason ?? null,
|
|
39
|
+
e.evidence != null ? JSON.stringify(e.evidence) : null,
|
|
40
|
+
);
|
|
24
41
|
}
|
|
25
42
|
});
|
|
26
43
|
insertMany(edges);
|
|
27
44
|
}
|
|
28
45
|
|
|
46
|
+
/** Batch-fetch edges for multiple source IDs using a single SQL IN query. Batches at 50 IDs. */
|
|
47
|
+
export function getEdgesForSources(db: Database.Database, sourceIds: string[]): MonographEdge[] {
|
|
48
|
+
if (sourceIds.length === 0) return [];
|
|
49
|
+
const BATCH = 50;
|
|
50
|
+
const results: MonographEdge[] = [];
|
|
51
|
+
for (let i = 0; i < sourceIds.length; i += BATCH) {
|
|
52
|
+
const chunk = sourceIds.slice(i, i + BATCH);
|
|
53
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
54
|
+
const rows = db
|
|
55
|
+
.prepare(`SELECT * FROM edges WHERE source_id IN (${placeholders})`)
|
|
56
|
+
.all(...chunk) as Record<string, unknown>[];
|
|
57
|
+
for (const row of rows) results.push(rowToEdge(row));
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
|
|
29
62
|
export function getEdgesForSource(db: Database.Database, sourceId: string): MonographEdge[] {
|
|
30
63
|
const rows = db
|
|
31
64
|
.prepare('SELECT * FROM edges WHERE source_id = ?')
|
|
@@ -6,18 +6,25 @@ import type Database from 'better-sqlite3';
|
|
|
6
6
|
* Vectors are stored as BLOBs (raw Float32Array bytes) and reconstructed on read.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Ensure the embeddings table has the content_hash column introduced in a later
|
|
11
|
+
* schema version. Call this ONCE before a batch of upserts rather than inside
|
|
12
|
+
* upsertEmbedding itself to avoid running ALTER TABLE on every row write.
|
|
13
|
+
*/
|
|
14
|
+
export function ensureEmbeddingSchema(db: Database.Database): void {
|
|
15
|
+
try {
|
|
16
|
+
db.exec('ALTER TABLE embeddings ADD COLUMN content_hash TEXT');
|
|
17
|
+
} catch {
|
|
18
|
+
// Column already exists — ignore.
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
export function upsertEmbedding(
|
|
10
23
|
db: Database.Database,
|
|
11
24
|
nodeId: string,
|
|
12
25
|
vector: Float32Array,
|
|
13
26
|
contentHash?: string,
|
|
14
27
|
): void {
|
|
15
|
-
// Migrate existing DBs that may not have the content_hash column yet.
|
|
16
|
-
try {
|
|
17
|
-
db.exec('ALTER TABLE embeddings ADD COLUMN content_hash TEXT');
|
|
18
|
-
} catch {
|
|
19
|
-
// Column already exists — ignore.
|
|
20
|
-
}
|
|
21
28
|
const buf = Buffer.from(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
22
29
|
db
|
|
23
30
|
.prepare(
|
|
@@ -26,6 +33,31 @@ export function upsertEmbedding(
|
|
|
26
33
|
.run(nodeId, buf, contentHash ?? null);
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Bulk-upsert multiple embeddings in a single transaction.
|
|
38
|
+
* Calls ensureEmbeddingSchema once before writing, then wraps all inserts in
|
|
39
|
+
* a transaction for 10-100x faster throughput vs per-row upsertEmbedding calls.
|
|
40
|
+
*/
|
|
41
|
+
export function batchUpsertEmbeddings(
|
|
42
|
+
db: Database.Database,
|
|
43
|
+
entries: Array<{ nodeId: string; vector: Float32Array; contentHash?: string }>,
|
|
44
|
+
): void {
|
|
45
|
+
if (entries.length === 0) return;
|
|
46
|
+
ensureEmbeddingSchema(db);
|
|
47
|
+
const stmt = db.prepare(
|
|
48
|
+
'INSERT OR REPLACE INTO embeddings (node_id, vector, content_hash) VALUES (?, ?, ?)',
|
|
49
|
+
);
|
|
50
|
+
const insertMany = db.transaction(
|
|
51
|
+
(rows: Array<{ nodeId: string; vector: Float32Array; contentHash?: string }>) => {
|
|
52
|
+
for (const e of rows) {
|
|
53
|
+
const buf = Buffer.from(e.vector.buffer, e.vector.byteOffset, e.vector.byteLength);
|
|
54
|
+
stmt.run(e.nodeId, buf, e.contentHash ?? null);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
insertMany(entries);
|
|
59
|
+
}
|
|
60
|
+
|
|
29
61
|
export function getEmbeddingContentHash(db: Database.Database, nodeId: string): string | null {
|
|
30
62
|
const row = db
|
|
31
63
|
.prepare('SELECT content_hash FROM embeddings WHERE node_id = ?')
|
|
@@ -27,34 +27,48 @@ export function updateFileCache(db: MonographDb, entry: FileCacheEntry): void {
|
|
|
27
27
|
`).run(entry.filePath, entry.contentHash, entry.lastParsed, entry.nodeCount, entry.edgeCount);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Bulk-upsert multiple file cache entries in a single transaction.
|
|
32
|
+
* Prefer this over calling updateFileCache in a loop for pipeline batch writes.
|
|
33
|
+
*/
|
|
34
|
+
export function batchUpdateFileCache(db: MonographDb, entries: FileCacheEntry[]): void {
|
|
35
|
+
if (entries.length === 0) return;
|
|
36
|
+
const stmt = db.prepare(`
|
|
37
|
+
INSERT OR REPLACE INTO file_cache (file_path, content_hash, last_parsed, node_count, edge_count)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?)
|
|
39
|
+
`);
|
|
40
|
+
const insertMany = db.transaction((rows: FileCacheEntry[]) => {
|
|
41
|
+
for (const e of rows) {
|
|
42
|
+
stmt.run(e.filePath, e.contentHash, e.lastParsed, e.nodeCount, e.edgeCount);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
insertMany(entries);
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
export function getFileCacheStats(db: MonographDb): {
|
|
31
49
|
totalCached: number;
|
|
32
50
|
hitRate: number;
|
|
33
51
|
stalePaths: string[];
|
|
34
52
|
} {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const hitRate = totalNodes > 0 ? totalCached / totalNodes : 0;
|
|
42
|
-
|
|
43
|
-
// Find stale paths: cached entries whose file no longer exists in nodes table
|
|
44
|
-
const cachedPaths = db.prepare('SELECT file_path, content_hash FROM file_cache').all() as {
|
|
45
|
-
file_path: string;
|
|
46
|
-
content_hash: string;
|
|
47
|
-
}[];
|
|
53
|
+
// Single query: count cached entries and distinct indexed file paths in one pass.
|
|
54
|
+
const counts = db.prepare(`
|
|
55
|
+
SELECT
|
|
56
|
+
(SELECT COUNT(*) FROM file_cache) AS total_cached,
|
|
57
|
+
(SELECT COUNT(DISTINCT file_path) FROM nodes WHERE file_path IS NOT NULL) AS total_indexed
|
|
58
|
+
`).get() as { total_cached: number; total_indexed: number };
|
|
48
59
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
file_path: string;
|
|
52
|
-
}[]).map(r => r.file_path)
|
|
53
|
-
);
|
|
60
|
+
const totalCached = counts.total_cached;
|
|
61
|
+
const hitRate = counts.total_indexed > 0 ? totalCached / counts.total_indexed : 0;
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
// Find stale paths via SQL LEFT JOIN — avoids loading all rows into JS memory.
|
|
64
|
+
const stalePaths = (db.prepare(`
|
|
65
|
+
SELECT fc.file_path
|
|
66
|
+
FROM file_cache fc
|
|
67
|
+
LEFT JOIN (
|
|
68
|
+
SELECT DISTINCT file_path FROM nodes WHERE file_path IS NOT NULL
|
|
69
|
+
) n ON fc.file_path = n.file_path
|
|
70
|
+
WHERE n.file_path IS NULL
|
|
71
|
+
`).all() as { file_path: string }[]).map(r => r.file_path);
|
|
58
72
|
|
|
59
73
|
return { totalCached, hitRate, stalePaths };
|
|
60
74
|
}
|
package/src/storage/fts-store.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface FtsResult {
|
|
|
7
7
|
filePath: string | null;
|
|
8
8
|
label: string;
|
|
9
9
|
rank: number;
|
|
10
|
+
/** First line of the symbol in its source file (1-based, null if unknown). */
|
|
11
|
+
startLine: number | null;
|
|
12
|
+
/** Last line of the symbol in its source file (1-based, null if unknown). */
|
|
13
|
+
endLine: number | null;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export function ftsSearch(
|
|
@@ -25,7 +29,7 @@ export function ftsSearch(
|
|
|
25
29
|
|
|
26
30
|
let matchSql = `
|
|
27
31
|
SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
|
|
28
|
-
nodes_fts.rank
|
|
32
|
+
n.start_line, n.end_line, nodes_fts.rank
|
|
29
33
|
FROM nodes_fts
|
|
30
34
|
JOIN nodes n ON n.rowid = nodes_fts.rowid
|
|
31
35
|
WHERE nodes_fts MATCH ?
|
|
@@ -55,7 +59,7 @@ export function ftsSearch(
|
|
|
55
59
|
const likePattern = `%${escapedQuery}%`;
|
|
56
60
|
let likeSql = `
|
|
57
61
|
SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
|
|
58
|
-
0 AS rank
|
|
62
|
+
n.start_line, n.end_line, 0 AS rank
|
|
59
63
|
FROM nodes n
|
|
60
64
|
WHERE (n.name LIKE ? OR n.norm_label LIKE ? OR n.file_path LIKE ?)
|
|
61
65
|
`;
|
|
@@ -89,6 +93,8 @@ export function ftsSearch(
|
|
|
89
93
|
filePath: (r.file_path as string | null) ?? null,
|
|
90
94
|
label: r.label as string,
|
|
91
95
|
rank: r.rank as number,
|
|
96
|
+
startLine: (r.start_line as number | null) ?? null,
|
|
97
|
+
endLine: (r.end_line as number | null) ?? null,
|
|
92
98
|
}));
|
|
93
99
|
}
|
|
94
100
|
|
|
@@ -183,7 +189,8 @@ export function hybridSearch(
|
|
|
183
189
|
const escapedSafeQuery = safeQuery.replace(/%/g, '\\%').replace(/_/g, '\\_');
|
|
184
190
|
const likePattern = `%${escapedSafeQuery}%`;
|
|
185
191
|
let likeSql = `
|
|
186
|
-
SELECT n.id, n.name, n.norm_label, n.file_path, n.label
|
|
192
|
+
SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
|
|
193
|
+
n.start_line, n.end_line
|
|
187
194
|
FROM nodes n
|
|
188
195
|
WHERE (n.name LIKE ? OR n.norm_label LIKE ?)
|
|
189
196
|
`;
|
|
@@ -211,6 +218,8 @@ export function hybridSearch(
|
|
|
211
218
|
filePath: (r.file_path as string | null) ?? null,
|
|
212
219
|
label: lbl,
|
|
213
220
|
rank: 0,
|
|
221
|
+
startLine: (r.start_line as number | null) ?? null,
|
|
222
|
+
endLine: (r.end_line as number | null) ?? null,
|
|
214
223
|
combinedScore: combined,
|
|
215
224
|
matchStrategy: 'like',
|
|
216
225
|
});
|
|
@@ -23,15 +23,53 @@ export function insertNode(db: Database.Database, node: MonographNode): void {
|
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
const INSERT_SQL = `
|
|
27
|
+
INSERT OR REPLACE INTO nodes
|
|
28
|
+
(id, label, name, norm_label, file_path, start_line, end_line,
|
|
29
|
+
community_id, is_exported, language, properties)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
31
|
+
`;
|
|
32
|
+
|
|
26
33
|
export function insertNodes(db: Database.Database, nodes: MonographNode[]): void {
|
|
34
|
+
if (nodes.length === 0) return;
|
|
35
|
+
// Prepare once and reuse across all rows — avoids N redundant prepare calls
|
|
36
|
+
const stmt = db.prepare(INSERT_SQL);
|
|
27
37
|
const insertMany = db.transaction((rows: MonographNode[]) => {
|
|
28
38
|
for (const n of rows) {
|
|
29
|
-
|
|
39
|
+
stmt.run(
|
|
40
|
+
n.id,
|
|
41
|
+
n.label,
|
|
42
|
+
n.name,
|
|
43
|
+
n.normLabel ?? toNormLabel(n.name),
|
|
44
|
+
n.filePath ?? null,
|
|
45
|
+
n.startLine ?? null,
|
|
46
|
+
n.endLine ?? null,
|
|
47
|
+
n.communityId ?? null,
|
|
48
|
+
n.isExported ? 1 : 0,
|
|
49
|
+
n.language ?? null,
|
|
50
|
+
n.properties ? JSON.stringify(n.properties) : null,
|
|
51
|
+
);
|
|
30
52
|
}
|
|
31
53
|
});
|
|
32
54
|
insertMany(nodes);
|
|
33
55
|
}
|
|
34
56
|
|
|
57
|
+
/** Batch-fetch multiple nodes by ID using a single SQL IN query. Batches at 50 IDs. */
|
|
58
|
+
export function getNodesByIds(db: Database.Database, ids: string[]): MonographNode[] {
|
|
59
|
+
if (ids.length === 0) return [];
|
|
60
|
+
const BATCH = 50;
|
|
61
|
+
const results: MonographNode[] = [];
|
|
62
|
+
for (let i = 0; i < ids.length; i += BATCH) {
|
|
63
|
+
const chunk = ids.slice(i, i + BATCH);
|
|
64
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
65
|
+
const rows = db
|
|
66
|
+
.prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)
|
|
67
|
+
.all(...chunk) as Record<string, unknown>[];
|
|
68
|
+
for (const row of rows) results.push(rowToNode(row));
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
35
73
|
export function getNode(db: Database.Database, id: string): MonographNode | undefined {
|
|
36
74
|
const row = db
|
|
37
75
|
.prepare('SELECT * FROM nodes WHERE id = ?')
|
|
@@ -160,7 +160,11 @@ export async function generateWikiPage(
|
|
|
160
160
|
content = (block?.type === 'text') ? block.text : '';
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
// 7. Persist to DB
|
|
163
|
+
// 7. Persist to DB — prepend OKF frontmatter if not already present
|
|
164
|
+
if (!content.startsWith('---')) {
|
|
165
|
+
const safeLabel = label.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
166
|
+
content = `---\ntype: Code Community Wiki\ntitle: "${safeLabel}"\ntags: [monograph, wiki]\ntimestamp: ${new Date().toISOString()}\n---\n\n${content}`;
|
|
167
|
+
}
|
|
164
168
|
upsertWikiPage(db, communityIdStr, content);
|
|
165
169
|
|
|
166
170
|
return content;
|
package/src/coverage/runtime.ts
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import type { MonographDb } from '../storage/db.js';
|
|
2
|
-
|
|
3
|
-
export type RuntimeClassification =
|
|
4
|
-
| 'SafeToDelete' // graph-dead AND not covered at runtime
|
|
5
|
-
| 'ReviewRequired' // graph-dead BUT has runtime coverage
|
|
6
|
-
| 'LowTraffic' // covered but <5% call rate
|
|
7
|
-
| 'Active' // actively covered
|
|
8
|
-
| 'CoverageUnavailable'; // in graph but no coverage data for this file
|
|
9
|
-
|
|
10
|
-
export interface FunctionCoverageEntry {
|
|
11
|
-
nodeId: string;
|
|
12
|
-
name: string;
|
|
13
|
-
filePath: string | null;
|
|
14
|
-
startLine: number | null;
|
|
15
|
-
covered: boolean;
|
|
16
|
-
callCount: number;
|
|
17
|
-
classification: RuntimeClassification;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface RuntimeCoverageReport {
|
|
21
|
-
entries: FunctionCoverageEntry[];
|
|
22
|
-
safeToDelete: number;
|
|
23
|
-
reviewRequired: number;
|
|
24
|
-
active: number;
|
|
25
|
-
coverageUnavailable: number;
|
|
26
|
-
blastRadius: string[]; // active entry-point file paths (covered + reachable)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// V8 coverage format (subset of what Node/Chrome produces)
|
|
30
|
-
export interface V8CoverageFunction {
|
|
31
|
-
functionName: string;
|
|
32
|
-
url: string; // file URL like 'file:///path/to/file.js'
|
|
33
|
-
ranges: Array<{ startOffset: number; endOffset: number; count: number }>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface V8Coverage {
|
|
37
|
-
result: Array<{
|
|
38
|
-
url: string;
|
|
39
|
-
functions: V8CoverageFunction[];
|
|
40
|
-
}>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface CoverageMapEntry {
|
|
44
|
-
covered: boolean;
|
|
45
|
-
callCount: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface SymbolNodeRow {
|
|
49
|
-
id: string;
|
|
50
|
-
name: string;
|
|
51
|
-
file_path: string | null;
|
|
52
|
-
start_line: number | null;
|
|
53
|
-
properties: string | null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function analyzeRuntimeCoverage(
|
|
57
|
-
db: MonographDb,
|
|
58
|
-
coverage: V8Coverage,
|
|
59
|
-
repoRoot?: string
|
|
60
|
-
): RuntimeCoverageReport {
|
|
61
|
-
// Build a map: filePath -> Map<functionName, CoverageMapEntry>
|
|
62
|
-
const fileCoverageMap = new Map<string, Map<string, CoverageMapEntry>>();
|
|
63
|
-
|
|
64
|
-
for (const scriptCov of coverage.result) {
|
|
65
|
-
let filePath = scriptCov.url;
|
|
66
|
-
|
|
67
|
-
// Strip file:// prefix
|
|
68
|
-
if (filePath.startsWith('file://')) {
|
|
69
|
-
filePath = filePath.slice('file://'.length);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Resolve relative to repoRoot if provided and path is relative
|
|
73
|
-
if (repoRoot && !filePath.startsWith('/')) {
|
|
74
|
-
filePath = repoRoot.replace(/\/$/, '') + '/' + filePath;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const fnMap = fileCoverageMap.get(filePath) ?? new Map<string, CoverageMapEntry>();
|
|
78
|
-
|
|
79
|
-
for (const fn of scriptCov.functions) {
|
|
80
|
-
const covered = fn.ranges.some(r => r.count > 0);
|
|
81
|
-
const callCount = fn.ranges.reduce((max, r) => Math.max(max, r.count), 0);
|
|
82
|
-
const existing = fnMap.get(fn.functionName);
|
|
83
|
-
if (!existing || callCount > existing.callCount) {
|
|
84
|
-
fnMap.set(fn.functionName, { covered, callCount });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
fileCoverageMap.set(filePath, fnMap);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Helper: normalize a file path for lookup
|
|
92
|
-
function normalizeForLookup(fp: string): string {
|
|
93
|
-
return fp.replace(/\\/g, '/');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Build alternate lookup: basename -> filePath list
|
|
97
|
-
const filePathIndex = new Map<string, string[]>();
|
|
98
|
-
for (const fp of fileCoverageMap.keys()) {
|
|
99
|
-
const norm = normalizeForLookup(fp);
|
|
100
|
-
filePathIndex.set(norm, (filePathIndex.get(norm) ?? []).concat(fp));
|
|
101
|
-
// Also store by just the filename for relative matching
|
|
102
|
-
const parts = norm.split('/');
|
|
103
|
-
const base = parts[parts.length - 1];
|
|
104
|
-
if (base && base !== norm) {
|
|
105
|
-
filePathIndex.set(base, (filePathIndex.get(base) ?? []).concat(fp));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Compute total call count across all functions for LowTraffic threshold
|
|
110
|
-
let totalCallCount = 0;
|
|
111
|
-
for (const fnMap of fileCoverageMap.values()) {
|
|
112
|
-
for (const entry of fnMap.values()) {
|
|
113
|
-
totalCallCount += entry.callCount;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const lowTrafficThreshold = totalCallCount * 0.05;
|
|
117
|
-
|
|
118
|
-
// Query all Symbol nodes that are Functions/Methods
|
|
119
|
-
const rows = db.prepare(`
|
|
120
|
-
SELECT id, name, file_path, start_line, properties
|
|
121
|
-
FROM nodes
|
|
122
|
-
WHERE label IN ('Function', 'Method')
|
|
123
|
-
`).all() as SymbolNodeRow[];
|
|
124
|
-
|
|
125
|
-
const entries: FunctionCoverageEntry[] = [];
|
|
126
|
-
|
|
127
|
-
for (const row of rows) {
|
|
128
|
-
const props = row.properties ? JSON.parse(row.properties) : {};
|
|
129
|
-
const reachabilityRole: string = props.reachabilityRole ?? '';
|
|
130
|
-
|
|
131
|
-
let fnEntry: CoverageMapEntry | null = null;
|
|
132
|
-
|
|
133
|
-
if (row.file_path) {
|
|
134
|
-
const normFilePath = normalizeForLookup(row.file_path);
|
|
135
|
-
|
|
136
|
-
// Try exact match first
|
|
137
|
-
const exactFnMap = fileCoverageMap.get(normFilePath) ?? fileCoverageMap.get(row.file_path);
|
|
138
|
-
if (exactFnMap) {
|
|
139
|
-
const match = exactFnMap.get(row.name);
|
|
140
|
-
if (match) fnEntry = match;
|
|
141
|
-
// If no exact function name match, aggregate all functions for this file
|
|
142
|
-
if (!fnEntry) {
|
|
143
|
-
// Best-effort: file is covered if any function is covered
|
|
144
|
-
let fileCovered = false;
|
|
145
|
-
let fileMaxCount = 0;
|
|
146
|
-
for (const e of exactFnMap.values()) {
|
|
147
|
-
if (e.covered) fileCovered = true;
|
|
148
|
-
fileMaxCount = Math.max(fileMaxCount, e.callCount);
|
|
149
|
-
}
|
|
150
|
-
if (exactFnMap.size > 0) {
|
|
151
|
-
fnEntry = { covered: fileCovered, callCount: fileMaxCount };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
155
|
-
// Try relative path matching
|
|
156
|
-
for (const [covPath, fnMap] of fileCoverageMap.entries()) {
|
|
157
|
-
if (covPath.endsWith(normFilePath) || normFilePath.endsWith(covPath.replace(/.*\//, ''))) {
|
|
158
|
-
const match = fnMap.get(row.name);
|
|
159
|
-
if (match) { fnEntry = match; break; }
|
|
160
|
-
// Aggregate file-level coverage
|
|
161
|
-
let fileCovered = false;
|
|
162
|
-
let fileMaxCount = 0;
|
|
163
|
-
for (const e of fnMap.values()) {
|
|
164
|
-
if (e.covered) fileCovered = true;
|
|
165
|
-
fileMaxCount = Math.max(fileMaxCount, e.callCount);
|
|
166
|
-
}
|
|
167
|
-
if (fnMap.size > 0) {
|
|
168
|
-
fnEntry = { covered: fileCovered, callCount: fileMaxCount };
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let classification: RuntimeClassification;
|
|
177
|
-
let covered = false;
|
|
178
|
-
let callCount = 0;
|
|
179
|
-
|
|
180
|
-
if (!fnEntry) {
|
|
181
|
-
classification = 'CoverageUnavailable';
|
|
182
|
-
} else {
|
|
183
|
-
covered = fnEntry.covered;
|
|
184
|
-
callCount = fnEntry.callCount;
|
|
185
|
-
|
|
186
|
-
if (!covered && reachabilityRole === 'unreachable') {
|
|
187
|
-
classification = 'SafeToDelete';
|
|
188
|
-
} else if (covered && reachabilityRole === 'unreachable') {
|
|
189
|
-
classification = 'ReviewRequired';
|
|
190
|
-
} else if (covered && callCount < lowTrafficThreshold) {
|
|
191
|
-
classification = 'LowTraffic';
|
|
192
|
-
} else if (covered) {
|
|
193
|
-
classification = 'Active';
|
|
194
|
-
} else {
|
|
195
|
-
// not covered, not unreachable — treat as unavailable
|
|
196
|
-
classification = 'CoverageUnavailable';
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
entries.push({
|
|
201
|
-
nodeId: row.id,
|
|
202
|
-
name: row.name,
|
|
203
|
-
filePath: row.file_path,
|
|
204
|
-
startLine: row.start_line,
|
|
205
|
-
covered,
|
|
206
|
-
callCount,
|
|
207
|
-
classification,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Aggregate counts
|
|
212
|
-
let safeToDelete = 0;
|
|
213
|
-
let reviewRequired = 0;
|
|
214
|
-
let active = 0;
|
|
215
|
-
let coverageUnavailable = 0;
|
|
216
|
-
|
|
217
|
-
const blastRadiusSet = new Set<string>();
|
|
218
|
-
|
|
219
|
-
for (const entry of entries) {
|
|
220
|
-
switch (entry.classification) {
|
|
221
|
-
case 'SafeToDelete': safeToDelete++; break;
|
|
222
|
-
case 'ReviewRequired': reviewRequired++; break;
|
|
223
|
-
case 'Active': active++; break;
|
|
224
|
-
case 'CoverageUnavailable': coverageUnavailable++; break;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// BlastRadius: Active nodes with reachabilityRole === 'runtime'
|
|
228
|
-
if (entry.classification === 'Active' && entry.filePath) {
|
|
229
|
-
const nodeProps = entries.find(e => e.nodeId === entry.nodeId);
|
|
230
|
-
// Re-query to get reachabilityRole for blast radius check
|
|
231
|
-
const nodeRow = db.prepare(`SELECT properties FROM nodes WHERE id = ?`).get(entry.nodeId) as { properties: string | null } | undefined;
|
|
232
|
-
if (nodeRow) {
|
|
233
|
-
const p = nodeRow.properties ? JSON.parse(nodeRow.properties) : {};
|
|
234
|
-
if (p.reachabilityRole === 'runtime') {
|
|
235
|
-
blastRadiusSet.add(entry.filePath);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
entries,
|
|
243
|
-
safeToDelete,
|
|
244
|
-
reviewRequired,
|
|
245
|
-
active,
|
|
246
|
-
coverageUnavailable,
|
|
247
|
-
blastRadius: Array.from(blastRadiusSet),
|
|
248
|
-
};
|
|
249
|
-
}
|