@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
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/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -23,6 +23,10 @@ import { loadWebTreeSitter } from "./deps/web-tree-sitter.js";
|
|
|
23
23
|
import { getProjectIgnoreMatcher, isExcludedDirName } from "./file-utils.js";
|
|
24
24
|
import { downloadGrammar, grammarBlockReason, LANGUAGE_TO_GRAMMAR, } from "./grammar-source.js";
|
|
25
25
|
import { resolvePackagePath } from "./package-root.js";
|
|
26
|
+
import { recordDegradation } from "./degradation-ledger.js";
|
|
27
|
+
import { assertInstallAllowed, getProjectTrustGeneration, } from "./project-trust.js";
|
|
28
|
+
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
29
|
+
import { notifyUserDegradation } from "./user-notify.js";
|
|
26
30
|
const _require = createRequire(import.meta.url);
|
|
27
31
|
import { createTreeCacheCounters, TreeCache, } from "./tree-sitter-cache.js";
|
|
28
32
|
import { TreeSitterNavigator } from "./tree-sitter-navigator.js";
|
|
@@ -37,6 +41,10 @@ const TREE_SITTER_MAX_SCAN_FILES = 20_000;
|
|
|
37
41
|
// failure (offline lazy fetch, mid-scan load error) recover on a later scan
|
|
38
42
|
// instead of paying the 3.3x per-rule fallback for the process lifetime (#889).
|
|
39
43
|
const QUERY_BATCH_MAX_LOAD_FAILURES = 3;
|
|
44
|
+
// Keep post-filter tree walks bounded. A malformed or unexpectedly huge tree
|
|
45
|
+
// must not stall the batched query walk; the filter fails open when this cap
|
|
46
|
+
// is reached so unrelated diagnostics and the current match are preserved.
|
|
47
|
+
const NO_NESTED_ANCHOR_VISIT_CAP = 10_000;
|
|
40
48
|
const NOT_PARSED = { parsed: false };
|
|
41
49
|
function createParserCounters() {
|
|
42
50
|
return {
|
|
@@ -60,6 +68,8 @@ export class TreeSitterClient {
|
|
|
60
68
|
grammarsDir;
|
|
61
69
|
/** In-flight/settled lazy grammar fetches, keyed by wasm filename. */
|
|
62
70
|
grammarEnsurePromises = new Map();
|
|
71
|
+
trustBlockedGrammarNotifications = new Set();
|
|
72
|
+
trustNotificationsGeneration = getProjectTrustGeneration();
|
|
63
73
|
// biome-ignore lint/suspicious/noExplicitAny: Optional dependency loaded dynamically
|
|
64
74
|
ParserClass = null;
|
|
65
75
|
// biome-ignore lint/suspicious/noExplicitAny: Language loader from module
|
|
@@ -68,6 +78,38 @@ export class TreeSitterClient {
|
|
|
68
78
|
queryCache = new Map();
|
|
69
79
|
/** Combined multi-rule queries by language + rule-set identity (null = don't retry). */
|
|
70
80
|
queryBatchCache = new Map();
|
|
81
|
+
static QUERY_CACHE_MAX_ENTRIES = 256;
|
|
82
|
+
static QUERY_BATCH_CACHE_MAX_ENTRIES = 256;
|
|
83
|
+
queryCacheCap() {
|
|
84
|
+
const value = Number.parseInt(process.env.PI_LENS_TREE_SITTER_QUERY_CACHE_CAP ?? "", 10);
|
|
85
|
+
return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_CACHE_MAX_ENTRIES;
|
|
86
|
+
}
|
|
87
|
+
queryBatchCacheCap() {
|
|
88
|
+
const value = Number.parseInt(process.env.PI_LENS_TREE_SITTER_QUERY_BATCH_CACHE_CAP ?? "", 10);
|
|
89
|
+
return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES;
|
|
90
|
+
}
|
|
91
|
+
cacheQuery(key, value) {
|
|
92
|
+
this.queryCache.delete(key);
|
|
93
|
+
this.queryCache.set(key, value);
|
|
94
|
+
while (this.queryCache.size > this.queryCacheCap()) {
|
|
95
|
+
const oldest = this.queryCache.entries().next().value;
|
|
96
|
+
if (!oldest)
|
|
97
|
+
break;
|
|
98
|
+
this.queryCache.delete(oldest[0]);
|
|
99
|
+
oldest[1]?.query?.delete?.();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
cacheQueryBatch(key, value) {
|
|
103
|
+
this.queryBatchCache.delete(key);
|
|
104
|
+
this.queryBatchCache.set(key, value);
|
|
105
|
+
while (this.queryBatchCache.size > this.queryBatchCacheCap()) {
|
|
106
|
+
const oldest = this.queryBatchCache.entries().next().value;
|
|
107
|
+
if (!oldest)
|
|
108
|
+
break;
|
|
109
|
+
this.queryBatchCache.delete(oldest[0]);
|
|
110
|
+
oldest[1]?.query?.delete?.();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
71
113
|
/** Consecutive grammar-load failures per batch key — bounds load retries (#889). */
|
|
72
114
|
queryBatchLoadFailures = new Map();
|
|
73
115
|
queryLoader = new TreeSitterQueryLoader();
|
|
@@ -98,10 +140,40 @@ export class TreeSitterClient {
|
|
|
98
140
|
return false;
|
|
99
141
|
if (!this.wasmAborted) {
|
|
100
142
|
this.wasmAborted = true;
|
|
143
|
+
recordDegradation({
|
|
144
|
+
kind: "wasm-abort",
|
|
145
|
+
subject: "web-tree-sitter",
|
|
146
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
147
|
+
});
|
|
101
148
|
this.onWasmAbort?.();
|
|
102
149
|
}
|
|
103
150
|
return true;
|
|
104
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* O(1) runtime footprint counters (#1123 item 2 memory attribution): every
|
|
154
|
+
* field is a `Map.size` read, never an iteration over the maps' contents.
|
|
155
|
+
* `wasmMemoryBytes` is deliberately NOT included here — web-tree-sitter
|
|
156
|
+
* 0.25.10's Emscripten `Module` (which owns the WASM linear memory /
|
|
157
|
+
* `HEAPU8.buffer`) is a private closure variable in the package's
|
|
158
|
+
* `bindings.ts` and is not exposed through any public export (`Parser`,
|
|
159
|
+
* `Language`, `Query`, ...); reaching it would require either reflecting
|
|
160
|
+
* into the package's internal module state (brittle across web-tree-sitter
|
|
161
|
+
* versions/bundling) or overriding Emscripten's `wasmMemory` module option
|
|
162
|
+
* at `Parser.init()` time with a hand-constructed `WebAssembly.Memory`
|
|
163
|
+
* matching the library's own default page-count math (risks a memory-import
|
|
164
|
+
* mismatch that would break ALL structural analysis, for an
|
|
165
|
+
* observability-only feature). `process.memoryUsage().arrayBuffers` is the
|
|
166
|
+
* process-wide proxy used instead (WASM linear memory backs an ArrayBuffer,
|
|
167
|
+
* so it is included there) — see clients/memory-sampler.ts.
|
|
168
|
+
*/
|
|
169
|
+
getRuntimeStats() {
|
|
170
|
+
return {
|
|
171
|
+
languagesLoaded: this.languages.size,
|
|
172
|
+
parsersLoaded: this.parsers.size,
|
|
173
|
+
queryCacheSize: this.queryCache.size,
|
|
174
|
+
queryBatchCacheSize: this.queryBatchCache.size,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
105
177
|
getParseCacheStats() {
|
|
106
178
|
return {
|
|
107
179
|
...this.treeCache.getStats(),
|
|
@@ -145,7 +217,13 @@ export class TreeSitterClient {
|
|
|
145
217
|
/** Debug logging helper */
|
|
146
218
|
dbg(msg) {
|
|
147
219
|
if (this.verbose) {
|
|
148
|
-
|
|
220
|
+
// #1333: verbose gating is preserved, but the SINK is tree-sitter.log —
|
|
221
|
+
// a raw write would corrupt pi's frame the moment verbose is enabled.
|
|
222
|
+
logTreeSitterDiagnostic({
|
|
223
|
+
subsystem: "tree-sitter-client",
|
|
224
|
+
level: "debug",
|
|
225
|
+
message: msg,
|
|
226
|
+
});
|
|
149
227
|
}
|
|
150
228
|
}
|
|
151
229
|
/**
|
|
@@ -285,6 +363,32 @@ export class TreeSitterClient {
|
|
|
285
363
|
if (this.resolveGrammarFile(grammarFile)) {
|
|
286
364
|
return true;
|
|
287
365
|
}
|
|
366
|
+
if (!assertInstallAllowed(`tree-sitter grammar fetch: ${grammarFile}`)) {
|
|
367
|
+
const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable because the project is not trusted; ` +
|
|
368
|
+
`runtime grammar downloads are disabled until trust is granted.`;
|
|
369
|
+
logTreeSitterDiagnostic({
|
|
370
|
+
subsystem: "tree-sitter-client",
|
|
371
|
+
message: unavailable,
|
|
372
|
+
metadata: { grammarFile, outcome: "trust-gated" },
|
|
373
|
+
});
|
|
374
|
+
recordDegradation({
|
|
375
|
+
kind: "grammar-blocked",
|
|
376
|
+
subject: grammarFile,
|
|
377
|
+
reason: "runtime grammar download blocked because project is untrusted",
|
|
378
|
+
});
|
|
379
|
+
// Lazy clear-on-transition (#1363 review): compare the trust
|
|
380
|
+
// generation at use time -- no listener registration, no retention.
|
|
381
|
+
const generation = getProjectTrustGeneration();
|
|
382
|
+
if (generation !== this.trustNotificationsGeneration) {
|
|
383
|
+
this.trustNotificationsGeneration = generation;
|
|
384
|
+
this.trustBlockedGrammarNotifications.clear();
|
|
385
|
+
}
|
|
386
|
+
if (!this.trustBlockedGrammarNotifications.has(grammarFile)) {
|
|
387
|
+
this.trustBlockedGrammarNotifications.add(grammarFile);
|
|
388
|
+
notifyUserDegradation(`pi-lens: ${unavailable}`);
|
|
389
|
+
}
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
288
392
|
const inflight = this.grammarEnsurePromises.get(grammarFile);
|
|
289
393
|
if (inflight)
|
|
290
394
|
return inflight;
|
|
@@ -300,17 +404,36 @@ export class TreeSitterClient {
|
|
|
300
404
|
if (ok) {
|
|
301
405
|
if (!this.grammarsDir)
|
|
302
406
|
this.grammarsDir = dir;
|
|
303
|
-
|
|
407
|
+
logTreeSitterDiagnostic({
|
|
408
|
+
subsystem: "tree-sitter-client",
|
|
409
|
+
level: "warn",
|
|
410
|
+
message: `fetched missing tree-sitter grammar ${grammarFile} at runtime (install scripts were skipped by the package manager)`,
|
|
411
|
+
metadata: { grammarFile, outcome: "fetched" },
|
|
412
|
+
});
|
|
304
413
|
}
|
|
305
414
|
else {
|
|
306
415
|
// Surface the degradation once per grammar (the promise cache dedupes)
|
|
307
416
|
// instead of failing silently — otherwise pnpm/bun users offline get
|
|
308
417
|
// no signal that a language's tree-sitter features are unavailable.
|
|
309
|
-
|
|
418
|
+
const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable — ` +
|
|
310
419
|
`symbol search, module reports and structural rules for this language will be degraded. ` +
|
|
311
420
|
`The package manager skipped install scripts and the runtime download failed (offline or CDN unreachable). ` +
|
|
312
421
|
`Fix: reinstall with a manager that runs postinstall, allow its build scripts ` +
|
|
313
|
-
`(pnpm approve-builds / bun trustedDependencies), or restore network access
|
|
422
|
+
`(pnpm approve-builds / bun trustedDependencies), or restore network access.`;
|
|
423
|
+
logTreeSitterDiagnostic({
|
|
424
|
+
subsystem: "tree-sitter-client",
|
|
425
|
+
message: unavailable,
|
|
426
|
+
metadata: { grammarFile, outcome: "unavailable" },
|
|
427
|
+
});
|
|
428
|
+
recordDegradation({
|
|
429
|
+
kind: "grammar-blocked",
|
|
430
|
+
subject: grammarFile,
|
|
431
|
+
reason: "runtime grammar download failed",
|
|
432
|
+
});
|
|
433
|
+
// HUMAN-audience: an offline grammar fetch silently degrades this
|
|
434
|
+
// language's features, so it reaches the user through the HOST's
|
|
435
|
+
// render path (#1333) rather than a raw terminal write.
|
|
436
|
+
notifyUserDegradation(`pi-lens: ${unavailable}`);
|
|
314
437
|
}
|
|
315
438
|
return ok;
|
|
316
439
|
})();
|
|
@@ -393,6 +516,11 @@ export class TreeSitterClient {
|
|
|
393
516
|
const blockReason = grammarBlockReason(grammarFile);
|
|
394
517
|
if (blockReason) {
|
|
395
518
|
this.dbg(`Grammar ${grammarFile} blocked on this runtime — ${blockReason}`);
|
|
519
|
+
recordDegradation({
|
|
520
|
+
kind: "grammar-blocked",
|
|
521
|
+
subject: grammarFile,
|
|
522
|
+
reason: blockReason,
|
|
523
|
+
});
|
|
396
524
|
return null;
|
|
397
525
|
}
|
|
398
526
|
// Look across the bundled core `grammars/` dir and the postinstall/lazy
|
|
@@ -744,8 +872,11 @@ export class TreeSitterClient {
|
|
|
744
872
|
.digest("hex");
|
|
745
873
|
const cacheKey = this.getQueryCacheKey(`batch:${identity}`, languageId);
|
|
746
874
|
const cached = this.queryBatchCache.get(cacheKey);
|
|
747
|
-
if (cached !== undefined)
|
|
875
|
+
if (cached !== undefined) {
|
|
876
|
+
this.queryBatchCache.delete(cacheKey);
|
|
877
|
+
this.queryBatchCache.set(cacheKey, cached);
|
|
748
878
|
return cached;
|
|
879
|
+
}
|
|
749
880
|
// A loadLanguage() failure is transient (offline lazy grammar fetch,
|
|
750
881
|
// transient mid-scan load error) — do NOT cache null for it, or every
|
|
751
882
|
// later scan pays the ~3.3x per-rule fallback for the process lifetime
|
|
@@ -757,7 +888,7 @@ export class TreeSitterClient {
|
|
|
757
888
|
if (failures >= QUERY_BATCH_MAX_LOAD_FAILURES) {
|
|
758
889
|
this.dbg(`Batch: grammar for ${languageId} failed to load ${failures} times — caching miss`);
|
|
759
890
|
this.queryBatchLoadFailures.delete(cacheKey);
|
|
760
|
-
this.
|
|
891
|
+
this.cacheQueryBatch(cacheKey, null);
|
|
761
892
|
}
|
|
762
893
|
else {
|
|
763
894
|
this.queryBatchLoadFailures.set(cacheKey, failures);
|
|
@@ -817,7 +948,7 @@ export class TreeSitterClient {
|
|
|
817
948
|
}
|
|
818
949
|
};
|
|
819
950
|
const batch = await build();
|
|
820
|
-
this.
|
|
951
|
+
this.cacheQueryBatch(cacheKey, batch);
|
|
821
952
|
return batch;
|
|
822
953
|
}
|
|
823
954
|
/**
|
|
@@ -928,7 +1059,10 @@ export class TreeSitterClient {
|
|
|
928
1059
|
// Check cache first
|
|
929
1060
|
if (this.queryCache.has(cacheKey)) {
|
|
930
1061
|
this.dbg(`Query cache hit: ${cacheKey}`);
|
|
931
|
-
|
|
1062
|
+
const cached = this.queryCache.get(cacheKey);
|
|
1063
|
+
this.queryCache.delete(cacheKey);
|
|
1064
|
+
this.queryCache.set(cacheKey, cached);
|
|
1065
|
+
return cached;
|
|
932
1066
|
}
|
|
933
1067
|
const language = await this.loadLanguage(languageId);
|
|
934
1068
|
if (!language) {
|
|
@@ -945,7 +1079,7 @@ export class TreeSitterClient {
|
|
|
945
1079
|
this.dbg(`Query compiled with ${query.patternCount()} patterns`);
|
|
946
1080
|
const result = { query, metavars, postFilter, postFilterParams };
|
|
947
1081
|
// Cache the compiled query
|
|
948
|
-
this.
|
|
1082
|
+
this.cacheQuery(cacheKey, result);
|
|
949
1083
|
return result;
|
|
950
1084
|
}
|
|
951
1085
|
catch (err) {
|
|
@@ -958,7 +1092,10 @@ export class TreeSitterClient {
|
|
|
958
1092
|
async compileRawQuery(queryId, queryStr, metavars, languageId, postFilter, postFilterParams) {
|
|
959
1093
|
const cacheKey = this.getQueryCacheKey(`raw:${queryId}:${queryStr}`, languageId);
|
|
960
1094
|
if (this.queryCache.has(cacheKey)) {
|
|
961
|
-
|
|
1095
|
+
const cached = this.queryCache.get(cacheKey);
|
|
1096
|
+
this.queryCache.delete(cacheKey);
|
|
1097
|
+
this.queryCache.set(cacheKey, cached);
|
|
1098
|
+
return cached;
|
|
962
1099
|
}
|
|
963
1100
|
const language = await this.loadLanguage(languageId);
|
|
964
1101
|
if (!language)
|
|
@@ -969,7 +1106,7 @@ export class TreeSitterClient {
|
|
|
969
1106
|
// biome-ignore lint/suspicious/noExplicitAny: Language type compatibility
|
|
970
1107
|
const query = new Query(language, queryStr);
|
|
971
1108
|
const result = { query, metavars, postFilter, postFilterParams };
|
|
972
|
-
this.
|
|
1109
|
+
this.cacheQuery(cacheKey, result);
|
|
973
1110
|
return result;
|
|
974
1111
|
}
|
|
975
1112
|
catch (err) {
|
|
@@ -989,13 +1126,59 @@ export class TreeSitterClient {
|
|
|
989
1126
|
if (this.reportedCompileFailures.has(key))
|
|
990
1127
|
return;
|
|
991
1128
|
this.reportedCompileFailures.add(key);
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1129
|
+
logTreeSitterDiagnostic({
|
|
1130
|
+
subsystem: "tree-sitter-client",
|
|
1131
|
+
languageId,
|
|
1132
|
+
message: `tree-sitter rule '${ruleId}' failed to compile against '${languageId}' — ` +
|
|
1133
|
+
`matches for this rule are silently dropped rather than reported. ` +
|
|
1134
|
+
`Fix the query in the rule definition to re-enable it. (${err})`,
|
|
1135
|
+
metadata: { ruleId },
|
|
1136
|
+
});
|
|
995
1137
|
}
|
|
996
1138
|
hasChildToken(node, token) {
|
|
997
1139
|
return node.children?.some((child) => child.type === token || child.text === token);
|
|
998
1140
|
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Collect names introduced by a parameter binding pattern.
|
|
1143
|
+
*
|
|
1144
|
+
* Binding-aware: only identifiers in a *binding* position are counted.
|
|
1145
|
+
* Two node shapes hold reference expressions rather than bindings and
|
|
1146
|
+
* must not be descended into:
|
|
1147
|
+
* - `assignment_pattern` / `object_assignment_pattern` (`pattern = default`,
|
|
1148
|
+
* e.g. `[a = b]` or `{a = b}`) — the `value`/`right` side is a default
|
|
1149
|
+
* *expression*, not a new name (`b` in `{a = b}` is a reference).
|
|
1150
|
+
* - `computed_property_name` (`[expr]:` in a destructuring pattern,
|
|
1151
|
+
* e.g. `{[key]: a}`) — `expr` is a reference, not a binding.
|
|
1152
|
+
* Nested binding positions (destructured sub-patterns) are still walked
|
|
1153
|
+
* so renamed/duplicate collisions inside them are found.
|
|
1154
|
+
*/
|
|
1155
|
+
bindingNames(node) {
|
|
1156
|
+
const names = new Set();
|
|
1157
|
+
if (!node)
|
|
1158
|
+
return names;
|
|
1159
|
+
const stack = [node];
|
|
1160
|
+
while (stack.length > 0) {
|
|
1161
|
+
const current = stack.pop();
|
|
1162
|
+
if (current.type === "identifier")
|
|
1163
|
+
names.add(current.text);
|
|
1164
|
+
else if (current.type === "shorthand_property_identifier_pattern") {
|
|
1165
|
+
names.add(current.text);
|
|
1166
|
+
}
|
|
1167
|
+
if (current.type === "property_identifier" ||
|
|
1168
|
+
current.type === "type_identifier" ||
|
|
1169
|
+
current.type === "computed_property_name")
|
|
1170
|
+
continue;
|
|
1171
|
+
if (current.type === "assignment_pattern" ||
|
|
1172
|
+
current.type === "object_assignment_pattern") {
|
|
1173
|
+
const left = current.childForFieldName?.("left");
|
|
1174
|
+
if (left)
|
|
1175
|
+
stack.push(left);
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
stack.push(...(current.children ?? []));
|
|
1179
|
+
}
|
|
1180
|
+
return names;
|
|
1181
|
+
}
|
|
999
1182
|
containsYieldInFunctionBody(node, root = node) {
|
|
1000
1183
|
for (const child of node.children ?? []) {
|
|
1001
1184
|
if (child.type === "yield")
|
|
@@ -1030,6 +1213,122 @@ export class TreeSitterClient {
|
|
|
1030
1213
|
// First named child is the case's value expression (`case <value>`).
|
|
1031
1214
|
return named.slice(1);
|
|
1032
1215
|
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Whether a case body carries an intentional fallthrough marker comment
|
|
1218
|
+
* (`// fallthrough`, `// falls through`, …). Only a *trailing* marker is
|
|
1219
|
+
* honored: a comment attached to the case node after its body statements
|
|
1220
|
+
* (TypeScript), the case's next sibling in the switch body (JavaScript), or
|
|
1221
|
+
* one inside the trailing body statement's block. A comment in a nested
|
|
1222
|
+
* function or an earlier statement is not a marker for this case and must
|
|
1223
|
+
* not suppress a genuine fall-through. Only comment nodes are checked, never
|
|
1224
|
+
* the case value or statement text.
|
|
1225
|
+
*/
|
|
1226
|
+
hasFallthroughMarker(caseNode) {
|
|
1227
|
+
const comments = [];
|
|
1228
|
+
const body = this.switchCaseBodyStatements(caseNode);
|
|
1229
|
+
const last = body[body.length - 1];
|
|
1230
|
+
const lastEnd = last?.endIndex ?? caseNode.startIndex;
|
|
1231
|
+
// TypeScript attaches a trailing `// fallthrough` as a direct child of the
|
|
1232
|
+
// case node, after the body statements.
|
|
1233
|
+
for (const c of caseNode.children ?? []) {
|
|
1234
|
+
if (c.type.includes("comment") && c.startIndex >= lastEnd) {
|
|
1235
|
+
comments.push(c);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// JavaScript attaches it to the switch body as the case's next sibling.
|
|
1239
|
+
const siblings = (caseNode.parent?.children ?? []).filter((c) => c.isNamed);
|
|
1240
|
+
const index = siblings.findIndex((c) => c.startIndex === caseNode.startIndex);
|
|
1241
|
+
const next = index >= 0 ? siblings[index + 1] : undefined;
|
|
1242
|
+
if (next?.type.includes("comment"))
|
|
1243
|
+
comments.push(next);
|
|
1244
|
+
// A marker inside the trailing body statement's block (e.g. `{ work();
|
|
1245
|
+
// /* fallthrough */ }`). Only the trailing statement is walked, and nested
|
|
1246
|
+
// functions are not descended into, so a comment in a nested function or
|
|
1247
|
+
// an earlier statement can't suppress a genuine fall-through.
|
|
1248
|
+
if (last) {
|
|
1249
|
+
const NESTED_FN = new Set([
|
|
1250
|
+
"function_declaration",
|
|
1251
|
+
"function_expression",
|
|
1252
|
+
"arrow_function",
|
|
1253
|
+
"method_definition",
|
|
1254
|
+
"generator_function",
|
|
1255
|
+
"generator_function_declaration",
|
|
1256
|
+
"class_declaration",
|
|
1257
|
+
]);
|
|
1258
|
+
const stack = [last];
|
|
1259
|
+
for (let visited = 0; stack.length > 0 && visited < 500; visited++) {
|
|
1260
|
+
const node = stack.pop();
|
|
1261
|
+
if (!node)
|
|
1262
|
+
break;
|
|
1263
|
+
if (node.type.includes("comment"))
|
|
1264
|
+
comments.push(node);
|
|
1265
|
+
if (!NESTED_FN.has(node.type)) {
|
|
1266
|
+
stack.push(...(node.children ?? []));
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return comments.some((c) => /falls?\s?through/i.test(c.text));
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Whether a statement terminates control flow (does not fall through to the
|
|
1274
|
+
* next statement). Handles terminators, trailing blocks, try/catch/finally,
|
|
1275
|
+
* and exhaustive if/else. Fail-safe: any unrecognized shape returns false
|
|
1276
|
+
* (falls through), so a blocking rule never under-reports. Depth-bounded so a
|
|
1277
|
+
* pathological nesting cannot blow the stack.
|
|
1278
|
+
*/
|
|
1279
|
+
statementTerminates(node, depth = 0) {
|
|
1280
|
+
if (depth > 20)
|
|
1281
|
+
return false;
|
|
1282
|
+
const t = node.type;
|
|
1283
|
+
const TERMINATORS = new Set([
|
|
1284
|
+
"break_statement",
|
|
1285
|
+
"return_statement",
|
|
1286
|
+
"throw_statement",
|
|
1287
|
+
"continue_statement",
|
|
1288
|
+
]);
|
|
1289
|
+
if (TERMINATORS.has(t))
|
|
1290
|
+
return true;
|
|
1291
|
+
// Wrapper nodes whose trailing statement decides termination: a block, an
|
|
1292
|
+
// `else` clause, a `catch` clause, or a `finally` clause.
|
|
1293
|
+
if (t === "statement_block" ||
|
|
1294
|
+
t === "else_clause" ||
|
|
1295
|
+
t === "catch_clause" ||
|
|
1296
|
+
t === "finally_clause") {
|
|
1297
|
+
const inner = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1298
|
+
const last = inner[inner.length - 1];
|
|
1299
|
+
if (!last)
|
|
1300
|
+
return false; // empty wrapper falls through
|
|
1301
|
+
return this.statementTerminates(last, depth + 1);
|
|
1302
|
+
}
|
|
1303
|
+
if (t === "try_statement") {
|
|
1304
|
+
// The try body is the first statement_block child. A returning/throwing
|
|
1305
|
+
// try completes after finally, so only a catch that handles a thrown try
|
|
1306
|
+
// and falls through can still reach the end. A try body that completes
|
|
1307
|
+
// normally reaches the end unless a finally terminates it.
|
|
1308
|
+
const tryBody = (node.children ?? []).find((c) => c.type === "statement_block");
|
|
1309
|
+
const tryTerminates = tryBody
|
|
1310
|
+
? this.statementTerminates(tryBody, depth + 1)
|
|
1311
|
+
: false;
|
|
1312
|
+
const catches = (node.children ?? []).filter((c) => c.type === "catch_clause");
|
|
1313
|
+
const fin = (node.children ?? []).find((c) => c.type === "finally_clause");
|
|
1314
|
+
if (tryTerminates) {
|
|
1315
|
+
return catches.every((c) => this.statementTerminates(c, depth + 1));
|
|
1316
|
+
}
|
|
1317
|
+
return !!fin && this.statementTerminates(fin, depth + 1);
|
|
1318
|
+
}
|
|
1319
|
+
if (t === "if_statement") {
|
|
1320
|
+
// An if only terminates when it has an else and both branches do.
|
|
1321
|
+
const named = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1322
|
+
// named = [condition, consequence, (else_clause)]
|
|
1323
|
+
const consequence = named[1];
|
|
1324
|
+
const alternative = named[2];
|
|
1325
|
+
if (!consequence || !alternative)
|
|
1326
|
+
return false;
|
|
1327
|
+
return (this.statementTerminates(consequence, depth + 1) &&
|
|
1328
|
+
this.statementTerminates(alternative, depth + 1));
|
|
1329
|
+
}
|
|
1330
|
+
return false;
|
|
1331
|
+
}
|
|
1033
1332
|
/**
|
|
1034
1333
|
* Whether a `switch_case`'s next sibling is another label — `case "a": case
|
|
1035
1334
|
* "b": handle()` groups two labels onto one body, which is idiomatic, not a
|
|
@@ -1524,7 +1823,71 @@ export class TreeSitterClient {
|
|
|
1524
1823
|
}
|
|
1525
1824
|
return slots;
|
|
1526
1825
|
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Escape a string for safe interpolation into a `RegExp` source —
|
|
1828
|
+
* needed anywhere an identifier's raw text is combined with regex
|
|
1829
|
+
* metacharacters like `\b` word boundaries (see
|
|
1830
|
+
* "not_closed_or_try_with_resources" below; #1089 P2).
|
|
1831
|
+
*/
|
|
1832
|
+
function escapeRegExp(s) {
|
|
1833
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1834
|
+
}
|
|
1527
1835
|
switch (postFilter) {
|
|
1836
|
+
case "no_nested_anchor_chain": {
|
|
1837
|
+
// Tree-sitter queries can express the opening-tag shape, but they
|
|
1838
|
+
// cannot express both an arbitrary-depth descendant relationship and
|
|
1839
|
+
// an ancestor exclusion. Walk the captured JSX element here so the
|
|
1840
|
+
// TSX path agrees with the ast-grep rule: keep exactly the outermost
|
|
1841
|
+
// anchor that contains another anchor, including through wrappers.
|
|
1842
|
+
// This post-filter runs inside a batched query walk. It must never
|
|
1843
|
+
// abort that walk or turn a malformed tree into a false negative.
|
|
1844
|
+
try {
|
|
1845
|
+
const outer = captures.OUTER_ELEMENT;
|
|
1846
|
+
if (!outer) {
|
|
1847
|
+
this.reportPostFilterFailure(postFilter, "missing OUTER_ELEMENT capture");
|
|
1848
|
+
return true;
|
|
1849
|
+
}
|
|
1850
|
+
const isAnchorElement = (node) => {
|
|
1851
|
+
if (node.type !== "jsx_element")
|
|
1852
|
+
return false;
|
|
1853
|
+
const opening = node.childForFieldName?.("open_tag") ??
|
|
1854
|
+
node.children?.find((child) => child.type === "jsx_opening_element");
|
|
1855
|
+
const name = opening?.childForFieldName?.("name") ??
|
|
1856
|
+
opening?.children?.find((child) => child.type === "identifier");
|
|
1857
|
+
return name?.text === "a";
|
|
1858
|
+
};
|
|
1859
|
+
let visited = 0;
|
|
1860
|
+
let ancestor = outer.parent;
|
|
1861
|
+
while (ancestor) {
|
|
1862
|
+
if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
|
|
1863
|
+
this.reportPostFilterFailure(postFilter, `ancestor walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
|
|
1864
|
+
return true;
|
|
1865
|
+
}
|
|
1866
|
+
if (isAnchorElement(ancestor))
|
|
1867
|
+
return false;
|
|
1868
|
+
ancestor = ancestor.parent;
|
|
1869
|
+
}
|
|
1870
|
+
const pending = [...(outer.children ?? [])];
|
|
1871
|
+
while (pending.length > 0) {
|
|
1872
|
+
const node = pending.pop();
|
|
1873
|
+
if (!node)
|
|
1874
|
+
continue;
|
|
1875
|
+
if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
|
|
1876
|
+
this.reportPostFilterFailure(postFilter, `descendant walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
if (isAnchorElement(node))
|
|
1880
|
+
return true;
|
|
1881
|
+
pending.push(...(node.children ?? []));
|
|
1882
|
+
}
|
|
1883
|
+
return false;
|
|
1884
|
+
}
|
|
1885
|
+
catch (error) {
|
|
1886
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1887
|
+
this.reportPostFilterFailure(postFilter, `tree walk failed${reason ? `: ${reason}` : ""}`);
|
|
1888
|
+
return true;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1528
1891
|
case "differs_only_by_case": {
|
|
1529
1892
|
try {
|
|
1530
1893
|
const field = captures.FIELD?.text ?? "";
|
|
@@ -1637,7 +2000,11 @@ export class TreeSitterClient {
|
|
|
1637
2000
|
]) ?? rootNode;
|
|
1638
2001
|
if (!scope)
|
|
1639
2002
|
return true;
|
|
1640
|
-
|
|
2003
|
+
// `\b` in a template literal is the BACKSPACE control char
|
|
2004
|
+
// (U+0008), not a regex word-boundary escape — that requires
|
|
2005
|
+
// the literal two-character sequence `\\b`. The identifier
|
|
2006
|
+
// itself must also be regex-escaped (#1089 P2).
|
|
2007
|
+
const resourceWord = new RegExp(`\\b${escapeRegExp(resource)}\\b`);
|
|
1641
2008
|
const stack = [scope];
|
|
1642
2009
|
for (let visited = 0; stack.length > 0 && visited < 10_000; visited++) {
|
|
1643
2010
|
const node = stack.pop();
|
|
@@ -1846,41 +2213,27 @@ export class TreeSitterClient {
|
|
|
1846
2213
|
return !this.bodyHasLoopExit(bodyNode, false);
|
|
1847
2214
|
}
|
|
1848
2215
|
case "same_param_name": {
|
|
1849
|
-
// duplicate-function-arg: keep the pair
|
|
1850
|
-
//
|
|
1851
|
-
const first = captures.PARAM1
|
|
1852
|
-
const second = captures.NAME
|
|
1853
|
-
return
|
|
2216
|
+
// duplicate-function-arg: keep the pair when the captured binding
|
|
2217
|
+
// patterns share any name.
|
|
2218
|
+
const first = this.bindingNames(captures.PARAM1);
|
|
2219
|
+
const second = this.bindingNames(captures.NAME);
|
|
2220
|
+
return [...first].some((name) => second.has(name));
|
|
1854
2221
|
}
|
|
1855
2222
|
case "no_terminating_statement": {
|
|
1856
2223
|
// switch-case-termination: keep a non-empty case (already known to be
|
|
1857
|
-
// followed by another case via the query) whose last body statement
|
|
1858
|
-
// not
|
|
1859
|
-
// empty-switch-case, so require at least one body statement here.
|
|
2224
|
+
// followed by another case via the query) whose last body statement
|
|
2225
|
+
// does not terminate, i.e. it falls through. Empty cases are handled
|
|
2226
|
+
// by empty-switch-case, so require at least one body statement here.
|
|
2227
|
+
// An intentional `// fallthrough` marker suppresses the finding.
|
|
1860
2228
|
const caseNode = captures.CASE;
|
|
1861
2229
|
if (!caseNode)
|
|
1862
2230
|
return false;
|
|
2231
|
+
if (this.hasFallthroughMarker(caseNode))
|
|
2232
|
+
return false;
|
|
1863
2233
|
const body = this.switchCaseBodyStatements(caseNode);
|
|
1864
2234
|
if (body.length === 0)
|
|
1865
2235
|
return false;
|
|
1866
|
-
|
|
1867
|
-
while (last.type === "statement_block") {
|
|
1868
|
-
// A block-scoped case body terminates when its trailing statement
|
|
1869
|
-
// terminates. Keep an empty block as the last node so it still
|
|
1870
|
-
// reports fall-through.
|
|
1871
|
-
const inner = (last.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1872
|
-
const next = inner[inner.length - 1];
|
|
1873
|
-
if (!next)
|
|
1874
|
-
break;
|
|
1875
|
-
last = next;
|
|
1876
|
-
}
|
|
1877
|
-
const TERMINATORS = new Set([
|
|
1878
|
-
"break_statement",
|
|
1879
|
-
"return_statement",
|
|
1880
|
-
"throw_statement",
|
|
1881
|
-
"continue_statement",
|
|
1882
|
-
]);
|
|
1883
|
-
return !TERMINATORS.has(last.type);
|
|
2236
|
+
return !this.statementTerminates(body[body.length - 1]);
|
|
1884
2237
|
}
|
|
1885
2238
|
case "no_break_or_return": {
|
|
1886
2239
|
// infinite-loop-java: keep a `while(true)`/`for(;;)` whose body has no
|
|
@@ -1981,18 +2334,17 @@ export class TreeSitterClient {
|
|
|
1981
2334
|
// A typed `except` clause has a named child for the exception
|
|
1982
2335
|
// spec — one of: identifier (e.g. `except ValueError`),
|
|
1983
2336
|
// attribute (e.g. `except asyncio.TimeoutError` — dotted name),
|
|
1984
|
-
// tuple (e.g. `except (E, F)`),
|
|
2337
|
+
// tuple (e.g. `except (E, F)`), as_pattern (e.g. `except E as e`),
|
|
2338
|
+
// parenthesized_expression (e.g. `except (E)`), or subscript
|
|
2339
|
+
// (e.g. `except dict[str, int]` — #1244, mirrors the ast-grep
|
|
2340
|
+
// rule's shape space).
|
|
1985
2341
|
// Bare `except:` has NO named children (just the `except` keyword,
|
|
1986
2342
|
// the `:` colon, and the body block).
|
|
1987
2343
|
// biome-ignore lint/suspicious/noExplicitAny: AST iteration
|
|
1988
2344
|
const hasExceptionSpec = clauseNode.children.some((c) => {
|
|
1989
2345
|
if (!c.isNamed)
|
|
1990
2346
|
return false;
|
|
1991
|
-
return (c.type
|
|
1992
|
-
c.type === "attribute" ||
|
|
1993
|
-
c.type === "tuple" ||
|
|
1994
|
-
c.type === "as_pattern" ||
|
|
1995
|
-
c.type === "parenthesized_expression");
|
|
2347
|
+
return (c.type !== "block");
|
|
1996
2348
|
});
|
|
1997
2349
|
// Fire ONLY when bare (no exception spec)
|
|
1998
2350
|
return !hasExceptionSpec;
|
|
@@ -2756,14 +3108,31 @@ export class TreeSitterClient {
|
|
|
2756
3108
|
}
|
|
2757
3109
|
}
|
|
2758
3110
|
reportedMissingPostFilters = new Set();
|
|
3111
|
+
reportedPostFilterFailures = new Set();
|
|
3112
|
+
/** Keep a failed post-filter match and leave a bounded diagnostic trail. */
|
|
3113
|
+
reportPostFilterFailure(postFilter, reason) {
|
|
3114
|
+
if (this.reportedPostFilterFailures.has(postFilter))
|
|
3115
|
+
return;
|
|
3116
|
+
this.reportedPostFilterFailures.add(postFilter);
|
|
3117
|
+
logTreeSitterDiagnostic({
|
|
3118
|
+
subsystem: "tree-sitter-client",
|
|
3119
|
+
message: `tree-sitter rule post_filter '${postFilter}' failed — ` +
|
|
3120
|
+
`keeping the diagnostic (fail-open): ${reason}.`,
|
|
3121
|
+
metadata: { postFilter },
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
2759
3124
|
/** Warn once per unimplemented post_filter — a silent rule needs a trail. */
|
|
2760
3125
|
reportMissingPostFilter(postFilter) {
|
|
2761
3126
|
if (this.reportedMissingPostFilters.has(postFilter))
|
|
2762
3127
|
return;
|
|
2763
3128
|
this.reportedMissingPostFilters.add(postFilter);
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
`
|
|
3129
|
+
logTreeSitterDiagnostic({
|
|
3130
|
+
subsystem: "tree-sitter-client",
|
|
3131
|
+
message: `tree-sitter rule post_filter '${postFilter}' is not implemented — ` +
|
|
3132
|
+
`matches for the rules using it are suppressed rather than reported unfiltered. ` +
|
|
3133
|
+
`Implement it in applyPostFilter (clients/tree-sitter-client.ts) to re-enable them.`,
|
|
3134
|
+
metadata: { postFilter },
|
|
3135
|
+
});
|
|
2767
3136
|
}
|
|
2768
3137
|
/**
|
|
2769
3138
|
* Evaluate text predicates (#match?, #eq?) for a query match.
|