@herbertgao/pi-extensions 2026.9.0 → 2026.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
- package/node_modules/@czottmann/pi-automode/README.md +20 -0
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
- package/node_modules/pi-lens/CHANGELOG.md +988 -0
- package/node_modules/pi-lens/README.md +3 -0
- package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
- package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
- package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
- package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
- package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
- package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
- package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
- package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
- package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
- package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
- package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
- package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
- package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
- package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
- package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
- package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
- package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
- package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
- package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
- package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
- package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
- package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
- package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
- package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
- package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
- package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
- package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
- package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
- package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
- package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
- package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
- package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
- package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
- package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
- package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
- package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
- package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
- package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
- package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
- package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
- package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
- package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
- package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
- package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
- package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
- package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
- package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
- package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
- package/node_modules/pi-lens/dist/index.js +64335 -54300
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +127 -5
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- package/node_modules/pi-lens/docs/agent-guide.md +64 -1
- package/node_modules/pi-lens/docs/configuration.md +222 -0
- package/node_modules/pi-lens/docs/dependencies.md +3 -3
- package/node_modules/pi-lens/docs/features.md +44 -5
- package/node_modules/pi-lens/docs/language-coverage.md +2 -2
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
- package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
- package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
- package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
- package/node_modules/pi-lens/package.json +15 -23
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
- package/node_modules/pi-web-access/CHANGELOG.md +28 -0
- package/node_modules/pi-web-access/README.md +62 -15
- package/node_modules/pi-web-access/curator-page.ts +3 -1
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/gemini-search.ts +8 -4
- package/node_modules/pi-web-access/github-extract.ts +242 -1
- package/node_modules/pi-web-access/index.ts +118 -64
- package/node_modules/pi-web-access/mistral-search.ts +281 -0
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/perplexity.ts +14 -1
- package/node_modules/pi-web-access/utils.ts +23 -6
- package/node_modules/pi-web-access/xai-search.ts +96 -33
- package/package.json +5 -5
|
@@ -18,7 +18,7 @@ import { containerNameChain, getOpenDocumentSymbols, lspSymbolKindName, } from "
|
|
|
18
18
|
import { isAtOrAboveHomeDir, normalizeFilePath, normalizeMapKey, toProjectRelativePath, } from "../path-utils.js";
|
|
19
19
|
import { collectProjectSourceFilesWithBudgetAsync } from "../project-scan-policy.js";
|
|
20
20
|
import { getReviewGraphMaxFilesDerived } from "../project-scale.js";
|
|
21
|
-
import { compareOrdinal } from "../string-utils.js";
|
|
21
|
+
import { compareOrdinal, escapeRegExp } from "../string-utils.js";
|
|
22
22
|
import { BoundedLruCache } from "../bounded-cache.js";
|
|
23
23
|
import { jsTsCandidatePaths, resolveAliasedImport, resolveImportToFiles, resolveProjectReferenceImport, resolveWorkspacePackageImport, } from "./import-resolvers.js";
|
|
24
24
|
import { RUNTIME_CONFIG } from "../runtime-config.js";
|
|
@@ -27,7 +27,7 @@ import { buildQualifiedName, findOwnerName } from "../symbol-containment.js";
|
|
|
27
27
|
import { logTreeSitterCacheStats } from "../tree-sitter-logger.js";
|
|
28
28
|
import { flushReviewGraphLogSync, logReviewGraph, makeReviewGraphBuildMetadata, } from "../review-graph-logger.js";
|
|
29
29
|
import { getSharedTreeSitterClient } from "../tree-sitter-shared.js";
|
|
30
|
-
import { TreeSitterSymbolExtractor, } from "../tree-sitter-symbol-extractor.js";
|
|
30
|
+
import { symbolExtractionGrammar, TreeSitterSymbolExtractor, } from "../tree-sitter-symbol-extractor.js";
|
|
31
31
|
import { withTreeSitterRoot } from "../tree-sitter-shared.js";
|
|
32
32
|
import { incrementDegradationCount } from "../degradation-ledger.js";
|
|
33
33
|
import { resolveGitIdentity } from "./git-identity.js";
|
|
@@ -103,27 +103,20 @@ const REVIEW_GRAPH_MAX_WARM_WORKSPACES = 8;
|
|
|
103
103
|
// spelling is resolved once; repeated builds reuse the result. The project
|
|
104
104
|
// memo is bounded and workspace-cache clears provide a freshness boundary.
|
|
105
105
|
const REVIEW_GRAPH_SOURCE_PATH_MEMO_ENTRIES = 16_384;
|
|
106
|
-
|
|
106
|
+
// BoundedLruCache, not BoundedFifoMap: a hit here must refresh the workspace's
|
|
107
|
+
// recency (the hand-rolled version did its own delete+set for exactly that),
|
|
108
|
+
// so a workspace still being built is never the one evicted (#2442).
|
|
109
|
+
const _sourcePathMemos = new BoundedLruCache(REVIEW_GRAPH_MAX_WARM_WORKSPACES);
|
|
107
110
|
function sourcePathMemo(cwd) {
|
|
108
111
|
const key = normalizeMapKey(path.resolve(cwd));
|
|
109
112
|
const existing = _sourcePathMemos.get(key);
|
|
110
|
-
if (existing)
|
|
111
|
-
// The workspace map is also bounded LRU; a hit must refresh its recency.
|
|
112
|
-
_sourcePathMemos.delete(key);
|
|
113
|
-
_sourcePathMemos.set(key, existing);
|
|
113
|
+
if (existing)
|
|
114
114
|
return existing;
|
|
115
|
-
}
|
|
116
115
|
const memo = {
|
|
117
116
|
cache: new BoundedLruCache(REVIEW_GRAPH_SOURCE_PATH_MEMO_ENTRIES),
|
|
118
117
|
normalizeCalls: { value: 0 },
|
|
119
118
|
};
|
|
120
119
|
_sourcePathMemos.set(key, memo);
|
|
121
|
-
while (_sourcePathMemos.size > REVIEW_GRAPH_MAX_WARM_WORKSPACES) {
|
|
122
|
-
const oldest = _sourcePathMemos.keys().next().value;
|
|
123
|
-
if (oldest === undefined)
|
|
124
|
-
break;
|
|
125
|
-
_sourcePathMemos.delete(oldest);
|
|
126
|
-
}
|
|
127
120
|
return memo;
|
|
128
121
|
}
|
|
129
122
|
function normalizeGraphSourcePath(memo, raw) {
|
|
@@ -528,12 +521,15 @@ function headSliceGraph(graph, cap) {
|
|
|
528
521
|
* This bounds the VALUE, not the store, and deliberately adds no second bounding
|
|
529
522
|
* mechanism to `FactStore` (#2243 owns the store-level discipline).
|
|
530
523
|
*
|
|
531
|
-
* `sessionFacts` entry COUNT
|
|
532
|
-
*
|
|
533
|
-
* (`dispatch/integration.ts`)
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
524
|
+
* `sessionFacts` entry COUNT (a separate #2240 sibling, #2282) is now bounded
|
|
525
|
+
* too: `session.baseline.${path}` (`dispatch/dispatcher.ts`),
|
|
526
|
+
* `session.baseline.cascade.${path}` (`dispatch/integration.ts`), and this
|
|
527
|
+
* module's own `changedSymbols`/`entitySnapshot` per-file keys all go through
|
|
528
|
+
* `FactStore.setBoundedSessionFact`/`getBoundedSessionFact`, an LRU-count-cap
|
|
529
|
+
* sibling of the per-file `fileFacts` bound reusing the SAME discipline
|
|
530
|
+
* (#2243), not a second mechanism. `session.reviewGraph` itself stays on the
|
|
531
|
+
* plain, unbounded `sessionFacts` map — its key is a fixed singleton, so the
|
|
532
|
+
* VALUE bound below is what keeps IT small, not an entry-count cap.
|
|
537
533
|
*/
|
|
538
534
|
function setSessionReviewGraphFact(cwd, facts, graph) {
|
|
539
535
|
const retained = retainedGraph(cwd, graph);
|
|
@@ -826,9 +822,6 @@ function makeCtx(filePath, cwd, facts) {
|
|
|
826
822
|
log: () => { },
|
|
827
823
|
};
|
|
828
824
|
}
|
|
829
|
-
function escapeRegExp(string) {
|
|
830
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
831
|
-
}
|
|
832
825
|
function createEmptyGraph() {
|
|
833
826
|
return {
|
|
834
827
|
version: REVIEW_GRAPH_VERSION,
|
|
@@ -1226,13 +1219,15 @@ function indexEdge(graph, edge) {
|
|
|
1226
1219
|
graph.edgesByTo.set(edge.to, to);
|
|
1227
1220
|
}
|
|
1228
1221
|
/**
|
|
1229
|
-
* Drop `edge` from both adjacency buckets
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1222
|
+
* Drop `edge` from both adjacency buckets. This is kept for the single-edge
|
|
1223
|
+
* dedupe path; multi-file removal uses `unindexEdges` below so a shared hub
|
|
1224
|
+
* bucket is scanned once rather than once per removed edge (#2074).
|
|
1232
1225
|
*/
|
|
1233
1226
|
function unindexEdge(graph, edge) {
|
|
1234
1227
|
const from = graph.edgesByFrom.get(edge.from);
|
|
1235
1228
|
if (from) {
|
|
1229
|
+
// Count the full bucket as the linear `indexOf` scan's bounded work.
|
|
1230
|
+
_rebuildCounters.removeOwnedEdgePositions += from.length;
|
|
1236
1231
|
const at = from.indexOf(edge);
|
|
1237
1232
|
if (at >= 0)
|
|
1238
1233
|
from.splice(at, 1);
|
|
@@ -1241,6 +1236,7 @@ function unindexEdge(graph, edge) {
|
|
|
1241
1236
|
}
|
|
1242
1237
|
const to = graph.edgesByTo.get(edge.to);
|
|
1243
1238
|
if (to) {
|
|
1239
|
+
_rebuildCounters.removeOwnedEdgePositions += to.length;
|
|
1244
1240
|
const at = to.indexOf(edge);
|
|
1245
1241
|
if (at >= 0)
|
|
1246
1242
|
to.splice(at, 1);
|
|
@@ -1248,6 +1244,49 @@ function unindexEdge(graph, edge) {
|
|
|
1248
1244
|
graph.edgesByTo.delete(edge.to);
|
|
1249
1245
|
}
|
|
1250
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Remove a batch of edges from both live adjacency indexes. Each touched
|
|
1249
|
+
* bucket is filtered once, so the work is proportional to the bucket lengths,
|
|
1250
|
+
* not to the product of a hub's fan-in and its removed-edge count (#2074).
|
|
1251
|
+
*/
|
|
1252
|
+
function unindexEdges(graph, removedEdges) {
|
|
1253
|
+
const fromIds = new Set();
|
|
1254
|
+
const toIds = new Set();
|
|
1255
|
+
for (const edge of removedEdges) {
|
|
1256
|
+
fromIds.add(edge.from);
|
|
1257
|
+
toIds.add(edge.to);
|
|
1258
|
+
}
|
|
1259
|
+
for (const fromId of fromIds) {
|
|
1260
|
+
const bucket = graph.edgesByFrom.get(fromId);
|
|
1261
|
+
if (!bucket)
|
|
1262
|
+
continue;
|
|
1263
|
+
const kept = [];
|
|
1264
|
+
for (const edge of bucket) {
|
|
1265
|
+
_rebuildCounters.removeOwnedEdgePositions++;
|
|
1266
|
+
if (!removedEdges.has(edge))
|
|
1267
|
+
kept.push(edge);
|
|
1268
|
+
}
|
|
1269
|
+
if (kept.length === 0)
|
|
1270
|
+
graph.edgesByFrom.delete(fromId);
|
|
1271
|
+
else
|
|
1272
|
+
graph.edgesByFrom.set(fromId, kept);
|
|
1273
|
+
}
|
|
1274
|
+
for (const toId of toIds) {
|
|
1275
|
+
const bucket = graph.edgesByTo.get(toId);
|
|
1276
|
+
if (!bucket)
|
|
1277
|
+
continue;
|
|
1278
|
+
const kept = [];
|
|
1279
|
+
for (const edge of bucket) {
|
|
1280
|
+
_rebuildCounters.removeOwnedEdgePositions++;
|
|
1281
|
+
if (!removedEdges.has(edge))
|
|
1282
|
+
kept.push(edge);
|
|
1283
|
+
}
|
|
1284
|
+
if (kept.length === 0)
|
|
1285
|
+
graph.edgesByTo.delete(toId);
|
|
1286
|
+
else
|
|
1287
|
+
graph.edgesByTo.set(toId, kept);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1251
1290
|
function addEdge(graph, edge) {
|
|
1252
1291
|
graph.edges.push(edge);
|
|
1253
1292
|
indexEdge(graph, edge);
|
|
@@ -2621,9 +2660,16 @@ async function tryResumeFromCheckpoint(cwd, filesToBuild, ignoredIds) {
|
|
|
2621
2660
|
}
|
|
2622
2661
|
const graph = loaded.graph;
|
|
2623
2662
|
// Evict every stale (content-changed) processed file so its outdated
|
|
2624
|
-
// contribution is replaced by a fresh walk below.
|
|
2663
|
+
// contribution is replaced by a fresh walk below. One shared drop set,
|
|
2664
|
+
// compacted once (#2074) — pruneOrphanNonFileNodes reads graph.edges next,
|
|
2665
|
+
// so the compaction must land before it runs.
|
|
2666
|
+
const removedEdges = new Set();
|
|
2625
2667
|
for (const file of stale)
|
|
2626
|
-
removeFileOwnedGraphData(graph, file);
|
|
2668
|
+
removeFileOwnedGraphData(graph, file, removedEdges);
|
|
2669
|
+
if (removedEdges.size > 0) {
|
|
2670
|
+
unindexEdges(graph, removedEdges);
|
|
2671
|
+
graph.edges = graph.edges.filter((edge) => !removedEdges.has(edge));
|
|
2672
|
+
}
|
|
2627
2673
|
pruneOrphanNonFileNodes(graph);
|
|
2628
2674
|
graph.changedSymbolsByFile = new Map();
|
|
2629
2675
|
const remaining = filesToBuild.filter((file) => !reusableHashes.has(file));
|
|
@@ -2654,7 +2700,7 @@ export function _readReviewGraphCheckpointForTests(cwd) {
|
|
|
2654
2700
|
}
|
|
2655
2701
|
/** Test hook: force any pending debounced persist to write immediately. */
|
|
2656
2702
|
export function flushReviewGraphPersistsForTests() {
|
|
2657
|
-
for (const key of
|
|
2703
|
+
for (const key of _pendingPersist.keys()) {
|
|
2658
2704
|
const pending = _pendingPersist.get(key);
|
|
2659
2705
|
if (!pending)
|
|
2660
2706
|
continue;
|
|
@@ -2718,7 +2764,7 @@ export function flushReviewGraphPersist(cwd, source = "cli") {
|
|
|
2718
2764
|
let pending = _pendingPersist.get(key);
|
|
2719
2765
|
const removedWorkerRequests = [];
|
|
2720
2766
|
let selectedWorkerRequest;
|
|
2721
|
-
for (const [id, request] of
|
|
2767
|
+
for (const [id, request] of _workerRequests) {
|
|
2722
2768
|
if (request.key !== key)
|
|
2723
2769
|
continue;
|
|
2724
2770
|
_workerRequests.delete(id);
|
|
@@ -2883,7 +2929,7 @@ function upsertChangedSymbols(graph, facts, filePath) {
|
|
|
2883
2929
|
if (detectFileRole(filePath) === "test")
|
|
2884
2930
|
return;
|
|
2885
2931
|
const normalized = normalizeMapKey(filePath);
|
|
2886
|
-
const changed = facts.
|
|
2932
|
+
const changed = facts.getBoundedSessionFact(`${CHANGED_SYMBOLS_PREFIX}${normalized}`);
|
|
2887
2933
|
if (changed && changed.length > 0) {
|
|
2888
2934
|
graph.changedSymbolsByFile.set(normalized, [...changed]);
|
|
2889
2935
|
}
|
|
@@ -3158,45 +3204,13 @@ function addJsTsFile(graph, cwd, filePath, facts, ignoredIds) {
|
|
|
3158
3204
|
}
|
|
3159
3205
|
}
|
|
3160
3206
|
}
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
return "rust";
|
|
3169
|
-
case "ruby":
|
|
3170
|
-
return "ruby";
|
|
3171
|
-
case "cxx": {
|
|
3172
|
-
const ext = filePath ? path.extname(filePath).toLowerCase() : "";
|
|
3173
|
-
return ext === ".c" || ext === ".h" ? "c" : "cpp";
|
|
3174
|
-
}
|
|
3175
|
-
case "java":
|
|
3176
|
-
return "java";
|
|
3177
|
-
case "kotlin":
|
|
3178
|
-
return "kotlin";
|
|
3179
|
-
case "dart":
|
|
3180
|
-
return "dart";
|
|
3181
|
-
case "elixir":
|
|
3182
|
-
return "elixir";
|
|
3183
|
-
case "csharp":
|
|
3184
|
-
return "csharp";
|
|
3185
|
-
case "php":
|
|
3186
|
-
return "php";
|
|
3187
|
-
case "swift":
|
|
3188
|
-
return "swift";
|
|
3189
|
-
case "lua":
|
|
3190
|
-
return "lua";
|
|
3191
|
-
case "ocaml":
|
|
3192
|
-
return "ocaml";
|
|
3193
|
-
case "zig":
|
|
3194
|
-
return "zig";
|
|
3195
|
-
case "shell":
|
|
3196
|
-
return "bash";
|
|
3197
|
-
default:
|
|
3198
|
-
return undefined;
|
|
3199
|
-
}
|
|
3207
|
+
// The review graph's kind -> tree-sitter grammar answer. Delegates to the
|
|
3208
|
+
// registry-derived resolver (#2424): this used to be a hand-written switch that
|
|
3209
|
+
// re-derived the `.c`/`.h` split inline and drifted from the ext -> grammar
|
|
3210
|
+
// authority. Exported for the language-identity golden snapshot
|
|
3211
|
+
// (scripts/gen-language-snapshot.mjs).
|
|
3212
|
+
export function mapKindToTreeSitterLanguage(kind, filePath) {
|
|
3213
|
+
return symbolExtractionGrammar(kind, filePath);
|
|
3200
3214
|
}
|
|
3201
3215
|
async function getExtractor(languageId) {
|
|
3202
3216
|
if (extractorCache.has(languageId))
|
|
@@ -3622,46 +3636,55 @@ function addCxxIncludeEdges(graph, cwd, filePath, ignoredIds, contentOverride) {
|
|
|
3622
3636
|
});
|
|
3623
3637
|
}
|
|
3624
3638
|
}
|
|
3625
|
-
|
|
3639
|
+
/**
|
|
3640
|
+
* Drop the nodes/edges a changed file owns, and index-maintain as it goes.
|
|
3641
|
+
* `removedEdges` accumulates edges to drop from `graph.edges` — the CALLER
|
|
3642
|
+
* compacts the array once for the whole batch (#2074): before this, each call
|
|
3643
|
+
* ran its own `graph.edges.filter()`, so a batch of N changed files scanned
|
|
3644
|
+
* the whole edge array N times (changedFiles x graph, not changedFiles x
|
|
3645
|
+
* fan-in/out).
|
|
3646
|
+
*
|
|
3647
|
+
* `removedIds` is exactly `fileNodes`/`symbolNodesByFile`'s entries for this
|
|
3648
|
+
* file — `file` and `symbol` are the only node kinds that ever set `filePath`
|
|
3649
|
+
* (`module`/`external` placeholders never do) — so no `graph.nodes` scan is
|
|
3650
|
+
* needed either, and the owned edges are read straight off `edgesByFrom` /
|
|
3651
|
+
* `edgesByTo` instead of scanning `graph.edges`.
|
|
3652
|
+
*/
|
|
3653
|
+
function removeFileOwnedGraphData(graph, filePath, removedEdges) {
|
|
3626
3654
|
const normalized = normalizeMapKey(filePath);
|
|
3627
|
-
const fileNodeId = `file:${normalized}`;
|
|
3628
|
-
const
|
|
3629
|
-
const
|
|
3630
|
-
for (const [id, node] of graph.nodes) {
|
|
3631
|
-
if (node.filePath !== normalized)
|
|
3632
|
-
continue;
|
|
3633
|
-
removedIds.add(id);
|
|
3634
|
-
if (node.kind === "symbol")
|
|
3635
|
-
removedSymbolIds.add(id);
|
|
3636
|
-
}
|
|
3655
|
+
const fileNodeId = graph.fileNodes.get(normalized) ?? `file:${normalized}`;
|
|
3656
|
+
const removedSymbolIds = new Set(graph.symbolNodesByFile.get(normalized));
|
|
3657
|
+
const removedIds = new Set(removedSymbolIds);
|
|
3637
3658
|
if (graph.nodes.has(fileNodeId))
|
|
3638
3659
|
removedIds.add(fileNodeId);
|
|
3660
|
+
const candidates = new Set();
|
|
3661
|
+
for (const id of removedIds) {
|
|
3662
|
+
for (const edge of graph.edgesByFrom.get(id) ?? []) {
|
|
3663
|
+
_rebuildCounters.removeOwnedEdgeVisits++;
|
|
3664
|
+
candidates.add(edge);
|
|
3665
|
+
}
|
|
3666
|
+
for (const edge of graph.edgesByTo.get(id) ?? []) {
|
|
3667
|
+
_rebuildCounters.removeOwnedEdgeVisits++;
|
|
3668
|
+
candidates.add(edge);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3639
3671
|
const preservedIncomingSymbolEdges = [];
|
|
3640
|
-
const
|
|
3641
|
-
|
|
3672
|
+
for (const edge of candidates) {
|
|
3673
|
+
// A cross-edge can be discovered from both changed files' adjacency
|
|
3674
|
+
// buckets. Preserve and remove it only once, matching eager unindexing
|
|
3675
|
+
// while the batch indexes remain live until the end.
|
|
3676
|
+
if (removedEdges.has(edge))
|
|
3677
|
+
continue;
|
|
3642
3678
|
const fromRemoved = removedIds.has(edge.from);
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
if (removedSymbolIds.has(edge.to)) {
|
|
3679
|
+
// Preserve importer edges to the stable file node id; the node is
|
|
3680
|
+
// re-added below.
|
|
3681
|
+
if (!fromRemoved && edge.to === fileNodeId)
|
|
3682
|
+
continue;
|
|
3683
|
+
if (!fromRemoved && removedSymbolIds.has(edge.to)) {
|
|
3649
3684
|
preservedIncomingSymbolEdges.push({ ...edge });
|
|
3650
|
-
droppedEdges.push(edge);
|
|
3651
|
-
return false;
|
|
3652
3685
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
return true;
|
|
3656
|
-
if (toRemoved)
|
|
3657
|
-
droppedEdges.push(edge);
|
|
3658
|
-
return !toRemoved;
|
|
3659
|
-
});
|
|
3660
|
-
// #2074: keep the adjacency indexes live instead of rebuilding them over the
|
|
3661
|
-
// whole graph afterwards. Only the dropped edges' own buckets are touched.
|
|
3662
|
-
// Unconditional: on an unindexed graph every bucket lookup simply misses.
|
|
3663
|
-
for (const edge of droppedEdges)
|
|
3664
|
-
unindexEdge(graph, edge);
|
|
3686
|
+
removedEdges.add(edge);
|
|
3687
|
+
}
|
|
3665
3688
|
for (const id of removedIds) {
|
|
3666
3689
|
const node = graph.nodes.get(id);
|
|
3667
3690
|
graph.nodes.delete(id);
|
|
@@ -3769,18 +3792,30 @@ async function addFileToGraph(graph, cwd, file, facts, ignoredIds, contentOverri
|
|
|
3769
3792
|
/**
|
|
3770
3793
|
* #2074 acceptance instrumentation. `restoreComparisons` counts edge-metadata
|
|
3771
3794
|
* stringifications inside `restoreValidIncomingEdges`; `importTargetEdgeScans`
|
|
3772
|
-
* counts edges visited by `importTargetsForFile
|
|
3773
|
-
*
|
|
3774
|
-
*
|
|
3775
|
-
*
|
|
3795
|
+
* counts edges visited by `importTargetsForFile`; `removeOwnedEdgeVisits`
|
|
3796
|
+
* counts edges visited while collecting a changed file's owned edges in
|
|
3797
|
+
* `removeFileOwnedGraphData`. Before this change all three grew with the whole
|
|
3798
|
+
* graph on every one-file rebuild, and `removeOwnedEdgeVisits` grew with
|
|
3799
|
+
* changedFiles x graph on a multi-file batch (one full `graph.edges` scan per
|
|
3800
|
+
* file). `removeOwnedEdgePositions` counts adjacency positions examined while
|
|
3801
|
+
* removing edges; batching keeps a high-fan-in bucket linear instead of
|
|
3802
|
+
* rescanning its prefix for every edge. They are the count-based signal the
|
|
3803
|
+
* issue asks for, because wall time on this hardware is IO-noisy (#1920).
|
|
3776
3804
|
*/
|
|
3777
|
-
const _rebuildCounters = {
|
|
3805
|
+
const _rebuildCounters = {
|
|
3806
|
+
restoreComparisons: 0,
|
|
3807
|
+
importTargetEdgeScans: 0,
|
|
3808
|
+
removeOwnedEdgeVisits: 0,
|
|
3809
|
+
removeOwnedEdgePositions: 0,
|
|
3810
|
+
};
|
|
3778
3811
|
export function _getReviewGraphRebuildCountersForTests() {
|
|
3779
3812
|
return { ..._rebuildCounters };
|
|
3780
3813
|
}
|
|
3781
3814
|
export function _resetReviewGraphRebuildCountersForTests() {
|
|
3782
3815
|
_rebuildCounters.restoreComparisons = 0;
|
|
3783
3816
|
_rebuildCounters.importTargetEdgeScans = 0;
|
|
3817
|
+
_rebuildCounters.removeOwnedEdgeVisits = 0;
|
|
3818
|
+
_rebuildCounters.removeOwnedEdgePositions = 0;
|
|
3784
3819
|
}
|
|
3785
3820
|
/**
|
|
3786
3821
|
* Identity of an edge for dedupe purposes, WITHIN one target bucket: the array
|
|
@@ -3893,10 +3928,18 @@ async function updateGraphFiles(graph, cwd, files, facts, ignoredIds) {
|
|
|
3893
3928
|
priorTargets: importTargetsForFile(graph, file),
|
|
3894
3929
|
}));
|
|
3895
3930
|
const preservedIncoming = [];
|
|
3931
|
+
// #2074: one shared drop set for the whole batch, compacted into graph.edges
|
|
3932
|
+
// exactly once below — not once per file, which made a multi-file rebuild
|
|
3933
|
+
// scan the edge array changedFiles times over.
|
|
3934
|
+
const removedEdges = new Set();
|
|
3896
3935
|
for (const file of files) {
|
|
3897
|
-
preservedIncoming.push(...removeFileOwnedGraphData(graph, file));
|
|
3936
|
+
preservedIncoming.push(...removeFileOwnedGraphData(graph, file, removedEdges));
|
|
3898
3937
|
await addFileToGraph(graph, cwd, file, facts, ignoredIds);
|
|
3899
3938
|
}
|
|
3939
|
+
if (removedEdges.size > 0) {
|
|
3940
|
+
unindexEdges(graph, removedEdges);
|
|
3941
|
+
graph.edges = graph.edges.filter((edge) => !removedEdges.has(edge));
|
|
3942
|
+
}
|
|
3900
3943
|
restoreValidIncomingEdges(graph, preservedIncoming);
|
|
3901
3944
|
resolveDeferredSymbolEdges(graph, false);
|
|
3902
3945
|
graph.changedSymbolsByFile.clear();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { recordDegradationOnce } from "../degradation-ledger.js";
|
|
2
|
+
import { isAbsoluteFilePath, normalizeMapKey } from "../path-utils.js";
|
|
1
3
|
import { computeImpactCascade as computeImpactCascadeImpl, computeTransitiveImpact as computeTransitiveImpactImpl, } from "./query.js";
|
|
2
4
|
import { buildOrUpdateGraph as buildOrUpdateGraphImpl, } from "./builder.js";
|
|
3
5
|
import { formatImpactCascade as formatImpactCascadeImpl } from "./format.js";
|
|
@@ -19,7 +21,57 @@ export function computeTransitiveImpact(graph, seedFile, options) {
|
|
|
19
21
|
return computeTransitiveImpactImpl(graph, seedFile, options);
|
|
20
22
|
}
|
|
21
23
|
export function recordEntitySnapshotDiff(facts, filePath, nextSnapshot) {
|
|
22
|
-
|
|
24
|
+
// #2477 round 2 (reviewer F1/F2): folding `cwd` into this key was REVERTED.
|
|
25
|
+
// The writer (`clients/dispatch/runners/tree-sitter.ts`) passes `ctx.cwd`,
|
|
26
|
+
// which `createDispatchContext` (`clients/dispatch/dispatcher.ts:324-326`)
|
|
27
|
+
// sets to the file's NEAREST LANGUAGE-ROOT (`resolveLanguageRootForFile`),
|
|
28
|
+
// while the reader (`upsertChangedSymbols`, `clients/review-graph/builder.ts`,
|
|
29
|
+
// reached via `computeCascadeForFile` → `buildOrUpdateGraph`) keys off the
|
|
30
|
+
// WORKSPACE cwd. In a monorepo/cargo-workspace/go-module layout those two
|
|
31
|
+
// cwd values diverge, so a folded `(cwd, path)` key the builder reader can
|
|
32
|
+
// never hit zeroed `graphChangedSymbolCount` on every build — reproduced:
|
|
33
|
+
// master returns `["alpha","beta"]`, the folded-key version returns `[]`
|
|
34
|
+
// — and made the cascade over-fan to every symbol (`query.ts:230-238`
|
|
35
|
+
// treats an empty `changedSymbols` as "no narrowing").
|
|
36
|
+
//
|
|
37
|
+
// The #2477 cross-project collision this was meant to close cannot reach
|
|
38
|
+
// this function in production: the sole writer always passes `ctx.filePath`,
|
|
39
|
+
// which `createDispatchContext` sets via `resolveRunnerPath` to an
|
|
40
|
+
// ALWAYS-ABSOLUTE path (the #2016 invariant, `dispatcher.ts:320,327,346`).
|
|
41
|
+
// A relative `filePath` here can only happen through a caller regression.
|
|
42
|
+
// Enforce that instead of re-keying: reject a non-absolute `filePath` with
|
|
43
|
+
// a visible, bounded ledger record (never silently, per AGENTS.md) and
|
|
44
|
+
// skip the diff rather than compute one under a key the real reader could
|
|
45
|
+
// never reach anyway. `recordDegradationOnce` (not a thrown error) is used
|
|
46
|
+
// because the sole call site's surrounding try/catch
|
|
47
|
+
// (`tree-sitter.ts` — "entity snapshot / blast-radius enrichment is
|
|
48
|
+
// best-effort") is a BARE, UNLOGGED catch: a thrown error there vanishes
|
|
49
|
+
// with no trace, which is strictly worse than a ledger record.
|
|
50
|
+
if (!isAbsoluteFilePath(filePath)) {
|
|
51
|
+
recordDegradationOnce({
|
|
52
|
+
kind: "review-graph-non-absolute-entity-path",
|
|
53
|
+
subject: filePath,
|
|
54
|
+
reason: "recordEntitySnapshotDiff requires an absolute filePath (refs #2477)",
|
|
55
|
+
});
|
|
56
|
+
return { added: [], removed: [], modified: [] };
|
|
57
|
+
}
|
|
58
|
+
// Normalize once at this boundary, then reuse the folded path for both
|
|
59
|
+
// per-file facts. An unnormalized write is a key the builder reader can never
|
|
60
|
+
// hit, and an unnormalized snapshot forks a second empty diff (#2355).
|
|
61
|
+
const normalizedFilePath = normalizeMapKey(filePath);
|
|
62
|
+
const snapshotKey = `${ENTITY_SNAPSHOT_PREFIX}${normalizedFilePath}`;
|
|
63
|
+
const changedSymbolsKey = `${CHANGED_SYMBOLS_PREFIX}${normalizedFilePath}`;
|
|
64
|
+
const stored = facts.getBoundedSessionFact(snapshotKey);
|
|
65
|
+
// An evicted snapshot is unknown, not empty. Diffing against an empty Map
|
|
66
|
+
// puts every entity in `added`, which reads downstream as "the whole file
|
|
67
|
+
// changed" and schedules a blast-radius run for a file that did not change.
|
|
68
|
+
// Re-seed the snapshot and report no diff instead (#2282 review F1).
|
|
69
|
+
if (stored === undefined && facts.wasBoundedSessionFactEvicted(snapshotKey)) {
|
|
70
|
+
facts.setBoundedSessionFact(snapshotKey, new Map(nextSnapshot));
|
|
71
|
+
facts.setBoundedSessionFact(changedSymbolsKey, []);
|
|
72
|
+
return { added: [], removed: [], modified: [] };
|
|
73
|
+
}
|
|
74
|
+
const prev = stored ?? new Map();
|
|
23
75
|
const added = [];
|
|
24
76
|
const removed = [];
|
|
25
77
|
const modified = [];
|
|
@@ -38,7 +90,7 @@ export function recordEntitySnapshotDiff(facts, filePath, nextSnapshot) {
|
|
|
38
90
|
.map((key) => key.split(":")[1])
|
|
39
91
|
.filter(Boolean)),
|
|
40
92
|
];
|
|
41
|
-
facts.
|
|
42
|
-
facts.
|
|
93
|
+
facts.setBoundedSessionFact(snapshotKey, new Map(nextSnapshot));
|
|
94
|
+
facts.setBoundedSessionFact(changedSymbolsKey, changedSymbols);
|
|
43
95
|
return { added, removed, modified };
|
|
44
96
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { BoundedFifoMap } from "../bounded-cache.js";
|
|
2
3
|
import { normalizeMapKey } from "../path-utils.js";
|
|
3
4
|
/**
|
|
4
5
|
* THE producer/consumer hash contract, in one place (#955 review): scanner
|
|
@@ -17,7 +18,10 @@ export function reviewGraphIrContentHash(content) {
|
|
|
17
18
|
// registry's lifetime is the scan-to-build window, not the process lifetime.
|
|
18
19
|
const MAX_ROOTS = 8;
|
|
19
20
|
const MAX_FILES_PER_ROOT = 20_000;
|
|
20
|
-
|
|
21
|
+
// Both levels are FIFO, matching the hand-rolled blocks these replaced: no
|
|
22
|
+
// read on either map refreshed recency before #2442, and the consume-once
|
|
23
|
+
// contract above means a read is normally a DELETE anyway.
|
|
24
|
+
const roots = new BoundedFifoMap(MAX_ROOTS);
|
|
21
25
|
let accepted = 0;
|
|
22
26
|
let rejected = 0;
|
|
23
27
|
function rootKey(cwd) {
|
|
@@ -33,23 +37,11 @@ export function publishReviewGraphFileIr(cwd, entry) {
|
|
|
33
37
|
const key = rootKey(cwd);
|
|
34
38
|
let files = roots.get(key);
|
|
35
39
|
if (!files) {
|
|
36
|
-
files = new
|
|
40
|
+
files = new BoundedFifoMap(MAX_FILES_PER_ROOT);
|
|
37
41
|
roots.set(key, files);
|
|
38
|
-
while (roots.size > MAX_ROOTS) {
|
|
39
|
-
const oldest = roots.keys().next().value;
|
|
40
|
-
if (oldest === undefined)
|
|
41
|
-
break;
|
|
42
|
-
roots.delete(oldest);
|
|
43
|
-
}
|
|
44
42
|
}
|
|
45
43
|
const fileKey = normalizeMapKey(entry.filePath);
|
|
46
|
-
files.delete(fileKey);
|
|
47
|
-
while (files.size >= MAX_FILES_PER_ROOT) {
|
|
48
|
-
const oldest = files.keys().next().value;
|
|
49
|
-
if (oldest === undefined)
|
|
50
|
-
break;
|
|
51
|
-
files.delete(oldest);
|
|
52
|
-
}
|
|
44
|
+
files.delete(fileKey); // republish is a refresh: it becomes the newest
|
|
53
45
|
files.set(fileKey, { ...entry, filePath: fileKey });
|
|
54
46
|
}
|
|
55
47
|
/**
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
+
import { hasCargoWorkspaceTable, readCargoDependencyNames, readCargoPackageName, readCargoWorkspaceMembers, } from "../cargo-manifest.js";
|
|
9
10
|
import { getProjectIgnoreMatcher, isExcludedDirName, } from "../file-utils.js";
|
|
10
11
|
import { normalizeMapKey } from "../path-utils.js";
|
|
11
12
|
import { getWorkspaceManifestMarkers } from "../workspace-topology.js";
|
|
@@ -52,8 +53,12 @@ function detectWorkspaceType(cwd) {
|
|
|
52
53
|
if (markers.hasCargoToml) {
|
|
53
54
|
try {
|
|
54
55
|
const content = fs.readFileSync(path.join(cwd, "Cargo.toml"), "utf-8");
|
|
55
|
-
|
|
56
|
+
// The shared presence check (#2498) — see its own doc comment in
|
|
57
|
+
// cargo-manifest.ts for why a bare `.includes`/regex reimplementation
|
|
58
|
+
// of this test is wrong (comment/sentinel handling).
|
|
59
|
+
if (hasCargoWorkspaceTable(content)) {
|
|
56
60
|
return "cargo";
|
|
61
|
+
}
|
|
57
62
|
}
|
|
58
63
|
catch { }
|
|
59
64
|
}
|
|
@@ -146,71 +151,6 @@ function extractYamlList(content, key) {
|
|
|
146
151
|
}
|
|
147
152
|
return values;
|
|
148
153
|
}
|
|
149
|
-
function extractTomlArray(content, key) {
|
|
150
|
-
const prefix = `${key}`;
|
|
151
|
-
let collecting = false;
|
|
152
|
-
let buffer = "";
|
|
153
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
154
|
-
const line = rawLine.split("#", 1)[0].trim();
|
|
155
|
-
if (!line)
|
|
156
|
-
continue;
|
|
157
|
-
if (!collecting) {
|
|
158
|
-
if (!line.startsWith(prefix))
|
|
159
|
-
continue;
|
|
160
|
-
const equalsIndex = line.indexOf("=");
|
|
161
|
-
if (equalsIndex === -1 || line.slice(0, equalsIndex).trim() !== key)
|
|
162
|
-
continue;
|
|
163
|
-
const afterEquals = line.slice(equalsIndex + 1).trim();
|
|
164
|
-
if (!afterEquals.startsWith("["))
|
|
165
|
-
continue;
|
|
166
|
-
collecting = true;
|
|
167
|
-
buffer += afterEquals.slice(1);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
buffer += `,${line}`;
|
|
171
|
-
}
|
|
172
|
-
const closeIndex = buffer.indexOf("]");
|
|
173
|
-
if (closeIndex !== -1) {
|
|
174
|
-
buffer = buffer.slice(0, closeIndex);
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return buffer
|
|
179
|
-
.split(",")
|
|
180
|
-
.map((s) => stripQuotes(s.trim()))
|
|
181
|
-
.filter(Boolean);
|
|
182
|
-
}
|
|
183
|
-
function extractTomlSection(content, section) {
|
|
184
|
-
const lines = [];
|
|
185
|
-
let inSection = false;
|
|
186
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
187
|
-
const trimmed = rawLine.trim();
|
|
188
|
-
if (trimmed === `[${section}]`) {
|
|
189
|
-
inSection = true;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (inSection && trimmed.startsWith("[") && trimmed.endsWith("]"))
|
|
193
|
-
break;
|
|
194
|
-
if (inSection)
|
|
195
|
-
lines.push(rawLine);
|
|
196
|
-
}
|
|
197
|
-
return lines;
|
|
198
|
-
}
|
|
199
|
-
function extractTomlString(content, key) {
|
|
200
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
201
|
-
const line = rawLine.split("#", 1)[0].trim();
|
|
202
|
-
const equalsIndex = line.indexOf("=");
|
|
203
|
-
if (equalsIndex === -1 || line.slice(0, equalsIndex).trim() !== key)
|
|
204
|
-
continue;
|
|
205
|
-
const value = line.slice(equalsIndex + 1).trim();
|
|
206
|
-
const quote = value[0];
|
|
207
|
-
if ((quote !== '"' && quote !== "'") || value.length < 2)
|
|
208
|
-
return undefined;
|
|
209
|
-
const endIndex = value.indexOf(quote, 1);
|
|
210
|
-
return endIndex === -1 ? undefined : value.slice(1, endIndex);
|
|
211
|
-
}
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
154
|
function moduleFromPackageJson(cwd, pkgRoot) {
|
|
215
155
|
const pkgJson = readJsonSafe(path.join(pkgRoot, "package.json"));
|
|
216
156
|
if (!pkgJson?.name)
|
|
@@ -250,7 +190,7 @@ function scanCargoModules(cwd) {
|
|
|
250
190
|
let members = [];
|
|
251
191
|
try {
|
|
252
192
|
const content = fs.readFileSync(path.join(cwd, "Cargo.toml"), "utf-8");
|
|
253
|
-
members =
|
|
193
|
+
members = readCargoWorkspaceMembers(content);
|
|
254
194
|
}
|
|
255
195
|
catch {
|
|
256
196
|
return [];
|
|
@@ -260,15 +200,11 @@ function scanCargoModules(cwd) {
|
|
|
260
200
|
const pkgRoot = path.resolve(cwd, member);
|
|
261
201
|
const memberToml = path.join(pkgRoot, "Cargo.toml");
|
|
262
202
|
let name = "";
|
|
263
|
-
|
|
203
|
+
let deps = [];
|
|
264
204
|
try {
|
|
265
205
|
const content = fs.readFileSync(memberToml, "utf-8");
|
|
266
|
-
name =
|
|
267
|
-
|
|
268
|
-
const depName = line.trim().match(/^([A-Za-z0-9_-]+)\s*=/);
|
|
269
|
-
if (depName)
|
|
270
|
-
deps.push(depName[1]);
|
|
271
|
-
}
|
|
206
|
+
name = readCargoPackageName(content) ?? "";
|
|
207
|
+
deps = readCargoDependencyNames(content);
|
|
272
208
|
}
|
|
273
209
|
catch {
|
|
274
210
|
continue;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { isTestMode } from "./env-utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
4
|
+
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
4
5
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
5
6
|
import { normalizeFilePath } from "./path-utils.js";
|
|
6
|
-
const REVIEW_GRAPH_LOG_DIR =
|
|
7
|
+
const REVIEW_GRAPH_LOG_DIR = getGlobalPiLensLogDir();
|
|
7
8
|
const REVIEW_GRAPH_LOG_FILE = path.join(REVIEW_GRAPH_LOG_DIR, "review-graph.log");
|
|
8
|
-
|
|
9
|
+
// Bounded like every other global sink (#2505) — see sessionstart-logger.
|
|
10
|
+
const writer = createNdjsonLogger({
|
|
11
|
+
filePath: REVIEW_GRAPH_LOG_FILE,
|
|
12
|
+
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|
|
13
|
+
backupPath: `${REVIEW_GRAPH_LOG_FILE}.1`,
|
|
14
|
+
});
|
|
9
15
|
/**
|
|
10
16
|
* Build the one canonical graph metadata shape used by build and persist logs.
|
|
11
17
|
* Counts come from the graph instance; source-file count may use the exact
|