@herbertgao/pi-extensions 2026.8.5 → 2026.8.7
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 +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.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 +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -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 +61 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- 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/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- 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/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- 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-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- 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-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- 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-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -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 +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- 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 +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +13 -13
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
18
|
+
import { createDeadline, forEachCooperatively, yieldIfOverBudget, } from "./cooperative-budget.js";
|
|
19
|
+
import { KIND_EXTENSIONS } from "./file-kinds.js";
|
|
18
20
|
import { PathKeyedMap } from "./path-keyed-map.js";
|
|
19
21
|
import { isAtOrAboveHomeDir, normalizeEphemeralMapKey } from "./path-utils.js";
|
|
20
22
|
import { createDebounceScheduler, } from "./persist-debounce.js";
|
|
21
23
|
import { getWordIndexMaxFilesDerived } from "./project-scale.js";
|
|
22
24
|
import { logWordIndex } from "./word-index-logger.js";
|
|
23
|
-
import { createDeadline, forEachCooperatively, yieldIfOverBudget, } from "./cooperative-budget.js";
|
|
24
25
|
/**
|
|
25
26
|
* The single normalizer every word-index path key folds through (#1025). The
|
|
26
27
|
* CHEAP form — slash-fold + win32-lowercase, NO filesystem I/O — because the
|
|
@@ -38,11 +39,50 @@ export const wordIndexKey = normalizeEphemeralMapKey;
|
|
|
38
39
|
// postings without improving relevance. Kept deliberately small and
|
|
39
40
|
// language-agnostic.
|
|
40
41
|
const STOPWORDS = new Set([
|
|
41
|
-
"the",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
42
|
+
"the",
|
|
43
|
+
"and",
|
|
44
|
+
"for",
|
|
45
|
+
"let",
|
|
46
|
+
"var",
|
|
47
|
+
"const",
|
|
48
|
+
"function",
|
|
49
|
+
"return",
|
|
50
|
+
"if",
|
|
51
|
+
"else",
|
|
52
|
+
"import",
|
|
53
|
+
"export",
|
|
54
|
+
"from",
|
|
55
|
+
"class",
|
|
56
|
+
"interface",
|
|
57
|
+
"type",
|
|
58
|
+
"enum",
|
|
59
|
+
"new",
|
|
60
|
+
"this",
|
|
61
|
+
"self",
|
|
62
|
+
"void",
|
|
63
|
+
"null",
|
|
64
|
+
"true",
|
|
65
|
+
"false",
|
|
66
|
+
"async",
|
|
67
|
+
"await",
|
|
68
|
+
"public",
|
|
69
|
+
"private",
|
|
70
|
+
"protected",
|
|
71
|
+
"static",
|
|
72
|
+
"def",
|
|
73
|
+
"fn",
|
|
74
|
+
"func",
|
|
75
|
+
"struct",
|
|
76
|
+
"impl",
|
|
77
|
+
"pub",
|
|
78
|
+
"use",
|
|
79
|
+
"mod",
|
|
80
|
+
"in",
|
|
81
|
+
"of",
|
|
82
|
+
"as",
|
|
83
|
+
"is",
|
|
84
|
+
"not",
|
|
85
|
+
"with",
|
|
46
86
|
]);
|
|
47
87
|
const TEST_VENDOR_RE = /(?:(^|[\\/])(?:tests?|__tests__|spec|specs|__mocks__|vendor|node_modules|examples?|fixtures?|\.git|dist|build|coverage)([\\/]|$))|(?:\.(?:test|spec)\.[a-z]+$)/i;
|
|
48
88
|
const DOC_FILE_RE = /\.(?:md|mdx|markdown|json|json5|jsonc|txt|rst|lock|ya?ml|toml|csv)$/i;
|
|
@@ -498,6 +538,10 @@ export async function collectWordIndexDocs(root, shouldContinue = () => true, pr
|
|
|
498
538
|
}
|
|
499
539
|
return docs;
|
|
500
540
|
}
|
|
541
|
+
// Node's fs.promises.stat runs on libuv's threadpool (default 4 slots), so
|
|
542
|
+
// real parallelism tops out there; the surplus workers are queue depth that
|
|
543
|
+
// keeps the pool saturated without starving other threadpool consumers.
|
|
544
|
+
export const WORD_INDEX_STAT_CONCURRENCY = 8;
|
|
501
545
|
const WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD = 0.3;
|
|
502
546
|
// A ratio alone misclassifies one stale file in a three-file project as dense.
|
|
503
547
|
// Below this absolute floor, per-document replacement is bounded and cheaper
|
|
@@ -531,19 +575,22 @@ const WORD_INDEX_POSTING_SCAN_TOKEN_COST = 0.2;
|
|
|
531
575
|
* tokenized. Expected dense/legacy states return `full-required` BEFORE any
|
|
532
576
|
* mutation; unexpected corruption or supersession still throws.
|
|
533
577
|
*/
|
|
534
|
-
export async function refreshWordIndexIncrementally(index, root, shouldContinue = () => true) {
|
|
578
|
+
export async function refreshWordIndexIncrementally(index, root, shouldContinue = () => true, options = {}) {
|
|
535
579
|
if (!index.forward || !index.fileMtimes || !index.fileSizes) {
|
|
536
580
|
return {
|
|
537
581
|
mode: "full-required",
|
|
538
582
|
reason: "missing-incremental-metadata",
|
|
583
|
+
timings: { sourceWalkMs: 0, statWalkMs: 0, refreshReadsMs: 0 },
|
|
539
584
|
};
|
|
540
585
|
}
|
|
541
586
|
const { collectSourceFilesAsync } = await import("./source-filter.js");
|
|
542
587
|
const maxFiles = getWordIndexMaxFilesDerived(root);
|
|
588
|
+
const sourceWalkStartMs = Date.now();
|
|
543
589
|
const walked = await collectSourceFilesAsync(root, {
|
|
544
590
|
maxFiles,
|
|
545
591
|
prioritizeCodeKinds: true,
|
|
546
592
|
});
|
|
593
|
+
const sourceWalkMs = Date.now() - sourceWalkStartMs;
|
|
547
594
|
if (!shouldContinue())
|
|
548
595
|
throw new Error("word index refresh superseded");
|
|
549
596
|
// This set-difference must run in the SAME normalized key space the path
|
|
@@ -559,30 +606,50 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
559
606
|
// `wordIndexKey` keeps build/edit/refresh convergent; `current`'s value
|
|
560
607
|
// retains the raw walk path for the stat/read and for the display key.
|
|
561
608
|
const current = new Map();
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
609
|
+
const statWalkStartMs = Date.now();
|
|
610
|
+
const statFile = options.statFile ?? ((file) => fs.promises.stat(file));
|
|
611
|
+
const requestedConcurrency = options.statConcurrency ?? WORD_INDEX_STAT_CONCURRENCY;
|
|
612
|
+
const statConcurrency = Number.isFinite(requestedConcurrency) && requestedConcurrency > 0
|
|
613
|
+
? Math.max(1, Math.floor(requestedConcurrency))
|
|
614
|
+
: WORD_INDEX_STAT_CONCURRENCY;
|
|
615
|
+
const statResults = new Array(walked.length);
|
|
616
|
+
let cursor = 0;
|
|
617
|
+
let superseded = false;
|
|
618
|
+
const worker = async () => {
|
|
619
|
+
while (true) {
|
|
620
|
+
if (!shouldContinue()) {
|
|
621
|
+
superseded = true;
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const slot = cursor++;
|
|
625
|
+
if (slot >= walked.length)
|
|
626
|
+
return;
|
|
627
|
+
const file = walked[slot];
|
|
628
|
+
try {
|
|
629
|
+
const stat = await statFile(file);
|
|
630
|
+
if (stat.size <= WORD_INDEX_MAX_BYTES) {
|
|
631
|
+
statResults[slot] = {
|
|
632
|
+
path: file,
|
|
633
|
+
mtimeMs: stat.mtimeMs,
|
|
634
|
+
size: stat.size,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
// A rejection has statSync parity: the file is simply absent.
|
|
576
640
|
}
|
|
577
641
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
642
|
+
};
|
|
643
|
+
await Promise.all(Array.from({ length: Math.min(statConcurrency, walked.length) }, () => worker()));
|
|
644
|
+
if (superseded || !shouldContinue()) {
|
|
645
|
+
throw new Error("word index refresh superseded");
|
|
646
|
+
}
|
|
647
|
+
const statWalkMs = Date.now() - statWalkStartMs;
|
|
648
|
+
for (const result of statResults) {
|
|
649
|
+
if (result)
|
|
650
|
+
current.set(wordIndexKey(result.path), result);
|
|
585
651
|
}
|
|
652
|
+
const timings = { sourceWalkMs, statWalkMs, refreshReadsMs: 0 };
|
|
586
653
|
const preflightFiles = Object.assign([...current.values()], {
|
|
587
654
|
truncated: walked.length === maxFiles,
|
|
588
655
|
});
|
|
@@ -596,7 +663,12 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
596
663
|
changedSet++;
|
|
597
664
|
const denominator = Math.max(oldSet.size, current.size, 1);
|
|
598
665
|
if (changedSet / denominator > WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD) {
|
|
599
|
-
return {
|
|
666
|
+
return {
|
|
667
|
+
mode: "full-required",
|
|
668
|
+
reason: "file-set-churn",
|
|
669
|
+
preflightFiles,
|
|
670
|
+
timings,
|
|
671
|
+
};
|
|
600
672
|
}
|
|
601
673
|
// Replacing one document filters every shared posting array for that
|
|
602
674
|
// document's tokens. That is excellent for a sparse edit, but repeating it
|
|
@@ -667,8 +739,10 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
667
739
|
mode: "full-required",
|
|
668
740
|
reason: "stale-document-churn",
|
|
669
741
|
preflightFiles,
|
|
742
|
+
timings,
|
|
670
743
|
};
|
|
671
744
|
}
|
|
745
|
+
const deadline = createDeadline(WORD_INDEX_BUILD_YIELD_BUDGET_MS);
|
|
672
746
|
let dropped = 0;
|
|
673
747
|
for (const key of oldSet) {
|
|
674
748
|
if (!current.has(key)) {
|
|
@@ -682,6 +756,7 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
682
756
|
}
|
|
683
757
|
let refreshed = 0;
|
|
684
758
|
let skipped = 0;
|
|
759
|
+
const refreshReadsStartMs = Date.now();
|
|
685
760
|
for (const { path: file, mtimeMs, size } of current.values()) {
|
|
686
761
|
// #1105: mtime-first, size-second freshness — mtime alone is a stale
|
|
687
762
|
// signal (mtime-preserving content changes serve stale identifiers to
|
|
@@ -726,6 +801,7 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
726
801
|
throw new Error("word index refresh superseded");
|
|
727
802
|
}
|
|
728
803
|
}
|
|
804
|
+
timings.refreshReadsMs = Date.now() - refreshReadsStartMs;
|
|
729
805
|
index.truncated = walked.length === maxFiles;
|
|
730
806
|
return {
|
|
731
807
|
mode: "incremental",
|
|
@@ -733,16 +809,212 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
|
|
|
733
809
|
dropped,
|
|
734
810
|
skipped,
|
|
735
811
|
reused: current.size - refreshed - skipped,
|
|
812
|
+
timings,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
export const WORD_INDEX_QUERY_FILTER_KEYS = ["lang", "file", "ext"];
|
|
816
|
+
/** Thrown by {@link parseWordIndexQuery}/{@link buildWordIndexQueryFilter} for
|
|
817
|
+
* an unrecognized `key:` prefix or an unrecognized `lang:` value — a query
|
|
818
|
+
* typo fails loudly with the supported list instead of silently degrading
|
|
819
|
+
* into a literal search term (#1450 acceptance criterion). */
|
|
820
|
+
export class WordIndexQueryError extends Error {
|
|
821
|
+
constructor(message) {
|
|
822
|
+
super(message);
|
|
823
|
+
this.name = "WordIndexQueryError";
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
const WORD_INDEX_FILTER_TOKEN_RE = /^(-)?([A-Za-z][A-Za-z0-9_-]*):(.+)$/;
|
|
827
|
+
/**
|
|
828
|
+
* Split a word-index query string into plain search terms and `key:value`
|
|
829
|
+
* prefix filters (#1450). Whitespace-delimited, one regex per token — no
|
|
830
|
+
* quoting scheme invented here, matching the pre-existing query path (which
|
|
831
|
+
* never supported quoting either). A `-` immediately before a recognized
|
|
832
|
+
* `key:` negates that filter; a bare `-term` (no colon) is left as an
|
|
833
|
+
* ordinary token, unchanged from prior behavior — `tokenizeLine` already
|
|
834
|
+
* strips the leading `-` when it extracts identifiers.
|
|
835
|
+
*
|
|
836
|
+
* An unrecognized `key:` (e.g. `type:foo`) throws {@link WordIndexQueryError}
|
|
837
|
+
* naming the supported prefixes, rather than falling through as a literal
|
|
838
|
+
* term — a typo must fail loudly, never silently rank as if the filter never
|
|
839
|
+
* existed. An empty value (`lang:`) is not treated as a filter token at all
|
|
840
|
+
* (kept as a term) since there is nothing to filter by.
|
|
841
|
+
*/
|
|
842
|
+
export function parseWordIndexQuery(query) {
|
|
843
|
+
const filters = [];
|
|
844
|
+
const termParts = [];
|
|
845
|
+
for (const raw of query.split(/\s+/)) {
|
|
846
|
+
if (!raw)
|
|
847
|
+
continue;
|
|
848
|
+
const match = raw.match(WORD_INDEX_FILTER_TOKEN_RE);
|
|
849
|
+
if (!match) {
|
|
850
|
+
termParts.push(raw);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const [, negation, keyRaw, value] = match;
|
|
854
|
+
const key = keyRaw.toLowerCase();
|
|
855
|
+
if (!value) {
|
|
856
|
+
termParts.push(raw);
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
if (!WORD_INDEX_QUERY_FILTER_KEYS.includes(key)) {
|
|
860
|
+
// Not a recognized filter key. Ordinary search terms legitimately
|
|
861
|
+
// contain colons (std::vector, error:foo, http://…, C:\ paths, a
|
|
862
|
+
// TODO:tag), and the old tokenizer searched them fine — a hard
|
|
863
|
+
// error here is a usability regression, not typo protection. The
|
|
864
|
+
// token passes through as a plain term; the loud-failure contract
|
|
865
|
+
// survives where it is unambiguous: a KNOWN key with a bad value
|
|
866
|
+
// (lang:notalang) still throws.
|
|
867
|
+
termParts.push(raw);
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
filters.push({
|
|
871
|
+
key: key,
|
|
872
|
+
value,
|
|
873
|
+
negated: negation === "-",
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
return { terms: termParts.join(" "), filters };
|
|
877
|
+
}
|
|
878
|
+
/** `lang:X` → the extension set for FileKind X (case-insensitive), drawn
|
|
879
|
+
* exclusively from `KIND_EXTENSIONS` (clients/file-kinds.ts) — the single
|
|
880
|
+
* source of truth (#894 invariant: no second, hand-maintained language list).
|
|
881
|
+
* An unrecognized kind throws {@link WordIndexQueryError} listing the accepted
|
|
882
|
+
* kinds (KIND_EXTENSIONS' own keys), the same loud-failure contract as an
|
|
883
|
+
* unsupported `key:` prefix. */
|
|
884
|
+
function resolveLangExtensions(value) {
|
|
885
|
+
const kind = value.toLowerCase();
|
|
886
|
+
const extensions = KIND_EXTENSIONS[kind];
|
|
887
|
+
if (!extensions) {
|
|
888
|
+
const known = Object.keys(KIND_EXTENSIONS).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).join(", ");
|
|
889
|
+
throw new WordIndexQueryError(`Unknown lang: "${value}" in word-index query — supported languages: ${known}.`);
|
|
890
|
+
}
|
|
891
|
+
return extensions;
|
|
892
|
+
}
|
|
893
|
+
/** `ext:ts` / `ext:.ts` → normalized to a single leading-dot, lowercased form for extname comparison. */
|
|
894
|
+
function normalizeExtFilterValue(value) {
|
|
895
|
+
return `.${value.replace(/^\.+/, "").toLowerCase()}`;
|
|
896
|
+
}
|
|
897
|
+
function resolveWordIndexFilter(filter) {
|
|
898
|
+
if (filter.key === "lang") {
|
|
899
|
+
const extensions = resolveLangExtensions(filter.value);
|
|
900
|
+
return {
|
|
901
|
+
key: filter.key,
|
|
902
|
+
negated: filter.negated,
|
|
903
|
+
test: (file) => extensions.includes(path.extname(file).toLowerCase()),
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
if (filter.key === "ext") {
|
|
907
|
+
const normalized = normalizeExtFilterValue(filter.value);
|
|
908
|
+
return {
|
|
909
|
+
key: filter.key,
|
|
910
|
+
negated: filter.negated,
|
|
911
|
+
test: (file) => path.extname(file).toLowerCase() === normalized,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
// "file": substring match against the index's own normalized display path
|
|
915
|
+
// (#1450 — see the doc comment on {@link buildWordIndexQueryFilter} for the
|
|
916
|
+
// case-behavior choice). Both sides fold through `wordIndexKey` so the
|
|
917
|
+
// comparison matches the SAME normalization the index's own path keys use.
|
|
918
|
+
const needle = wordIndexKey(filter.value);
|
|
919
|
+
return {
|
|
920
|
+
key: filter.key,
|
|
921
|
+
negated: filter.negated,
|
|
922
|
+
test: (_file, displayPath) => displayPath.includes(needle),
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Build a pre-ranking predicate from parsed query filters (#1450). Composes
|
|
927
|
+
* (AND) with `searchWordIndex`'s pre-existing `fileFilter` option (#771) —
|
|
928
|
+
* `symbol_search`'s structured `paths`/`lang` params and this query's inline
|
|
929
|
+
* filters both apply when both are present.
|
|
930
|
+
*
|
|
931
|
+
* Semantics: multiple positive filters of the SAME key OR together
|
|
932
|
+
* (`lang:ts lang:go` matches either); filters of DIFFERENT keys AND
|
|
933
|
+
* (`lang:ts file:clients/` requires both); a negated filter always subtracts,
|
|
934
|
+
* regardless of any positive filter sharing its key.
|
|
935
|
+
*
|
|
936
|
+
* All filters are RESOLVED EAGERLY (extension lookups, `WordIndexQueryError`
|
|
937
|
+
* thrown) here at build time — before any candidate file is tested — so a bad
|
|
938
|
+
* `lang:` value fails once, loudly, rather than per-file during the scoring
|
|
939
|
+
* loop.
|
|
940
|
+
*
|
|
941
|
+
* `file:` case behavior: matched via `wordIndexKey` (this module's single
|
|
942
|
+
* path-key normalizer, `normalizeEphemeralMapKey`) on BOTH the candidate path
|
|
943
|
+
* and the filter value — slash-folded everywhere, and case-folded ONLY on
|
|
944
|
+
* win32 (Windows' case-insensitive filesystem), preserved on POSIX. This
|
|
945
|
+
* mirrors the SAME normalization the word index's own path-keyed maps already
|
|
946
|
+
* apply (#1025), rather than inventing a second, divergent case rule for this
|
|
947
|
+
* one filter. The candidate path matched is the RAW index-stored path (not
|
|
948
|
+
* cwd-relativized) — the index already stores paths in the form the caller
|
|
949
|
+
* built it with (relative in tests, absolute in the real project walk), and
|
|
950
|
+
* an absolute path's tail always contains its project-relative suffix, so
|
|
951
|
+
* `file:clients/word-index.ts` still matches naturally either way.
|
|
952
|
+
*/
|
|
953
|
+
export function buildWordIndexQueryFilter(filters) {
|
|
954
|
+
if (filters.length === 0)
|
|
955
|
+
return undefined;
|
|
956
|
+
const resolved = filters.map(resolveWordIndexFilter);
|
|
957
|
+
const positivesByKey = new Map();
|
|
958
|
+
const negatives = [];
|
|
959
|
+
for (const r of resolved) {
|
|
960
|
+
if (r.negated) {
|
|
961
|
+
negatives.push(r);
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
const group = positivesByKey.get(r.key) ?? [];
|
|
965
|
+
group.push(r);
|
|
966
|
+
positivesByKey.set(r.key, group);
|
|
967
|
+
}
|
|
968
|
+
return (file) => {
|
|
969
|
+
const displayPath = wordIndexKey(file);
|
|
970
|
+
for (const negative of negatives) {
|
|
971
|
+
if (negative.test(file, displayPath))
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
for (const group of positivesByKey.values()) {
|
|
975
|
+
if (!group.some((r) => r.test(file, displayPath)))
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
return true;
|
|
736
979
|
};
|
|
737
980
|
}
|
|
981
|
+
function combineFileFilters(a, b) {
|
|
982
|
+
if (!a)
|
|
983
|
+
return b;
|
|
984
|
+
if (!b)
|
|
985
|
+
return a;
|
|
986
|
+
return (file) => a(file) && b(file);
|
|
987
|
+
}
|
|
738
988
|
/**
|
|
739
989
|
* Rank files for a query by BM25 over the query's identifier tokens, then apply
|
|
740
990
|
* priors: demote test/vendor and doc/data files, and boost by graph centrality
|
|
741
991
|
* when supplied. Returns the top {@link RankOptions.limit} files, highest first.
|
|
992
|
+
*
|
|
993
|
+
* The query string may mix plain terms with `lang:`/`file:`/`ext:` prefix
|
|
994
|
+
* filters (+ `-` negation, #1450) — parsed by {@link parseWordIndexQuery} and
|
|
995
|
+
* applied as a `fileFilter` (composed, AND, with any caller-supplied one)
|
|
996
|
+
* BEFORE scoring, same as the pre-existing `paths`/`lang` options (#771): a
|
|
997
|
+
* surviving file's score is unaffected by filtering. A query with no filter
|
|
998
|
+
* tokens reproduces prior output byte-for-byte.
|
|
999
|
+
*
|
|
1000
|
+
* DF-normalization note: BM25's per-token `idf` is computed from `docFrequency`
|
|
1001
|
+
* over the FULL (unfiltered) postings for that token — `fileFilter` (whether
|
|
1002
|
+
* from `options.fileFilter` or from this query's inline filters) is applied
|
|
1003
|
+
* AFTER `idf` is computed, per file, inside the same loop. This means a
|
|
1004
|
+
* filtered query reuses the GLOBAL, corpus-wide document frequency rather than
|
|
1005
|
+
* recomputing it over just the filtered subset. That is the pre-existing #771
|
|
1006
|
+
* behavior this change does not alter; it is an acceptable approximation
|
|
1007
|
+
* (idf reflects true corpus rarity, not an artifact of the filter) and keeps
|
|
1008
|
+
* filtered/unfiltered scores for the SAME file directly comparable, which is
|
|
1009
|
+
* the property #1450 asks for ("BM25 and centrality stay comparable within
|
|
1010
|
+
* the filtered set").
|
|
742
1011
|
*/
|
|
743
1012
|
export function searchWordIndex(index, query, options = {}) {
|
|
744
1013
|
const { demoteTestVendor = true, demoteDocs = true, centrality, limit = 20, fileFilter, } = options;
|
|
745
|
-
const
|
|
1014
|
+
const parsedQuery = parseWordIndexQuery(query);
|
|
1015
|
+
const queryFilter = buildWordIndexQueryFilter(parsedQuery.filters);
|
|
1016
|
+
const combinedFilter = combineFileFilters(fileFilter, queryFilter);
|
|
1017
|
+
const queryTokens = [...new Set(tokenizeLine(parsedQuery.terms))];
|
|
746
1018
|
if (queryTokens.length === 0)
|
|
747
1019
|
return [];
|
|
748
1020
|
const docCount = index.docCount || 1;
|
|
@@ -763,7 +1035,7 @@ export function searchWordIndex(index, query, options = {}) {
|
|
|
763
1035
|
const docFrequency = linesByFile.size;
|
|
764
1036
|
const idf = Math.log(1 + (docCount - docFrequency + 0.5) / (docFrequency + 0.5));
|
|
765
1037
|
for (const [file, lines] of linesByFile) {
|
|
766
|
-
if (
|
|
1038
|
+
if (combinedFilter && !combinedFilter(file))
|
|
767
1039
|
continue;
|
|
768
1040
|
const termFrequency = lines.length;
|
|
769
1041
|
const docLength = index.docLengths.get(file) ?? avgDocLength;
|
|
@@ -977,7 +1249,7 @@ export function triggerBackgroundWordIndexBuild(cwd, dbg, options = {}) {
|
|
|
977
1249
|
void (async () => {
|
|
978
1250
|
const startMs = Date.now();
|
|
979
1251
|
try {
|
|
980
|
-
const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./project-snapshot.js");
|
|
1252
|
+
const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION, } = await import("./project-snapshot.js");
|
|
981
1253
|
const docs = await collectWordIndexDocs(key);
|
|
982
1254
|
const index = await buildWordIndexAsync(docs);
|
|
983
1255
|
const existing = loadProjectSnapshot(key);
|
|
@@ -1059,7 +1331,7 @@ function getWordIndexPersistScheduler() {
|
|
|
1059
1331
|
}
|
|
1060
1332
|
async function writeWordIndexSnapshot(cwd, index, dbg) {
|
|
1061
1333
|
try {
|
|
1062
|
-
const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./project-snapshot.js");
|
|
1334
|
+
const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION, } = await import("./project-snapshot.js");
|
|
1063
1335
|
const existing = loadProjectSnapshot(cwd);
|
|
1064
1336
|
const snapshot = existing ?? {
|
|
1065
1337
|
version: PROJECT_SNAPSHOT_VERSION,
|