@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
|
@@ -176,7 +176,10 @@ const FORMATTER_POLICY_BY_EXTENSION = new Map([
|
|
|
176
176
|
{
|
|
177
177
|
formatterNames: ["prettier"],
|
|
178
178
|
defaultFormatter: "prettier",
|
|
179
|
-
|
|
179
|
+
// HTML commonly carries template markers ({{JS}} embeds, Handlebars,
|
|
180
|
+
// Helm-adjacent Go templates). Prettier reinterprets them as code and
|
|
181
|
+
// corrupts them. Opt in via project prettier config; do not run by default.
|
|
182
|
+
defaultWhenUnconfigured: false,
|
|
180
183
|
gate: "smart-default",
|
|
181
184
|
},
|
|
182
185
|
],
|
|
@@ -185,7 +188,7 @@ const FORMATTER_POLICY_BY_EXTENSION = new Map([
|
|
|
185
188
|
{
|
|
186
189
|
formatterNames: ["prettier"],
|
|
187
190
|
defaultFormatter: "prettier",
|
|
188
|
-
defaultWhenUnconfigured:
|
|
191
|
+
defaultWhenUnconfigured: false,
|
|
189
192
|
gate: "smart-default",
|
|
190
193
|
},
|
|
191
194
|
],
|
|
@@ -194,7 +197,10 @@ const FORMATTER_POLICY_BY_EXTENSION = new Map([
|
|
|
194
197
|
{
|
|
195
198
|
formatterNames: ["prettier"],
|
|
196
199
|
defaultFormatter: "prettier",
|
|
197
|
-
|
|
200
|
+
// YAML commonly carries template markers (Helm `{{ .Values.x }}`,
|
|
201
|
+
// Ansible `{{ var }}`). Prettier splits `{{` into `{ {` and reindents
|
|
202
|
+
// embedded templates. Opt in via project prettier config.
|
|
203
|
+
defaultWhenUnconfigured: false,
|
|
198
204
|
gate: "smart-default",
|
|
199
205
|
},
|
|
200
206
|
],
|
|
@@ -203,7 +209,7 @@ const FORMATTER_POLICY_BY_EXTENSION = new Map([
|
|
|
203
209
|
{
|
|
204
210
|
formatterNames: ["prettier"],
|
|
205
211
|
defaultFormatter: "prettier",
|
|
206
|
-
defaultWhenUnconfigured:
|
|
212
|
+
defaultWhenUnconfigured: false,
|
|
207
213
|
gate: "smart-default",
|
|
208
214
|
},
|
|
209
215
|
],
|
|
@@ -750,7 +756,7 @@ const FORMATTER_POLICY_BY_FILENAME = new Map(TERRAGRUNT_FILENAMES.map((name) =>
|
|
|
750
756
|
// lines don't fall at the head of the pre-existing lookup-table duplication and
|
|
751
757
|
// get counted as new duplicated code. Bindings are live, so position is safe.
|
|
752
758
|
export { AUTO_INSTALLABLE_DEFAULT_FORMATTERS, FORMATTER_POLICY_BY_EXTENSION, FORMATTER_POLICY_BY_FILENAME, };
|
|
753
|
-
|
|
759
|
+
function getFormatterPolicyForExtension(ext) {
|
|
754
760
|
return FORMATTER_POLICY_BY_EXTENSION.get(ext.toLowerCase());
|
|
755
761
|
}
|
|
756
762
|
export function getFormatterPolicyForFile(filePath) {
|
|
@@ -2236,8 +2242,14 @@ export function _getSpotlessGradleReadCountForTests() {
|
|
|
2236
2242
|
* executable Gradle source. This is deliberately a small lexical pre-pass,
|
|
2237
2243
|
* not a Groovy/Kotlin parser. In particular, statically disabled constructs
|
|
2238
2244
|
* such as `if (false) { ktlint() }` remain a documented false-positive.
|
|
2245
|
+
*
|
|
2246
|
+
* Exported (with `gradleBlockRanges` below) so `clients/gradle-ktfmt-
|
|
2247
|
+
* style.ts` (#2468) reuses this exact pre-pass for the `ktfmt { }` extension
|
|
2248
|
+
* block instead of a second hand-rolled Gradle lexer — the same
|
|
2249
|
+
* single-source-of-truth reuse `clients/cargo-manifest.ts` did for TOML
|
|
2250
|
+
* parsing (#2466).
|
|
2239
2251
|
*/
|
|
2240
|
-
function stripGradleCommentsAndStrings(source) {
|
|
2252
|
+
export function stripGradleCommentsAndStrings(source) {
|
|
2241
2253
|
let result = "";
|
|
2242
2254
|
let state = "code";
|
|
2243
2255
|
let quote = "";
|
|
@@ -2303,24 +2315,82 @@ function stripGradleCommentsAndStrings(source) {
|
|
|
2303
2315
|
}
|
|
2304
2316
|
return result;
|
|
2305
2317
|
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2318
|
+
const GRADLE_IDENTIFIER_CHAR = /[A-Za-z0-9_$]/;
|
|
2319
|
+
/**
|
|
2320
|
+
* The identifier immediately preceding a `{`, or `""`. Mirrors the
|
|
2321
|
+
* `\b<name>\s*\{` shape the named lookup used before it became a filter over
|
|
2322
|
+
* this scanner: whitespace (including newlines) between the name and the
|
|
2323
|
+
* brace is skipped, and `.` ends the identifier so `com.ncorti.ktfmt { }`
|
|
2324
|
+
* still reads as `ktfmt` while `something_ktfmt { }` does not.
|
|
2325
|
+
*/
|
|
2326
|
+
function gradleBlockNameBefore(source, open) {
|
|
2327
|
+
let index = open - 1;
|
|
2328
|
+
while (index >= 0 && /\s/.test(source[index]))
|
|
2329
|
+
index -= 1;
|
|
2330
|
+
const end = index + 1;
|
|
2331
|
+
while (index >= 0 && GRADLE_IDENTIFIER_CHAR.test(source[index]))
|
|
2332
|
+
index -= 1;
|
|
2333
|
+
return source.slice(index + 1, end);
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Body ranges of EVERY brace pair in `source`, in source order, each labelled
|
|
2337
|
+
* with the identifier that introduced it.
|
|
2338
|
+
*
|
|
2339
|
+
* This is the repo's only Gradle brace scanner: `namedGradleBlockRanges` and
|
|
2340
|
+
* `namedGradleBlockBodies` are filters over it. Scanning every pair — rather
|
|
2341
|
+
* than only the pairs whose name a caller thought to ask about — is what lets
|
|
2342
|
+
* `clients/gradle-ktfmt-style.ts` (#2468 review round 3) answer "what block
|
|
2343
|
+
* ENCLOSES this one?" instead of only "is it inside the one name I checked?".
|
|
2344
|
+
* The distinction is load-bearing there: `configure(subprojects.filter { … })
|
|
2345
|
+
* { ktfmt { } }`, `project(":app") { ktfmt { } }` and a convention-plugin
|
|
2346
|
+
* wrapper all have to be recognized as *some* enclosing scope the resolver
|
|
2347
|
+
* cannot evaluate, so it can fail closed rather than treat the block as
|
|
2348
|
+
* top-level.
|
|
2349
|
+
*
|
|
2350
|
+
* Unbalanced braces are best-effort: an unmatched `}` is ignored and an
|
|
2351
|
+
* unclosed `{` yields no range, exactly as the previous per-name depth scan
|
|
2352
|
+
* behaved.
|
|
2353
|
+
*/
|
|
2354
|
+
export function gradleBlockRanges(source) {
|
|
2355
|
+
const ranges = [];
|
|
2356
|
+
const opens = [];
|
|
2357
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
2358
|
+
const char = source[index];
|
|
2359
|
+
if (char === "{") {
|
|
2360
|
+
opens.push(index);
|
|
2361
|
+
continue;
|
|
2321
2362
|
}
|
|
2363
|
+
if (char !== "}")
|
|
2364
|
+
continue;
|
|
2365
|
+
const open = opens.pop();
|
|
2366
|
+
if (open === undefined)
|
|
2367
|
+
continue;
|
|
2368
|
+
ranges.push({
|
|
2369
|
+
name: gradleBlockNameBefore(source, open),
|
|
2370
|
+
start: open + 1,
|
|
2371
|
+
end: index,
|
|
2372
|
+
});
|
|
2322
2373
|
}
|
|
2323
|
-
|
|
2374
|
+
// Pairs are completed innermost-first; callers want source order.
|
|
2375
|
+
return ranges.sort((a, b) => a.start - b.start);
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Body ranges of every `<name> { ... }` block, in source order.
|
|
2379
|
+
*
|
|
2380
|
+
* The offsets exist so a caller can ask where a block sits RELATIVE to
|
|
2381
|
+
* another one — `clients/gradle-ktfmt-style.ts` (#2468) needs to know which
|
|
2382
|
+
* block encloses a `ktfmt { }` one, because Gradle's `subprojects { }`
|
|
2383
|
+
* applies it to the children and NOT to the project declaring it, while
|
|
2384
|
+
* `allprojects { }` applies to both. Containment of two ranges is the only
|
|
2385
|
+
* reliable way to answer that; matching body TEXT would collide whenever two
|
|
2386
|
+
* blocks happen to have identical bodies. `namedGradleBlockBodies` stays the
|
|
2387
|
+
* ergonomic form for callers that only need the text.
|
|
2388
|
+
*/
|
|
2389
|
+
function namedGradleBlockRanges(source, name) {
|
|
2390
|
+
return gradleBlockRanges(source).filter((range) => range.name === name);
|
|
2391
|
+
}
|
|
2392
|
+
function namedGradleBlockBodies(source, name) {
|
|
2393
|
+
return namedGradleBlockRanges(source, name).map((range) => source.slice(range.start, range.end));
|
|
2324
2394
|
}
|
|
2325
2395
|
/**
|
|
2326
2396
|
* Resolve the formatter elected by a Spotless `kotlin { ... }` block.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
15
15
|
import * as crypto from "node:crypto";
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
|
+
import { BoundedFifoMap } from "./bounded-cache.js";
|
|
17
18
|
import { normalizeFilePath } from "./path-utils.js";
|
|
18
19
|
const TREE_RETIREMENT_GRACE_MICROTASKS = 4;
|
|
19
20
|
export const TREE_CACHE_COUNTER_KEYS = [
|
|
@@ -82,17 +83,23 @@ export function deriveScanTreeCacheCapacity(fileCount, currentMaxSize) {
|
|
|
82
83
|
return Math.max(currentMaxSize, target);
|
|
83
84
|
}
|
|
84
85
|
export class TreeCache {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// BoundedFifoMap, not BoundedLruCache, even though eviction here IS true
|
|
87
|
+
// LRU: recency is refreshed by this class's own explicit `delete`+`set`
|
|
88
|
+
// touch on a VALIDATED hit (see `get`), and only there. A `get` that
|
|
89
|
+
// promoted on every read would also promote entries `get` goes on to
|
|
90
|
+
// reject (content-hash mismatch returns null without removing), silently
|
|
91
|
+
// changing which entry eviction targets (#2442 review F7).
|
|
92
|
+
cache = new BoundedFifoMap(TREE_CACHE_DEFAULT_MAX_SIZE);
|
|
93
|
+
recentlyEvicted;
|
|
88
94
|
evictionHistoryMax;
|
|
89
95
|
debug;
|
|
90
96
|
counters = createTreeCacheCounters();
|
|
91
97
|
counterObserver;
|
|
92
98
|
treeErrorObserver;
|
|
93
99
|
constructor(maxSize = TREE_CACHE_DEFAULT_MAX_SIZE, debug = false, evictionHistoryMax = 4096, counterObserver, treeErrorObserver) {
|
|
94
|
-
this.
|
|
100
|
+
this.cache.setMaxEntries(Math.max(1, Math.floor(maxSize)));
|
|
95
101
|
this.evictionHistoryMax = Math.max(1, Math.floor(evictionHistoryMax));
|
|
102
|
+
this.recentlyEvicted = new BoundedFifoMap(this.evictionHistoryMax);
|
|
96
103
|
this.counterObserver = counterObserver;
|
|
97
104
|
this.treeErrorObserver = treeErrorObserver;
|
|
98
105
|
this.debug = debug
|
|
@@ -109,7 +116,7 @@ export class TreeCache {
|
|
|
109
116
|
}
|
|
110
117
|
/** Current capacity ceiling (entry count, not bytes — see class docstring). */
|
|
111
118
|
getMaxSize() {
|
|
112
|
-
return this.
|
|
119
|
+
return this.cache.getMaxEntries();
|
|
113
120
|
}
|
|
114
121
|
/**
|
|
115
122
|
* Resize the capacity ceiling (#1715). Growing only changes the bound —
|
|
@@ -120,17 +127,24 @@ export class TreeCache {
|
|
|
120
127
|
* entries can never record a hit.
|
|
121
128
|
*/
|
|
122
129
|
setMaxSize(n) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
this.retireEvicted(this.cache.setMaxEntries(Math.max(1, Math.floor(n))));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Book-keep entries the bounded map dropped: ghost history, the eviction
|
|
134
|
+
* counter, and the WASM-heap tree retirement (#417/#890) that made this
|
|
135
|
+
* cache's eviction side-effect-coupled in the first place. One
|
|
136
|
+
* implementation, shared by the two paths that can overflow
|
|
137
|
+
* ({@link setMaxSize} and {@link set}) — the shape `BoundedFifoMap.set`'s
|
|
138
|
+
* `[key, value]` return exists to enable (#2442 review F7).
|
|
139
|
+
*/
|
|
140
|
+
retireEvicted(evicted) {
|
|
141
|
+
for (const [key, cached] of evicted) {
|
|
142
|
+
this.rememberEviction(key, cached);
|
|
132
143
|
this.recordCounter("evictions");
|
|
133
|
-
|
|
144
|
+
// The bounded map already removed the entry, so retire the tree
|
|
145
|
+
// directly rather than through removeEntry's re-lookup.
|
|
146
|
+
this.retireTree(cached.tree);
|
|
147
|
+
this.debug(`Evicted: ${key}`);
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
150
|
/**
|
|
@@ -175,14 +189,9 @@ export class TreeCache {
|
|
|
175
189
|
}
|
|
176
190
|
rememberEviction(key, cached) {
|
|
177
191
|
this.recentlyEvicted.delete(key);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.recentlyEvicted.delete(oldestKey);
|
|
182
|
-
this.recordCounter("ghostHistoryDrops");
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
this.recentlyEvicted.set(key, cached.contentHash);
|
|
192
|
+
const dropped = this.recentlyEvicted.set(key, cached.contentHash);
|
|
193
|
+
if (dropped.length > 0)
|
|
194
|
+
this.recordCounter("ghostHistoryDrops");
|
|
186
195
|
}
|
|
187
196
|
/**
|
|
188
197
|
* Generate hash for file content
|
|
@@ -298,20 +307,11 @@ export class TreeCache {
|
|
|
298
307
|
this.recordCounter("replacements");
|
|
299
308
|
this.removeEntry(key);
|
|
300
309
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (firstKey) {
|
|
307
|
-
const evicted = this.cache.get(firstKey);
|
|
308
|
-
if (evicted)
|
|
309
|
-
this.rememberEviction(firstKey, evicted);
|
|
310
|
-
this.recordCounter("evictions");
|
|
311
|
-
this.removeEntry(firstKey);
|
|
312
|
-
this.debug(`Evicted: ${firstKey}`);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
310
|
+
// Overflow eviction itself now happens on the `this.cache.set` below —
|
|
311
|
+
// the bounded map evicts the least-recently-used entry (this class's
|
|
312
|
+
// explicit delete+set touch on a validated hit makes insertion order
|
|
313
|
+
// recency order, #890) and hands back the dropped [key, tree] pair for
|
|
314
|
+
// `retireEvicted` to free.
|
|
315
315
|
let mtime = 0;
|
|
316
316
|
try {
|
|
317
317
|
mtime = fs.statSync(filePath).mtimeMs;
|
|
@@ -320,14 +320,14 @@ export class TreeCache {
|
|
|
320
320
|
// File deleted between parse and cache — cache with mtime=0;
|
|
321
321
|
// next get() will miss on mtime check and re-parse
|
|
322
322
|
}
|
|
323
|
-
this.cache.set(key, {
|
|
323
|
+
this.retireEvicted(this.cache.set(key, {
|
|
324
324
|
tree,
|
|
325
325
|
contentHash,
|
|
326
326
|
languageId,
|
|
327
327
|
fileSize: Buffer.byteLength(content, "utf8"),
|
|
328
328
|
lineCount: content.split("\n").length,
|
|
329
329
|
lastModified: mtime,
|
|
330
|
-
});
|
|
330
|
+
}));
|
|
331
331
|
this.debug(`Cached: ${filePath} (${content.split("\n").length} lines)`);
|
|
332
332
|
}
|
|
333
333
|
/**
|
|
@@ -355,7 +355,7 @@ export class TreeCache {
|
|
|
355
355
|
return {
|
|
356
356
|
...this.counters,
|
|
357
357
|
size: this.cache.size,
|
|
358
|
-
maxSize: this.
|
|
358
|
+
maxSize: this.cache.getMaxEntries(),
|
|
359
359
|
totalLines,
|
|
360
360
|
totalBytes,
|
|
361
361
|
misses: this.counters.lookups - this.counters.hits,
|
|
@@ -20,17 +20,20 @@ import * as fs from "node:fs";
|
|
|
20
20
|
import { createRequire } from "node:module";
|
|
21
21
|
import * as path from "node:path";
|
|
22
22
|
import { getDegradationLedgerGeneration, incrementDegradationCount, recordDegradation, recordDegradationOnce, } from "./degradation-ledger.js";
|
|
23
|
+
import { BoundedFifoMap } from "./bounded-cache.js";
|
|
23
24
|
import { loadWebTreeSitter } from "./deps/web-tree-sitter.js";
|
|
24
25
|
import { transientRetryDelayMs } from "./dispatch/runners/utils/availability-policy.js";
|
|
25
26
|
import { getProjectIgnoreMatcher, isExcludedDirName } from "./file-utils.js";
|
|
26
27
|
import { downloadGrammarDetailed, fileHasWasmMagic, grammarBlockReason, grammarFileSha256, isVendoredGrammar, LANGUAGE_TO_GRAMMAR, pinnedGrammarHash, vendoredGrammarRefusal, vendoredGrammarsDir, } from "./grammar-source.js";
|
|
27
28
|
import { resolvePackagePath } from "./package-root.js";
|
|
28
29
|
import { assertInstallAllowed, getProjectTrustGeneration, } from "./project-trust.js";
|
|
30
|
+
import { escapeRegExp } from "./string-utils.js";
|
|
29
31
|
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
30
32
|
import { notifyUserDegradation } from "./user-notify.js";
|
|
31
33
|
const _require = createRequire(import.meta.url);
|
|
32
34
|
import { createTreeCacheCounters, deriveScanTreeCacheCapacity, TreeCache, } from "./tree-sitter-cache.js";
|
|
33
35
|
import { TreeSitterNavigator } from "./tree-sitter-navigator.js";
|
|
36
|
+
import { isProvenSqlAlchemySessionReceiver, isSafePsycopgIdentifierComposition, isSqlAlchemyEntityQueryArgument, isSqlAlchemyStatementArgument, PYTHON_SQLALCHEMY_RECEIVER_NAMES, PYTHON_SQLALCHEMY_STATEMENT_BUILDERS, } from "./python-provenance.js";
|
|
34
37
|
import { TreeSitterQueryLoader, } from "./tree-sitter-query-loader.js";
|
|
35
38
|
// Hard cap on a single structural-search file walk. Bounds a misrooted scan so
|
|
36
39
|
// it can't enumerate an unbounded tree synchronously before result collection
|
|
@@ -46,6 +49,14 @@ const QUERY_BATCH_MAX_LOAD_FAILURES = 3;
|
|
|
46
49
|
// must not stall the batched query walk; the filter fails open when this cap
|
|
47
50
|
// is reached so unrelated diagnostics and the current match are preserved.
|
|
48
51
|
const NO_NESTED_ANCHOR_VISIT_CAP = 10_000;
|
|
52
|
+
// The execute-style Python DB-API/ORM methods this rule treats as SQL sinks.
|
|
53
|
+
// Hoisted so the post-filter does not rebuild the set per match.
|
|
54
|
+
const PYTHON_SQL_SINK_METHODS = new Set([
|
|
55
|
+
"execute",
|
|
56
|
+
"executemany",
|
|
57
|
+
"query",
|
|
58
|
+
"raw",
|
|
59
|
+
]);
|
|
49
60
|
const NOT_PARSED = { parsed: false };
|
|
50
61
|
function createParserCounters() {
|
|
51
62
|
return {
|
|
@@ -69,7 +80,7 @@ function grammarFileStamp(filePath) {
|
|
|
69
80
|
return undefined;
|
|
70
81
|
}
|
|
71
82
|
}
|
|
72
|
-
|
|
83
|
+
function isTreeSitterWasmAbortError(error) {
|
|
73
84
|
const message = error instanceof Error ? error.message : String(error);
|
|
74
85
|
return message.includes("Aborted") || message.includes("abort()");
|
|
75
86
|
}
|
|
@@ -269,12 +280,19 @@ export class TreeSitterClient {
|
|
|
269
280
|
ParserClass = null;
|
|
270
281
|
// biome-ignore lint/suspicious/noExplicitAny: Language loader from module
|
|
271
282
|
LanguageLoader = null;
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
/** Combined multi-rule queries by language + rule-set identity (null = don't retry). */
|
|
275
|
-
queryBatchCache = new Map();
|
|
283
|
+
// Declared BEFORE the two caches below: a static read by an instance field
|
|
284
|
+
// initializer must already be initialized (TS2729).
|
|
276
285
|
static QUERY_CACHE_MAX_ENTRIES = 256;
|
|
277
286
|
static QUERY_BATCH_CACHE_MAX_ENTRIES = 256;
|
|
287
|
+
// biome-ignore lint/suspicious/noExplicitAny: Compiled query cache by language+pattern hash
|
|
288
|
+
// BoundedFifoMap, not BoundedLruCache: recency here is refreshed by this
|
|
289
|
+
// class's own explicit delete+set on both the read and the write path (see
|
|
290
|
+
// cacheQuery and the two lookup sites) — exactly the raw-`Map` discipline
|
|
291
|
+
// the FIFO map documents. A get() that promoted on its own would change
|
|
292
|
+
// which entry eviction targets (#2442 review F5/F7).
|
|
293
|
+
queryCache = new BoundedFifoMap(TreeSitterClient.QUERY_CACHE_MAX_ENTRIES);
|
|
294
|
+
/** Combined multi-rule queries by language + rule-set identity (null = don't retry). */
|
|
295
|
+
queryBatchCache = new BoundedFifoMap(TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES);
|
|
278
296
|
queryCacheCap() {
|
|
279
297
|
const value = Number.parseInt(process.env.PI_LENS_TREE_SITTER_QUERY_CACHE_CAP ?? "", 10);
|
|
280
298
|
return Number.isSafeInteger(value) && value > 0
|
|
@@ -287,27 +305,25 @@ export class TreeSitterClient {
|
|
|
287
305
|
? value
|
|
288
306
|
: TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES;
|
|
289
307
|
}
|
|
308
|
+
// biome-ignore lint/suspicious/noExplicitAny: compiled query objects
|
|
290
309
|
cacheQuery(key, value) {
|
|
291
|
-
this.queryCache.delete(key);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
this.queryCache.delete(key); // write-refresh: this write is the newest
|
|
311
|
+
// The cap is env-readable, so re-apply it before every write — a raised
|
|
312
|
+
// or lowered ceiling has to take effect on the next insert, not on the
|
|
313
|
+
// next restart. Both calls hand back the dropped [key, value] pairs so
|
|
314
|
+
// the evicted compiled query's native handle is still freed (#2442
|
|
315
|
+
// review F5/F7 — this is why set() returns the VALUE, not just the key).
|
|
316
|
+
const evicted = this.queryCache.setMaxEntries(this.queryCacheCap());
|
|
317
|
+
evicted.push(...this.queryCache.set(key, value));
|
|
318
|
+
for (const [, dropped] of evicted)
|
|
319
|
+
dropped?.query?.delete?.();
|
|
300
320
|
}
|
|
301
321
|
cacheQueryBatch(key, value) {
|
|
302
322
|
this.queryBatchCache.delete(key);
|
|
303
|
-
this.queryBatchCache.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
break;
|
|
308
|
-
this.queryBatchCache.delete(oldest[0]);
|
|
309
|
-
oldest[1]?.query?.delete?.();
|
|
310
|
-
}
|
|
323
|
+
const evicted = this.queryBatchCache.setMaxEntries(this.queryBatchCacheCap());
|
|
324
|
+
evicted.push(...this.queryBatchCache.set(key, value));
|
|
325
|
+
for (const [, dropped] of evicted)
|
|
326
|
+
dropped?.query?.delete?.();
|
|
311
327
|
}
|
|
312
328
|
/** Consecutive grammar-load failures per batch key — bounds load retries (#889). */
|
|
313
329
|
queryBatchLoadFailures = new Map();
|
|
@@ -1448,7 +1464,8 @@ export class TreeSitterClient {
|
|
|
1448
1464
|
const perQuery = new Map();
|
|
1449
1465
|
await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
|
|
1450
1466
|
try {
|
|
1451
|
-
|
|
1467
|
+
const rootNode = tree.rootNode;
|
|
1468
|
+
for (const match of batch.query.matches(rootNode)) {
|
|
1452
1469
|
const owner = batch.ownerOfPattern[match.patternIndex];
|
|
1453
1470
|
if (owner === undefined)
|
|
1454
1471
|
continue;
|
|
@@ -1465,7 +1482,7 @@ export class TreeSitterClient {
|
|
|
1465
1482
|
if (!this.evaluatePredicates(batch.query, match))
|
|
1466
1483
|
continue;
|
|
1467
1484
|
if (entry.postFilter &&
|
|
1468
|
-
!this.applyPostFilter(entry.postFilter, entry.postFilterParams, captures,
|
|
1485
|
+
!this.applyPostFilter(entry.postFilter, entry.postFilterParams, captures, rootNode)) {
|
|
1469
1486
|
continue;
|
|
1470
1487
|
}
|
|
1471
1488
|
if (match.captures.length === 0)
|
|
@@ -1836,14 +1853,16 @@ export class TreeSitterClient {
|
|
|
1836
1853
|
}
|
|
1837
1854
|
return false;
|
|
1838
1855
|
}
|
|
1856
|
+
/**
|
|
1857
|
+
* SQLAlchemy sessions are conventionally bound to one of a handful of
|
|
1858
|
+
* receiver names. Structural provenance (`python-provenance.ts`) proves the
|
|
1859
|
+
* annotated case; this name check keeps the far more common unannotated
|
|
1860
|
+
* `session.execute(stmt)` quiet, as it has been since the exemption was
|
|
1861
|
+
* added. Removing it regresses every codebase that never annotates.
|
|
1862
|
+
*/
|
|
1839
1863
|
isLikelySqlAlchemyReceiver(text) {
|
|
1840
1864
|
const tail = text.split(".").pop() ?? text;
|
|
1841
|
-
return
|
|
1842
|
-
"session",
|
|
1843
|
-
"db_session",
|
|
1844
|
-
"async_session",
|
|
1845
|
-
"sync_session",
|
|
1846
|
-
]).has(tail.toLowerCase());
|
|
1865
|
+
return PYTHON_SQLALCHEMY_RECEIVER_NAMES.has(tail.toLowerCase());
|
|
1847
1866
|
}
|
|
1848
1867
|
/**
|
|
1849
1868
|
* The body statements of a `switch_case`, in order. A switch_case's named
|
|
@@ -2397,12 +2416,20 @@ export class TreeSitterClient {
|
|
|
2397
2416
|
return null;
|
|
2398
2417
|
return object.text;
|
|
2399
2418
|
}
|
|
2419
|
+
/**
|
|
2420
|
+
* `session.execute(select(...))` and friends pass a statement OBJECT, not a
|
|
2421
|
+
* SQL string: parameterized by construction, and far too noisy as blockers.
|
|
2422
|
+
*/
|
|
2400
2423
|
isSafeSqlAlchemyExpressionCall(node) {
|
|
2401
2424
|
if (node.type !== "call")
|
|
2402
2425
|
return false;
|
|
2403
2426
|
const callee = node.children?.[0]?.text ?? "";
|
|
2404
2427
|
const expression = node.text;
|
|
2405
|
-
|
|
2428
|
+
for (const name of PYTHON_SQLALCHEMY_STATEMENT_BUILDERS) {
|
|
2429
|
+
if (callee === name || expression.startsWith(`${name}(`))
|
|
2430
|
+
return true;
|
|
2431
|
+
}
|
|
2432
|
+
return false;
|
|
2406
2433
|
}
|
|
2407
2434
|
/**
|
|
2408
2435
|
* Post-filter predicate: returns true if the match should be kept, false to skip.
|
|
@@ -2469,15 +2496,6 @@ export class TreeSitterClient {
|
|
|
2469
2496
|
}
|
|
2470
2497
|
return slots;
|
|
2471
2498
|
}
|
|
2472
|
-
/**
|
|
2473
|
-
* Escape a string for safe interpolation into a `RegExp` source —
|
|
2474
|
-
* needed anywhere an identifier's raw text is combined with regex
|
|
2475
|
-
* metacharacters like `\b` word boundaries (see
|
|
2476
|
-
* "not_closed_or_try_with_resources" below; #1089 P2).
|
|
2477
|
-
*/
|
|
2478
|
-
function escapeRegExp(s) {
|
|
2479
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2480
|
-
}
|
|
2481
2499
|
switch (postFilter) {
|
|
2482
2500
|
case "no_nested_anchor_chain": {
|
|
2483
2501
|
// Tree-sitter queries can express the opening-tag shape, but they
|
|
@@ -3546,20 +3564,39 @@ export class TreeSitterClient {
|
|
|
3546
3564
|
/^(Open|OpenFile|ReadFile|WriteFile|Create|Remove|RemoveAll)$/.test(captures.FN?.text ?? ""));
|
|
3547
3565
|
case "py_sql_injection_sink": {
|
|
3548
3566
|
const fn = captures.FN?.text ?? "";
|
|
3549
|
-
if (!
|
|
3567
|
+
if (!PYTHON_SQL_SINK_METHODS.has(fn))
|
|
3550
3568
|
return false;
|
|
3551
|
-
}
|
|
3552
3569
|
const sqlNode = captures.SQL;
|
|
3553
|
-
const receiver = captures.OBJ
|
|
3554
|
-
//
|
|
3555
|
-
//
|
|
3556
|
-
//
|
|
3557
|
-
if (fn === "execute" &&
|
|
3570
|
+
const receiver = captures.OBJ;
|
|
3571
|
+
// Pre-existing exemptions — kept verbatim so no current user
|
|
3572
|
+
// regresses (#2577 review): an unannotated session receiver, and a
|
|
3573
|
+
// statement-builder call in argument position.
|
|
3574
|
+
if (fn === "execute" &&
|
|
3575
|
+
this.isLikelySqlAlchemyReceiver(receiver?.text ?? "")) {
|
|
3558
3576
|
return false;
|
|
3559
3577
|
}
|
|
3560
3578
|
if (sqlNode && this.isSafeSqlAlchemyExpressionCall(sqlNode)) {
|
|
3561
3579
|
return false;
|
|
3562
3580
|
}
|
|
3581
|
+
// #2576: a receiver PROVEN to be a sqlalchemy Session/AsyncSession.
|
|
3582
|
+
// `Session.query` takes entity classes and `Session.execute` a
|
|
3583
|
+
// statement object (a builder call, or a name bound to one —
|
|
3584
|
+
// `stmt = select(User)`, which the check above cannot see). Neither
|
|
3585
|
+
// suppression may swallow composed SQL text (#2577 review F1/F2).
|
|
3586
|
+
if (isProvenSqlAlchemySessionReceiver(receiver, rootNode)) {
|
|
3587
|
+
if (fn === "query" &&
|
|
3588
|
+
isSqlAlchemyEntityQueryArgument(sqlNode, rootNode)) {
|
|
3589
|
+
return false;
|
|
3590
|
+
}
|
|
3591
|
+
if (fn === "execute" &&
|
|
3592
|
+
isSqlAlchemyStatementArgument(sqlNode, rootNode)) {
|
|
3593
|
+
return false;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
// #2576: psycopg `sql.SQL("...").format(sql.Identifier(...))`.
|
|
3597
|
+
if (isSafePsycopgIdentifierComposition(sqlNode, rootNode)) {
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3563
3600
|
return true;
|
|
3564
3601
|
}
|
|
3565
3602
|
case "go_sql_injection_sink":
|
|
@@ -3868,7 +3905,8 @@ export class TreeSitterClient {
|
|
|
3868
3905
|
const matches = [];
|
|
3869
3906
|
await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
|
|
3870
3907
|
try {
|
|
3871
|
-
const
|
|
3908
|
+
const rootNode = tree.rootNode;
|
|
3909
|
+
const queryMatches = query.matches(rootNode);
|
|
3872
3910
|
for (const match of queryMatches) {
|
|
3873
3911
|
const captures = {};
|
|
3874
3912
|
for (const capture of match.captures) {
|
|
@@ -3883,7 +3921,7 @@ export class TreeSitterClient {
|
|
|
3883
3921
|
continue;
|
|
3884
3922
|
}
|
|
3885
3923
|
if (postFilter &&
|
|
3886
|
-
!this.applyPostFilter(postFilter, postFilterParams, captures,
|
|
3924
|
+
!this.applyPostFilter(postFilter, postFilterParams, captures, rootNode)) {
|
|
3887
3925
|
continue;
|
|
3888
3926
|
}
|
|
3889
3927
|
if (match.captures.length > 0) {
|
|
@@ -3974,54 +4012,3 @@ export class TreeSitterClient {
|
|
|
3974
4012
|
}
|
|
3975
4013
|
}
|
|
3976
4014
|
// --- Simplified Pattern Search (regex fallback) ---
|
|
3977
|
-
/**
|
|
3978
|
-
* Fallback structural search using regex when tree-sitter unavailable
|
|
3979
|
-
* Less accurate but works without WASM dependencies
|
|
3980
|
-
*/
|
|
3981
|
-
export function regexStructuralSearch(pattern, files, options = {}) {
|
|
3982
|
-
const matches = [];
|
|
3983
|
-
const maxResults = options.maxResults ?? 50;
|
|
3984
|
-
// Extract pattern structure for regex
|
|
3985
|
-
// "console.log($MSG)" -> /console\.log\(([^)]+)\)/
|
|
3986
|
-
const regexPattern = pattern
|
|
3987
|
-
.replace(/\\/g, "\\\\")
|
|
3988
|
-
.replace(/\./g, "\\.")
|
|
3989
|
-
.replace(/\$\$\$[A-Z_][A-Z0-9_]*/g, "(.*?)") // variadic - non-greedy
|
|
3990
|
-
.replace(/\$[A-Z_][A-Z0-9_]*/g, "([^,)]+)"); // single - capture group
|
|
3991
|
-
try {
|
|
3992
|
-
const regex = new RegExp(regexPattern, "g");
|
|
3993
|
-
for (const file of files) {
|
|
3994
|
-
if (matches.length >= maxResults)
|
|
3995
|
-
break;
|
|
3996
|
-
try {
|
|
3997
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
3998
|
-
const lines = content.split("\n");
|
|
3999
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4000
|
-
regex.lastIndex = 0;
|
|
4001
|
-
const match = regex.exec(lines[i]);
|
|
4002
|
-
if (match) {
|
|
4003
|
-
const captures = {};
|
|
4004
|
-
// Extract captures
|
|
4005
|
-
for (let j = 1; j < match.length; j++) {
|
|
4006
|
-
captures[`$${j}`] = match[j];
|
|
4007
|
-
}
|
|
4008
|
-
matches.push({
|
|
4009
|
-
file,
|
|
4010
|
-
line: i + 1,
|
|
4011
|
-
column: match.index + 1,
|
|
4012
|
-
matchedText: match[0],
|
|
4013
|
-
captures,
|
|
4014
|
-
});
|
|
4015
|
-
if (matches.length >= maxResults)
|
|
4016
|
-
break;
|
|
4017
|
-
}
|
|
4018
|
-
}
|
|
4019
|
-
}
|
|
4020
|
-
catch { }
|
|
4021
|
-
}
|
|
4022
|
-
}
|
|
4023
|
-
catch {
|
|
4024
|
-
// Invalid regex
|
|
4025
|
-
}
|
|
4026
|
-
return matches;
|
|
4027
|
-
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { isTestMode } from "./env-utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
4
4
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
5
5
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
6
6
|
import { normalizeLoggedPath } from "./path-utils.js";
|
|
7
|
-
const TREE_SITTER_LOG_DIR =
|
|
7
|
+
const TREE_SITTER_LOG_DIR = getGlobalPiLensLogDir();
|
|
8
8
|
const TREE_SITTER_LOG_FILE = path.join(TREE_SITTER_LOG_DIR, "tree-sitter.log");
|
|
9
9
|
const writer = createNdjsonLogger({
|
|
10
10
|
filePath: TREE_SITTER_LOG_FILE,
|