@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
|
@@ -160,6 +160,41 @@ export function resetSinkWriteFailures() {
|
|
|
160
160
|
for (const state of ndjsonGlobalState.writers.values())
|
|
161
161
|
state.writeFailures = 0;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Snapshot of mid-session rotations, one entry per sink that has rotated
|
|
165
|
+
* at least once (#2505). Pure in-memory read — no I/O — so a caller
|
|
166
|
+
* (`degradation-ledger.ts`) can fold this into a durable ledger entry the
|
|
167
|
+
* same way it folds `getSinkWriteFailures()`, without this module importing
|
|
168
|
+
* the ledger back (see `NdjsonWriterState.rotationCount`'s doc comment).
|
|
169
|
+
*/
|
|
170
|
+
export function getSinkRotations() {
|
|
171
|
+
if (!ndjsonGlobalState)
|
|
172
|
+
return [];
|
|
173
|
+
const result = [];
|
|
174
|
+
for (const state of ndjsonGlobalState.writers.values()) {
|
|
175
|
+
if (state.rotationCount > 0 || state.rotationFailures > 0) {
|
|
176
|
+
result.push({
|
|
177
|
+
file: state.file,
|
|
178
|
+
rotationCount: state.rotationCount,
|
|
179
|
+
failureCount: state.rotationFailures,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Session-boundary reset (catalog shape 17), same shape as
|
|
187
|
+
* `resetSinkWriteFailures`. Wired into `resetDegradationLedger()`.
|
|
188
|
+
*/
|
|
189
|
+
export function resetSinkRotations() {
|
|
190
|
+
if (!ndjsonGlobalState)
|
|
191
|
+
return;
|
|
192
|
+
for (const state of ndjsonGlobalState.writers.values()) {
|
|
193
|
+
state.rotationCount = 0;
|
|
194
|
+
state.rotationFailures = 0;
|
|
195
|
+
state.rotationRetryAfterMs = 0;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
163
198
|
function requireCurrentGlobalState() {
|
|
164
199
|
if (ndjsonGlobalState)
|
|
165
200
|
return ndjsonGlobalState;
|
|
@@ -191,10 +226,14 @@ function assertCompatibleWriterOptions(existing, maxBytes, backupPath) {
|
|
|
191
226
|
function writeQueueItemSync(state, item) {
|
|
192
227
|
if (item.kind === "truncate") {
|
|
193
228
|
fs.writeFileSync(state.file, "");
|
|
229
|
+
noteTruncated(state);
|
|
194
230
|
}
|
|
195
231
|
else {
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
// One line, so the batch cap has nothing to cap: the plan is only
|
|
233
|
+
// ever "rotate first if this line no longer fits".
|
|
234
|
+
const plan = planBoundedWrite(state, [item.line]);
|
|
235
|
+
fs.appendFileSync(state.file, plan.joined);
|
|
236
|
+
noteWriteSucceeded(state, plan.bytes);
|
|
198
237
|
}
|
|
199
238
|
}
|
|
200
239
|
/**
|
|
@@ -269,6 +308,20 @@ function createWriterState(file, maxBytes, backupPath) {
|
|
|
269
308
|
// A state adopted from a pre-#1970 module graph predates this field.
|
|
270
309
|
if (typeof existing.writeFailures !== "number")
|
|
271
310
|
existing.writeFailures = 0;
|
|
311
|
+
// A state adopted from a pre-#2505 module graph predates these fields.
|
|
312
|
+
// (`knownSize` itself needs no adoption shim: its type is
|
|
313
|
+
// `number | undefined`, so a value that is neither is unreachable —
|
|
314
|
+
// #2515 review S2.)
|
|
315
|
+
if (typeof existing.writesSinceStatSync !== "number")
|
|
316
|
+
existing.writesSinceStatSync = 0;
|
|
317
|
+
if (typeof existing.lastStatSyncMs !== "number")
|
|
318
|
+
existing.lastStatSyncMs = 0;
|
|
319
|
+
if (typeof existing.rotationCount !== "number")
|
|
320
|
+
existing.rotationCount = 0;
|
|
321
|
+
if (typeof existing.rotationFailures !== "number")
|
|
322
|
+
existing.rotationFailures = 0;
|
|
323
|
+
if (typeof existing.rotationRetryAfterMs !== "number")
|
|
324
|
+
existing.rotationRetryAfterMs = 0;
|
|
272
325
|
return existing;
|
|
273
326
|
}
|
|
274
327
|
const state = {};
|
|
@@ -282,6 +335,12 @@ function createWriterState(file, maxBytes, backupPath) {
|
|
|
282
335
|
state.ensuredDir = false;
|
|
283
336
|
state.exitFlusher = () => flushStateSync(state);
|
|
284
337
|
state.writeFailures = 0;
|
|
338
|
+
state.knownSize = undefined;
|
|
339
|
+
state.writesSinceStatSync = 0;
|
|
340
|
+
state.lastStatSyncMs = 0;
|
|
341
|
+
state.rotationCount = 0;
|
|
342
|
+
state.rotationFailures = 0;
|
|
343
|
+
state.rotationRetryAfterMs = 0;
|
|
285
344
|
globalState.writers.set(file, state);
|
|
286
345
|
registerWriter(state);
|
|
287
346
|
return state;
|
|
@@ -305,32 +364,191 @@ async function ensureDirAsync(state) {
|
|
|
305
364
|
// telemetry is best-effort
|
|
306
365
|
}
|
|
307
366
|
}
|
|
308
|
-
|
|
309
|
-
|
|
367
|
+
// Rotation-check cadence (#2505): the real `fs.statSync` reconciliation below
|
|
368
|
+
// is throttled to at most once per this many writes OR this many elapsed ms,
|
|
369
|
+
// whichever comes first — never once per individual write. Between
|
|
370
|
+
// reconciliations, `state.knownSize` (updated cheaply, no I/O, after every
|
|
371
|
+
// successful write) is trusted. A short window in both axes bounds how far a
|
|
372
|
+
// concurrent writer to the same global log (another pi-lens process sharing
|
|
373
|
+
// ~/.pi-lens) can drift `knownSize` from the real file before the next
|
|
374
|
+
// reconciliation self-heals it.
|
|
375
|
+
const ROTATION_STAT_RESYNC_WRITES = 25;
|
|
376
|
+
const ROTATION_STAT_RESYNC_MS = 2_000;
|
|
377
|
+
/** Reconcile `state.knownSize` with a real stat, gated by the cadence above. */
|
|
378
|
+
function syncKnownSizeIfDue(state, now) {
|
|
379
|
+
const due = state.knownSize === undefined ||
|
|
380
|
+
state.writesSinceStatSync >= ROTATION_STAT_RESYNC_WRITES ||
|
|
381
|
+
now - state.lastStatSyncMs >= ROTATION_STAT_RESYNC_MS;
|
|
382
|
+
if (!due)
|
|
310
383
|
return;
|
|
384
|
+
state.writesSinceStatSync = 0;
|
|
385
|
+
state.lastStatSyncMs = now;
|
|
386
|
+
try {
|
|
387
|
+
state.knownSize = fs.statSync(state.file).size;
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
// No file yet (first write) or a transient stat error — treat as
|
|
391
|
+
// empty; the next successful write/rotate re-establishes the truth.
|
|
392
|
+
state.knownSize = 0;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* A rotation attempt that FAILS (an unwritable backup path, or the Windows
|
|
397
|
+
* sharing violation another process holding the file open produces) must not
|
|
398
|
+
* be re-attempted by every subsequent write: that is a syscall storm that
|
|
399
|
+
* never succeeds. After a failure the next attempt is deferred by this many
|
|
400
|
+
* ms. The sink keeps writing during the window — dropping lines would be a
|
|
401
|
+
* worse trade than an oversized file — and the failure is reported through
|
|
402
|
+
* `getSinkRotations()` as `log-sink-rotate-failed`, so an unbounded log is
|
|
403
|
+
* visible in `pilens_health` instead of silent (#2505 review F2).
|
|
404
|
+
*/
|
|
405
|
+
const ROTATION_FAILURE_BACKOFF_MS = 10_000;
|
|
406
|
+
/**
|
|
407
|
+
* Move the active file aside, and report whether that actually happened.
|
|
408
|
+
*
|
|
409
|
+
* The in-memory size check that gets us here is deliberately cheap and
|
|
410
|
+
* therefore fallible: another process writing the SAME global log (a pi
|
|
411
|
+
* session and the warm MCP server share one global read-guard.log) moves the
|
|
412
|
+
* real file under us, so a cached size can be stale in either direction.
|
|
413
|
+
* Believing a stale-HIGH size is destructive, not merely wasteful — it
|
|
414
|
+
* force-removes the other process backup and renames that process freshly
|
|
415
|
+
* rotated (tiny) file on top of it, losing real log data. So the decision is
|
|
416
|
+
* CONFIRMED against a real stat here, on the rare path where a rotation is
|
|
417
|
+
* believed due, instead of on the hot path where every write would pay for
|
|
418
|
+
* it (#2505 review F4).
|
|
419
|
+
*/
|
|
420
|
+
function rotateNow(state, incomingBytes, now) {
|
|
421
|
+
if (state.maxBytes === undefined)
|
|
422
|
+
return false;
|
|
423
|
+
if (now < state.rotationRetryAfterMs)
|
|
424
|
+
return false;
|
|
425
|
+
let actualSize;
|
|
426
|
+
try {
|
|
427
|
+
actualSize = fs.statSync(state.file).size;
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
// No file yet — nothing to rotate, and the next write creates it.
|
|
431
|
+
state.knownSize = 0;
|
|
432
|
+
state.writesSinceStatSync = 0;
|
|
433
|
+
state.lastStatSyncMs = now;
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
state.knownSize = actualSize;
|
|
437
|
+
state.writesSinceStatSync = 0;
|
|
438
|
+
state.lastStatSyncMs = now;
|
|
439
|
+
// The real file disagrees with the cached belief: nothing to do. This is
|
|
440
|
+
// the cross-process case above, and declining here is exactly what
|
|
441
|
+
// preserves the other process backup.
|
|
442
|
+
if (actualSize === 0)
|
|
443
|
+
return false;
|
|
444
|
+
if (actualSize + incomingBytes < state.maxBytes)
|
|
445
|
+
return false;
|
|
446
|
+
const backup = state.backupPath ?? `${state.file}.1`;
|
|
311
447
|
try {
|
|
312
|
-
const size = fs.statSync(state.file).size;
|
|
313
|
-
if (size < state.maxBytes)
|
|
314
|
-
return;
|
|
315
|
-
const backup = state.backupPath ?? `${state.file}.1`;
|
|
316
448
|
runBestEffort(() => fs.rmSync(backup, { force: true }));
|
|
317
449
|
fs.renameSync(state.file, backup);
|
|
318
450
|
}
|
|
319
451
|
catch {
|
|
320
|
-
//
|
|
452
|
+
// The rename failed and the file is still there at `actualSize` (which
|
|
453
|
+
// `knownSize` now holds — no guessing, the stat above just confirmed
|
|
454
|
+
// it). Counted rather than swallowed so `log-sink-rotate-failed` can
|
|
455
|
+
// surface an unbounded sink, and backed off so the next write does not
|
|
456
|
+
// re-run the same failing syscall.
|
|
457
|
+
state.rotationFailures += 1;
|
|
458
|
+
state.rotationRetryAfterMs = now + ROTATION_FAILURE_BACKOFF_MS;
|
|
459
|
+
return false;
|
|
321
460
|
}
|
|
461
|
+
state.knownSize = 0;
|
|
462
|
+
state.rotationCount += 1;
|
|
463
|
+
state.rotationRetryAfterMs = 0;
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
function takeAll(lines) {
|
|
467
|
+
const joined = lines.join("");
|
|
468
|
+
return { count: lines.length, joined, bytes: Buffer.byteLength(joined) };
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Decide what this append writes, rotating first when the file already on
|
|
472
|
+
* disk leaves no room. Two things have to hold, and the pre-#2505 write path
|
|
473
|
+
* did neither:
|
|
474
|
+
*
|
|
475
|
+
* 1. The bound is checked against the size this write PRODUCES, not only
|
|
476
|
+
* the size already on disk — otherwise the write that crosses the bound
|
|
477
|
+
* is always allowed through and only the NEXT one rotates (one write
|
|
478
|
+
* late), which on a long-lived or idle-tailed session (the warm MCP
|
|
479
|
+
* server never re-runs the session-start sweep in `log-cleanup.ts`) may
|
|
480
|
+
* be a long time or never.
|
|
481
|
+
* 2. The batch ITSELF is capped at the bound. Rotation only ever moves
|
|
482
|
+
* PRE-EXISTING content aside, so an oversized batch written whole into
|
|
483
|
+
* an empty (or just-rotated) file lands past the bound with nothing left
|
|
484
|
+
* to rotate away: a 500-line drain against a 1KB bound landed at ~107KB,
|
|
485
|
+
* 106x over, with zero rotations. Capping makes the drain loop come back
|
|
486
|
+
* around for the remainder instead.
|
|
487
|
+
*
|
|
488
|
+
* A single NDJSON line is atomic and cannot be split, so at least one line
|
|
489
|
+
* is always taken: the enforced invariant is "the bound plus at most one
|
|
490
|
+
* line", never "the bound plus an unbounded batch".
|
|
491
|
+
*/
|
|
492
|
+
function planBoundedWrite(state, lines) {
|
|
493
|
+
if (state.maxBytes === undefined || lines.length === 0)
|
|
494
|
+
return takeAll(lines);
|
|
495
|
+
const now = Date.now();
|
|
496
|
+
syncKnownSizeIfDue(state, now);
|
|
497
|
+
state.writesSinceStatSync += 1;
|
|
498
|
+
const firstBytes = Buffer.byteLength(lines[0] ?? "");
|
|
499
|
+
let known = state.knownSize ?? 0;
|
|
500
|
+
if (known > 0 && known + firstBytes >= state.maxBytes) {
|
|
501
|
+
rotateNow(state, firstBytes, now);
|
|
502
|
+
known = state.knownSize ?? 0;
|
|
503
|
+
if (known > 0 && known + firstBytes >= state.maxBytes) {
|
|
504
|
+
// Rotation is unavailable (a failing rename, or its backoff window).
|
|
505
|
+
// Capping cannot bound a file that cannot be rotated — it would only
|
|
506
|
+
// shred each drain into one append per line for the rest of the
|
|
507
|
+
// window. Write the whole batch; the reason the bound is not holding
|
|
508
|
+
// is reported through `log-sink-rotate-failed`.
|
|
509
|
+
return takeAll(lines);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let bytes = 0;
|
|
513
|
+
let count = 0;
|
|
514
|
+
for (const line of lines) {
|
|
515
|
+
const lineBytes = Buffer.byteLength(line);
|
|
516
|
+
if (count > 0 && known + bytes + lineBytes >= state.maxBytes)
|
|
517
|
+
break;
|
|
518
|
+
bytes += lineBytes;
|
|
519
|
+
count += 1;
|
|
520
|
+
}
|
|
521
|
+
if (count === lines.length)
|
|
522
|
+
return takeAll(lines);
|
|
523
|
+
return { count, joined: lines.slice(0, count).join(""), bytes };
|
|
524
|
+
}
|
|
525
|
+
/** Cheap in-memory bookkeeping after a write actually lands (#2505). */
|
|
526
|
+
function noteWriteSucceeded(state, bytes) {
|
|
527
|
+
if (state.maxBytes === undefined)
|
|
528
|
+
return;
|
|
529
|
+
state.knownSize = (state.knownSize ?? 0) + bytes;
|
|
530
|
+
}
|
|
531
|
+
/** The file is now definitively empty — no guessing needed (#2505). */
|
|
532
|
+
function noteTruncated(state) {
|
|
533
|
+
if (state.maxBytes === undefined)
|
|
534
|
+
return;
|
|
535
|
+
state.knownSize = 0;
|
|
536
|
+
state.writesSinceStatSync = 0;
|
|
537
|
+
state.lastStatSyncMs = Date.now();
|
|
322
538
|
}
|
|
323
539
|
async function writeQueueItemAsync(state, item) {
|
|
324
540
|
if (item.kind === "truncate") {
|
|
325
541
|
await fs.promises.writeFile(state.file, "");
|
|
542
|
+
noteTruncated(state);
|
|
326
543
|
}
|
|
327
544
|
else {
|
|
328
545
|
// Rotation is deliberately synchronous here. This function is only
|
|
329
546
|
// reached from the already-deferred drain, and keeping stat/rm/rename
|
|
330
547
|
// in one synchronous section prevents flushSync from racing a late
|
|
331
548
|
// async rename after it has written new data.
|
|
332
|
-
|
|
333
|
-
await fs.promises.appendFile(state.file,
|
|
549
|
+
const plan = planBoundedWrite(state, [item.line]);
|
|
550
|
+
await fs.promises.appendFile(state.file, plan.joined);
|
|
551
|
+
noteWriteSucceeded(state, plan.bytes);
|
|
334
552
|
}
|
|
335
553
|
}
|
|
336
554
|
/** See `applyQueueItemSync`'s doc comment for the reopen-and-retry-once shape. */
|
|
@@ -363,20 +581,29 @@ async function drainLoop(state) {
|
|
|
363
581
|
? state.queue.findIndex((queued) => queued.kind === "truncate")
|
|
364
582
|
: 0;
|
|
365
583
|
const pendingEnd = truncateIndex === -1 ? state.queue.length : truncateIndex;
|
|
366
|
-
const
|
|
584
|
+
const offered = item.kind === "truncate" ? [item] : state.queue.slice(0, pendingEnd);
|
|
585
|
+
// Rotation and the batch cap are decided synchronously, BEFORE this
|
|
586
|
+
// batch is published as in-flight: rotation stays inside the already-
|
|
587
|
+
// deferred drain (only the append remains async), so no awaited
|
|
588
|
+
// rotation step can run after flushSync has written to the active file.
|
|
589
|
+
const plan = item.kind === "truncate"
|
|
590
|
+
? null
|
|
591
|
+
: planBoundedWrite(state, offered.map((queued) => queued.line));
|
|
592
|
+
// Only the lines the plan actually takes belong to THIS write. A batch
|
|
593
|
+
// that would itself cross `maxBytes` is cut at the bound and the loop
|
|
594
|
+
// comes back around for the remainder (#2505) — writing it whole would
|
|
595
|
+
// sail past a bound that rotation cannot recover, because rotation only
|
|
596
|
+
// moves pre-existing content aside.
|
|
597
|
+
const pending = plan === null ? offered : offered.slice(0, plan.count);
|
|
367
598
|
state.inFlightBatch = pending;
|
|
368
599
|
const writeBatch = async () => {
|
|
369
|
-
if (
|
|
600
|
+
if (plan === null) {
|
|
370
601
|
await fs.promises.writeFile(state.file, "");
|
|
602
|
+
noteTruncated(state);
|
|
371
603
|
}
|
|
372
604
|
else {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
// flushSync has written to the active file.
|
|
376
|
-
rotateIfNeeded(state);
|
|
377
|
-
await fs.promises.appendFile(state.file, pending
|
|
378
|
-
.map((queued) => queued.line)
|
|
379
|
-
.join(""));
|
|
605
|
+
await fs.promises.appendFile(state.file, plan.joined);
|
|
606
|
+
noteWriteSucceeded(state, plan.bytes);
|
|
380
607
|
}
|
|
381
608
|
};
|
|
382
609
|
try {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What pi-lens ALREADY tracks, and where an observed change is replayed to
|
|
3
|
+
* (#2430).
|
|
4
|
+
*
|
|
5
|
+
* The observational net has to answer one question in three places — the
|
|
6
|
+
* `tool_call` pre-snapshot, the `tool_result` diff, and the `agent_settled`
|
|
7
|
+
* sweep — and the answer must be the same set every time, or a file drifts in
|
|
8
|
+
* and out of coverage depending on which seam asked. This module is that single
|
|
9
|
+
* source: the read-guard's read/write set, the widget's diagnostic files, and
|
|
10
|
+
* the documents open on any live language server rooted at the workspace.
|
|
11
|
+
*
|
|
12
|
+
* There is deliberately NO workspace walk here. The whole point of #2430 item 3
|
|
13
|
+
* is that the turn-boundary net costs a bounded number of stats over files
|
|
14
|
+
* pi-lens has already seen; a walk would make the settle boundary pay for the
|
|
15
|
+
* project's size on every run.
|
|
16
|
+
*
|
|
17
|
+
* The replay recorder is the mutation bridge. Nothing in this module writes
|
|
18
|
+
* bookkeeping itself: an observed change goes through exactly the seam an
|
|
19
|
+
* in-process producer uses, so the read-guard stamp, the `turn-state.json`
|
|
20
|
+
* entry, the attributed change-log receipt and the deferred format at
|
|
21
|
+
* `agent_settled` are the SAME code path a `write` takes.
|
|
22
|
+
*/
|
|
23
|
+
import { getLSPService } from "./lsp/index.js";
|
|
24
|
+
import { getMutationBridge } from "./mutation-bridge.js";
|
|
25
|
+
import { getFileDiagnosticSummaries } from "./widget-state.js";
|
|
26
|
+
/**
|
|
27
|
+
* Files pi-lens has read, written, diagnosed, or opened on a language server.
|
|
28
|
+
*
|
|
29
|
+
* Order is deliberate and the cap is applied ACROSS the whole union: the
|
|
30
|
+
* read-guard set is the most direct evidence the agent is working on a file,
|
|
31
|
+
* so it is added first and a truncating cap never drops it in favour of a
|
|
32
|
+
* cascade neighbour some server happened to open.
|
|
33
|
+
*/
|
|
34
|
+
export function collectTrackedPaths(args) {
|
|
35
|
+
const tracked = new Set();
|
|
36
|
+
const add = (paths) => {
|
|
37
|
+
for (const filePath of paths) {
|
|
38
|
+
if (tracked.size >= args.limit)
|
|
39
|
+
return;
|
|
40
|
+
if (typeof filePath === "string" && filePath.length > 0)
|
|
41
|
+
tracked.add(filePath);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
add(args.readGuard?.getTrackedPaths?.() ?? []);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// A guard mid-eviction contributes nothing; the sweep stays advisory.
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
add(getFileDiagnosticSummaries().map((summary) => summary.filePath));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Widget state is display state; it must never break the net.
|
|
55
|
+
}
|
|
56
|
+
if (args.cwd) {
|
|
57
|
+
try {
|
|
58
|
+
add(getLSPService().getOpenDocumentPaths(args.cwd));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// No pool, or a client torn down mid-enumeration.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [...tracked];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read-guard stored per-line hashes for a file, newest record first.
|
|
68
|
+
*
|
|
69
|
+
* This is the "content diff against the read-guard's stored content" #2430
|
|
70
|
+
* asks for: #505 already keeps an FNV-1a hash per line for every read, so a
|
|
71
|
+
* post-mutation re-hash names exactly the lines that changed without pi-lens
|
|
72
|
+
* having to have kept the bytes.
|
|
73
|
+
*/
|
|
74
|
+
export function storedLineHashesFor(readGuard, filePath) {
|
|
75
|
+
try {
|
|
76
|
+
const history = readGuard?.getReadHistory?.(filePath) ?? [];
|
|
77
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
78
|
+
const hashes = history[index]?.lineHashes;
|
|
79
|
+
if (hashes && Object.keys(hashes).length > 0)
|
|
80
|
+
return hashes;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// No history is the same answer as no hashes: derive no ranges.
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Replay an observed change through the mutation bridge.
|
|
90
|
+
*
|
|
91
|
+
* Returns `false` when the bridge is not mounted (pi-lens not activated, or a
|
|
92
|
+
* unit test that never registered one) or when the bridge itself declined the
|
|
93
|
+
* entry — out of scope, ignored, or a bookkeeping failure. A `false` is a real
|
|
94
|
+
* answer the caller acts on: it does NOT count as evidence the tool was clean.
|
|
95
|
+
*/
|
|
96
|
+
export function replayThroughMutationBridge(entry) {
|
|
97
|
+
const bridge = getMutationBridge();
|
|
98
|
+
if (!bridge)
|
|
99
|
+
return false;
|
|
100
|
+
return bridge.recordMutation(entry);
|
|
101
|
+
}
|