@herbertgao/pi-extensions 2026.9.0 → 2026.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
- package/node_modules/@narumitw/pi-btw/package.json +2 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
- package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
- 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 +1072 -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 -9
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- 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 -9
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -6
- 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-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -9
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +3 -11
- 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 +40 -19
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -9
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- 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 +50 -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 +164 -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 +165 -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 +234 -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 -6
- 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-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +15 -5
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -44
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +27 -8
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
- 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 +383 -27
- 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 +4 -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 +1 -2
- 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 +14 -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 +6 -3
- 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 +1 -2
- 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 +1 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -3
- 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 -67
- 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/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -10
- 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/event-loop-monitor.js +2 -2
- 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-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-time.js +7 -2
- package/node_modules/pi-lens/dist/clients/file-utils.js +117 -10
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +385 -104
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +116 -22
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +6 -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/grammar-source.js +3 -3
- 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 +289 -59
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +110 -236
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
- package/node_modules/pi-lens/dist/clients/language-registry.js +599 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +165 -9
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +34 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +160 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +33 -41
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +77 -6
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -38
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +172 -33
- package/node_modules/pi-lens/dist/clients/lsp/config.js +484 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +18 -5
- package/node_modules/pi-lens/dist/clients/lsp/index.js +892 -96
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -177
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -18
- 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/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +14 -7
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.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/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +3 -5
- package/node_modules/pi-lens/dist/clients/module-report.js +21 -46
- 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 -24
- 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 +70 -28
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +5 -1
- 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 +451 -15
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- 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 +79 -18
- 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-changes.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
- 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/jscpd.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +28 -4
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +438 -122
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- 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 +16 -5
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +26 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +155 -114
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- 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 +19 -3
- 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 +184 -32
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +179 -36
- 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 -12
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- 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/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +7 -6
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +4 -4
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- 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 +244 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +92 -22
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +87 -100
- 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/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -6
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -9
- package/node_modules/pi-lens/dist/clients/word-index-store.js +23 -12
- package/node_modules/pi-lens/dist/clients/word-index.js +252 -47
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +4 -1
- package/node_modules/pi-lens/dist/index.js +65899 -55340
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -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 +96 -25
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +104 -86
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
- 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/globalconfig.md +20 -0
- package/node_modules/pi-lens/docs/language-coverage.md +2 -2
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- 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 +33 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +49 -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 +190 -0
- package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
- 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/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +18 -35
- 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/rules/typos/_typos.toml +4 -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/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- 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 +6 -6
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pi-hashline-edit-pro` anchor resolution (#2423 review round 1, finding F1).
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists
|
|
5
|
+
*
|
|
6
|
+
* `pi-hashline-edit-pro` addresses lines by a bare THREE-CHARACTER base62
|
|
7
|
+
* anchor — `"aB3"`, never `"42"`. Its schema says so in as many words
|
|
8
|
+
* (`src/payload-contract.ts`: *"Bare 3-char anchor only (e.g. \"aB3\")"*), and
|
|
9
|
+
* its parser accepts exactly `trimmed.length === ANCHOR_LEN && ALPH_RE.test()`
|
|
10
|
+
* (`src/hashline/parse.ts`). The first cut of the shape adapter in
|
|
11
|
+
* `clients/mutating-tool.ts` read those fields as DECIMAL line numbers, so
|
|
12
|
+
* against the real extension every anchor was "malformed" and the adapter
|
|
13
|
+
* blocked a working edit tool.
|
|
14
|
+
*
|
|
15
|
+
* This module reproduces the extension's own line-hash function so pi-lens can
|
|
16
|
+
* map an anchor back to a line, and does it from the extension's source rather
|
|
17
|
+
* than from a guess. Everything below is a port of
|
|
18
|
+
* `pi-hashline-edit-pro@8f027ca1c2a0796ccf1c15bd5a0d12db5d9fb9f0`
|
|
19
|
+
* (github.com/YuGiMob/pi-hashline-edit-pro, branch `master`):
|
|
20
|
+
*
|
|
21
|
+
* - `src/hashline/alphabet.ts` — `HASH_LEN = 3`, the 62-character alphabet
|
|
22
|
+
* `A-Za-z0-9`.
|
|
23
|
+
* - `src/hashline/hash.ts` — `hashSource` (strip `\r`, `trimEnd`, truncate to
|
|
24
|
+
* `MAX_HASH_SOURCE_BYTES = 500` UTF-8 bytes on a code-point boundary),
|
|
25
|
+
* `baseIdx = (xxh32(source) >>> 14) % 62**3`, and open-addressed collision
|
|
26
|
+
* probing with stride `62**2 + 62 + 1` over a used-index bitset.
|
|
27
|
+
* - `src/hashline/hasher.ts` — `xxh32` is `xxhash-wasm`'s `h32`, i.e. standard
|
|
28
|
+
* XXH32 with seed 0 over the UTF-8 bytes. Reimplemented here in ~40 lines of
|
|
29
|
+
* integer arithmetic rather than taking a runtime dependency (pi installs
|
|
30
|
+
* pi-lens with `npm install --omit=dev`, so a hashing dep would ship to every
|
|
31
|
+
* user for one adapter).
|
|
32
|
+
* - `src/hashline/resolve.ts` — `resAnchorFromMap`: an anchor resolves ONLY
|
|
33
|
+
* when it matches exactly one line. Zero matches is `not_found`, two or more
|
|
34
|
+
* is `ambiguous`, and the extension refuses both rather than guessing.
|
|
35
|
+
*
|
|
36
|
+
* `tests/clients/hashline-anchor.test.ts` pins the port against a vector table
|
|
37
|
+
* generated by RUNNING the upstream code with the real `xxhash-wasm`, so a
|
|
38
|
+
* drift in either direction is a red test rather than a silent misattribution.
|
|
39
|
+
*
|
|
40
|
+
* ## Why resolution is gated on UNIQUE CONTENT (review round 3, finding F1) —
|
|
41
|
+
* and why the gate is NOT a correctness proof (review round 4, finding F1)
|
|
42
|
+
*
|
|
43
|
+
* The live extension does not recompute hashes on every read. It persists a
|
|
44
|
+
* per-file snapshot (`src/hash-store.ts`) and, after every edit, carries a
|
|
45
|
+
* surviving line's old anchor forward (`mapStableHashes` in
|
|
46
|
+
* `src/hashline/hash.ts`) before persisting the result — so the anchors the
|
|
47
|
+
* agent sends back are STORE-CARRIED, not freshly computed. pi-lens has no
|
|
48
|
+
* access to that store and can only recompute with `_lineHashesPure`.
|
|
49
|
+
*
|
|
50
|
+
* Where a line's canonical content is unique in the file, the store's anchor
|
|
51
|
+
* for it and pi-lens's own recomputed anchor for it USUALLY agree — that is
|
|
52
|
+
* the case the gate below exists to hold. Where content is NOT unique — `}`,
|
|
53
|
+
* a blank line, `continue;`, a bare comment-close line — the anchor comes
|
|
54
|
+
* from probing, whose result depends on every line before it, so an edit
|
|
55
|
+
* anywhere upstream repermutes those anchors among the identical lines. A
|
|
56
|
+
* store-carried anchor then still matches EXACTLY ONE line under
|
|
57
|
+
* recomputation: a confident, unique, WRONG answer.
|
|
58
|
+
*
|
|
59
|
+
* Gating on "the matched line's content is unique" narrows that failure a
|
|
60
|
+
* long way, but it is not a proof, and review round 4 measured the two
|
|
61
|
+
* mechanisms that survive it:
|
|
62
|
+
*
|
|
63
|
+
* (a) The gate is ONE-SIDED: it checks the MATCHED line's content, never the
|
|
64
|
+
* anchor's own provenance. A probe-derived anchor that truly belongs to
|
|
65
|
+
* some duplicate-content line elsewhere can, by construction of the
|
|
66
|
+
* probe stride, land on the same anchor VALUE as a completely unrelated
|
|
67
|
+
* unique-content line's own base index. Measured case: a file with 25
|
|
68
|
+
* occurrences of a bare block-comment-close line — the store-carried
|
|
69
|
+
* anchor for one of them resolved to an unrelated unique-content line
|
|
70
|
+
* 1532 lines away, and the gate answered it, because that OTHER line's
|
|
71
|
+
* content is (correctly) unique. A unique answer is not evidence the
|
|
72
|
+
* anchor was ever meant for it.
|
|
73
|
+
* (b) Content uniqueness does not imply base-index uniqueness. `baseIdx =
|
|
74
|
+
* (xxh32(source) >>> 14) % 238328` has only 238,328 slots; over enough
|
|
75
|
+
* DISTINCT line contents in a real file, two different contents collide
|
|
76
|
+
* on the same base index and one of them is probed to a different slot
|
|
77
|
+
* — the identical open-addressing mechanism as a literal duplicate, just
|
|
78
|
+
* between lines whose content is each unique. Measured across 220 files:
|
|
79
|
+
* 194 of 74,386 unique-content lines carry a probe-derived anchor, not
|
|
80
|
+
* their own content's base index.
|
|
81
|
+
*
|
|
82
|
+
* So `resolveHashlineAnchor` answers when the matched line's canonical
|
|
83
|
+
* content occurs exactly once in the file — a gate that removes nearly all
|
|
84
|
+
* of the literal-duplicate case and neither (a) nor (b). Measured over 220
|
|
85
|
+
* files and five edit shapes (~550,000 anchors asked): roughly 2 anchors
|
|
86
|
+
* resolved to the wrong line, against ~67% of anchors answered at all. That
|
|
87
|
+
* residual is ACCEPTED (review round 4 orchestrator decision), not fixed
|
|
88
|
+
* here: closing it needs either the extension's own store, which pi-lens
|
|
89
|
+
* does not have, or refusing every anchor within probing distance of a
|
|
90
|
+
* collision, which costs far more coverage than the ~2-in-550,000 residual
|
|
91
|
+
* is worth. Everything the gate refuses is `content_not_unique`: unresolved,
|
|
92
|
+
* which is a report and never a block. pi-lens's uncertainty about a
|
|
93
|
+
* third-party tool's addressing is not evidence that the agent's edit is
|
|
94
|
+
* wrong. An unresolved anchor degrades to "mutation recognized, lines
|
|
95
|
+
* unknown" — the same state a host `edit` with an unknown schema reaches on
|
|
96
|
+
* master — so the file still gets its turn-state entry (the whole file as
|
|
97
|
+
* its modified range), its deferred format and its change-log receipt.
|
|
98
|
+
*
|
|
99
|
+
* This costs coverage: roughly a third of the lines in a typical TypeScript
|
|
100
|
+
* file are blank or a bare brace. Trading those for an almost-always-correct
|
|
101
|
+
* answer on the rest is the deliberate choice — a wrong line range is worse
|
|
102
|
+
* than no line range on average, because the guard acts on it, but "gated on
|
|
103
|
+
* unique content" is a reduction of the wrong-line rate, not its elimination.
|
|
104
|
+
*/
|
|
105
|
+
import * as nodeFs from "node:fs";
|
|
106
|
+
import { BoundedFifoMap } from "./bounded-cache.js";
|
|
107
|
+
/** `HASH_LEN` in `src/hashline/alphabet.ts`. */
|
|
108
|
+
const HASHLINE_ANCHOR_LENGTH = 3;
|
|
109
|
+
/** `ALPH` in `src/hashline/alphabet.ts`. Order is load-bearing: it is the digit set. */
|
|
110
|
+
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
111
|
+
/** `HASH_SPACE` — 62**3 = 238328 addressable anchors. */
|
|
112
|
+
const HASH_SPACE = ALPHABET.length ** HASHLINE_ANCHOR_LENGTH;
|
|
113
|
+
/** `HASH_PROBE_STRIDE` — 62**2 + 62 + 1, the open-addressing step. */
|
|
114
|
+
const HASH_PROBE_STRIDE = ALPHABET.length ** 2 + ALPHABET.length + 1;
|
|
115
|
+
/** `MAX_HASH_SOURCE_BYTES` in `src/constants.ts`. */
|
|
116
|
+
const MAX_HASH_SOURCE_BYTES = 500;
|
|
117
|
+
const BITSET_WORDS = Math.ceil(HASH_SPACE / 32);
|
|
118
|
+
const ANCHOR_TOKEN_RE = /^[A-Za-z0-9]{3}$/;
|
|
119
|
+
// --- XXH32 (seed 0), the algorithm behind `xxhash-wasm`'s `h32` -------------
|
|
120
|
+
const XXH_PRIME1 = 0x9e3779b1;
|
|
121
|
+
const XXH_PRIME2 = 0x85ebca77;
|
|
122
|
+
const XXH_PRIME3 = 0xc2b2ae3d;
|
|
123
|
+
const XXH_PRIME4 = 0x27d4eb2f;
|
|
124
|
+
const XXH_PRIME5 = 0x165667b1;
|
|
125
|
+
function rotl32(value, bits) {
|
|
126
|
+
return (value << bits) | (value >>> (32 - bits));
|
|
127
|
+
}
|
|
128
|
+
function xxhRound(acc, input) {
|
|
129
|
+
return (Math.imul(rotl32((acc + Math.imul(input, XXH_PRIME2)) | 0, 13), XXH_PRIME1) | 0);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Standard XXH32 with seed 0 over `bytes`. Returns an unsigned 32-bit integer,
|
|
133
|
+
* matching `xxh32()` in `src/hashline/hasher.ts`.
|
|
134
|
+
*/
|
|
135
|
+
export function xxh32Bytes(bytes) {
|
|
136
|
+
const length = bytes.length;
|
|
137
|
+
let index = 0;
|
|
138
|
+
let hash;
|
|
139
|
+
if (length >= 16) {
|
|
140
|
+
const limit = length - 16;
|
|
141
|
+
let v1 = (XXH_PRIME1 + XXH_PRIME2) | 0;
|
|
142
|
+
let v2 = XXH_PRIME2 | 0;
|
|
143
|
+
let v3 = 0;
|
|
144
|
+
let v4 = -XXH_PRIME1 | 0;
|
|
145
|
+
do {
|
|
146
|
+
v1 = xxhRound(v1, readU32(bytes, index));
|
|
147
|
+
v2 = xxhRound(v2, readU32(bytes, index + 4));
|
|
148
|
+
v3 = xxhRound(v3, readU32(bytes, index + 8));
|
|
149
|
+
v4 = xxhRound(v4, readU32(bytes, index + 12));
|
|
150
|
+
index += 16;
|
|
151
|
+
} while (index <= limit);
|
|
152
|
+
hash =
|
|
153
|
+
(rotl32(v1, 1) + rotl32(v2, 7) + rotl32(v3, 12) + rotl32(v4, 18)) | 0;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
hash = XXH_PRIME5 | 0;
|
|
157
|
+
}
|
|
158
|
+
hash = (hash + length) | 0;
|
|
159
|
+
while (index + 4 <= length) {
|
|
160
|
+
hash =
|
|
161
|
+
Math.imul(rotl32((hash + Math.imul(readU32(bytes, index), XXH_PRIME3)) | 0, 17), XXH_PRIME4) | 0;
|
|
162
|
+
index += 4;
|
|
163
|
+
}
|
|
164
|
+
while (index < length) {
|
|
165
|
+
hash =
|
|
166
|
+
Math.imul(rotl32((hash + Math.imul(bytes[index], XXH_PRIME5)) | 0, 11), XXH_PRIME1) | 0;
|
|
167
|
+
index += 1;
|
|
168
|
+
}
|
|
169
|
+
hash = (hash ^ (hash >>> 15)) | 0;
|
|
170
|
+
hash = Math.imul(hash, XXH_PRIME2) | 0;
|
|
171
|
+
hash = (hash ^ (hash >>> 13)) | 0;
|
|
172
|
+
hash = Math.imul(hash, XXH_PRIME3) | 0;
|
|
173
|
+
hash = (hash ^ (hash >>> 16)) | 0;
|
|
174
|
+
return hash >>> 0;
|
|
175
|
+
}
|
|
176
|
+
function readU32(bytes, offset) {
|
|
177
|
+
return (bytes[offset] |
|
|
178
|
+
(bytes[offset + 1] << 8) |
|
|
179
|
+
(bytes[offset + 2] << 16) |
|
|
180
|
+
(bytes[offset + 3] << 24) |
|
|
181
|
+
0);
|
|
182
|
+
}
|
|
183
|
+
// --- The line-hash function -------------------------------------------------
|
|
184
|
+
/** `canon` in `src/hashline/hash.ts`. */
|
|
185
|
+
function canon(line) {
|
|
186
|
+
return line.replace(/\r/g, "").trimEnd();
|
|
187
|
+
}
|
|
188
|
+
/** `splitLines` in `src/utils.ts`. An empty file is ONE empty line. */
|
|
189
|
+
function splitLines(text) {
|
|
190
|
+
if (text.length === 0)
|
|
191
|
+
return [""];
|
|
192
|
+
const lines = text.split("\n");
|
|
193
|
+
return text.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* `hashSource` in `src/hashline/hash.ts`, as bytes.
|
|
197
|
+
*
|
|
198
|
+
* Upstream truncates the STRING to 500 UTF-8 bytes by iterating code points
|
|
199
|
+
* (`truncateToBytes` in `src/utils.ts`) and then hashes its UTF-8 encoding.
|
|
200
|
+
* Cutting the encoded buffer back to the last non-continuation byte is the
|
|
201
|
+
* same operation, because a UTF-8 code point occupies contiguous bytes.
|
|
202
|
+
*/
|
|
203
|
+
function hashSourceBytes(line) {
|
|
204
|
+
const bytes = Buffer.from(canon(line), "utf8");
|
|
205
|
+
if (bytes.length <= MAX_HASH_SOURCE_BYTES)
|
|
206
|
+
return bytes;
|
|
207
|
+
let cut = MAX_HASH_SOURCE_BYTES;
|
|
208
|
+
while (cut > 0 && (bytes[cut] & 0xc0) === 0x80)
|
|
209
|
+
cut -= 1;
|
|
210
|
+
return bytes.subarray(0, cut);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The same `hashSource` value as a string — the key upstream itself compares
|
|
214
|
+
* lines by (`newByContent` / `removedByContent` in `mapStableHashes`). Two
|
|
215
|
+
* lines with the same key are indistinguishable to BOTH implementations, which
|
|
216
|
+
* is exactly the condition the uniqueness gate tests. Decoding the truncated
|
|
217
|
+
* buffer is lossless because the cut lands on a code-point boundary.
|
|
218
|
+
*/
|
|
219
|
+
function hashSourceKey(line) {
|
|
220
|
+
return Buffer.from(hashSourceBytes(line)).toString("utf8");
|
|
221
|
+
}
|
|
222
|
+
function indexToAnchor(index) {
|
|
223
|
+
let out = "";
|
|
224
|
+
let remaining = index;
|
|
225
|
+
for (let digit = 0; digit < HASHLINE_ANCHOR_LENGTH; digit += 1) {
|
|
226
|
+
out = ALPHABET[remaining % ALPHABET.length] + out;
|
|
227
|
+
remaining = Math.floor(remaining / ALPHABET.length);
|
|
228
|
+
}
|
|
229
|
+
return out;
|
|
230
|
+
}
|
|
231
|
+
function getBit(bits, index) {
|
|
232
|
+
return ((bits[index >>> 5] >>> (index & 31)) & 1) !== 0;
|
|
233
|
+
}
|
|
234
|
+
function setBit(bits, index) {
|
|
235
|
+
bits[index >>> 5] |= 1 << (index & 31);
|
|
236
|
+
}
|
|
237
|
+
function nextZeroBit(bits, start) {
|
|
238
|
+
let index = start % HASH_SPACE;
|
|
239
|
+
for (let step = 0; step < HASH_SPACE; step += 1) {
|
|
240
|
+
if (!getBit(bits, index))
|
|
241
|
+
return index;
|
|
242
|
+
index += HASH_PROBE_STRIDE;
|
|
243
|
+
if (index >= HASH_SPACE)
|
|
244
|
+
index -= HASH_SPACE;
|
|
245
|
+
}
|
|
246
|
+
// Upstream throws `[E_FILE_TOO_LARGE]` here. pi-lens is an observer, so it
|
|
247
|
+
// reports "cannot address this file" instead of failing the caller.
|
|
248
|
+
return -1;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* `_lineHashesPure` in `src/hashline/hash.ts`: one anchor per line of
|
|
252
|
+
* ALREADY-NORMALIZED content (BOM stripped, CRLF folded to LF).
|
|
253
|
+
*
|
|
254
|
+
* Returns `undefined` for a file with more lines than the anchor space can
|
|
255
|
+
* address, which is where upstream raises `[E_FILE_TOO_LARGE]`.
|
|
256
|
+
*/
|
|
257
|
+
export function computeHashlineAnchors(content) {
|
|
258
|
+
const lines = splitLines(content);
|
|
259
|
+
if (lines.length > HASH_SPACE)
|
|
260
|
+
return undefined;
|
|
261
|
+
const anchors = [];
|
|
262
|
+
anchors.length = lines.length;
|
|
263
|
+
const used = new Uint32Array(BITSET_WORDS);
|
|
264
|
+
let hint = 0;
|
|
265
|
+
const sourceCache = new Map();
|
|
266
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
267
|
+
const line = lines[i];
|
|
268
|
+
let baseIndex = sourceCache.get(line);
|
|
269
|
+
if (baseIndex === undefined) {
|
|
270
|
+
baseIndex = (xxh32Bytes(hashSourceBytes(line)) >>> 14) % HASH_SPACE;
|
|
271
|
+
sourceCache.set(line, baseIndex);
|
|
272
|
+
}
|
|
273
|
+
if (!getBit(used, baseIndex)) {
|
|
274
|
+
setBit(used, baseIndex);
|
|
275
|
+
hint = baseIndex + HASH_PROBE_STRIDE;
|
|
276
|
+
anchors[i] = indexToAnchor(baseIndex);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const probed = nextZeroBit(used, hint);
|
|
280
|
+
if (probed < 0)
|
|
281
|
+
return undefined;
|
|
282
|
+
setBit(used, probed);
|
|
283
|
+
hint = probed + HASH_PROBE_STRIDE;
|
|
284
|
+
anchors[i] = indexToAnchor(probed);
|
|
285
|
+
}
|
|
286
|
+
return anchors;
|
|
287
|
+
}
|
|
288
|
+
// --- Anchor tokens ----------------------------------------------------------
|
|
289
|
+
/**
|
|
290
|
+
* The anchor token an edit-pro request carries, or `undefined` when the value
|
|
291
|
+
* is not one. Mirrors `parseRef` in `src/hashline/parse.ts`: trim, then require
|
|
292
|
+
* exactly three characters from the alphabet. A decimal line number is NOT an
|
|
293
|
+
* anchor.
|
|
294
|
+
*/
|
|
295
|
+
export function normalizeHashlineAnchorToken(value) {
|
|
296
|
+
if (typeof value !== "string")
|
|
297
|
+
return undefined;
|
|
298
|
+
const trimmed = value.trim();
|
|
299
|
+
return ANCHOR_TOKEN_RE.test(trimmed) ? trimmed : undefined;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Files larger than this are not anchor-resolved. The extension itself caps at
|
|
303
|
+
* `MAX_HASH_LINES` (62**3 lines); this byte cap keeps the synchronous read on
|
|
304
|
+
* the tool_call path bounded regardless of line length.
|
|
305
|
+
*/
|
|
306
|
+
const MAX_ANCHOR_FILE_BYTES = 4 * 1024 * 1024;
|
|
307
|
+
/**
|
|
308
|
+
* Bounded memo. `classifyMutatingTool` runs several times per tool call and the
|
|
309
|
+
* same file is asked about repeatedly; without this every ask re-reads and
|
|
310
|
+
* re-hashes the file. Keyed by path and invalidated by mtime/size, so an edit
|
|
311
|
+
* between two asks is USUALLY not answered from the stale index.
|
|
312
|
+
*
|
|
313
|
+
* "Usually" (review round 4, finding F5): mtime/size alone is not sufficient.
|
|
314
|
+
* A same-size rewrite that lands within one filesystem mtime tick (mtime
|
|
315
|
+
* resolution is coarser than a millisecond on some platforms/filesystems)
|
|
316
|
+
* changes neither key, so the memo can serve stale content for a file that
|
|
317
|
+
* genuinely changed. The memo only ever needs to survive WITHIN one tool
|
|
318
|
+
* call — the repeated-ask case above — so `dropHashlineAnchorMemo` is called
|
|
319
|
+
* at the `tool_call` boundary in `runtime-tool-call.ts` (`handleToolCallImpl`
|
|
320
|
+
* entry) to bound its lifetime to that, rather than trusting mtime/size
|
|
321
|
+
* across calls.
|
|
322
|
+
*/
|
|
323
|
+
const ANCHOR_INDEX_CACHE_LIMIT = 8;
|
|
324
|
+
const ANCHOR_INDEX_CACHE = new BoundedFifoMap(ANCHOR_INDEX_CACHE_LIMIT);
|
|
325
|
+
/**
|
|
326
|
+
* Drop the memo. Called at the `tool_call` boundary in production
|
|
327
|
+
* (`runtime-tool-call.ts`) so a stale mtime/size match from a PRIOR tool call
|
|
328
|
+
* can never leak into this one (review round 4, finding F5), and as a test
|
|
329
|
+
* seam so a fixture rewrite is never answered from cache.
|
|
330
|
+
*/
|
|
331
|
+
export function dropHashlineAnchorMemo() {
|
|
332
|
+
ANCHOR_INDEX_CACHE.clear();
|
|
333
|
+
}
|
|
334
|
+
function buildAnchorIndex(filePath) {
|
|
335
|
+
let stats;
|
|
336
|
+
try {
|
|
337
|
+
stats = nodeFs.statSync(filePath);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return { failure: "file_unreadable" };
|
|
341
|
+
}
|
|
342
|
+
if (!stats.isFile())
|
|
343
|
+
return { failure: "file_unreadable" };
|
|
344
|
+
if (stats.size > MAX_ANCHOR_FILE_BYTES)
|
|
345
|
+
return { failure: "file_too_large" };
|
|
346
|
+
const cached = ANCHOR_INDEX_CACHE.get(filePath);
|
|
347
|
+
if (cached &&
|
|
348
|
+
cached.mtimeMs === stats.mtimeMs &&
|
|
349
|
+
cached.size === stats.size) {
|
|
350
|
+
return { index: cached.index, contentUnique: cached.contentUnique };
|
|
351
|
+
}
|
|
352
|
+
let raw;
|
|
353
|
+
try {
|
|
354
|
+
raw = nodeFs.readFileSync(filePath, "utf8");
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
return { failure: "file_unreadable" };
|
|
358
|
+
}
|
|
359
|
+
// `readNormFile` in the extension strips the BOM and folds line endings to
|
|
360
|
+
// LF before hashing, so the same normalization has to happen here.
|
|
361
|
+
const normalized = (raw.startsWith("") ? raw.slice(1) : raw)
|
|
362
|
+
.replace(/\r\n/g, "\n")
|
|
363
|
+
.replace(/\r/g, "\n");
|
|
364
|
+
const anchors = computeHashlineAnchors(normalized);
|
|
365
|
+
if (anchors === undefined)
|
|
366
|
+
return { failure: "file_too_large" };
|
|
367
|
+
const index = new Map();
|
|
368
|
+
for (let i = 0; i < anchors.length; i += 1) {
|
|
369
|
+
index.set(anchors[i], i + 1);
|
|
370
|
+
}
|
|
371
|
+
// Content multiplicity, keyed exactly as upstream keys it. A line whose key
|
|
372
|
+
// repeats is unanswerable: see the module header.
|
|
373
|
+
const lines = splitLines(normalized);
|
|
374
|
+
const keys = [];
|
|
375
|
+
keys.length = lines.length;
|
|
376
|
+
const keyCounts = new Map();
|
|
377
|
+
const keyCache = new Map();
|
|
378
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
379
|
+
const line = lines[i];
|
|
380
|
+
let key = keyCache.get(line);
|
|
381
|
+
if (key === undefined) {
|
|
382
|
+
key = hashSourceKey(line);
|
|
383
|
+
keyCache.set(line, key);
|
|
384
|
+
}
|
|
385
|
+
keys[i] = key;
|
|
386
|
+
keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
|
|
387
|
+
}
|
|
388
|
+
const contentUnique = keys.map((key) => keyCounts.get(key) === 1);
|
|
389
|
+
ANCHOR_INDEX_CACHE.set(filePath, {
|
|
390
|
+
mtimeMs: stats.mtimeMs,
|
|
391
|
+
size: stats.size,
|
|
392
|
+
index,
|
|
393
|
+
contentUnique,
|
|
394
|
+
});
|
|
395
|
+
return { index, contentUnique };
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Resolve one anchor token against the file's CURRENT content.
|
|
399
|
+
*
|
|
400
|
+
* TWO conditions, both required (review round 3, finding F1):
|
|
401
|
+
*
|
|
402
|
+
* 1. The anchor matches a line of the recomputed table — `resAnchorFromMap` in
|
|
403
|
+
* `src/hashline/resolve.ts` requires that much.
|
|
404
|
+
* 2. That line's canonical content occurs exactly once in the file. Without
|
|
405
|
+
* this, a store-carried anchor for one `}` resolves confidently to a
|
|
406
|
+
* DIFFERENT `}`, and the wrong range reaches the read guard. The module
|
|
407
|
+
* header has the measurement — including the residual this narrows but
|
|
408
|
+
* does not close (review round 4, finding F1).
|
|
409
|
+
*/
|
|
410
|
+
export function resolveHashlineAnchor(filePath, anchor) {
|
|
411
|
+
const token = normalizeHashlineAnchorToken(anchor);
|
|
412
|
+
if (token === undefined)
|
|
413
|
+
return { failure: "not_an_anchor" };
|
|
414
|
+
const built = buildAnchorIndex(filePath);
|
|
415
|
+
if ("failure" in built)
|
|
416
|
+
return { failure: built.failure };
|
|
417
|
+
const line = built.index.get(token);
|
|
418
|
+
if (line === undefined)
|
|
419
|
+
return { failure: "anchor_not_found" };
|
|
420
|
+
if (built.contentUnique[line - 1] !== true) {
|
|
421
|
+
return { failure: "content_not_unique" };
|
|
422
|
+
}
|
|
423
|
+
return { line };
|
|
424
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-hook wall-clock budgets, in ONE table (#2523).
|
|
3
|
+
*
|
|
4
|
+
* ## What a budget here means
|
|
5
|
+
*
|
|
6
|
+
* The maintainer contract behind #2523: only the write/edit `tool_result`
|
|
7
|
+
* path may block pi's host loop. `session_start`, `turn_end`, `agent_end`,
|
|
8
|
+
* `agent_settled` and read-only `tool_result` must either finish inside a
|
|
9
|
+
* small bound or deliver their work OFF-hook (`clients/deferred-lsp-work.ts`,
|
|
10
|
+
* `publishActionableWarningsReport` #2509, the test-runner `agent_settled`
|
|
11
|
+
* delivery #2366, the cascade deferred settle #450) — deliver, never drop.
|
|
12
|
+
*
|
|
13
|
+
* A number here is the TOTAL wall time the hook may spend, not a per-await
|
|
14
|
+
* allowance. `0` means the hook may not await anything at all: whatever it
|
|
15
|
+
* does must be synchronous bookkeeping or fire-and-forget with its own
|
|
16
|
+
* bound.
|
|
17
|
+
*
|
|
18
|
+
* The numbers are the contract's, not a measurement: they are the ceilings
|
|
19
|
+
* the maintainer set for what the host may be made to wait for. #2523's
|
|
20
|
+
* sweep measured today's reality against them — `turn_end` p50 3687 ms /
|
|
21
|
+
* p90 14246 ms against a 3000 ms budget, `agent_end` p90 10043 ms against
|
|
22
|
+
* 1000 ms — which is what makes this table a target rather than a
|
|
23
|
+
* description.
|
|
24
|
+
*
|
|
25
|
+
* ## Why a table, and why it is not wired yet
|
|
26
|
+
*
|
|
27
|
+
* Slice 1 of #2523 ships the enforcement PRIMITIVE (`bounded()` in
|
|
28
|
+
* `clients/deadline-utils.ts`) and the guard that makes a new unbounded hook
|
|
29
|
+
* await impossible to add (`tests/config/hook-await-bounds.test.ts`). It
|
|
30
|
+
* deliberately changes NO hook's behavior. This table exists now so the
|
|
31
|
+
* guard's exemption table can say which hook each unbounded await belongs to
|
|
32
|
+
* — a claim that is checked, since every exemption's `hook` field must be a
|
|
33
|
+
* key of {@link HOOK_WALL_BUDGET_MS}. Slice 2 (AC3-AC8) wires it into the
|
|
34
|
+
* outer `bounded()` around each registered handler.
|
|
35
|
+
*
|
|
36
|
+
* #1978's phase-budget watchdog is the general form of this idea ("every
|
|
37
|
+
* recorded duration gets a declared budget"). When it lands it must CONSUME
|
|
38
|
+
* this table for the hook family rather than declare a second set of hook
|
|
39
|
+
* numbers — AGENTS.md's single-source-of-truth rule, and #2523 AC7 says so
|
|
40
|
+
* explicitly.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Total wall-clock budget per hook, in milliseconds. `0` = may not await.
|
|
44
|
+
*
|
|
45
|
+
* `session_start` is the one VISIBLE budget: 5 s of startup is work the user
|
|
46
|
+
* is already watching a progress line for. Everything else is time the user
|
|
47
|
+
* did not ask to spend.
|
|
48
|
+
*/
|
|
49
|
+
export const HOOK_WALL_BUDGET_MS = Object.freeze({
|
|
50
|
+
/** Visible startup work; the user is watching a progress line. */
|
|
51
|
+
session_start: 5000,
|
|
52
|
+
/** Pure bookkeeping — nothing here may await (#2523 contract). */
|
|
53
|
+
turn_start: 0,
|
|
54
|
+
/** Measured p50 3687 ms / p90 14246 ms today. The gap IS the issue. */
|
|
55
|
+
turn_end: 3000,
|
|
56
|
+
/** Measured p90 10043 ms today, from one unbounded pipeline re-entry. */
|
|
57
|
+
agent_end: 1000,
|
|
58
|
+
/** The designated place for settled-time sweeps; the widest non-edit budget. */
|
|
59
|
+
agent_settled: 10000,
|
|
60
|
+
/** Read/Grep/Glob/Bash: must never await analyzer bootstrap (AC5). */
|
|
61
|
+
tool_result_read_only: 500,
|
|
62
|
+
/** The ONLY path the contract lets block the host, and only this long. */
|
|
63
|
+
tool_result_edit: 10000,
|
|
64
|
+
/** Teardown: spawning or awaiting here aborts libuv (#234). */
|
|
65
|
+
session_shutdown: 0,
|
|
66
|
+
/** Synchronous message contribution; the host is blocked on the answer. */
|
|
67
|
+
context: 0,
|
|
68
|
+
/** Fork bookkeeping; the session is being replaced under it. */
|
|
69
|
+
session_before_fork: 0,
|
|
70
|
+
});
|
|
71
|
+
/** Every declared hook family, for sweeps that must enumerate them. */
|
|
72
|
+
export const HOOK_BUDGET_KEYS = Object.keys(HOOK_WALL_BUDGET_MS);
|
|
73
|
+
/** Type guard for a string that names a declared hook family. */
|
|
74
|
+
export function isHookBudgetKey(value) {
|
|
75
|
+
return Object.hasOwn(HOOK_WALL_BUDGET_MS, value);
|
|
76
|
+
}
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* SOURCE OF TRUTH: `@earendil-works/pi-coding-agent`
|
|
7
7
|
* `dist/core/tools/edit-diff.js` -- `normalizeForFuzzyMatch`, `normalizeToLF`,
|
|
8
|
-
* `detectLineEnding`, `restoreLineEndings
|
|
8
|
+
* `detectLineEnding`, `restoreLineEndings`. As of 0.85.1 the BOM
|
|
9
|
+
* split/strip primitive (`stripBom`'s underlying `splitBom`) moved to
|
|
10
|
+
* `dist/utils/text.js`, imported by edit-diff.js rather than inlined there
|
|
11
|
+
* -- see the sync test's two-source read.
|
|
9
12
|
*
|
|
10
13
|
* The host SDK is a *type-only* dependency at runtime (pi installs extensions
|
|
11
14
|
* with `npm install --omit=dev`, so the SDK is not in `node_modules`). These
|
|
@@ -25,7 +28,7 @@
|
|
|
25
28
|
* floor in package.json and re-confirm the sync test when the host changes its
|
|
26
29
|
* normalization ladder.
|
|
27
30
|
*/
|
|
28
|
-
export const HOST_EDIT_DIFF_SDK_FLOOR = "0.
|
|
31
|
+
export const HOST_EDIT_DIFF_SDK_FLOOR = "0.85.1";
|
|
29
32
|
// Host normalizeForFuzzyMatch code-point sets (edit-diff.js). Exported so the
|
|
30
33
|
// sync test can assert the host source still encodes exactly these.
|
|
31
34
|
/** U+2018 U+2019 U+201A U+201B -> ' */
|
|
@@ -34,6 +34,6 @@ export function createDefaultHostPorts(overrides = {}) {
|
|
|
34
34
|
// annotated rather than inferred.
|
|
35
35
|
return Object.fromEntries(Object.entries(defaults).map(([group, value]) => [
|
|
36
36
|
group,
|
|
37
|
-
{ ...value, ...
|
|
37
|
+
{ ...value, ...overrides[group] },
|
|
38
38
|
]));
|
|
39
39
|
}
|