pi-lens 4.3.0 → 4.4.0
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/CHANGELOG.md +1064 -0
- package/README.md +115 -25
- package/config/dependency-cruiser-eager-allowlist.json +6 -2
- package/dist/chunk-22AVIGXS.js +560 -0
- package/dist/chunk-27BBHSZC.js +106 -0
- package/dist/chunk-2BEMIWEW.js +51 -0
- package/dist/chunk-2EECBNC5.js +89 -0
- package/dist/chunk-34U7PLGE.js +23 -0
- package/dist/chunk-3I5QFT3N.js +181 -0
- package/dist/chunk-3MED75HT.js +37 -0
- package/dist/chunk-47D2BO4S.js +87 -0
- package/dist/chunk-4RERDEXR.js +348 -0
- package/dist/chunk-4W2PLC4J.js +27 -0
- package/dist/chunk-52H5B5TC.js +202 -0
- package/dist/chunk-54B42YRX.js +27 -0
- package/dist/chunk-5RXBNSW2.js +266 -0
- package/dist/chunk-5THXD6X5.js +201 -0
- package/dist/chunk-5W3R6LFT.js +24 -0
- package/dist/chunk-5ZEJHV35.js +341 -0
- package/dist/chunk-6P62QVEA.js +118 -0
- package/dist/chunk-6QIUX4EP.js +459 -0
- package/dist/chunk-6VCD4VKZ.js +78 -0
- package/dist/chunk-7F72PXY2.js +326 -0
- package/dist/chunk-7FBF7NPR.js +998 -0
- package/dist/chunk-7U2IDNXN.js +37 -0
- package/dist/chunk-ABBOA7UD.js +157 -0
- package/dist/chunk-AHDW3GPE.js +50 -0
- package/dist/chunk-AJ3QXTRQ.js +756 -0
- package/dist/chunk-AJVLWNFN.js +1610 -0
- package/dist/chunk-AJXODZY6.js +112 -0
- package/dist/chunk-ARFCXG7S.js +84 -0
- package/dist/chunk-AS44CFQ4.js +708 -0
- package/dist/chunk-AWE5TMPO.js +38 -0
- package/dist/chunk-B3I2UUT4.js +4692 -0
- package/dist/chunk-BRJKOBZP.js +331 -0
- package/dist/chunk-BW5KFIQC.js +59 -0
- package/dist/chunk-C2AUYXQH.js +149 -0
- package/dist/chunk-CGGAIP7J.js +29 -0
- package/dist/chunk-D6QS3U6M.js +30 -0
- package/dist/chunk-DFVMIKMQ.js +349 -0
- package/dist/chunk-DIULIWKU.js +169 -0
- package/dist/chunk-DQ2NZ7C4.js +2317 -0
- package/dist/chunk-E2WFBRHM.js +213 -0
- package/dist/chunk-E4MAEDPM.js +50 -0
- package/dist/chunk-EOKRQ3SA.js +30 -0
- package/dist/chunk-F3AU2TMM.js +2626 -0
- package/dist/chunk-F3P7VQ7H.js +170 -0
- package/dist/chunk-FC4RP4I2.js +12 -0
- package/dist/chunk-FJNKANOS.js +32 -0
- package/dist/chunk-GUOGVTCQ.js +146 -0
- package/dist/chunk-GVXA6CCE.js +18 -0
- package/dist/chunk-HHYAELLJ.js +2334 -0
- package/dist/chunk-HMFXILPV.js +83 -0
- package/dist/chunk-HVUZXSWK.js +421 -0
- package/dist/chunk-HWBZIGMJ.js +62 -0
- package/dist/chunk-I3R7GKDR.js +17 -0
- package/dist/chunk-I7SPGOWI.js +55 -0
- package/dist/chunk-IIXOAGHR.js +38 -0
- package/dist/chunk-IVP5KAB7.js +2250 -0
- package/dist/chunk-IXEBJSUE.js +22 -0
- package/dist/chunk-JOLITGCC.js +27 -0
- package/dist/chunk-K6VYLB2U.js +145 -0
- package/dist/chunk-LJDODBBZ.js +78 -0
- package/dist/chunk-LNAKEO63.js +78 -0
- package/dist/chunk-LSMJ6KBE.js +4139 -0
- package/dist/chunk-LW6XKDZQ.js +5565 -0
- package/dist/chunk-N3YQJI6O.js +899 -0
- package/dist/chunk-NABLC7W3.js +148 -0
- package/dist/chunk-NKOUFPQF.js +659 -0
- package/dist/chunk-NL5GLMFI.js +211 -0
- package/dist/chunk-NMABWBZN.js +327 -0
- package/dist/chunk-NTHH2J74.js +27 -0
- package/dist/chunk-NTUJEGDZ.js +59 -0
- package/dist/chunk-NXL4FFQQ.js +47 -0
- package/dist/chunk-NXPS2L6U.js +466 -0
- package/dist/chunk-O5WILGMI.js +42 -0
- package/dist/chunk-O6TQT6RI.js +48 -0
- package/dist/chunk-OAXNDZJ7.js +323 -0
- package/dist/chunk-ODEEOXPH.js +44 -0
- package/dist/chunk-OQGFCZJ4.js +4533 -0
- package/dist/chunk-ORTMY4Q4.js +282 -0
- package/dist/chunk-OS32RNR2.js +442 -0
- package/dist/chunk-PAK6IDME.js +189 -0
- package/dist/chunk-PWFHBTSF.js +406 -0
- package/dist/chunk-Q5U6FMDI.js +2352 -0
- package/dist/chunk-QOZXJ4NA.js +3364 -0
- package/dist/chunk-QYF5S6P6.js +45 -0
- package/dist/chunk-RIISMHKN.js +36 -0
- package/dist/chunk-RPD4ZSB7.js +25 -0
- package/dist/chunk-RPD5K5NC.js +79 -0
- package/dist/chunk-RUOG5D2C.js +262 -0
- package/dist/chunk-T4ERT73Y.js +599 -0
- package/dist/chunk-T6D4Q4AQ.js +54 -0
- package/dist/chunk-TGPXLY2J.js +391 -0
- package/dist/chunk-TLGFBM3F.js +12116 -0
- package/dist/chunk-TO6WL3GM.js +88 -0
- package/dist/chunk-TT22N74J.js +517 -0
- package/dist/chunk-TVN5N6GY.js +21216 -0
- package/dist/chunk-U3VGR4VU.js +308 -0
- package/dist/chunk-UK3CMEAL.js +1250 -0
- package/dist/chunk-UOPVJQ4N.js +103 -0
- package/dist/chunk-UXVNL3D7.js +23 -0
- package/dist/chunk-UY752QSN.js +45 -0
- package/dist/chunk-V5PXFSZK.js +188 -0
- package/dist/chunk-V7R425XG.js +398 -0
- package/dist/chunk-VCSRSYLF.js +81 -0
- package/dist/chunk-VD4JQCQC.js +162 -0
- package/dist/chunk-VDBPVIKD.js +62 -0
- package/dist/chunk-VHQLPIRJ.js +114 -0
- package/dist/chunk-VN2AXLEX.js +5414 -0
- package/dist/chunk-VNMT7C64.js +478 -0
- package/dist/chunk-WONERJTP.js +309 -0
- package/dist/chunk-XECLWQZN.js +85 -0
- package/dist/chunk-XIMYZ3JA.js +27 -0
- package/dist/chunk-XK4CIILI.js +144 -0
- package/dist/chunk-Y4T27COV.js +562 -0
- package/dist/chunk-YGVYKNSN.js +1860 -0
- package/dist/chunk-YLF3PPT5.js +308 -0
- package/dist/chunk-YSNFMJKN.js +14 -0
- package/dist/chunk-Z3CCVDUB.js +76 -0
- package/dist/chunk-Z54ZSOBZ.js +61 -0
- package/dist/chunk-ZAWW3AF6.js +97 -0
- package/dist/chunk-ZMOL6RQC.js +167 -0
- package/dist/index.js +16456 -8335
- package/dist/mcp/analyze-cli.js +186 -232
- package/dist/mcp/cli.js +137 -98
- package/dist/mcp/server.js +22589 -1573
- package/dist/mcp/worker.js +97 -38
- package/dist/probes/installer.js +135 -0
- package/dist/workers/project-snapshot-persist-worker.js +22 -0
- package/dist/workers/review-graph-persist-worker.js +15 -0
- package/docs/agent-guide.md +13 -4
- package/docs/agent-tools.md +42 -0
- package/docs/ast-grep_rules_catalog.md +3 -2
- package/docs/configuration.md +20 -1
- package/docs/environment-variables.md +52 -2
- package/docs/features.md +8 -4
- package/docs/globalconfig.md +1 -0
- package/docs/io-bridge-v2-migration.md +94 -0
- package/docs/lsp-capability-matrix.md +36 -4
- package/docs/lsp-idle-eviction.md +124 -0
- package/docs/mcp.md +21 -5
- package/docs/merge-queue-rollout.md +77 -0
- package/docs/pi-lens-fixer.md +156 -88
- package/docs/pi-lens-investigator.md +74 -26
- package/docs/pi-lens-merge-policy.md +694 -0
- package/docs/pi-lens-monitor.md +73 -7
- package/docs/pi-lens-retro.md +6 -4
- package/docs/pi-lens-reviewer.md +188 -48
- package/docs/pi-lens-subagent.md +246 -45
- package/docs/pi-lens-warden.md +18 -7
- package/docs/public-api-stability.md +55 -0
- package/docs/release-qa-baseline.md +84 -17
- package/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
- package/docs/servercapabilities.md +53 -49
- package/docs/settings.md +1 -0
- package/docs/usage.md +5 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-bash.wasm.json +3 -3
- package/package.json +19 -7
- package/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
- package/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
- package/scripts/analyze-pi-lens-logs.mjs +1275 -62
- package/scripts/download-grammars.js +5 -0
- package/scripts/grammars.lock.json +6 -1
- package/scripts/install-selftest.mjs +33 -34
- package/scripts/lib/host-provided-deps.mjs +13 -4
- package/scripts/lib/web-tree-sitter-dir.mjs +18 -0
- package/dist/clients/actionable-warnings-logger.js +0 -35
- package/dist/clients/actionable-warnings.js +0 -1610
- package/dist/clients/advisory-provenance.js +0 -593
- package/dist/clients/agent-behavior-client.js +0 -147
- package/dist/clients/agent-nudge.js +0 -325
- package/dist/clients/analysed-root.js +0 -1
- package/dist/clients/ast-grep-client.js +0 -731
- package/dist/clients/ast-grep-rule-manager.js +0 -146
- package/dist/clients/ast-grep-tool-logger.js +0 -118
- package/dist/clients/ast-grep-types.js +0 -10
- package/dist/clients/ast-grep-yaml-synth.js +0 -154
- package/dist/clients/atomic-write-staging.js +0 -75
- package/dist/clients/atomic-write.js +0 -189
- package/dist/clients/availability-probe-flight.js +0 -12
- package/dist/clients/bash-file-access.js +0 -1056
- package/dist/clients/biome-client.js +0 -357
- package/dist/clients/blocker-freshness.js +0 -837
- package/dist/clients/blocker-past-eof.js +0 -129
- package/dist/clients/bootstrap.js +0 -569
- package/dist/clients/bounded-cache.js +0 -190
- package/dist/clients/bounded-pid-file-lock.js +0 -202
- package/dist/clients/bounded-telemetry.js +0 -295
- package/dist/clients/build-identity.js +0 -95
- package/dist/clients/bundled-resource-health.js +0 -113
- package/dist/clients/bus-events-logger.js +0 -105
- package/dist/clients/bus-publish.js +0 -171
- package/dist/clients/cache/rule-cache.js +0 -190
- package/dist/clients/cache-manager.js +0 -524
- package/dist/clients/cache-observability.js +0 -1313
- package/dist/clients/call-graph.js +0 -852
- package/dist/clients/cargo-manifest.js +0 -422
- package/dist/clients/cascade-budget.js +0 -196
- package/dist/clients/cascade-format.js +0 -315
- package/dist/clients/cascade-logger.js +0 -31
- package/dist/clients/cascade-types.js +0 -9
- package/dist/clients/child-unref.js +0 -124
- package/dist/clients/code-quality-warnings.js +0 -286
- package/dist/clients/codebase-model.js +0 -173
- package/dist/clients/collateral-test-role.js +0 -40
- package/dist/clients/complexity-client.js +0 -462
- package/dist/clients/config-core/deny.js +0 -221
- package/dist/clients/config-core/index.js +0 -50
- package/dist/clients/config-core/merge.js +0 -357
- package/dist/clients/config-core/normalize.js +0 -340
- package/dist/clients/config-core/process-spec.js +0 -248
- package/dist/clients/config-core/provenance.js +0 -164
- package/dist/clients/config-core/records.js +0 -218
- package/dist/clients/config-core/resolve.js +0 -125
- package/dist/clients/config-core/safe-object.js +0 -78
- package/dist/clients/config-core/schema.js +0 -165
- package/dist/clients/config-diagnostic-codes.js +0 -285
- package/dist/clients/config-locations.js +0 -388
- package/dist/clients/config-resolve.js +0 -798
- package/dist/clients/config-schema.js +0 -251
- package/dist/clients/config-warn.js +0 -407
- package/dist/clients/console-guard-install.js +0 -26
- package/dist/clients/cooperative-budget.js +0 -42
- package/dist/clients/dead-code-client.js +0 -510
- package/dist/clients/dead-code-logger.js +0 -30
- package/dist/clients/deadline-utils.js +0 -280
- package/dist/clients/debug-handles.js +0 -211
- package/dist/clients/debug-heap.js +0 -174
- package/dist/clients/deferred-lsp-work.js +0 -106
- package/dist/clients/degradation-ledger.js +0 -426
- package/dist/clients/demoted-finding-render.js +0 -169
- package/dist/clients/dependency-checker.js +0 -885
- package/dist/clients/deps/ast-grep-napi.js +0 -22
- package/dist/clients/deps/js-yaml.js +0 -9
- package/dist/clients/deps/minimatch.js +0 -4
- package/dist/clients/deps/pi-tui.js +0 -14
- package/dist/clients/deps/typebox.js +0 -14
- package/dist/clients/deps/vscode-jsonrpc.js +0 -1
- package/dist/clients/deps/web-tree-sitter.js +0 -24
- package/dist/clients/diagnostic-dispositions.js +0 -630
- package/dist/clients/diagnostic-line-freshness.js +0 -305
- package/dist/clients/diagnostic-logger.js +0 -73
- package/dist/clients/diagnostic-tracker.js +0 -105
- package/dist/clients/diagnostics-publish.js +0 -230
- package/dist/clients/dispatch/ast-grep-catalog.js +0 -65
- package/dist/clients/dispatch/auxiliary-lsp.js +0 -343
- package/dist/clients/dispatch/collect-later-tier.js +0 -35
- package/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
- package/dist/clients/dispatch/dispatcher.js +0 -1269
- package/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/dist/clients/dispatch/fact-runner.js +0 -30
- package/dist/clients/dispatch/fact-scheduler.js +0 -68
- package/dist/clients/dispatch/fact-store.js +0 -377
- package/dist/clients/dispatch/facts/comment-facts.js +0 -22
- package/dist/clients/dispatch/facts/file-content.js +0 -19
- package/dist/clients/dispatch/facts/function-facts.js +0 -348
- package/dist/clients/dispatch/facts/import-facts.js +0 -204
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
- package/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
- package/dist/clients/dispatch/finding-policy.js +0 -339
- package/dist/clients/dispatch/indent-detect.js +0 -417
- package/dist/clients/dispatch/inline-suppressions.js +0 -89
- package/dist/clients/dispatch/integration.js +0 -2377
- package/dist/clients/dispatch/lazy.js +0 -11
- package/dist/clients/dispatch/pending-runner-findings.js +0 -87
- package/dist/clients/dispatch/plan.js +0 -278
- package/dist/clients/dispatch/priorities.js +0 -20
- package/dist/clients/dispatch/rule-id-normalize.js +0 -50
- package/dist/clients/dispatch/rule-ignores.js +0 -69
- package/dist/clients/dispatch/rule-policy.js +0 -135
- package/dist/clients/dispatch/rules/async-noise.js +0 -40
- package/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
- package/dist/clients/dispatch/rules/error-obscuring.js +0 -32
- package/dist/clients/dispatch/rules/error-swallowing.js +0 -31
- package/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
- package/dist/clients/dispatch/rules/high-complexity.js +0 -72
- package/dist/clients/dispatch/rules/high-fan-out.js +0 -74
- package/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
- package/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
- package/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
- package/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
- package/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
- package/dist/clients/dispatch/runner-context.js +0 -52
- package/dist/clients/dispatch/runners/actionlint.js +0 -111
- package/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
- package/dist/clients/dispatch/runners/biome-check.js +0 -296
- package/dist/clients/dispatch/runners/cpp-check.js +0 -219
- package/dist/clients/dispatch/runners/credo.js +0 -82
- package/dist/clients/dispatch/runners/cue-vet.js +0 -416
- package/dist/clients/dispatch/runners/dart-analyze.js +0 -183
- package/dist/clients/dispatch/runners/detekt.js +0 -176
- package/dist/clients/dispatch/runners/dotnet-build.js +0 -164
- package/dist/clients/dispatch/runners/elixir-check.js +0 -195
- package/dist/clients/dispatch/runners/eslint.js +0 -137
- package/dist/clients/dispatch/runners/fact-rules.js +0 -29
- package/dist/clients/dispatch/runners/fish-indent.js +0 -69
- package/dist/clients/dispatch/runners/gleam-check.js +0 -167
- package/dist/clients/dispatch/runners/go-vet.js +0 -104
- package/dist/clients/dispatch/runners/golangci-lint.js +0 -135
- package/dist/clients/dispatch/runners/hadolint.js +0 -91
- package/dist/clients/dispatch/runners/helm-lint.js +0 -171
- package/dist/clients/dispatch/runners/helm-render.js +0 -940
- package/dist/clients/dispatch/runners/htmlhint.js +0 -117
- package/dist/clients/dispatch/runners/index.js +0 -108
- package/dist/clients/dispatch/runners/javac.js +0 -99
- package/dist/clients/dispatch/runners/ktlint.js +0 -110
- package/dist/clients/dispatch/runners/lsp.js +0 -379
- package/dist/clients/dispatch/runners/markdownlint.js +0 -167
- package/dist/clients/dispatch/runners/mypy.js +0 -102
- package/dist/clients/dispatch/runners/oxlint.js +0 -414
- package/dist/clients/dispatch/runners/php-lint.js +0 -85
- package/dist/clients/dispatch/runners/phpstan.js +0 -120
- package/dist/clients/dispatch/runners/prisma-validate.js +0 -62
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
- package/dist/clients/dispatch/runners/pyright.js +0 -169
- package/dist/clients/dispatch/runners/rubocop.js +0 -99
- package/dist/clients/dispatch/runners/ruff.js +0 -88
- package/dist/clients/dispatch/runners/rust-clippy.js +0 -256
- package/dist/clients/dispatch/runners/shellcheck.js +0 -173
- package/dist/clients/dispatch/runners/shfmt.js +0 -102
- package/dist/clients/dispatch/runners/spellcheck.js +0 -128
- package/dist/clients/dispatch/runners/spotbugs.js +0 -259
- package/dist/clients/dispatch/runners/sqlfluff.js +0 -183
- package/dist/clients/dispatch/runners/stylelint.js +0 -166
- package/dist/clients/dispatch/runners/swiftlint.js +0 -163
- package/dist/clients/dispatch/runners/taplo.js +0 -174
- package/dist/clients/dispatch/runners/terragrunt.js +0 -168
- package/dist/clients/dispatch/runners/tflint.js +0 -125
- package/dist/clients/dispatch/runners/tree-sitter.js +0 -643
- package/dist/clients/dispatch/runners/trivy-config.js +0 -253
- package/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
- package/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
- package/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
- package/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
- package/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
- package/dist/clients/dispatch/runners/utils.js +0 -15
- package/dist/clients/dispatch/runners/vale.js +0 -128
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
- package/dist/clients/dispatch/runners/yamllint.js +0 -95
- package/dist/clients/dispatch/runners/zig-check.js +0 -79
- package/dist/clients/dispatch/suppress-writer.js +0 -79
- package/dist/clients/dispatch/tool-profile.js +0 -30
- package/dist/clients/dispatch/types.js +0 -21
- package/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
- package/dist/clients/disposition-logger.js +0 -32
- package/dist/clients/disposition-publish.js +0 -124
- package/dist/clients/durable-store.js +0 -97
- package/dist/clients/effective-config.js +0 -415
- package/dist/clients/env-utils.js +0 -83
- package/dist/clients/error-class.js +0 -23
- package/dist/clients/eval-timestamp.js +0 -17
- package/dist/clients/event-loop-hold.js +0 -274
- package/dist/clients/event-loop-monitor.js +0 -250
- package/dist/clients/extension-log.js +0 -504
- package/dist/clients/extension-mode.js +0 -73
- package/dist/clients/feature-hints.js +0 -103
- package/dist/clients/file-kinds.js +0 -412
- package/dist/clients/file-role.js +0 -134
- package/dist/clients/file-time.js +0 -160
- package/dist/clients/file-utils.js +0 -1032
- package/dist/clients/finding-delivery-gate.js +0 -494
- package/dist/clients/finding-identity.js +0 -88
- package/dist/clients/fix-worklog.js +0 -103
- package/dist/clients/format-events-publish.js +0 -343
- package/dist/clients/format-service.js +0 -222
- package/dist/clients/formatters-lazy.js +0 -9
- package/dist/clients/formatters.js +0 -2032
- package/dist/clients/freshness-cadence.js +0 -17
- package/dist/clients/freshness.js +0 -38
- package/dist/clients/generated-artifacts.js +0 -394
- package/dist/clients/generation-guard.js +0 -215
- package/dist/clients/git-guard.js +0 -1136
- package/dist/clients/git-tracked-ignore.js +0 -243
- package/dist/clients/gitleaks-client.js +0 -509
- package/dist/clients/go-client.js +0 -104
- package/dist/clients/govulncheck-client.js +0 -605
- package/dist/clients/gradle-ktfmt-style.js +0 -348
- package/dist/clients/grammar-source.js +0 -465
- package/dist/clients/gzip-stage-write.js +0 -135
- package/dist/clients/hashline-anchor.js +0 -424
- package/dist/clients/hook-budgets.js +0 -76
- package/dist/clients/host-edit-normalize.js +0 -140
- package/dist/clients/host-ports.js +0 -39
- package/dist/clients/indent-retarget.js +0 -154
- package/dist/clients/inline-blocker-dispositions.js +0 -186
- package/dist/clients/install-diagnostics.js +0 -156
- package/dist/clients/installer/index.js +0 -5416
- package/dist/clients/installer/managed-tool-refresh-session.js +0 -58
- package/dist/clients/installer/managed-tool-refresh.js +0 -783
- package/dist/clients/instance-reaper-state.js +0 -7
- package/dist/clients/instance-reaper.js +0 -1137
- package/dist/clients/instance-registry-lock.js +0 -145
- package/dist/clients/instance-registry.js +0 -773
- package/dist/clients/jscpd-client.js +0 -378
- package/dist/clients/json-cache-read.js +0 -65
- package/dist/clients/knip-client.js +0 -788
- package/dist/clients/language-policy.js +0 -252
- package/dist/clients/language-profile.js +0 -361
- package/dist/clients/language-registry.js +0 -603
- package/dist/clients/latency-logger.js +0 -727
- package/dist/clients/lazy-import.js +0 -21
- package/dist/clients/ledger-bounds.js +0 -54
- package/dist/clients/lens-config.js +0 -404
- package/dist/clients/lens-engine.js +0 -358
- package/dist/clients/lens-events.js +0 -125
- package/dist/clients/lens-flag-registry.js +0 -467
- package/dist/clients/lens-map.js +0 -1156
- package/dist/clients/live-bus-emitter.js +0 -74
- package/dist/clients/log-cleanup.js +0 -222
- package/dist/clients/lsp/aggregation.js +0 -178
- package/dist/clients/lsp/cascade-tier.js +0 -421
- package/dist/clients/lsp/client.js +0 -4593
- package/dist/clients/lsp/config.js +0 -691
- package/dist/clients/lsp/content-limits.js +0 -41
- package/dist/clients/lsp/diagnostic-binding.js +0 -225
- package/dist/clients/lsp/document-drift.js +0 -467
- package/dist/clients/lsp/edits.js +0 -1219
- package/dist/clients/lsp/index.js +0 -8305
- package/dist/clients/lsp/inferred-project.js +0 -216
- package/dist/clients/lsp/jvm-runtime.js +0 -298
- package/dist/clients/lsp/language.js +0 -63
- package/dist/clients/lsp/launch.js +0 -746
- package/dist/clients/lsp/lombok.js +0 -158
- package/dist/clients/lsp/path-utils.js +0 -5
- package/dist/clients/lsp/pending-aux-coverage.js +0 -251
- package/dist/clients/lsp/position-encoding.js +0 -61
- package/dist/clients/lsp/ruby-drive-dirs.js +0 -83
- package/dist/clients/lsp/server.js +0 -3290
- package/dist/clients/lsp/session-roots.js +0 -192
- package/dist/clients/lsp/spawn-history.js +0 -17
- package/dist/clients/lsp/sync-kind.js +0 -56
- package/dist/clients/lsp/tsserver-sync.js +0 -332
- package/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/dist/clients/lsp/wait-policy/classification.js +0 -85
- package/dist/clients/lsp/wait-policy/index.js +0 -10
- package/dist/clients/lsp/wait-policy/strategies.js +0 -392
- package/dist/clients/lsp/watch-queue.js +0 -61
- package/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
- package/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
- package/dist/clients/lsp/workspace-sweep-hold.js +0 -184
- package/dist/clients/lsp-budget.js +0 -186
- package/dist/clients/lsp-document-symbols.js +0 -110
- package/dist/clients/lsp-lazy.js +0 -9
- package/dist/clients/lsp-mutation.js +0 -396
- package/dist/clients/lsp-status.js +0 -59
- package/dist/clients/map-with-concurrency.js +0 -40
- package/dist/clients/mcp/analyze.js +0 -420
- package/dist/clients/mcp/host-shim.js +0 -31
- package/dist/clients/mcp/ipc.js +0 -523
- package/dist/clients/mcp/review.js +0 -197
- package/dist/clients/mcp/session.js +0 -412
- package/dist/clients/memory-sampler.js +0 -208
- package/dist/clients/message-end-attribution.js +0 -30
- package/dist/clients/metrics-client.js +0 -107
- package/dist/clients/metrics-history.js +0 -327
- package/dist/clients/middle-man-analysis.js +0 -263
- package/dist/clients/model-provider.js +0 -53
- package/dist/clients/module-report-lsp.js +0 -189
- package/dist/clients/module-report.js +0 -2150
- package/dist/clients/mutating-tool.js +0 -651
- package/dist/clients/mutation-attribution.js +0 -368
- package/dist/clients/mutation-bridge.js +0 -240
- package/dist/clients/ndjson-logger.js +0 -734
- package/dist/clients/observed-mutation-sources.js +0 -101
- package/dist/clients/observed-mutation.js +0 -1253
- package/dist/clients/oldtext-autopatch.js +0 -128
- package/dist/clients/opaque-mutation-scan.js +0 -496
- package/dist/clients/opengrep-client.js +0 -333
- package/dist/clients/opengrep-config.js +0 -76
- package/dist/clients/package-manager.js +0 -607
- package/dist/clients/package-root.js +0 -38
- package/dist/clients/partial-edit-apply.js +0 -439
- package/dist/clients/path-attribution-telemetry.js +0 -43
- package/dist/clients/path-keyed-map.js +0 -108
- package/dist/clients/path-utils.js +0 -1244
- package/dist/clients/performance-report.js +0 -331
- package/dist/clients/persist-debounce.js +0 -70
- package/dist/clients/persistent-reverify.js +0 -271
- package/dist/clients/php-cs-fixer-config.js +0 -119
- package/dist/clients/pipeline.js +0 -1480
- package/dist/clients/post-autofix-notice.js +0 -37
- package/dist/clients/print-mode.js +0 -21
- package/dist/clients/probe-home-state.js +0 -228
- package/dist/clients/process-bridge.js +0 -66
- package/dist/clients/process-singletons.js +0 -210
- package/dist/clients/process-snapshot.js +0 -68
- package/dist/clients/project-changes.js +0 -181
- package/dist/clients/project-conventions.js +0 -171
- package/dist/clients/project-diagnostics/cache.js +0 -137
- package/dist/clients/project-diagnostics/extractors.js +0 -109
- package/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
- package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
- package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
- package/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
- package/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
- package/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
- package/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
- package/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
- package/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
- package/dist/clients/project-diagnostics/scanner.js +0 -520
- package/dist/clients/project-diagnostics/types.js +0 -1
- package/dist/clients/project-lens-config.js +0 -807
- package/dist/clients/project-report.js +0 -701
- package/dist/clients/project-scale.js +0 -237
- package/dist/clients/project-scan-policy.js +0 -11
- package/dist/clients/project-snapshot-fingerprint.js +0 -48
- package/dist/clients/project-snapshot-persist-worker.js +0 -20
- package/dist/clients/project-snapshot.js +0 -1653
- package/dist/clients/project-trust.js +0 -181
- package/dist/clients/python-environment.js +0 -217
- package/dist/clients/python-provenance.js +0 -639
- package/dist/clients/quiet-window-config.js +0 -47
- package/dist/clients/quiet-window.js +0 -236
- package/dist/clients/read-bridge.js +0 -139
- package/dist/clients/read-expansion.js +0 -266
- package/dist/clients/read-guard-logger.js +0 -251
- package/dist/clients/read-guard-tool-lines.js +0 -1306
- package/dist/clients/read-guard.js +0 -1561
- package/dist/clients/recent-touches.js +0 -240
- package/dist/clients/redact/secrets.js +0 -449
- package/dist/clients/resource-sampler.js +0 -628
- package/dist/clients/reverse-deps.js +0 -270
- package/dist/clients/review-graph/builder.js +0 -5126
- package/dist/clients/review-graph/format.js +0 -33
- package/dist/clients/review-graph/git-identity.js +0 -163
- package/dist/clients/review-graph/import-resolvers.js +0 -481
- package/dist/clients/review-graph/persist-worker.js +0 -10
- package/dist/clients/review-graph/query.js +0 -244
- package/dist/clients/review-graph/revision-drift.js +0 -21
- package/dist/clients/review-graph/service.js +0 -96
- package/dist/clients/review-graph/shared-extraction-ir.js +0 -82
- package/dist/clients/review-graph/symbol-id.js +0 -103
- package/dist/clients/review-graph/tsconfig-paths.js +0 -299
- package/dist/clients/review-graph/types.js +0 -1
- package/dist/clients/review-graph/workspace-modules.js +0 -449
- package/dist/clients/review-graph-logger.js +0 -81
- package/dist/clients/ruff-client.js +0 -179
- package/dist/clients/rules-scanner.js +0 -131
- package/dist/clients/run-duration.js +0 -55
- package/dist/clients/runtime-agent-end.js +0 -746
- package/dist/clients/runtime-config.js +0 -77
- package/dist/clients/runtime-context.js +0 -211
- package/dist/clients/runtime-coordinator.js +0 -1324
- package/dist/clients/runtime-session.js +0 -2485
- package/dist/clients/runtime-tool-call.js +0 -1295
- package/dist/clients/runtime-tool-result.js +0 -2049
- package/dist/clients/runtime-turn.js +0 -3767
- package/dist/clients/rust-client.js +0 -97
- package/dist/clients/safe-spawn.js +0 -2357
- package/dist/clients/sanitize.js +0 -286
- package/dist/clients/scan-utils.js +0 -25
- package/dist/clients/scratch-tree-policy.js +0 -165
- package/dist/clients/search-read-registration.js +0 -94
- package/dist/clients/secret-findings.js +0 -114
- package/dist/clients/security-scan-client.js +0 -352
- package/dist/clients/session-event-guard.js +0 -297
- package/dist/clients/session-lifecycle.js +0 -484
- package/dist/clients/session-start-observability.js +0 -94
- package/dist/clients/session-state-store.js +0 -101
- package/dist/clients/session-summary.js +0 -15
- package/dist/clients/sessionstart-logger.js +0 -27
- package/dist/clients/sg-runner.js +0 -876
- package/dist/clients/sgconfig.js +0 -347
- package/dist/clients/shared-checkout-guard.js +0 -338
- package/dist/clients/single-flight.js +0 -158
- package/dist/clients/situational-tool-telemetry.js +0 -108
- package/dist/clients/skills-resolver.js +0 -141
- package/dist/clients/slow-fs.js +0 -138
- package/dist/clients/smells-rollup.js +0 -231
- package/dist/clients/source-filter.js +0 -596
- package/dist/clients/source-walker.js +0 -211
- package/dist/clients/spawn-output-cap.js +0 -120
- package/dist/clients/spawn-timeout-cooldown.js +0 -110
- package/dist/clients/stale-marker.js +0 -17
- package/dist/clients/startup-scan.js +0 -400
- package/dist/clients/startup-timing.js +0 -130
- package/dist/clients/string-utils.js +0 -33
- package/dist/clients/subagent-mode.js +0 -111
- package/dist/clients/symbol-containment.js +0 -24
- package/dist/clients/symbol-types.js +0 -5
- package/dist/clients/test-runner-client.js +0 -2507
- package/dist/clients/test-runner-delivery.js +0 -287
- package/dist/clients/todo-scanner.js +0 -229
- package/dist/clients/tool-agreement.js +0 -423
- package/dist/clients/tool-config.js +0 -249
- package/dist/clients/tool-cwd.js +0 -270
- package/dist/clients/tool-definition.js +0 -47
- package/dist/clients/tool-event.js +0 -105
- package/dist/clients/tool-policy.js +0 -2755
- package/dist/clients/tool-probe.js +0 -53
- package/dist/clients/tool-render.js +0 -156
- package/dist/clients/tool-set-policy.js +0 -118
- package/dist/clients/tree-sitter-cache.js +0 -364
- package/dist/clients/tree-sitter-client.js +0 -4195
- package/dist/clients/tree-sitter-logger.js +0 -100
- package/dist/clients/tree-sitter-navigator.js +0 -269
- package/dist/clients/tree-sitter-query-loader.js +0 -569
- package/dist/clients/tree-sitter-shared.js +0 -158
- package/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
- package/dist/clients/trivy-client.js +0 -467
- package/dist/clients/tui-fit.js +0 -50
- package/dist/clients/turn-context.js +0 -52
- package/dist/clients/turn-end/lane.js +0 -51
- package/dist/clients/turn-end/lanes/govulncheck.js +0 -112
- package/dist/clients/turn-end/lanes/secrets.js +0 -204
- package/dist/clients/turn-summary-render.js +0 -72
- package/dist/clients/turn-summary.js +0 -132
- package/dist/clients/typos-config.js +0 -29
- package/dist/clients/user-notify.js +0 -51
- package/dist/clients/vanished-instance-marker.js +0 -57
- package/dist/clients/warm-attach.js +0 -305
- package/dist/clients/widget-state.js +0 -1808
- package/dist/clients/word-index-logger.js +0 -49
- package/dist/clients/word-index-store.js +0 -459
- package/dist/clients/word-index.js +0 -2080
- package/dist/clients/workspace-topology.js +0 -443
- package/dist/clients/write-ordering-guard.js +0 -65
- package/dist/clients/zizmor-config.js +0 -340
- package/dist/i18n.js +0 -69
- package/dist/mcp/build-staleness.js +0 -124
- package/dist/scripts/lib/process-scan.mjs +0 -583
- package/dist/scripts/lib/skills-predicate.mjs +0 -129
- package/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
- package/dist/tools/activate-tools.js +0 -87
- package/dist/tools/ast-grep-outline.js +0 -137
- package/dist/tools/ast-grep-replace.js +0 -244
- package/dist/tools/ast-grep-search.js +0 -762
- package/dist/tools/effective-config.js +0 -90
- package/dist/tools/lens-diagnostic-mark.js +0 -375
- package/dist/tools/lens-diagnostics.js +0 -2571
- package/dist/tools/lsp-diagnostics.js +0 -1745
- package/dist/tools/lsp-navigation.js +0 -1367
- package/dist/tools/lsp-structured-output.js +0 -294
- package/dist/tools/module-report.js +0 -382
- package/dist/tools/project-report.js +0 -86
- package/dist/tools/render-compact.js +0 -374
- package/dist/tools/scan-progress.js +0 -53
- package/dist/tools/shared.js +0 -29
- package/dist/tools/symbol-search.js +0 -155
|
@@ -1,852 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-file call graph — Sections 1, 2 & 3 of issue #154.
|
|
3
|
-
*
|
|
4
|
-
* Builds a bidirectional function-level call graph by resolving symbol
|
|
5
|
-
* references across files. Provides BFS impact analysis with severity tiers.
|
|
6
|
-
* Uses the Symbol/SymbolRef data produced by TreeSitterSymbolExtractor and
|
|
7
|
-
* persists the result under the canonical review-graph identity; freshness is
|
|
8
|
-
* owned exclusively by the review-graph snapshot.
|
|
9
|
-
*/
|
|
10
|
-
import * as fs from "node:fs";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
import { getProjectDataDir } from "./file-utils.js";
|
|
13
|
-
import { writeFileAtomic } from "./atomic-write.js";
|
|
14
|
-
import { parseSymbolKey as parseCanonicalSymbolKey } from "./review-graph/symbol-id.js";
|
|
15
|
-
import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
|
|
16
|
-
/** Persisted call-graph cache format version. Bump on breaking format changes. */
|
|
17
|
-
export const CALL_GRAPH_CACHE_VERSION = 5;
|
|
18
|
-
function severityForDepth(depth) {
|
|
19
|
-
if (depth === 1)
|
|
20
|
-
return "WillBreak";
|
|
21
|
-
if (depth === 2)
|
|
22
|
-
return "MayBreak";
|
|
23
|
-
return "Review";
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* BFS upstream through the callers map from `startKey`.
|
|
27
|
-
*
|
|
28
|
-
* Returns all symbols that would be affected if `startKey` changes,
|
|
29
|
-
* classified by severity:
|
|
30
|
-
* depth 1 → WillBreak (direct callers)
|
|
31
|
-
* depth 2 → MayBreak (callers of callers)
|
|
32
|
-
* depth 3+ → Review (transitive)
|
|
33
|
-
*
|
|
34
|
-
* @param maxDepth Limit traversal depth (default 3 — Review tier cutoff).
|
|
35
|
-
* @param minWeight Only follow edges with weight ≥ this threshold (default 0.1).
|
|
36
|
-
* Filters out highly ambiguous name resolutions.
|
|
37
|
-
*/
|
|
38
|
-
export function impact(graph, startKey, maxDepth = 3, minWeight = 0.1) {
|
|
39
|
-
const results = [];
|
|
40
|
-
const visited = new Set([startKey]);
|
|
41
|
-
const queue = [
|
|
42
|
-
{ key: startKey, depth: 0 },
|
|
43
|
-
];
|
|
44
|
-
// Build a weight lookup from edges for filtering
|
|
45
|
-
const edgeWeightMap = new Map();
|
|
46
|
-
for (const edge of graph.edges) {
|
|
47
|
-
const edgeKey = `${edge.calleeKey}→${edge.callerKey}`;
|
|
48
|
-
const existing = edgeWeightMap.get(edgeKey) ?? 0;
|
|
49
|
-
edgeWeightMap.set(edgeKey, Math.max(existing, edge.weight));
|
|
50
|
-
}
|
|
51
|
-
while (queue.length > 0) {
|
|
52
|
-
const item = queue.shift();
|
|
53
|
-
if (item.depth >= maxDepth)
|
|
54
|
-
continue;
|
|
55
|
-
const directCallers = graph.callers.get(item.key);
|
|
56
|
-
if (!directCallers)
|
|
57
|
-
continue;
|
|
58
|
-
for (const callerKey of directCallers) {
|
|
59
|
-
if (visited.has(callerKey))
|
|
60
|
-
continue;
|
|
61
|
-
// Check edge weight — skip highly ambiguous resolutions
|
|
62
|
-
const edgeKey = `${item.key}→${callerKey}`;
|
|
63
|
-
const weight = edgeWeightMap.get(edgeKey) ?? 1.0;
|
|
64
|
-
if (weight < minWeight)
|
|
65
|
-
continue;
|
|
66
|
-
visited.add(callerKey);
|
|
67
|
-
const depth = item.depth + 1;
|
|
68
|
-
results.push({
|
|
69
|
-
symbolKey: callerKey,
|
|
70
|
-
depth,
|
|
71
|
-
severity: severityForDepth(depth),
|
|
72
|
-
});
|
|
73
|
-
queue.push({ key: callerKey, depth });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Sort by depth then symbolKey for stable output
|
|
77
|
-
return results.sort((a, b) => a.depth - b.depth || a.symbolKey.localeCompare(b.symbolKey));
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Splits a `SymbolKey` into its file and symbol-name parts. Two shapes exist
|
|
81
|
-
* (see `buildCallGraph` above): the normal `"filePath:symbolName"` form, and
|
|
82
|
-
* the file-level fallback `"file:filePath"` used when no enclosing symbol was
|
|
83
|
-
* found for a call site (module-level code). Splitting at the LAST colon
|
|
84
|
-
* (rather than a naive `split(":")`) means a Windows drive letter in
|
|
85
|
-
* `filePath` (`C:\...`) is never mistaken for the name/file separator; the
|
|
86
|
-
* `"file:"` fallback is matched as a literal prefix first so it isn't
|
|
87
|
-
* misparsed by the same last-colon rule.
|
|
88
|
-
*/
|
|
89
|
-
export function parseSymbolKey(key, knownFilePath) {
|
|
90
|
-
return parseCanonicalSymbolKey(key, knownFilePath);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Format an impact result set as a compact human-readable summary.
|
|
94
|
-
* Example: "handleToolResult (WillBreak) → handleAgentEnd (MayBreak) → 3 Review callers"
|
|
95
|
-
*/
|
|
96
|
-
export function formatImpact(results, projectRoot) {
|
|
97
|
-
if (results.length === 0)
|
|
98
|
-
return "";
|
|
99
|
-
const willBreak = results.filter((r) => r.severity === "WillBreak");
|
|
100
|
-
const mayBreak = results.filter((r) => r.severity === "MayBreak");
|
|
101
|
-
const review = results.filter((r) => r.severity === "Review");
|
|
102
|
-
const parts = [];
|
|
103
|
-
const label = (r) => {
|
|
104
|
-
const parsed = parseSymbolKey(r.symbolKey);
|
|
105
|
-
const name = parsed.symbolName ?? r.symbolKey;
|
|
106
|
-
const file = toProjectRelativePath(parsed.filePath, projectRoot);
|
|
107
|
-
return file ? `${name} (${file})` : name;
|
|
108
|
-
};
|
|
109
|
-
if (willBreak.length > 0) {
|
|
110
|
-
parts.push(willBreak
|
|
111
|
-
.slice(0, 3)
|
|
112
|
-
.map((r) => `${label(r)} ⚠ WillBreak`)
|
|
113
|
-
.join(", "));
|
|
114
|
-
}
|
|
115
|
-
if (mayBreak.length > 0) {
|
|
116
|
-
parts.push(mayBreak
|
|
117
|
-
.slice(0, 2)
|
|
118
|
-
.map((r) => `${label(r)} MayBreak`)
|
|
119
|
-
.join(", "));
|
|
120
|
-
}
|
|
121
|
-
if (review.length > 0) {
|
|
122
|
-
parts.push(`${review.length} Review caller${review.length === 1 ? "" : "s"}`);
|
|
123
|
-
}
|
|
124
|
-
return parts.join(" → ");
|
|
125
|
-
}
|
|
126
|
-
// ── Stdlib / builtin noise filter ─────────────────────────────────────────────
|
|
127
|
-
/**
|
|
128
|
-
* Common stdlib / builtin names that appear in call sites but never resolve
|
|
129
|
-
* to project-defined symbols. Filtering them cuts noise significantly.
|
|
130
|
-
*/
|
|
131
|
-
const STDLIB_NAMES = new Set([
|
|
132
|
-
// JS/TS
|
|
133
|
-
"console",
|
|
134
|
-
"Math",
|
|
135
|
-
"Object",
|
|
136
|
-
"Array",
|
|
137
|
-
"String",
|
|
138
|
-
"Number",
|
|
139
|
-
"Boolean",
|
|
140
|
-
"Promise",
|
|
141
|
-
"Error",
|
|
142
|
-
"Map",
|
|
143
|
-
"Set",
|
|
144
|
-
"WeakMap",
|
|
145
|
-
"WeakSet",
|
|
146
|
-
"JSON",
|
|
147
|
-
"Date",
|
|
148
|
-
"RegExp",
|
|
149
|
-
"Symbol",
|
|
150
|
-
"BigInt",
|
|
151
|
-
"parseInt",
|
|
152
|
-
"parseFloat",
|
|
153
|
-
"isNaN",
|
|
154
|
-
"isFinite",
|
|
155
|
-
"setTimeout",
|
|
156
|
-
"clearTimeout",
|
|
157
|
-
"setInterval",
|
|
158
|
-
"clearInterval",
|
|
159
|
-
"fetch",
|
|
160
|
-
"URL",
|
|
161
|
-
"URLSearchParams",
|
|
162
|
-
"Buffer",
|
|
163
|
-
"process",
|
|
164
|
-
"require",
|
|
165
|
-
// Python
|
|
166
|
-
"print",
|
|
167
|
-
"len",
|
|
168
|
-
"range",
|
|
169
|
-
"list",
|
|
170
|
-
"dict",
|
|
171
|
-
"str",
|
|
172
|
-
"int",
|
|
173
|
-
"float",
|
|
174
|
-
"bool",
|
|
175
|
-
"open",
|
|
176
|
-
"isinstance",
|
|
177
|
-
"issubclass",
|
|
178
|
-
"type",
|
|
179
|
-
"super",
|
|
180
|
-
"hasattr",
|
|
181
|
-
"getattr",
|
|
182
|
-
"setattr",
|
|
183
|
-
"enumerate",
|
|
184
|
-
"zip",
|
|
185
|
-
"map",
|
|
186
|
-
"filter",
|
|
187
|
-
"sorted",
|
|
188
|
-
"reversed",
|
|
189
|
-
// Go
|
|
190
|
-
"fmt",
|
|
191
|
-
"log",
|
|
192
|
-
"os",
|
|
193
|
-
"io",
|
|
194
|
-
"err",
|
|
195
|
-
"make",
|
|
196
|
-
"append",
|
|
197
|
-
"cap",
|
|
198
|
-
"copy",
|
|
199
|
-
"close",
|
|
200
|
-
"delete",
|
|
201
|
-
"panic",
|
|
202
|
-
"recover",
|
|
203
|
-
"new",
|
|
204
|
-
// Rust
|
|
205
|
-
"eprintln",
|
|
206
|
-
"eprint",
|
|
207
|
-
"vec",
|
|
208
|
-
"Some",
|
|
209
|
-
"None",
|
|
210
|
-
"Ok",
|
|
211
|
-
"Err",
|
|
212
|
-
"Box",
|
|
213
|
-
"Rc",
|
|
214
|
-
"Arc",
|
|
215
|
-
"Vec",
|
|
216
|
-
"HashMap",
|
|
217
|
-
"HashSet",
|
|
218
|
-
"format",
|
|
219
|
-
// Java/Kotlin
|
|
220
|
-
"System",
|
|
221
|
-
"toString",
|
|
222
|
-
"equals",
|
|
223
|
-
"hashCode",
|
|
224
|
-
"Objects",
|
|
225
|
-
// Generic
|
|
226
|
-
"this",
|
|
227
|
-
"self",
|
|
228
|
-
"nil",
|
|
229
|
-
"null",
|
|
230
|
-
"undefined",
|
|
231
|
-
"true",
|
|
232
|
-
"false",
|
|
233
|
-
]);
|
|
234
|
-
// ── Core resolution ────────────────────────────────────────────────────────────
|
|
235
|
-
/**
|
|
236
|
-
* Build def index: symbol name → list of SymbolKeys that define it.
|
|
237
|
-
* Exported symbols and all symbols are indexed; the ambiguity weight
|
|
238
|
-
* discounts edges when many files define the same name.
|
|
239
|
-
*/
|
|
240
|
-
function buildDefIndex(allSymbols) {
|
|
241
|
-
const byName = new Map();
|
|
242
|
-
const byId = new Map();
|
|
243
|
-
for (const [, symbols] of allSymbols) {
|
|
244
|
-
for (const sym of symbols) {
|
|
245
|
-
if (!sym.name || !sym.id)
|
|
246
|
-
continue;
|
|
247
|
-
const key = sym.id;
|
|
248
|
-
byId.set(key, sym);
|
|
249
|
-
const existing = byName.get(sym.name) ?? [];
|
|
250
|
-
if (!existing.includes(key))
|
|
251
|
-
existing.push(key);
|
|
252
|
-
byName.set(sym.name, existing);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return { byName, byId };
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Find the enclosing function/method for a ref at `refLine`. A symbol with
|
|
259
|
-
* an end line is eligible only while the reference is inside its inclusive
|
|
260
|
-
* source range; symbols without an end line retain the conservative start-line
|
|
261
|
-
* fallback. Returns undefined when no symbol encloses the reference so callers
|
|
262
|
-
* can use the file-level module key.
|
|
263
|
-
*/
|
|
264
|
-
function findEnclosingSymbol(symbols, refLine) {
|
|
265
|
-
let best;
|
|
266
|
-
for (const sym of symbols) {
|
|
267
|
-
if (sym.line <= refLine &&
|
|
268
|
-
(sym.endLine === undefined || refLine <= sym.endLine) &&
|
|
269
|
-
(sym.kind === "function" || sym.kind === "method")) {
|
|
270
|
-
if (!best || sym.line > best.line)
|
|
271
|
-
best = sym;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return best;
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Build the function-level call graph from normalized review-graph evidence.
|
|
278
|
-
*
|
|
279
|
-
* The adapter supplies canonical target ids for graph evidence. Such evidence
|
|
280
|
-
* is accepted only when it points at a concrete symbol and has a non-ambiguous
|
|
281
|
-
* resolution. The old name-only path remains for legacy SymbolRef callers, but
|
|
282
|
-
* it is deliberately not used by the review-graph adapter (#1070).
|
|
283
|
-
*/
|
|
284
|
-
export function buildCallGraph(
|
|
285
|
-
/** Keys are normalizeMapKey-canonical file paths; Symbol.filePath values retain their display spelling. */
|
|
286
|
-
allSymbols,
|
|
287
|
-
/** Keys are normalizeMapKey-canonical caller file paths; SymbolRef.filePath retains its display spelling. */
|
|
288
|
-
allRefs, inputCoverage) {
|
|
289
|
-
// Keep the lookup index canonical even for legacy direct callers that still
|
|
290
|
-
// construct these maps from display paths; persisted/review-graph callers
|
|
291
|
-
// already satisfy the canonical-key contract documented above. A caller that
|
|
292
|
-
// supplies TWO raw spellings of the same file gets last-writer-wins here —
|
|
293
|
-
// raw compatibility is best-effort tolerance, not a merge contract.
|
|
294
|
-
const canonicalSymbols = new Map();
|
|
295
|
-
for (const [filePath, symbols] of allSymbols)
|
|
296
|
-
canonicalSymbols.set(normalizeMapKey(filePath), symbols);
|
|
297
|
-
const defIndex = buildDefIndex(canonicalSymbols);
|
|
298
|
-
const callees = new Map();
|
|
299
|
-
const callers = new Map();
|
|
300
|
-
const inDegree = new Map();
|
|
301
|
-
const edgeByPair = new Map();
|
|
302
|
-
let unresolvedRefs = 0;
|
|
303
|
-
let totalRefs = 0;
|
|
304
|
-
const hasInputCoverage = inputCoverage !== undefined;
|
|
305
|
-
const coverage = inputCoverage
|
|
306
|
-
? { ...inputCoverage }
|
|
307
|
-
: {
|
|
308
|
-
totalEvidence: 0,
|
|
309
|
-
callsEvidence: 0,
|
|
310
|
-
referencesEvidence: 0,
|
|
311
|
-
eligibleEvidence: 0,
|
|
312
|
-
resolvedEvidence: 0,
|
|
313
|
-
unresolvedEvidence: 0,
|
|
314
|
-
typeOnlyEvidence: 0,
|
|
315
|
-
unsupportedEvidence: 0,
|
|
316
|
-
sameFileEvidence: 0,
|
|
317
|
-
duplicateEvidence: 0,
|
|
318
|
-
complete: false,
|
|
319
|
-
};
|
|
320
|
-
for (const [callerFile, refs] of allRefs) {
|
|
321
|
-
const callerFileKey = normalizeMapKey(callerFile);
|
|
322
|
-
const callerSymbols = canonicalSymbols.get(callerFileKey) ?? [];
|
|
323
|
-
for (const ref of refs) {
|
|
324
|
-
totalRefs++;
|
|
325
|
-
if (!hasInputCoverage) {
|
|
326
|
-
// Legacy callers do not carry an evidenceKind, but each ref is still
|
|
327
|
-
// raw call-graph evidence and must contribute to coverage totals.
|
|
328
|
-
coverage.totalEvidence++;
|
|
329
|
-
if (ref.evidenceKind === "references" || ref.referenceKind === "type")
|
|
330
|
-
coverage.referencesEvidence++;
|
|
331
|
-
else
|
|
332
|
-
coverage.callsEvidence++;
|
|
333
|
-
}
|
|
334
|
-
if (ref.referenceKind === "type") {
|
|
335
|
-
if (!hasInputCoverage)
|
|
336
|
-
coverage.typeOnlyEvidence++;
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if (ref.evidenceKind && ref.referenceKind !== "call") {
|
|
340
|
-
if (!hasInputCoverage)
|
|
341
|
-
coverage.unsupportedEvidence++;
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
const candidates = [];
|
|
345
|
-
if (ref.targetId) {
|
|
346
|
-
// Canonical graph identity is authoritative. Never recover a name by
|
|
347
|
-
// splitting an id: symbol ids contain kind and line components.
|
|
348
|
-
const callee = defIndex.byId.get(ref.targetId);
|
|
349
|
-
if (!callee ||
|
|
350
|
-
ref.resolution === "name-only" ||
|
|
351
|
-
ref.resolution === "unresolved") {
|
|
352
|
-
if (!hasInputCoverage) {
|
|
353
|
-
unresolvedRefs++;
|
|
354
|
-
coverage.unresolvedEvidence++;
|
|
355
|
-
}
|
|
356
|
-
continue;
|
|
357
|
-
}
|
|
358
|
-
candidates.push({
|
|
359
|
-
callee,
|
|
360
|
-
resolution: ref.resolution ?? "exact",
|
|
361
|
-
candidateCount: 1,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
// Compatibility for pre-#1070 callers that supplied only name refs.
|
|
366
|
-
// There is no typed/import evidence to select one definition, so retain
|
|
367
|
-
// every cross-file candidate and discount each edge. Picking [0] made
|
|
368
|
-
// graph iteration order decide which caller was reported.
|
|
369
|
-
const refName = ref.symbolName ??
|
|
370
|
-
parseSymbolKey(ref.symbolId, callerFile).symbolName ??
|
|
371
|
-
"";
|
|
372
|
-
if (STDLIB_NAMES.has(refName) || !refName) {
|
|
373
|
-
if (!hasInputCoverage)
|
|
374
|
-
coverage.unsupportedEvidence++;
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
const defs = defIndex.byName.get(refName);
|
|
378
|
-
if (!defs || defs.length === 0) {
|
|
379
|
-
if (!hasInputCoverage) {
|
|
380
|
-
unresolvedRefs++;
|
|
381
|
-
coverage.unresolvedEvidence++;
|
|
382
|
-
}
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
const crossFileDefs = defs
|
|
386
|
-
.map((id) => defIndex.byId.get(id))
|
|
387
|
-
.filter((candidate) => candidate !== undefined &&
|
|
388
|
-
normalizeMapKey(candidate.filePath) !== callerFileKey);
|
|
389
|
-
if (crossFileDefs.length === 0) {
|
|
390
|
-
if (!hasInputCoverage)
|
|
391
|
-
coverage.unsupportedEvidence++;
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
const resolution = crossFileDefs.length === 1 ? "exact" : "name-only";
|
|
395
|
-
for (const callee of crossFileDefs) {
|
|
396
|
-
candidates.push({
|
|
397
|
-
callee,
|
|
398
|
-
resolution,
|
|
399
|
-
candidateCount: crossFileDefs.length,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (ref.evidenceKind &&
|
|
404
|
-
candidates.some(({ resolution }) => resolution === "name-only")) {
|
|
405
|
-
if (!hasInputCoverage) {
|
|
406
|
-
unresolvedRefs++;
|
|
407
|
-
coverage.unresolvedEvidence++;
|
|
408
|
-
}
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
const caller = ref.callerSymbolId
|
|
412
|
-
? defIndex.byId.get(ref.callerSymbolId)
|
|
413
|
-
: undefined;
|
|
414
|
-
const enclosing = caller ?? findEnclosingSymbol(callerSymbols, ref.line);
|
|
415
|
-
const callerKey = enclosing?.id ?? `file:${callerFile}`;
|
|
416
|
-
let producedEdge = false;
|
|
417
|
-
let duplicateRef = false;
|
|
418
|
-
for (const { callee, resolution, candidateCount } of candidates) {
|
|
419
|
-
if (normalizeMapKey(callee.filePath) === callerFileKey) {
|
|
420
|
-
// This projection is intentionally cross-file-only. A canonical
|
|
421
|
-
// same-file target is resolved by the source graph, but is represented
|
|
422
|
-
// by an explicit coverage category rather than as unsupported evidence.
|
|
423
|
-
// That keeps the projection contract while allowing complete coverage
|
|
424
|
-
// to gate user-facing cross-file impact honestly.
|
|
425
|
-
coverage.sameFileEvidence++;
|
|
426
|
-
if (hasInputCoverage) {
|
|
427
|
-
coverage.eligibleEvidence--;
|
|
428
|
-
coverage.resolvedEvidence--;
|
|
429
|
-
}
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
producedEdge = true;
|
|
433
|
-
const pairKey = `${callerKey}\u0000${callee.id}`;
|
|
434
|
-
const existing = edgeByPair.get(pairKey);
|
|
435
|
-
if (existing) {
|
|
436
|
-
existing.evidenceCount = (existing.evidenceCount ?? 1) + 1;
|
|
437
|
-
existing.evidenceKind =
|
|
438
|
-
existing.evidenceKind === ref.evidenceKind
|
|
439
|
-
? existing.evidenceKind
|
|
440
|
-
: "mixed";
|
|
441
|
-
// Duplicate evidence is a property of the logical pair, not of
|
|
442
|
-
// whether the producer happened to tag the record. Keep the edge,
|
|
443
|
-
// coverage, and centrality views consistent for legacy refs too.
|
|
444
|
-
duplicateRef = true;
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
const weight = ref.evidenceKind ? 1 : 1 / Math.max(1, candidateCount);
|
|
448
|
-
const edge = {
|
|
449
|
-
callerFile,
|
|
450
|
-
callerSymbol: enclosing?.name,
|
|
451
|
-
callerKey,
|
|
452
|
-
callerKind: enclosing?.kind,
|
|
453
|
-
callerLine: enclosing?.line,
|
|
454
|
-
callerColumn: enclosing?.column,
|
|
455
|
-
calleeFile: callee.filePath,
|
|
456
|
-
calleeSymbol: callee.name,
|
|
457
|
-
calleeKey: callee.id,
|
|
458
|
-
calleeKind: callee.kind,
|
|
459
|
-
calleeLine: callee.line,
|
|
460
|
-
calleeColumn: callee.column,
|
|
461
|
-
evidenceKind: ref.evidenceKind,
|
|
462
|
-
resolution,
|
|
463
|
-
evidenceCount: 1,
|
|
464
|
-
weight,
|
|
465
|
-
};
|
|
466
|
-
edgeByPair.set(pairKey, edge);
|
|
467
|
-
const callerCallees = callees.get(callerKey) ?? new Set();
|
|
468
|
-
callerCallees.add(callee.id);
|
|
469
|
-
callees.set(callerKey, callerCallees);
|
|
470
|
-
const calleeCallers = callers.get(callee.id) ?? new Set();
|
|
471
|
-
calleeCallers.add(callerKey);
|
|
472
|
-
callers.set(callee.id, calleeCallers);
|
|
473
|
-
inDegree.set(callee.id, (inDegree.get(callee.id) ?? 0) + weight);
|
|
474
|
-
}
|
|
475
|
-
if (!hasInputCoverage && producedEdge) {
|
|
476
|
-
// Legacy name-only ambiguity can fan one raw record out to several
|
|
477
|
-
// weighted edges. Coverage remains a count of raw records, so account
|
|
478
|
-
// for eligibility and duplicates once per reference, not once per
|
|
479
|
-
// candidate edge.
|
|
480
|
-
coverage.eligibleEvidence++;
|
|
481
|
-
coverage.resolvedEvidence++;
|
|
482
|
-
if (duplicateRef)
|
|
483
|
-
coverage.duplicateEvidence++;
|
|
484
|
-
}
|
|
485
|
-
if (hasInputCoverage && producedEdge && duplicateRef)
|
|
486
|
-
coverage.duplicateEvidence++;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// With adapter-supplied coverage, totalRefs is the raw evidence count. Some
|
|
490
|
-
// raw records (for example an external node with no caller file) cannot be
|
|
491
|
-
// represented in allRefs, but they must not disappear from accounting.
|
|
492
|
-
if (hasInputCoverage)
|
|
493
|
-
totalRefs = coverage.totalEvidence;
|
|
494
|
-
const languagesKnownComplete = Object.values(coverage.languages ?? {}).every((status) => status === "complete");
|
|
495
|
-
coverage.complete =
|
|
496
|
-
coverage.complete &&
|
|
497
|
-
hasInputCoverage &&
|
|
498
|
-
coverage.unsupportedEvidence === 0 &&
|
|
499
|
-
languagesKnownComplete &&
|
|
500
|
-
(allSymbols.size > 0 || coverage.totalEvidence > 0);
|
|
501
|
-
return {
|
|
502
|
-
callees,
|
|
503
|
-
callers,
|
|
504
|
-
inDegree,
|
|
505
|
-
edges: [...edgeByPair.values()],
|
|
506
|
-
unresolvedRefs: hasInputCoverage
|
|
507
|
-
? coverage.unresolvedEvidence
|
|
508
|
-
: unresolvedRefs,
|
|
509
|
-
totalRefs,
|
|
510
|
-
coverage,
|
|
511
|
-
builtAt: new Date().toISOString(),
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
// ── Persistence ────────────────────────────────────────────────────────────────
|
|
515
|
-
function cacheFilePath(cwd) {
|
|
516
|
-
return path.join(getProjectDataDir(cwd), "cache", "call-graph.json");
|
|
517
|
-
}
|
|
518
|
-
function metaFilePath(cwd) {
|
|
519
|
-
return path.join(getProjectDataDir(cwd), "cache", "call-graph.meta.json");
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Persist a call-graph projection. Current callers pass the canonical
|
|
523
|
-
* review-graph identity. There is deliberately no independent file/mtime
|
|
524
|
-
* freshness argument: callers must use the review graph's canonical identity.
|
|
525
|
-
*/
|
|
526
|
-
export function saveCallGraph(cwd, graph, identity) {
|
|
527
|
-
const cacheFile = cacheFilePath(cwd);
|
|
528
|
-
const metaFile = metaFilePath(cwd);
|
|
529
|
-
try {
|
|
530
|
-
fs.mkdirSync(path.dirname(cacheFile), { recursive: true });
|
|
531
|
-
const persisted = {
|
|
532
|
-
version: CALL_GRAPH_CACHE_VERSION,
|
|
533
|
-
builtAt: graph.builtAt,
|
|
534
|
-
reviewGraphVersion: identity.reviewGraphVersion,
|
|
535
|
-
reviewGraphSignature: identity.reviewGraphSignature,
|
|
536
|
-
edges: graph.edges,
|
|
537
|
-
callees: [...graph.callees.entries()].map(([k, v]) => [k, [...v]]),
|
|
538
|
-
callers: [...graph.callers.entries()].map(([k, v]) => [k, [...v]]),
|
|
539
|
-
inDegree: [...graph.inDegree.entries()],
|
|
540
|
-
totalRefs: graph.totalRefs,
|
|
541
|
-
unresolvedRefs: graph.unresolvedRefs,
|
|
542
|
-
coverage: graph.coverage,
|
|
543
|
-
};
|
|
544
|
-
writeFileAtomic(cacheFile, JSON.stringify(persisted));
|
|
545
|
-
writeFileAtomic(metaFile, JSON.stringify({
|
|
546
|
-
savedAt: new Date().toISOString(),
|
|
547
|
-
edgeCount: graph.edges.length,
|
|
548
|
-
}));
|
|
549
|
-
}
|
|
550
|
-
catch {
|
|
551
|
-
// Non-fatal — next session rebuilds from scratch.
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
function inferLegacyCoverage(edges) {
|
|
555
|
-
let weightedTotalEvidence = 0;
|
|
556
|
-
let weightedReferencesEvidence = 0;
|
|
557
|
-
let weightedDuplicateEvidence = 0;
|
|
558
|
-
for (const edge of edges) {
|
|
559
|
-
const count = Number.isFinite(edge.evidenceCount) && (edge.evidenceCount ?? 0) > 0
|
|
560
|
-
? Math.floor(edge.evidenceCount)
|
|
561
|
-
: 1;
|
|
562
|
-
const weight = Number.isFinite(edge.weight) && edge.weight >= 0 ? edge.weight : 1;
|
|
563
|
-
weightedTotalEvidence += count * weight;
|
|
564
|
-
weightedDuplicateEvidence += Math.max(0, count - 1) * weight;
|
|
565
|
-
if (edge.evidenceKind === "references")
|
|
566
|
-
weightedReferencesEvidence += count * weight;
|
|
567
|
-
}
|
|
568
|
-
const totalEvidence = Math.max(0, Math.round(weightedTotalEvidence));
|
|
569
|
-
const referencesEvidence = Math.max(0, Math.round(weightedReferencesEvidence));
|
|
570
|
-
const duplicateEvidence = Math.max(0, Math.round(weightedDuplicateEvidence));
|
|
571
|
-
return {
|
|
572
|
-
totalEvidence,
|
|
573
|
-
callsEvidence: totalEvidence - referencesEvidence,
|
|
574
|
-
referencesEvidence,
|
|
575
|
-
eligibleEvidence: totalEvidence,
|
|
576
|
-
resolvedEvidence: totalEvidence,
|
|
577
|
-
unresolvedEvidence: 0,
|
|
578
|
-
typeOnlyEvidence: 0,
|
|
579
|
-
unsupportedEvidence: 0,
|
|
580
|
-
sameFileEvidence: 0,
|
|
581
|
-
duplicateEvidence,
|
|
582
|
-
// A legacy graph can still be queried, but it cannot prove that the old
|
|
583
|
-
// extractor covered every source/reference kind.
|
|
584
|
-
complete: false,
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
function loadCoverage(rawCoverage, edges) {
|
|
588
|
-
const inferred = inferLegacyCoverage(edges);
|
|
589
|
-
return rawCoverage
|
|
590
|
-
? {
|
|
591
|
-
...inferred,
|
|
592
|
-
...rawCoverage,
|
|
593
|
-
}
|
|
594
|
-
: inferred;
|
|
595
|
-
}
|
|
596
|
-
function finiteCount(value) {
|
|
597
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
598
|
-
}
|
|
599
|
-
function countsMatch(left, right) {
|
|
600
|
-
return (Math.abs(left - right) <=
|
|
601
|
-
1e-9 * Math.max(1, Math.abs(left), Math.abs(right)));
|
|
602
|
-
}
|
|
603
|
-
function validatePersistedCallGraph(raw, coverage) {
|
|
604
|
-
const nonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
605
|
-
const enumValue = (value, values) => typeof value === "string" && values.includes(value);
|
|
606
|
-
const edgeKinds = ["calls", "references", "mixed"];
|
|
607
|
-
const resolutions = [
|
|
608
|
-
"exact",
|
|
609
|
-
"import",
|
|
610
|
-
"receiver-type",
|
|
611
|
-
"name-only",
|
|
612
|
-
"unresolved",
|
|
613
|
-
];
|
|
614
|
-
if (!raw ||
|
|
615
|
-
raw.version !== CALL_GRAPH_CACHE_VERSION ||
|
|
616
|
-
!nonEmptyString(raw.builtAt))
|
|
617
|
-
return false;
|
|
618
|
-
if (!nonEmptyString(raw.reviewGraphVersion) ||
|
|
619
|
-
!nonEmptyString(raw.reviewGraphSignature))
|
|
620
|
-
return false;
|
|
621
|
-
if (raw.coverage !== undefined &&
|
|
622
|
-
(!raw.coverage ||
|
|
623
|
-
typeof raw.coverage !== "object" ||
|
|
624
|
-
Array.isArray(raw.coverage)))
|
|
625
|
-
return false;
|
|
626
|
-
if (!Array.isArray(raw.edges) ||
|
|
627
|
-
!Array.isArray(raw.callees) ||
|
|
628
|
-
!Array.isArray(raw.callers) ||
|
|
629
|
-
!Array.isArray(raw.inDegree))
|
|
630
|
-
return false;
|
|
631
|
-
const coverageValues = [
|
|
632
|
-
coverage.totalEvidence,
|
|
633
|
-
coverage.callsEvidence,
|
|
634
|
-
coverage.referencesEvidence,
|
|
635
|
-
coverage.eligibleEvidence,
|
|
636
|
-
coverage.resolvedEvidence,
|
|
637
|
-
coverage.unresolvedEvidence,
|
|
638
|
-
coverage.typeOnlyEvidence,
|
|
639
|
-
coverage.unsupportedEvidence,
|
|
640
|
-
coverage.sameFileEvidence,
|
|
641
|
-
coverage.duplicateEvidence,
|
|
642
|
-
];
|
|
643
|
-
if (typeof coverage.complete !== "boolean" ||
|
|
644
|
-
coverageValues.some((value) => !finiteCount(value)))
|
|
645
|
-
return false;
|
|
646
|
-
if (coverage.languages !== undefined &&
|
|
647
|
-
(!coverage.languages ||
|
|
648
|
-
typeof coverage.languages !== "object" ||
|
|
649
|
-
Array.isArray(coverage.languages) ||
|
|
650
|
-
Object.entries(coverage.languages).some(([language, status]) => !nonEmptyString(language) ||
|
|
651
|
-
!enumValue(status, ["complete", "partial", "unavailable"]))))
|
|
652
|
-
return false;
|
|
653
|
-
if (coverage.callsEvidence + coverage.referencesEvidence !==
|
|
654
|
-
coverage.totalEvidence)
|
|
655
|
-
return false;
|
|
656
|
-
if (coverage.resolvedEvidence > coverage.eligibleEvidence)
|
|
657
|
-
return false;
|
|
658
|
-
if (coverage.eligibleEvidence !== coverage.resolvedEvidence)
|
|
659
|
-
return false;
|
|
660
|
-
if (coverage.resolvedEvidence +
|
|
661
|
-
coverage.unresolvedEvidence +
|
|
662
|
-
coverage.typeOnlyEvidence +
|
|
663
|
-
coverage.unsupportedEvidence +
|
|
664
|
-
coverage.sameFileEvidence !==
|
|
665
|
-
coverage.totalEvidence)
|
|
666
|
-
return false;
|
|
667
|
-
if (coverage.complete && coverage.unsupportedEvidence > 0)
|
|
668
|
-
return false;
|
|
669
|
-
if (coverage.complete &&
|
|
670
|
-
Object.values(coverage.languages ?? {}).some((status) => status !== "complete"))
|
|
671
|
-
return false;
|
|
672
|
-
const expectedCallees = new Map();
|
|
673
|
-
const expectedCallers = new Map();
|
|
674
|
-
const expectedInDegree = new Map();
|
|
675
|
-
const pairKeys = new Set();
|
|
676
|
-
let weightedEdgeEvidence = 0;
|
|
677
|
-
let weightedDuplicateEvidence = 0;
|
|
678
|
-
for (const edge of raw.edges) {
|
|
679
|
-
if (!edge ||
|
|
680
|
-
typeof edge !== "object" ||
|
|
681
|
-
!nonEmptyString(edge.callerKey) ||
|
|
682
|
-
!nonEmptyString(edge.calleeKey) ||
|
|
683
|
-
!nonEmptyString(edge.callerFile) ||
|
|
684
|
-
!nonEmptyString(edge.calleeFile) ||
|
|
685
|
-
!nonEmptyString(edge.calleeSymbol) ||
|
|
686
|
-
typeof edge.weight !== "number" ||
|
|
687
|
-
!Number.isFinite(edge.weight) ||
|
|
688
|
-
edge.weight <= 0 ||
|
|
689
|
-
edge.weight > 1)
|
|
690
|
-
return false;
|
|
691
|
-
if (edge.callerSymbol !== undefined && !nonEmptyString(edge.callerSymbol))
|
|
692
|
-
return false;
|
|
693
|
-
if (edge.callerKind !== undefined && !nonEmptyString(edge.callerKind))
|
|
694
|
-
return false;
|
|
695
|
-
if (edge.calleeKind !== undefined && !nonEmptyString(edge.calleeKind))
|
|
696
|
-
return false;
|
|
697
|
-
if (edge.evidenceKind !== undefined &&
|
|
698
|
-
!enumValue(edge.evidenceKind, edgeKinds))
|
|
699
|
-
return false;
|
|
700
|
-
if (edge.resolution !== undefined &&
|
|
701
|
-
!enumValue(edge.resolution, resolutions))
|
|
702
|
-
return false;
|
|
703
|
-
if (edge.evidenceCount !== undefined &&
|
|
704
|
-
(!finiteCount(edge.evidenceCount) || edge.evidenceCount < 1))
|
|
705
|
-
return false;
|
|
706
|
-
const evidenceCount = edge.evidenceCount ?? 1;
|
|
707
|
-
const callerIdentity = parseCanonicalSymbolKey(edge.callerKey, edge.callerFile);
|
|
708
|
-
const calleeIdentity = parseCanonicalSymbolKey(edge.calleeKey, edge.calleeFile);
|
|
709
|
-
if (normalizeMapKey(callerIdentity.filePath) !==
|
|
710
|
-
normalizeMapKey(edge.callerFile) ||
|
|
711
|
-
normalizeMapKey(calleeIdentity.filePath) !==
|
|
712
|
-
normalizeMapKey(edge.calleeFile) ||
|
|
713
|
-
(calleeIdentity.symbolName !== undefined &&
|
|
714
|
-
calleeIdentity.symbolName !== edge.calleeSymbol) ||
|
|
715
|
-
(callerIdentity.symbolName !== undefined &&
|
|
716
|
-
edge.callerSymbol !== undefined &&
|
|
717
|
-
callerIdentity.symbolName !== edge.callerSymbol))
|
|
718
|
-
return false;
|
|
719
|
-
const pair = `${edge.callerKey}\u0000${edge.calleeKey}`;
|
|
720
|
-
if (pairKeys.has(pair))
|
|
721
|
-
return false;
|
|
722
|
-
pairKeys.add(pair);
|
|
723
|
-
// Ambiguous legacy name-only evidence may fan one raw record out to
|
|
724
|
-
// multiple weighted edges. Weight the persisted evidence back to raw
|
|
725
|
-
// records so semantic validation agrees with coverage accounting.
|
|
726
|
-
weightedEdgeEvidence += evidenceCount * edge.weight;
|
|
727
|
-
weightedDuplicateEvidence += (evidenceCount - 1) * edge.weight;
|
|
728
|
-
const callees = expectedCallees.get(edge.callerKey) ?? new Set();
|
|
729
|
-
callees.add(edge.calleeKey);
|
|
730
|
-
expectedCallees.set(edge.callerKey, callees);
|
|
731
|
-
const callers = expectedCallers.get(edge.calleeKey) ?? new Set();
|
|
732
|
-
callers.add(edge.callerKey);
|
|
733
|
-
expectedCallers.set(edge.calleeKey, callers);
|
|
734
|
-
expectedInDegree.set(edge.calleeKey, (expectedInDegree.get(edge.calleeKey) ?? 0) + edge.weight);
|
|
735
|
-
}
|
|
736
|
-
if (!countsMatch(weightedEdgeEvidence, coverage.resolvedEvidence))
|
|
737
|
-
return false;
|
|
738
|
-
if (!countsMatch(weightedDuplicateEvidence, coverage.duplicateEvidence))
|
|
739
|
-
return false;
|
|
740
|
-
if (!countsMatch(coverage.totalEvidence, weightedEdgeEvidence +
|
|
741
|
-
coverage.unresolvedEvidence +
|
|
742
|
-
coverage.typeOnlyEvidence +
|
|
743
|
-
coverage.unsupportedEvidence +
|
|
744
|
-
coverage.sameFileEvidence))
|
|
745
|
-
return false;
|
|
746
|
-
if (raw.totalRefs !== undefined && !finiteCount(raw.totalRefs))
|
|
747
|
-
return false;
|
|
748
|
-
if (raw.unresolvedRefs !== undefined && !finiteCount(raw.unresolvedRefs))
|
|
749
|
-
return false;
|
|
750
|
-
if (raw.totalRefs !== undefined && raw.totalRefs !== coverage.totalEvidence)
|
|
751
|
-
return false;
|
|
752
|
-
if (raw.unresolvedRefs !== undefined &&
|
|
753
|
-
raw.unresolvedRefs !== coverage.unresolvedEvidence)
|
|
754
|
-
return false;
|
|
755
|
-
const readAdjacency = (entries) => {
|
|
756
|
-
const result = new Map();
|
|
757
|
-
if (!Array.isArray(entries))
|
|
758
|
-
return undefined;
|
|
759
|
-
for (const entry of entries) {
|
|
760
|
-
if (!Array.isArray(entry) ||
|
|
761
|
-
!nonEmptyString(entry[0]) ||
|
|
762
|
-
!Array.isArray(entry[1]) ||
|
|
763
|
-
entry[1].some((key) => !nonEmptyString(key)))
|
|
764
|
-
return undefined;
|
|
765
|
-
const keys = entry[1];
|
|
766
|
-
if (new Set(keys).size !== keys.length || result.has(entry[0]))
|
|
767
|
-
return undefined;
|
|
768
|
-
result.set(entry[0], new Set(keys));
|
|
769
|
-
}
|
|
770
|
-
return result;
|
|
771
|
-
};
|
|
772
|
-
const actualCallees = readAdjacency(raw.callees);
|
|
773
|
-
const actualCallers = readAdjacency(raw.callers);
|
|
774
|
-
if (!actualCallees ||
|
|
775
|
-
!actualCallers ||
|
|
776
|
-
actualCallees.size !== expectedCallees.size ||
|
|
777
|
-
actualCallers.size !== expectedCallers.size)
|
|
778
|
-
return false;
|
|
779
|
-
const sameSets = (actual, expected) => {
|
|
780
|
-
for (const [key, values] of expected) {
|
|
781
|
-
const got = actual.get(key);
|
|
782
|
-
if (!got ||
|
|
783
|
-
got.size !== values.size ||
|
|
784
|
-
[...values].some((value) => !got.has(value)))
|
|
785
|
-
return false;
|
|
786
|
-
}
|
|
787
|
-
return true;
|
|
788
|
-
};
|
|
789
|
-
if (!sameSets(actualCallees, expectedCallees) ||
|
|
790
|
-
!sameSets(actualCallers, expectedCallers))
|
|
791
|
-
return false;
|
|
792
|
-
const actualInDegree = new Map();
|
|
793
|
-
for (const entry of raw.inDegree) {
|
|
794
|
-
if (!Array.isArray(entry) ||
|
|
795
|
-
!nonEmptyString(entry[0]) ||
|
|
796
|
-
typeof entry[1] !== "number" ||
|
|
797
|
-
!Number.isFinite(entry[1]) ||
|
|
798
|
-
entry[1] < 0 ||
|
|
799
|
-
actualInDegree.has(entry[0]))
|
|
800
|
-
return false;
|
|
801
|
-
actualInDegree.set(entry[0], entry[1]);
|
|
802
|
-
}
|
|
803
|
-
if (actualInDegree.size !== expectedInDegree.size)
|
|
804
|
-
return false;
|
|
805
|
-
for (const [key, value] of expectedInDegree) {
|
|
806
|
-
if (actualInDegree.get(key) !== value)
|
|
807
|
-
return false;
|
|
808
|
-
}
|
|
809
|
-
return true;
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* Load the persisted call graph from disk.
|
|
813
|
-
* Returns undefined if the cache is missing, version-mismatched, or corrupt.
|
|
814
|
-
*/
|
|
815
|
-
export function loadCallGraph(cwd, expectedIdentity) {
|
|
816
|
-
const cacheFile = cacheFilePath(cwd);
|
|
817
|
-
try {
|
|
818
|
-
const raw = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
|
|
819
|
-
if (raw.version !== CALL_GRAPH_CACHE_VERSION)
|
|
820
|
-
return undefined;
|
|
821
|
-
if (typeof raw.reviewGraphVersion !== "string" ||
|
|
822
|
-
typeof raw.reviewGraphSignature !== "string")
|
|
823
|
-
return undefined;
|
|
824
|
-
const identity = {
|
|
825
|
-
reviewGraphVersion: raw.reviewGraphVersion,
|
|
826
|
-
reviewGraphSignature: raw.reviewGraphSignature,
|
|
827
|
-
};
|
|
828
|
-
if (expectedIdentity &&
|
|
829
|
-
(identity.reviewGraphVersion !== expectedIdentity.reviewGraphVersion ||
|
|
830
|
-
identity.reviewGraphSignature !== expectedIdentity.reviewGraphSignature))
|
|
831
|
-
return undefined;
|
|
832
|
-
const coverage = loadCoverage(raw.coverage, raw.edges);
|
|
833
|
-
if (!validatePersistedCallGraph(raw, coverage))
|
|
834
|
-
return undefined;
|
|
835
|
-
return {
|
|
836
|
-
graph: {
|
|
837
|
-
callees: new Map(raw.callees.map(([k, v]) => [k, new Set(v)])),
|
|
838
|
-
callers: new Map(raw.callers.map(([k, v]) => [k, new Set(v)])),
|
|
839
|
-
inDegree: new Map(raw.inDegree),
|
|
840
|
-
edges: raw.edges,
|
|
841
|
-
unresolvedRefs: raw.unresolvedRefs ?? coverage.unresolvedEvidence,
|
|
842
|
-
totalRefs: raw.totalRefs ?? coverage.totalEvidence,
|
|
843
|
-
coverage,
|
|
844
|
-
builtAt: raw.builtAt,
|
|
845
|
-
},
|
|
846
|
-
identity,
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
catch {
|
|
850
|
-
return undefined;
|
|
851
|
-
}
|
|
852
|
-
}
|