@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,639 @@
|
|
|
1
|
+
const SUMMARY_BY_ROOT = new WeakMap();
|
|
2
|
+
const TRAVERSAL_VISIT_CAP = 50_000;
|
|
3
|
+
const TRAVERSAL_DEPTH_CAP = 128;
|
|
4
|
+
const EXPRESSION_DEPTH_CAP = 8;
|
|
5
|
+
const BINDING_SCAN_DEPTH_CAP = 32;
|
|
6
|
+
const ANCESTOR_DEPTH_CAP = 64;
|
|
7
|
+
/**
|
|
8
|
+
* Receiver names conventionally bound to a SQLAlchemy session. The pre-existing
|
|
9
|
+
* `session.execute(...)` exemption keys off these; structural provenance below
|
|
10
|
+
* proves the annotated case.
|
|
11
|
+
*/
|
|
12
|
+
export const PYTHON_SQLALCHEMY_RECEIVER_NAMES = new Set([
|
|
13
|
+
"session",
|
|
14
|
+
"db_session",
|
|
15
|
+
"async_session",
|
|
16
|
+
"sync_session",
|
|
17
|
+
]);
|
|
18
|
+
/** Statement constructors whose result is an expression object, never a string. */
|
|
19
|
+
export const PYTHON_SQLALCHEMY_STATEMENT_BUILDERS = new Set(["select", "insert", "update", "delete"]);
|
|
20
|
+
const FROM_IMPORT_PROVENANCE = new Map([
|
|
21
|
+
["sqlalchemy.orm:Session", "sqlalchemy-session"],
|
|
22
|
+
["sqlalchemy.ext.asyncio:AsyncSession", "sqlalchemy-session"],
|
|
23
|
+
["psycopg:sql", "psycopg-sql-module"],
|
|
24
|
+
["psycopg2:sql", "psycopg-sql-module"],
|
|
25
|
+
["psycopg.sql:SQL", "psycopg-sql-constructor"],
|
|
26
|
+
["psycopg2.sql:SQL", "psycopg-sql-constructor"],
|
|
27
|
+
["psycopg.sql:Identifier", "psycopg-identifier-constructor"],
|
|
28
|
+
["psycopg2.sql:Identifier", "psycopg-identifier-constructor"],
|
|
29
|
+
]);
|
|
30
|
+
const PLAIN_PACKAGE_PROVENANCE = new Map([
|
|
31
|
+
["sqlalchemy", "sqlalchemy-module"],
|
|
32
|
+
["psycopg", "psycopg-package"],
|
|
33
|
+
["psycopg2", "psycopg-package"],
|
|
34
|
+
]);
|
|
35
|
+
function nodeKey(node) {
|
|
36
|
+
return `${node.type}:${node.startIndex}:${node.endIndex}`;
|
|
37
|
+
}
|
|
38
|
+
function namedChildren(node) {
|
|
39
|
+
return (node.children ?? []).filter((child) => child.isNamed);
|
|
40
|
+
}
|
|
41
|
+
function calleeNode(call) {
|
|
42
|
+
if (call.type !== "call")
|
|
43
|
+
return undefined;
|
|
44
|
+
return call.childForFieldName?.("function") ?? namedChildren(call)[0];
|
|
45
|
+
}
|
|
46
|
+
function directNamedChild(node, type) {
|
|
47
|
+
return namedChildren(node).find((child) => child.type === type);
|
|
48
|
+
}
|
|
49
|
+
// Containers hold further binding positions; reference types (`a.b`, `a[b]`,
|
|
50
|
+
// `Mod.Case`) never introduce a name.
|
|
51
|
+
const BINDING_TARGET_CONTAINER_TYPES = new Set([
|
|
52
|
+
"tuple",
|
|
53
|
+
"pattern_list",
|
|
54
|
+
"list",
|
|
55
|
+
"list_pattern",
|
|
56
|
+
"tuple_pattern",
|
|
57
|
+
"starred_expression",
|
|
58
|
+
"list_splat_pattern",
|
|
59
|
+
"dictionary_splat_pattern",
|
|
60
|
+
]);
|
|
61
|
+
const BINDING_TARGET_REFERENCE_TYPES = new Set(["attribute", "subscript"]);
|
|
62
|
+
const BINDING_PATTERN_CONTAINER_TYPES = new Set([
|
|
63
|
+
"case_pattern",
|
|
64
|
+
"as_pattern",
|
|
65
|
+
"union_pattern",
|
|
66
|
+
"list_pattern",
|
|
67
|
+
"tuple_pattern",
|
|
68
|
+
"list_splat_pattern",
|
|
69
|
+
"dictionary_splat_pattern",
|
|
70
|
+
"class_pattern",
|
|
71
|
+
]);
|
|
72
|
+
const BINDING_PATTERN_REFERENCE_TYPES = new Set([
|
|
73
|
+
"attribute",
|
|
74
|
+
"qualified_pattern",
|
|
75
|
+
]);
|
|
76
|
+
function scanBindingNames(node, mode, scan, depth = 0) {
|
|
77
|
+
if (!node || depth > BINDING_SCAN_DEPTH_CAP) {
|
|
78
|
+
scan.unknown = true;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const descend = (child, next) => scanBindingNames(child, next, scan, depth + 1);
|
|
82
|
+
if (node.type === "as_pattern_target") {
|
|
83
|
+
descend(namedChildren(node)[0], "target");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (mode === "as") {
|
|
87
|
+
// `with ctx() as name` / `except E as name`: only the as-target binds.
|
|
88
|
+
for (const child of namedChildren(node))
|
|
89
|
+
descend(child, "as");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (node.type === "identifier") {
|
|
93
|
+
if (node.text !== "_")
|
|
94
|
+
scan.names.push(node.text);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (mode === "pattern") {
|
|
98
|
+
if (node.type === "dotted_name") {
|
|
99
|
+
// A bare `case name` captures; `case Mod.CONST` is a value reference.
|
|
100
|
+
if (!node.text.includes(".") && node.text !== "_") {
|
|
101
|
+
scan.names.push(node.text);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (BINDING_PATTERN_REFERENCE_TYPES.has(node.type))
|
|
106
|
+
return;
|
|
107
|
+
if (node.type === "keyword_pattern") {
|
|
108
|
+
// `Wrapper(field=capture)`: the keyword itself is not a binder.
|
|
109
|
+
const value = namedChildren(node).slice(1);
|
|
110
|
+
if (value.length !== 1)
|
|
111
|
+
scan.unknown = true;
|
|
112
|
+
else
|
|
113
|
+
descend(value[0], "pattern");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (BINDING_PATTERN_CONTAINER_TYPES.has(node.type)) {
|
|
117
|
+
for (const child of namedChildren(node))
|
|
118
|
+
descend(child, "pattern");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
scan.unknown = true;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (BINDING_TARGET_REFERENCE_TYPES.has(node.type))
|
|
125
|
+
return;
|
|
126
|
+
if (BINDING_TARGET_CONTAINER_TYPES.has(node.type)) {
|
|
127
|
+
for (const child of namedChildren(node))
|
|
128
|
+
descend(child, "target");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
scan.unknown = true;
|
|
132
|
+
}
|
|
133
|
+
/** Extract only identifiers in Python binding positions. */
|
|
134
|
+
function collectBindingNames(node, mode) {
|
|
135
|
+
const scan = { names: [], unknown: false };
|
|
136
|
+
scanBindingNames(node, mode, scan);
|
|
137
|
+
return scan;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* One import clause. `source` is the name as written in the module
|
|
141
|
+
* (`sql` in `from psycopg import sql`, `psycopg.sql` in `import psycopg.sql`);
|
|
142
|
+
* `local` is the name it binds.
|
|
143
|
+
*/
|
|
144
|
+
function parseImportBinding(node, isFrom) {
|
|
145
|
+
if (node.type === "aliased_import") {
|
|
146
|
+
const children = namedChildren(node);
|
|
147
|
+
const source = children[0]?.text;
|
|
148
|
+
const local = children.at(-1);
|
|
149
|
+
return source && local?.type === "identifier"
|
|
150
|
+
? { source, local: local.text }
|
|
151
|
+
: undefined;
|
|
152
|
+
}
|
|
153
|
+
if (node.type !== "dotted_name" && node.type !== "identifier") {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
if (isFrom)
|
|
157
|
+
return { source: node.text, local: node.text };
|
|
158
|
+
const local = node.text.split(".")[0];
|
|
159
|
+
return local ? { source: node.text, local } : undefined;
|
|
160
|
+
}
|
|
161
|
+
function directAnnotationName(parameter) {
|
|
162
|
+
// `typed_default_parameter` is FastAPI's `db: Session = Depends(get_db)`.
|
|
163
|
+
if (parameter.type !== "typed_parameter" &&
|
|
164
|
+
parameter.type !== "typed_default_parameter") {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const type = directNamedChild(parameter, "type");
|
|
168
|
+
const children = type ? namedChildren(type) : [];
|
|
169
|
+
return children.length === 1 && children[0]?.type === "identifier"
|
|
170
|
+
? children[0].text
|
|
171
|
+
: undefined;
|
|
172
|
+
}
|
|
173
|
+
function parameterName(parameter) {
|
|
174
|
+
if (parameter.type === "identifier")
|
|
175
|
+
return parameter.text;
|
|
176
|
+
return directNamedChild(parameter, "identifier")?.text;
|
|
177
|
+
}
|
|
178
|
+
function markInvalid(state) {
|
|
179
|
+
state.invalid = true;
|
|
180
|
+
}
|
|
181
|
+
function addBinding(state, name, functionChain) {
|
|
182
|
+
state.bindingCounts.set(name, (state.bindingCounts.get(name) ?? 0) + 1);
|
|
183
|
+
for (const fn of functionChain) {
|
|
184
|
+
const key = nodeKey(fn);
|
|
185
|
+
const bindings = state.functionBindings.get(key) ?? new Map();
|
|
186
|
+
bindings.set(name, (bindings.get(name) ?? 0) + 1);
|
|
187
|
+
state.functionBindings.set(key, bindings);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function addTarget(state, target, functionChain) {
|
|
191
|
+
const extracted = collectBindingNames(target, "target");
|
|
192
|
+
if (extracted.unknown)
|
|
193
|
+
markInvalid(state);
|
|
194
|
+
for (const name of extracted.names)
|
|
195
|
+
addBinding(state, name, functionChain);
|
|
196
|
+
}
|
|
197
|
+
function recordFunctionParameters(node, state) {
|
|
198
|
+
const annotations = new Map();
|
|
199
|
+
const parameters = directNamedChild(node, "parameters");
|
|
200
|
+
for (const parameter of namedChildren(parameters ?? node)) {
|
|
201
|
+
const name = parameterName(parameter);
|
|
202
|
+
if (!name) {
|
|
203
|
+
if (parameter.type !== "list_splat_pattern")
|
|
204
|
+
markInvalid(state);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
addBinding(state, name, state.functionChain);
|
|
208
|
+
const annotation = directAnnotationName(parameter);
|
|
209
|
+
if (annotation)
|
|
210
|
+
annotations.set(name, annotation);
|
|
211
|
+
}
|
|
212
|
+
state.functionAnnotations.set(nodeKey(node), annotations);
|
|
213
|
+
}
|
|
214
|
+
function recordFunctionDefinition(node, state) {
|
|
215
|
+
recordFunctionParameters(node, state);
|
|
216
|
+
recordDefinitionBinding(node, state);
|
|
217
|
+
}
|
|
218
|
+
function recordLambdaParameters(node, state) {
|
|
219
|
+
const parameters = directNamedChild(node, "lambda_parameters");
|
|
220
|
+
for (const parameter of namedChildren(parameters ?? node)) {
|
|
221
|
+
const name = parameterName(parameter);
|
|
222
|
+
if (name)
|
|
223
|
+
addBinding(state, name, state.functionChain);
|
|
224
|
+
else
|
|
225
|
+
markInvalid(state);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function recordImportBindings(node, state) {
|
|
229
|
+
const isFrom = node.type === "import_from_statement";
|
|
230
|
+
const children = namedChildren(node);
|
|
231
|
+
const moduleName = isFrom ? children[0]?.text : undefined;
|
|
232
|
+
// A star import can introduce anything: give up on the file.
|
|
233
|
+
if (node.text.includes("*") || (isFrom && !moduleName))
|
|
234
|
+
markInvalid(state);
|
|
235
|
+
for (const part of isFrom ? children.slice(1) : children) {
|
|
236
|
+
const binding = parseImportBinding(part, isFrom);
|
|
237
|
+
if (!binding) {
|
|
238
|
+
markInvalid(state);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
addBinding(state, binding.local, state.functionChain);
|
|
242
|
+
// Only imports at module top level prove provenance: a class-body or
|
|
243
|
+
// function-body import is not in scope for the sink's namespace.
|
|
244
|
+
if (!state.moduleDirect)
|
|
245
|
+
continue;
|
|
246
|
+
const provenance = isFrom
|
|
247
|
+
? FROM_IMPORT_PROVENANCE.get(`${moduleName}:${binding.source}`)
|
|
248
|
+
: PLAIN_PACKAGE_PROVENANCE.get(binding.source);
|
|
249
|
+
if (provenance) {
|
|
250
|
+
state.imports.set(binding.local, {
|
|
251
|
+
name: binding.local,
|
|
252
|
+
provenance,
|
|
253
|
+
endIndex: node.endIndex,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function recordTargetBinding(node, state) {
|
|
259
|
+
addTarget(state, node.childForFieldName?.("left") ?? namedChildren(node)[0], state.functionChain);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* `stmt = select(User)` — the assigned value, indexed by name. Only consulted
|
|
263
|
+
* when the name has exactly one binding in the file, so a rebound or shadowed
|
|
264
|
+
* name never proves anything.
|
|
265
|
+
*/
|
|
266
|
+
function recordAssignmentValue(node, state) {
|
|
267
|
+
recordTargetBinding(node, state);
|
|
268
|
+
const target = node.childForFieldName?.("left") ?? namedChildren(node)[0];
|
|
269
|
+
const value = node.childForFieldName?.("right") ?? namedChildren(node).at(-1);
|
|
270
|
+
if (target?.type !== "identifier" || !value || value === target)
|
|
271
|
+
return;
|
|
272
|
+
state.assignments.set(target.text, value);
|
|
273
|
+
}
|
|
274
|
+
function recordAsPatternBinding(node, state) {
|
|
275
|
+
const extracted = collectBindingNames(node, "as");
|
|
276
|
+
if (extracted.unknown)
|
|
277
|
+
markInvalid(state);
|
|
278
|
+
for (const name of extracted.names)
|
|
279
|
+
addBinding(state, name, state.functionChain);
|
|
280
|
+
}
|
|
281
|
+
function recordDeleteTargets(node, state) {
|
|
282
|
+
for (const child of namedChildren(node)) {
|
|
283
|
+
addTarget(state, child, state.functionChain);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function recordCaseBindings(node, state) {
|
|
287
|
+
const extracted = collectBindingNames(directNamedChild(node, "case_pattern"), "pattern");
|
|
288
|
+
if (extracted.unknown)
|
|
289
|
+
markInvalid(state);
|
|
290
|
+
for (const name of extracted.names)
|
|
291
|
+
addBinding(state, name, state.functionChain);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* `global sql` / `nonlocal sql` — the declaration alone is enough to give up on
|
|
295
|
+
* the name (covered by the "global" case of the fail-closed runner regression).
|
|
296
|
+
*/
|
|
297
|
+
function recordDeclarationBindings(node, state) {
|
|
298
|
+
for (const child of namedChildren(node)) {
|
|
299
|
+
addBinding(state, child.text, state.functionChain);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function recordDefinitionBinding(node, state) {
|
|
303
|
+
const name = directNamedChild(node, "identifier")?.text;
|
|
304
|
+
if (name)
|
|
305
|
+
addBinding(state, name, state.parentFunctionChain);
|
|
306
|
+
else
|
|
307
|
+
markInvalid(state);
|
|
308
|
+
}
|
|
309
|
+
function recordTypeAliasBinding(node, state) {
|
|
310
|
+
const name = directNamedChild(node, "type")?.children?.find((child) => child.type === "identifier")?.text;
|
|
311
|
+
if (name)
|
|
312
|
+
addBinding(state, name, state.functionChain);
|
|
313
|
+
else
|
|
314
|
+
markInvalid(state);
|
|
315
|
+
}
|
|
316
|
+
const DYNAMIC_NAMESPACE_BUILTINS = new Set([
|
|
317
|
+
"exec",
|
|
318
|
+
"eval",
|
|
319
|
+
"globals",
|
|
320
|
+
"locals",
|
|
321
|
+
"vars",
|
|
322
|
+
]);
|
|
323
|
+
/** `exec`/`eval`/`globals()` can rebind anything: give up on the whole file. */
|
|
324
|
+
function recordDynamicNamespaceHazard(node, state) {
|
|
325
|
+
const callee = calleeNode(node);
|
|
326
|
+
if (callee?.type === "identifier" &&
|
|
327
|
+
DYNAMIC_NAMESPACE_BUILTINS.has(callee.text)) {
|
|
328
|
+
markInvalid(state);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const SUMMARY_NODE_RECORDERS = Object.freeze({
|
|
332
|
+
function_definition: recordFunctionDefinition,
|
|
333
|
+
class_definition: recordDefinitionBinding,
|
|
334
|
+
lambda: recordLambdaParameters,
|
|
335
|
+
import_from_statement: recordImportBindings,
|
|
336
|
+
import_statement: recordImportBindings,
|
|
337
|
+
assignment: recordAssignmentValue,
|
|
338
|
+
augmented_assignment: recordTargetBinding,
|
|
339
|
+
named_expression: recordTargetBinding,
|
|
340
|
+
for_statement: recordTargetBinding,
|
|
341
|
+
for_in_clause: recordTargetBinding,
|
|
342
|
+
with_item: recordAsPatternBinding,
|
|
343
|
+
except_clause: recordAsPatternBinding,
|
|
344
|
+
except_group_clause: recordAsPatternBinding,
|
|
345
|
+
delete_statement: recordDeleteTargets,
|
|
346
|
+
case_clause: recordCaseBindings,
|
|
347
|
+
type_alias_statement: recordTypeAliasBinding,
|
|
348
|
+
global_statement: recordDeclarationBindings,
|
|
349
|
+
nonlocal_statement: recordDeclarationBindings,
|
|
350
|
+
call: recordDynamicNamespaceHazard,
|
|
351
|
+
});
|
|
352
|
+
class Summary {
|
|
353
|
+
invalid;
|
|
354
|
+
imports;
|
|
355
|
+
bindingCounts;
|
|
356
|
+
assignments;
|
|
357
|
+
functions;
|
|
358
|
+
constructor(root) {
|
|
359
|
+
const state = {
|
|
360
|
+
imports: new Map(),
|
|
361
|
+
bindingCounts: new Map(),
|
|
362
|
+
assignments: new Map(),
|
|
363
|
+
functionBindings: new Map(),
|
|
364
|
+
functionAnnotations: new Map(),
|
|
365
|
+
invalid: false,
|
|
366
|
+
visits: 0,
|
|
367
|
+
functionChain: [],
|
|
368
|
+
parentFunctionChain: [],
|
|
369
|
+
moduleDirect: false,
|
|
370
|
+
};
|
|
371
|
+
const visit = (node, depth, moduleDirect, activeFunctions) => {
|
|
372
|
+
if (++state.visits > TRAVERSAL_VISIT_CAP || depth > TRAVERSAL_DEPTH_CAP) {
|
|
373
|
+
markInvalid(state);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (node.hasError || node.type === "ERROR" || node.type === "MISSING") {
|
|
377
|
+
markInvalid(state);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const functionChain = node.type === "function_definition"
|
|
381
|
+
? [...activeFunctions, node]
|
|
382
|
+
: activeFunctions;
|
|
383
|
+
state.functionChain = functionChain;
|
|
384
|
+
state.parentFunctionChain = activeFunctions;
|
|
385
|
+
state.moduleDirect = moduleDirect;
|
|
386
|
+
const recorder = SUMMARY_NODE_RECORDERS[node.type];
|
|
387
|
+
if (recorder)
|
|
388
|
+
recorder(node, state);
|
|
389
|
+
for (const child of node.children ?? []) {
|
|
390
|
+
visit(child, depth + 1, node === root, functionChain);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
visit(root, 0, false, []);
|
|
394
|
+
this.invalid = state.invalid;
|
|
395
|
+
this.imports = state.imports;
|
|
396
|
+
this.bindingCounts = state.bindingCounts;
|
|
397
|
+
this.assignments = state.assignments;
|
|
398
|
+
this.functions = new Map([...state.functionAnnotations.entries()].map(([key, annotations]) => [
|
|
399
|
+
key,
|
|
400
|
+
{
|
|
401
|
+
parameterAnnotations: annotations,
|
|
402
|
+
bindingCounts: state.functionBindings.get(key) ?? new Map(),
|
|
403
|
+
},
|
|
404
|
+
]));
|
|
405
|
+
}
|
|
406
|
+
provenanceFor(name, reference) {
|
|
407
|
+
// Exactly one binding in the file, and it is the import itself: any
|
|
408
|
+
// shadow, rebind or `del` anywhere makes the name unusable as proof.
|
|
409
|
+
const candidate = this.boundOnce(name) ? this.imports.get(name) : undefined;
|
|
410
|
+
return candidate && candidate.endIndex <= reference.startIndex
|
|
411
|
+
? candidate.provenance
|
|
412
|
+
: null;
|
|
413
|
+
}
|
|
414
|
+
boundOnce(name) {
|
|
415
|
+
return !this.invalid && (this.bindingCounts.get(name) ?? 0) === 1;
|
|
416
|
+
}
|
|
417
|
+
singleAssignmentValue(name, reference) {
|
|
418
|
+
if (!this.boundOnce(name))
|
|
419
|
+
return null;
|
|
420
|
+
const value = this.assignments.get(name);
|
|
421
|
+
return value && value.endIndex <= reference.startIndex ? value : null;
|
|
422
|
+
}
|
|
423
|
+
isFunctionLocalName(reference) {
|
|
424
|
+
if (this.invalid || reference.type !== "identifier")
|
|
425
|
+
return true;
|
|
426
|
+
let current = reference.parent;
|
|
427
|
+
for (let depth = 0; current && depth < ANCESTOR_DEPTH_CAP; depth++) {
|
|
428
|
+
if (current.type === "function_definition") {
|
|
429
|
+
const summary = this.functions.get(nodeKey(current));
|
|
430
|
+
if (!summary)
|
|
431
|
+
return true;
|
|
432
|
+
if ((summary.bindingCounts.get(reference.text) ?? 0) > 0)
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
current = current.parent;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
isSqlAlchemySessionReceiver(receiver) {
|
|
440
|
+
if (this.invalid || receiver.type !== "identifier")
|
|
441
|
+
return false;
|
|
442
|
+
let current = receiver.parent;
|
|
443
|
+
for (let depth = 0; current && depth < ANCESTOR_DEPTH_CAP; depth++) {
|
|
444
|
+
if (current.type === "function_definition") {
|
|
445
|
+
const summary = this.functions.get(nodeKey(current));
|
|
446
|
+
if (!summary)
|
|
447
|
+
return false;
|
|
448
|
+
const annotation = summary.parameterAnnotations.get(receiver.text);
|
|
449
|
+
return (annotation !== undefined &&
|
|
450
|
+
(summary.bindingCounts.get(receiver.text) ?? 0) === 1 &&
|
|
451
|
+
this.provenanceFor(annotation, receiver) === "sqlalchemy-session");
|
|
452
|
+
}
|
|
453
|
+
current = current.parent;
|
|
454
|
+
}
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function getPythonProvenanceSummary(root) {
|
|
459
|
+
const cached = SUMMARY_BY_ROOT.get(root);
|
|
460
|
+
if (cached)
|
|
461
|
+
return cached;
|
|
462
|
+
const summary = new Summary(root);
|
|
463
|
+
SUMMARY_BY_ROOT.set(root, summary);
|
|
464
|
+
return summary;
|
|
465
|
+
}
|
|
466
|
+
function expressionProvenance(node, summary, depth = 0) {
|
|
467
|
+
if (!node || depth > EXPRESSION_DEPTH_CAP)
|
|
468
|
+
return null;
|
|
469
|
+
if (node.type === "identifier")
|
|
470
|
+
return summary.provenanceFor(node.text, node);
|
|
471
|
+
if (node.type !== "attribute")
|
|
472
|
+
return null;
|
|
473
|
+
const object = node.childForFieldName?.("object") ?? namedChildren(node)[0];
|
|
474
|
+
const attribute = node.childForFieldName?.("attribute") ?? namedChildren(node).at(-1);
|
|
475
|
+
const provenance = expressionProvenance(object, summary, depth + 1);
|
|
476
|
+
if (provenance === "psycopg-package" && attribute?.text === "sql") {
|
|
477
|
+
return "psycopg-sql-module";
|
|
478
|
+
}
|
|
479
|
+
if (provenance === "psycopg-sql-module") {
|
|
480
|
+
if (attribute?.text === "SQL")
|
|
481
|
+
return "psycopg-sql-constructor";
|
|
482
|
+
if (attribute?.text === "Identifier")
|
|
483
|
+
return "psycopg-identifier-constructor";
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
/** Exact static SQL(...).format(Identifier(...), ...) proof. */
|
|
488
|
+
export function isSafePsycopgIdentifierComposition(node, root) {
|
|
489
|
+
if (node?.type !== "call" || !root)
|
|
490
|
+
return false;
|
|
491
|
+
const summary = getPythonProvenanceSummary(root);
|
|
492
|
+
if (summary.invalid)
|
|
493
|
+
return false;
|
|
494
|
+
const formatCallee = node.childForFieldName?.("function") ?? namedChildren(node)[0];
|
|
495
|
+
if (formatCallee?.type !== "attribute")
|
|
496
|
+
return false;
|
|
497
|
+
const formatChildren = namedChildren(formatCallee);
|
|
498
|
+
const hasFormatMethod = formatChildren.some((child) => child.text === "format");
|
|
499
|
+
const sqlConstructor = formatChildren.find((child) => child.type === "call");
|
|
500
|
+
if (!hasFormatMethod || !sqlConstructor)
|
|
501
|
+
return false;
|
|
502
|
+
const constructorCallee = sqlConstructor.childForFieldName?.("function") ??
|
|
503
|
+
namedChildren(sqlConstructor)[0];
|
|
504
|
+
if (expressionProvenance(constructorCallee, summary) !==
|
|
505
|
+
"psycopg-sql-constructor")
|
|
506
|
+
return false;
|
|
507
|
+
const templateArgs = namedChildren(directNamedChild(sqlConstructor, "argument_list") ?? sqlConstructor).filter((child) => child.type !== "comment");
|
|
508
|
+
if (templateArgs.length !== 1 ||
|
|
509
|
+
templateArgs[0]?.type !== "string" ||
|
|
510
|
+
namedChildren(templateArgs[0]).some((child) => child.type === "interpolation"))
|
|
511
|
+
return false;
|
|
512
|
+
const formatArgs = namedChildren(directNamedChild(node, "argument_list") ?? node).filter((child) => child.type !== "comment");
|
|
513
|
+
if (formatArgs.length === 0)
|
|
514
|
+
return false;
|
|
515
|
+
return formatArgs.every((argument) => {
|
|
516
|
+
if (argument.type !== "call")
|
|
517
|
+
return false;
|
|
518
|
+
const callee = argument.childForFieldName?.("function") ?? namedChildren(argument)[0];
|
|
519
|
+
return (expressionProvenance(callee, summary) === "psycopg-identifier-constructor");
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
export function isProvenSqlAlchemySessionReceiver(receiver, root) {
|
|
523
|
+
return (!!receiver &&
|
|
524
|
+
!!root &&
|
|
525
|
+
getPythonProvenanceSummary(root).isSqlAlchemySessionReceiver(receiver));
|
|
526
|
+
}
|
|
527
|
+
function isStaticStringLiteral(node) {
|
|
528
|
+
return (node?.type === "string" &&
|
|
529
|
+
!namedChildren(node).some((child) => child.type === "interpolation"));
|
|
530
|
+
}
|
|
531
|
+
function callArguments(call) {
|
|
532
|
+
return namedChildren(directNamedChild(call, "argument_list") ?? call).filter((child) => child.type !== "comment");
|
|
533
|
+
}
|
|
534
|
+
// Node types that ARE SQL text, or that compose it (`+`, `%`).
|
|
535
|
+
const COMPOSED_SQL_NODE_TYPES = new Set([
|
|
536
|
+
"string",
|
|
537
|
+
"concatenated_string",
|
|
538
|
+
"binary_operator",
|
|
539
|
+
]);
|
|
540
|
+
/**
|
|
541
|
+
* True when an expression carries SQL text a caller composed — a literal, a
|
|
542
|
+
* concatenation, a `%` format, an f-string, `"...".format(...)`, or any call
|
|
543
|
+
* that wraps one. Fails closed at the depth cap: unknown means composed.
|
|
544
|
+
*/
|
|
545
|
+
function carriesComposedSql(node, depth = 0) {
|
|
546
|
+
if (!node)
|
|
547
|
+
return false;
|
|
548
|
+
if (depth > EXPRESSION_DEPTH_CAP)
|
|
549
|
+
return true;
|
|
550
|
+
if (COMPOSED_SQL_NODE_TYPES.has(node.type))
|
|
551
|
+
return true;
|
|
552
|
+
if (node.type !== "call")
|
|
553
|
+
return false;
|
|
554
|
+
const callee = calleeNode(node);
|
|
555
|
+
if (callee?.type === "attribute") {
|
|
556
|
+
// `"SELECT {}".format(uid)` — the template hangs off the callee.
|
|
557
|
+
const object = callee.childForFieldName?.("object") ?? namedChildren(callee)[0];
|
|
558
|
+
if (carriesComposedSql(object, depth + 1))
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
return callArguments(node).some((argument) => carriesComposedSql(argument, depth + 1));
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* The builder name a call invokes: a bare `select(...)`, or `sa.select(...)`
|
|
565
|
+
* where `sa` is a PROVEN sqlalchemy import. Without that proof any object's
|
|
566
|
+
* `.update(...)` would read as a statement builder (#2577 review F2).
|
|
567
|
+
*/
|
|
568
|
+
function builderCalleeName(call, summary) {
|
|
569
|
+
const callee = calleeNode(call);
|
|
570
|
+
if (callee?.type === "identifier")
|
|
571
|
+
return callee.text;
|
|
572
|
+
if (callee?.type !== "attribute")
|
|
573
|
+
return undefined;
|
|
574
|
+
const object = callee.childForFieldName?.("object") ?? namedChildren(callee)[0];
|
|
575
|
+
if (expressionProvenance(object, summary) !== "sqlalchemy-module") {
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
const attribute = callee.childForFieldName?.("attribute") ?? namedChildren(callee).at(-1);
|
|
579
|
+
return attribute?.type === "identifier" ? attribute.text : undefined;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* `select(User)`, `sa.update(User)`, `text("SELECT 1")` — a statement object,
|
|
583
|
+
* not a SQL string. `text()` is the one builder that carries raw SQL, so only a
|
|
584
|
+
* literal template counts; and no builder may wrap composed SQL, or
|
|
585
|
+
* `update("UPDATE t SET x=" + uid)` would launder it (#2577 review F2).
|
|
586
|
+
*/
|
|
587
|
+
function isStatementBuilderCall(node, summary) {
|
|
588
|
+
if (node?.type !== "call")
|
|
589
|
+
return false;
|
|
590
|
+
const name = builderCalleeName(node, summary);
|
|
591
|
+
if (!name)
|
|
592
|
+
return false;
|
|
593
|
+
const args = callArguments(node);
|
|
594
|
+
if (name === "text") {
|
|
595
|
+
return args.length === 1 && isStaticStringLiteral(args[0]);
|
|
596
|
+
}
|
|
597
|
+
if (!PYTHON_SQLALCHEMY_STATEMENT_BUILDERS.has(name))
|
|
598
|
+
return false;
|
|
599
|
+
return !args.some((argument) => carriesComposedSql(argument));
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* True when a statement-executing argument is a builder call, or a name bound
|
|
603
|
+
* exactly once in the file to one (`stmt = select(User); db.execute(stmt)`).
|
|
604
|
+
*/
|
|
605
|
+
export function isSqlAlchemyStatementArgument(node, root) {
|
|
606
|
+
if (!node || !root)
|
|
607
|
+
return false;
|
|
608
|
+
const summary = getPythonProvenanceSummary(root);
|
|
609
|
+
if (isStatementBuilderCall(node, summary))
|
|
610
|
+
return true;
|
|
611
|
+
if (node.type !== "identifier")
|
|
612
|
+
return false;
|
|
613
|
+
const bound = summary.singleAssignmentValue(node.text, node);
|
|
614
|
+
return isStatementBuilderCall(bound ?? undefined, summary);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* `Session.query` takes mapped classes, so it must NOT require a builder
|
|
618
|
+
* argument (`db.query(User)` is the case #2576 reports). It suppresses only
|
|
619
|
+
* what cannot be SQL text: composed strings and opaque function-local values
|
|
620
|
+
* stay diagnostic (#2577 review F1).
|
|
621
|
+
*/
|
|
622
|
+
export function isSqlAlchemyEntityQueryArgument(node, root) {
|
|
623
|
+
if (!node || !root)
|
|
624
|
+
return false;
|
|
625
|
+
const summary = getPythonProvenanceSummary(root);
|
|
626
|
+
if (isStatementBuilderCall(node, summary))
|
|
627
|
+
return true;
|
|
628
|
+
if (carriesComposedSql(node))
|
|
629
|
+
return false;
|
|
630
|
+
if (node.type !== "identifier")
|
|
631
|
+
return true;
|
|
632
|
+
// A name the enclosing function binds is a runtime value that may hold SQL
|
|
633
|
+
// text (`q = build_sql(uid); db.query(q)`), so it stays diagnostic whatever
|
|
634
|
+
// it was assigned; a free or module-level name is the mapped class this API
|
|
635
|
+
// expects. A model built inside the function (`User = get_model()`) is
|
|
636
|
+
// statically indistinguishable from the first and fires: accepted noise,
|
|
637
|
+
// since following the assignment loses the true positive (#2577 round 3).
|
|
638
|
+
return !summary.isFunctionLocalName(node);
|
|
639
|
+
}
|
|
@@ -100,7 +100,12 @@ export async function runQuietWindow(deps) {
|
|
|
100
100
|
const taskStart = Date.now();
|
|
101
101
|
let ok = true;
|
|
102
102
|
try {
|
|
103
|
-
await task.fn(
|
|
103
|
+
await task.fn({
|
|
104
|
+
runtime: deps.runtime,
|
|
105
|
+
cwd: deps.cwd,
|
|
106
|
+
sessionId: deps.sessionId,
|
|
107
|
+
ownerId: deps.ownerId,
|
|
108
|
+
});
|
|
104
109
|
}
|
|
105
110
|
catch (err) {
|
|
106
111
|
ok = false;
|