@herbertgao/pi-extensions 2026.8.6 → 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 +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- 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/renderer/compact-mode.ts +94 -77
- 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 +6 -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 +17 -13
- 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 +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +1 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -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 +2 -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 +120 -111
- 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 +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- 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 +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +909 -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 +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- 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 +61 -30
- 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/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- 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/dead-code-client.js +123 -31
- 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 +25 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- 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 +60 -53
- 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 +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- 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 +734 -64
- 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 +9 -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/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- 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/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 +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- 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 +82 -6
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- 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 +440 -11
- 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-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +102 -7
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2274 -147
- 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 +277 -16
- 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 +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- 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/session.js +10 -2
- 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 +154 -33
- 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 +84 -14
- 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/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 +52 -4
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +308 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +575 -67
- 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 +157 -24
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- 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/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +67738 -55739
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -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 +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -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 +15 -1
- 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-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 +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- 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-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 +57 -12
- 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-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 +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- 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 +6 -1
- 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 +53 -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 +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- 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/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -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-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- 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 +23 -0
- package/node_modules/pi-web-access/README.md +27 -9
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +28 -3
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/index.ts +41 -57
- package/node_modules/pi-web-access/package.json +1 -1
- 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 +44 -22
- 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/package.json +13 -13
|
@@ -68,12 +68,17 @@ function recordTouchedEvent(payload, getReadGuard) {
|
|
|
68
68
|
// recorded via the cross-process feed, now also reported by this
|
|
69
69
|
// session's own bus, upgrades to "local".
|
|
70
70
|
existing.origin = "local";
|
|
71
|
+
// #1464: a fresh touch invalidates any content already delivered for
|
|
72
|
+
// this path (the default deferred format at agent_end, a cascade
|
|
73
|
+
// autofix) — those bytes no longer describe the file.
|
|
74
|
+
existing.contentDelivered = false;
|
|
71
75
|
}
|
|
72
76
|
else {
|
|
73
77
|
_touched.set(mapKey, {
|
|
74
78
|
displayPath: rawPath,
|
|
75
79
|
reasons: new Set([payload.reason]),
|
|
76
80
|
origin: "local",
|
|
81
|
+
contentDelivered: false,
|
|
77
82
|
});
|
|
78
83
|
}
|
|
79
84
|
}
|
|
@@ -112,16 +117,47 @@ export function recordCrossProcessTouches(entries) {
|
|
|
112
117
|
existing.reasons.add(entry.reason);
|
|
113
118
|
// "local" is sticky (see AccumulatedFileOrigin) — never downgrade an
|
|
114
119
|
// already-local entry back to cross-process.
|
|
120
|
+
// #1464: a foreign process touching this file invalidates whatever
|
|
121
|
+
// content this session's write path already delivered for it.
|
|
122
|
+
existing.contentDelivered = false;
|
|
115
123
|
}
|
|
116
124
|
else {
|
|
117
125
|
_touched.set(mapKey, {
|
|
118
126
|
displayPath: entry.path,
|
|
119
127
|
reasons: new Set([entry.reason]),
|
|
120
128
|
origin: "cross-process",
|
|
129
|
+
contentDelivered: false,
|
|
121
130
|
});
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Record the write path's authoritative-content attachment outcome for one
|
|
136
|
+
* path (#1464).
|
|
137
|
+
*
|
|
138
|
+
* `attached` is the SAME boolean that drove the attachment in
|
|
139
|
+
* `handleToolResult` (clients/runtime-tool-result.ts) — this module never
|
|
140
|
+
* re-derives it from config, byte counts, or the aggregate budget. `true`
|
|
141
|
+
* means the post-fix bytes went out in the write's own tool result and the
|
|
142
|
+
* agent already holds them, so "re-read before editing" is noise. `false` is
|
|
143
|
+
* the size cap and the per-command aggregate budget degrading an attachment
|
|
144
|
+
* to a re-read warning: there the nudge is the whole signal, and calling this
|
|
145
|
+
* with `false` re-arms a path an earlier per-file decision had suppressed
|
|
146
|
+
* (the bash multi-file case, where the outer budget loop overrides the inner
|
|
147
|
+
* per-file "attached").
|
|
148
|
+
*
|
|
149
|
+
* Marks an EXISTING accumulator entry only. `publishFilesTouched` fires
|
|
150
|
+
* synchronously inside the `runPipeline` call this decision belongs to (pi's
|
|
151
|
+
* event bus is a plain `EventEmitter`), so the entry is already there for any
|
|
152
|
+
* path the relevance filter admitted; a path with no entry has nothing to
|
|
153
|
+
* suppress. Deferred-edit autofix at `agent_end` never reaches here — it
|
|
154
|
+
* attaches nothing, so it keeps nudging.
|
|
155
|
+
*/
|
|
156
|
+
export function noteAuthoritativeContentAttachment(filePath, attached) {
|
|
157
|
+
const entry = _touched.get(normalizeMapKey(filePath));
|
|
158
|
+
if (entry)
|
|
159
|
+
entry.contentDelivered = attached;
|
|
160
|
+
}
|
|
125
161
|
/**
|
|
126
162
|
* Subscribe to `pilens:files:touched` on pi's shared event bus. Called once
|
|
127
163
|
* at extension factory time from index.ts, mirroring `wireBusEmitter`'s
|
|
@@ -191,17 +227,29 @@ export function wireAgentNudgeSubscriber(args) {
|
|
|
191
227
|
* very first call of a fresh `agent_start` — so the accumulator surviving
|
|
192
228
|
* across run boundaries is exactly what makes that cross-run delivery work.
|
|
193
229
|
* Empty accumulator ⇒ returns undefined ⇒ zero bytes injected.
|
|
230
|
+
*
|
|
231
|
+
* #1464: paths whose post-fix content the write path already attached to its
|
|
232
|
+
* OWN tool result are dropped from the batch here (see
|
|
233
|
+
* `noteAuthoritativeContentAttachment`) — the agent holds those bytes, so
|
|
234
|
+
* "re-read before editing" would spend context to re-fetch what it has. A
|
|
235
|
+
* batch that is ENTIRELY such paths injects nothing.
|
|
194
236
|
*/
|
|
195
237
|
export function consumeAgentNudge(dbg) {
|
|
196
|
-
const
|
|
238
|
+
const drained = Array.from(_touched.values());
|
|
197
239
|
_touched.clear();
|
|
198
240
|
const filesFiltered = _relevanceFilteredCount;
|
|
199
241
|
_relevanceFilteredCount = 0;
|
|
200
242
|
if (!isAgentNudgeEnabled())
|
|
201
243
|
return undefined;
|
|
202
|
-
if (
|
|
244
|
+
if (drained.length === 0)
|
|
203
245
|
return undefined;
|
|
204
246
|
try {
|
|
247
|
+
// #1464: a write whose post-fix bytes were attached to its own tool
|
|
248
|
+
// result already told the agent everything this nudge would. Drop those
|
|
249
|
+
// paths from the message but keep counting them, so over-suppression is
|
|
250
|
+
// visible in telemetry instead of only in the absence of complaints.
|
|
251
|
+
const entries = drained.filter((e) => !e.contentDelivered);
|
|
252
|
+
const filesContentDelivered = drained.length - entries.length;
|
|
205
253
|
const filesTotal = entries.length;
|
|
206
254
|
const shown = entries.slice(0, MAX_NAMES_SHOWN);
|
|
207
255
|
const remaining = filesTotal - shown.length;
|
|
@@ -225,15 +273,6 @@ export function consumeAgentNudge(dbg) {
|
|
|
225
273
|
: names.join(", ");
|
|
226
274
|
const crossProcessCount = entries.filter((e) => e.origin === "cross-process").length;
|
|
227
275
|
const localCount = filesTotal - crossProcessCount;
|
|
228
|
-
// Three-way attribution (see the function doc): never assign a local
|
|
229
|
-
// file to another instance — a mixed batch keeps the local base framing
|
|
230
|
-
// and calls out the cross-process portion by exact count.
|
|
231
|
-
const attribution = localCount === 0
|
|
232
|
-
? "by an automatic run outside your turn"
|
|
233
|
-
: crossProcessCount === 0
|
|
234
|
-
? "after your last turn"
|
|
235
|
-
: `after your last turn (${crossProcessCount} of them by an automatic run outside it)`;
|
|
236
|
-
const message = `pi-lens: ${filesTotal} file(s) were ${verbLabel} ${attribution}: ${nameList} — working-tree changes to these are expected; re-read before editing.`;
|
|
237
276
|
logLatency({
|
|
238
277
|
type: "phase",
|
|
239
278
|
filePath: "<pi-lens>",
|
|
@@ -246,6 +285,10 @@ export function consumeAgentNudge(dbg) {
|
|
|
246
285
|
// never read/edited) — NOT the display overflow, which is
|
|
247
286
|
// filesTotal - filesShown.
|
|
248
287
|
filesFiltered,
|
|
288
|
+
// #1464: drops because the write's own tool result already carried
|
|
289
|
+
// the post-fix bytes. A third, distinct count: these paths passed
|
|
290
|
+
// the relevance filter and were accumulated, then suppressed here.
|
|
291
|
+
filesContentDelivered,
|
|
249
292
|
reasonMix: Array.from(allReasons),
|
|
250
293
|
// #492: origin mix so cross-process pickup rate is observable
|
|
251
294
|
// alongside the existing relevance-filter metric.
|
|
@@ -253,6 +296,19 @@ export function consumeAgentNudge(dbg) {
|
|
|
253
296
|
originCrossProcess: crossProcessCount,
|
|
254
297
|
},
|
|
255
298
|
});
|
|
299
|
+
// Everything drained was delivered in a write's own tool result — the
|
|
300
|
+
// row above records that it happened, and zero bytes get injected.
|
|
301
|
+
if (filesTotal === 0)
|
|
302
|
+
return undefined;
|
|
303
|
+
// Three-way attribution (see the function doc): never assign a local
|
|
304
|
+
// file to another instance — a mixed batch keeps the local base framing
|
|
305
|
+
// and calls out the cross-process portion by exact count.
|
|
306
|
+
const attribution = localCount === 0
|
|
307
|
+
? "by an automatic run outside your turn"
|
|
308
|
+
: crossProcessCount === 0
|
|
309
|
+
? "after your last turn"
|
|
310
|
+
: `after your last turn (${crossProcessCount} of them by an automatic run outside it)`;
|
|
311
|
+
const message = `pi-lens: ${filesTotal} file(s) were ${verbLabel} ${attribution}: ${nameList} — working-tree changes to these are expected; re-read before editing.`;
|
|
256
312
|
return {
|
|
257
313
|
messages: [
|
|
258
314
|
{
|
|
@@ -176,9 +176,12 @@ export class AstGrepClient {
|
|
|
176
176
|
* Each path is scanned independently; results are merged.
|
|
177
177
|
*/
|
|
178
178
|
async tempScanDetailed(dir, ruleYaml, options = {}) {
|
|
179
|
-
//
|
|
180
|
-
// match-only seam working. The
|
|
181
|
-
//
|
|
179
|
+
// SAFETY: keep tests and older embedders that replace the runner with the
|
|
180
|
+
// historic match-only seam working. The cast only makes
|
|
181
|
+
// `tempScanDetailedAsync` OPTIONAL on the runner surface — it claims
|
|
182
|
+
// nothing about the method existing. The call site below checks for it
|
|
183
|
+
// before invoking, and the production SgRunner always has it, so a
|
|
184
|
+
// failure there cannot be mistaken for an empty result.
|
|
182
185
|
const detailed = this.runner.tempScanDetailedAsync;
|
|
183
186
|
if (detailed) {
|
|
184
187
|
return detailed.call(this.runner, dir, "agent-rule", ruleYaml, 30_000, options);
|
|
@@ -568,12 +571,15 @@ message: found
|
|
|
568
571
|
return "";
|
|
569
572
|
const errors = diags.filter((d) => d.severity === "error");
|
|
570
573
|
const warnings = diags.filter((d) => d.severity === "warning");
|
|
574
|
+
const infos = diags.filter((d) => d.severity === "info");
|
|
571
575
|
const hints = diags.filter((d) => d.severity === "hint");
|
|
572
576
|
let output = `[ast-grep] ${diags.length} structural issue(s)`;
|
|
573
577
|
if (errors.length)
|
|
574
578
|
output += ` — ${errors.length} error(s)`;
|
|
575
579
|
if (warnings.length)
|
|
576
580
|
output += ` — ${warnings.length} warning(s)`;
|
|
581
|
+
if (infos.length)
|
|
582
|
+
output += ` — ${infos.length} info(s)`;
|
|
577
583
|
if (hints.length)
|
|
578
584
|
output += ` — ${hints.length} hint(s)`;
|
|
579
585
|
output += ":\n";
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* cycles still lose updates: each writer publishes the state it computed
|
|
39
39
|
* from the snapshot it read, and last-rename-wins silently discards the
|
|
40
40
|
* other's mutation. Callers needing this must serialize themselves (see
|
|
41
|
-
* `
|
|
41
|
+
* `registryChildMutationTail` in `instance-registry.ts`).
|
|
42
42
|
* - **No ordering.** Nothing sequences concurrent writers, within or across
|
|
43
43
|
* processes. The winner is whichever `rename` lands last, which is not
|
|
44
44
|
* necessarily the write that started or finished last.
|
|
@@ -143,7 +143,10 @@ export function writeFileAtomic(targetPath, data, options) {
|
|
|
143
143
|
const bestEffort = options?.bestEffort ?? true;
|
|
144
144
|
const tmpPath = stagePathFor(targetPath);
|
|
145
145
|
try {
|
|
146
|
-
fs.writeFileSync(tmpPath, data,
|
|
146
|
+
fs.writeFileSync(tmpPath, data, {
|
|
147
|
+
encoding: typeof data === "string" ? "utf-8" : undefined,
|
|
148
|
+
mode: options?.mode,
|
|
149
|
+
});
|
|
147
150
|
fs.renameSync(tmpPath, targetPath);
|
|
148
151
|
}
|
|
149
152
|
catch (err) {
|
|
@@ -167,7 +170,10 @@ export async function writeFileAtomicAsync(targetPath, data, options) {
|
|
|
167
170
|
const bestEffort = options?.bestEffort ?? true;
|
|
168
171
|
const tmpPath = stagePathFor(targetPath);
|
|
169
172
|
try {
|
|
170
|
-
await fs.promises.writeFile(tmpPath, data,
|
|
173
|
+
await fs.promises.writeFile(tmpPath, data, {
|
|
174
|
+
encoding: typeof data === "string" ? "utf-8" : undefined,
|
|
175
|
+
mode: options?.mode,
|
|
176
|
+
});
|
|
171
177
|
await fs.promises.rename(tmpPath, targetPath);
|
|
172
178
|
}
|
|
173
179
|
catch (err) {
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* - VIEW commands (cat/head/tail/sed -n) → reads, recorded with the exact line
|
|
6
6
|
* range shown (like the Read tool's delivered range).
|
|
7
|
-
* - WRITE commands (redirects, tee, sed -i, cp/mv dest, touch
|
|
7
|
+
* - WRITE commands (redirects, tee, sed -i, cp/mv dest, touch, and explicit
|
|
8
|
+
* in-place formatter/fixer targets) → the agent
|
|
8
9
|
* authored/owns the resulting file, exactly like the Write tool — these are
|
|
9
10
|
* registered via noteCreatedFile + recordWritten so a follow-up edit is not
|
|
10
11
|
* blocked.
|
|
@@ -90,7 +91,7 @@ export function tokenizeShellCommand(command) {
|
|
|
90
91
|
// Windows routinely contain native paths (C:\\src\\file.ts). Preserve
|
|
91
92
|
// backslashes before ordinary path characters while still honoring
|
|
92
93
|
// shell escapes and line continuations.
|
|
93
|
-
if (next === "\n" || /[\s\\'
|
|
94
|
+
if (next === "\n" || /[\s\\'";$|&<>]/.test(next ?? "")) {
|
|
94
95
|
escaped = true;
|
|
95
96
|
}
|
|
96
97
|
else {
|
|
@@ -162,6 +163,37 @@ function parseCountFlag(token) {
|
|
|
162
163
|
function commandSegments(command) {
|
|
163
164
|
return tokenizeShellCommand(command).map((segment) => segment.tokens);
|
|
164
165
|
}
|
|
166
|
+
/** Unwrap the common agent-authored `npx <tool>` launcher form. */
|
|
167
|
+
function unwrapCommand(tokens) {
|
|
168
|
+
let verb = path.basename(tokens[0] ?? "");
|
|
169
|
+
let args = tokens.slice(1);
|
|
170
|
+
if (verb === "npx") {
|
|
171
|
+
while (args[0] === "-y" || args[0] === "--yes")
|
|
172
|
+
args = args.slice(1);
|
|
173
|
+
verb = path.basename(args[0] ?? "");
|
|
174
|
+
args = args.slice(1);
|
|
175
|
+
}
|
|
176
|
+
return { verb, args };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Return source-file operands while excluding values consumed by known options.
|
|
180
|
+
* This stays deliberately narrower than a general CLI parser: only formatter
|
|
181
|
+
* options whose values can themselves look like source files belong here.
|
|
182
|
+
*/
|
|
183
|
+
function formatterFileArgs(args, start = 0, valueOptions = new Set()) {
|
|
184
|
+
const files = [];
|
|
185
|
+
for (let i = start; i < args.length; i += 1) {
|
|
186
|
+
const arg = args[i] ?? "";
|
|
187
|
+
if (valueOptions.has(arg)) {
|
|
188
|
+
i += 1;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (arg === "--" || arg.startsWith("-"))
|
|
192
|
+
continue;
|
|
193
|
+
files.push(arg);
|
|
194
|
+
}
|
|
195
|
+
return files;
|
|
196
|
+
}
|
|
165
197
|
/** Find redirect targets without treating quoted `>` characters as syntax. */
|
|
166
198
|
function extractRedirectTargets(command) {
|
|
167
199
|
const targets = [];
|
|
@@ -344,6 +376,80 @@ const GREP_OPTIONS_WITH_VALUE = new Set([
|
|
|
344
376
|
"--before-context",
|
|
345
377
|
"--context",
|
|
346
378
|
]);
|
|
379
|
+
function parseContextValue(inline, next) {
|
|
380
|
+
const raw = inline !== "" ? inline : next;
|
|
381
|
+
if (raw === undefined)
|
|
382
|
+
return undefined;
|
|
383
|
+
const n = Number.parseInt(raw, 10);
|
|
384
|
+
if (!Number.isFinite(n) || n < 0)
|
|
385
|
+
return undefined;
|
|
386
|
+
// Bound the credit: a huge -C would credit a whole file from one hit.
|
|
387
|
+
return Math.min(n, 100);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Read the context lines a `grep` invocation actually PRINTS around each hit
|
|
391
|
+
* (#1904 item 2). Only these lines were delivered to the model, so only these
|
|
392
|
+
* may be credited as read. Handles `-A N`, `-A3`, clustered short flags such as
|
|
393
|
+
* `-rnA3`, `--after-context=N`, the `-B`/`--before-context` mirror, `-C`
|
|
394
|
+
* (both sides), and the bare `-NUM` form.
|
|
395
|
+
*/
|
|
396
|
+
export function parseGrepContextLines(args) {
|
|
397
|
+
let before = 0;
|
|
398
|
+
let after = 0;
|
|
399
|
+
for (let i = 0; i < args.length; i++) {
|
|
400
|
+
const token = stripQuotes(args[i]);
|
|
401
|
+
if (token === "--")
|
|
402
|
+
break;
|
|
403
|
+
const nextToken = args[i + 1] === undefined ? undefined : stripQuotes(args[i + 1]);
|
|
404
|
+
const long = /^--(after|before)-context(?:=(\d+))?$/.exec(token);
|
|
405
|
+
if (long) {
|
|
406
|
+
const value = parseContextValue(long[2] ?? "", nextToken);
|
|
407
|
+
if (value === undefined)
|
|
408
|
+
continue;
|
|
409
|
+
if (long[1] === "after")
|
|
410
|
+
after = Math.max(after, value);
|
|
411
|
+
else
|
|
412
|
+
before = Math.max(before, value);
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const longBoth = /^--context(?:=(\d+))?$/.exec(token);
|
|
416
|
+
if (longBoth) {
|
|
417
|
+
const value = parseContextValue(longBoth[1] ?? "", nextToken);
|
|
418
|
+
if (value === undefined)
|
|
419
|
+
continue;
|
|
420
|
+
before = Math.max(before, value);
|
|
421
|
+
after = Math.max(after, value);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
// Bare numeric context form: `grep -3 pattern file`.
|
|
425
|
+
const bare = /^-(\d+)$/.exec(token);
|
|
426
|
+
if (bare) {
|
|
427
|
+
const value = parseContextValue(bare[1], undefined);
|
|
428
|
+
if (value === undefined)
|
|
429
|
+
continue;
|
|
430
|
+
before = Math.max(before, value);
|
|
431
|
+
after = Math.max(after, value);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
// Short flags, possibly clustered: the context flag must be LAST in the
|
|
435
|
+
// cluster because it consumes the remaining characters as its value.
|
|
436
|
+
const short = /^-[A-Za-z]*([ABC])(\d*)$/.exec(token);
|
|
437
|
+
if (!short)
|
|
438
|
+
continue;
|
|
439
|
+
const value = parseContextValue(short[2], nextToken);
|
|
440
|
+
if (value === undefined)
|
|
441
|
+
continue;
|
|
442
|
+
if (short[1] === "A")
|
|
443
|
+
after = Math.max(after, value);
|
|
444
|
+
else if (short[1] === "B")
|
|
445
|
+
before = Math.max(before, value);
|
|
446
|
+
else {
|
|
447
|
+
before = Math.max(before, value);
|
|
448
|
+
after = Math.max(after, value);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return { before, after };
|
|
452
|
+
}
|
|
347
453
|
function extractGrepSearchFiles(args, cwd) {
|
|
348
454
|
const files = [];
|
|
349
455
|
let patternSeen = false;
|
|
@@ -411,6 +517,11 @@ function parseGrepLineWithoutFile(line, file) {
|
|
|
411
517
|
function collectGrepCommandFiles(command, cwd) {
|
|
412
518
|
const files = new Set();
|
|
413
519
|
let hasLineNumberGrep = false;
|
|
520
|
+
// A command can chain several greps whose output all reaches the model, but
|
|
521
|
+
// the parsed hits carry no segment identity. Credit the SMALLEST context any
|
|
522
|
+
// contributing grep printed, so no line is credited that a hit from the
|
|
523
|
+
// narrowest grep never delivered.
|
|
524
|
+
let context;
|
|
414
525
|
for (const tokens of commandSegments(command)) {
|
|
415
526
|
const verb = path.basename(stripQuotes(tokens[0] ?? ""));
|
|
416
527
|
if (verb !== "grep" && verb !== "egrep" && verb !== "fgrep")
|
|
@@ -419,10 +530,21 @@ function collectGrepCommandFiles(command, cwd) {
|
|
|
419
530
|
if (!grepHasLineNumbers(args))
|
|
420
531
|
continue;
|
|
421
532
|
hasLineNumberGrep = true;
|
|
533
|
+
const segmentContext = parseGrepContextLines(args);
|
|
534
|
+
context = context
|
|
535
|
+
? {
|
|
536
|
+
before: Math.min(context.before, segmentContext.before),
|
|
537
|
+
after: Math.min(context.after, segmentContext.after),
|
|
538
|
+
}
|
|
539
|
+
: segmentContext;
|
|
422
540
|
for (const file of extractGrepSearchFiles(args, cwd))
|
|
423
541
|
files.add(file);
|
|
424
542
|
}
|
|
425
|
-
return {
|
|
543
|
+
return {
|
|
544
|
+
hasLineNumberGrep,
|
|
545
|
+
files,
|
|
546
|
+
context: context ?? { before: 0, after: 0 },
|
|
547
|
+
};
|
|
426
548
|
}
|
|
427
549
|
function dedupePushSearchRead(out, seen, loc) {
|
|
428
550
|
if (!loc)
|
|
@@ -452,17 +574,25 @@ function parseGrepOutputSearchReads(output, cwd, singleFile) {
|
|
|
452
574
|
* so the latter is only accepted when the command names exactly one source file.
|
|
453
575
|
*/
|
|
454
576
|
export function extractGrepSearchReadsFromOutput(command, cwd, output) {
|
|
455
|
-
const { hasLineNumberGrep, files } = collectGrepCommandFiles(command, cwd);
|
|
577
|
+
const { hasLineNumberGrep, files, context } = collectGrepCommandFiles(command, cwd);
|
|
456
578
|
if (!hasLineNumberGrep)
|
|
457
579
|
return [];
|
|
458
580
|
const singleFile = files.size === 1 ? [...files][0] : undefined;
|
|
459
|
-
|
|
581
|
+
const locations = parseGrepOutputSearchReads(output, cwd, singleFile);
|
|
582
|
+
// Only the printed context lines were delivered to the model, so only they
|
|
583
|
+
// are credited. A bare grep credits its match line alone (#1904 item 2).
|
|
584
|
+
for (const loc of locations) {
|
|
585
|
+
loc.contextBefore = context.before;
|
|
586
|
+
loc.contextAfter = context.after;
|
|
587
|
+
}
|
|
588
|
+
return locations;
|
|
460
589
|
}
|
|
461
590
|
/**
|
|
462
591
|
* Extract files a bash command WROTE/created, so the read-guard can treat them
|
|
463
592
|
* as authored by the agent (mirrors the Write tool). Handles:
|
|
464
593
|
* redirects: `> FILE`, `>> FILE`, `N> FILE`, `&> FILE` (with or without space)
|
|
465
|
-
* tee [-a] FILE...,
|
|
594
|
+
* tee [-a] FILE..., sed -i ... FILE, cp/mv/install ... DEST, touch FILE...,
|
|
595
|
+
* and common in-place formatter/fixer invocations with explicit file targets.
|
|
466
596
|
*
|
|
467
597
|
* Returns absolute paths. The file need not exist yet (it may be created) —
|
|
468
598
|
* existence is confirmed later by recordWritten at tool_result time.
|
|
@@ -490,8 +620,7 @@ export function extractWrittenPathsFromCommand(command, cwd) {
|
|
|
490
620
|
for (const tokens of commandSegments(command)) {
|
|
491
621
|
if (tokens.length === 0)
|
|
492
622
|
continue;
|
|
493
|
-
const verb =
|
|
494
|
-
const args = tokens.slice(1);
|
|
623
|
+
const { verb, args } = unwrapCommand(tokens);
|
|
495
624
|
if (verb === "tee" || verb === "touch") {
|
|
496
625
|
for (const a of args)
|
|
497
626
|
if (!a.startsWith("-"))
|
|
@@ -502,10 +631,89 @@ export function extractWrittenPathsFromCommand(command, cwd) {
|
|
|
502
631
|
add(a);
|
|
503
632
|
}
|
|
504
633
|
else if (verb === "cp" || verb === "mv" || verb === "install") {
|
|
634
|
+
// Known miss (#1668 review F3): the GNU `-t DEST`/`--target-directory`
|
|
635
|
+
// form puts the destination BEFORE the sources — this always treats
|
|
636
|
+
// the LAST non-flag argument as the destination, so `-t DEST SRC` and
|
|
637
|
+
// multi-source `-t DEST SRC1 SRC2...` are misread. Rare in agent-
|
|
638
|
+
// authored bash (source-before-dest is the common form); documented
|
|
639
|
+
// rather than fixed, since correctly parsing `-t` means recognizing it
|
|
640
|
+
// takes a value while telling it apart from every other single-letter
|
|
641
|
+
// flag this same branch already ignores.
|
|
505
642
|
const files = args.filter((a) => !a.startsWith("-"));
|
|
506
643
|
if (files.length >= 1)
|
|
507
644
|
add(files[files.length - 1]); // destination
|
|
508
645
|
}
|
|
646
|
+
else if (verb === "biome") {
|
|
647
|
+
const sub = args[0];
|
|
648
|
+
if ((sub === "format" || sub === "check") && args.includes("--write")) {
|
|
649
|
+
for (const file of formatterFileArgs(args, 1, new Set(["--config-path"])))
|
|
650
|
+
add(file);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else if (verb === "prettier" && args.includes("--write")) {
|
|
654
|
+
for (const file of formatterFileArgs(args, 0, new Set(["--config", "--ignore-path", "--parser", "--plugin"])))
|
|
655
|
+
add(file);
|
|
656
|
+
}
|
|
657
|
+
else if (verb === "eslint" && args.includes("--fix")) {
|
|
658
|
+
for (const file of formatterFileArgs(args, 0, new Set(["--config", "--ignore-path", "--parser", "--plugin"])))
|
|
659
|
+
add(file);
|
|
660
|
+
}
|
|
661
|
+
else if (verb === "ruff") {
|
|
662
|
+
const sub = args[0];
|
|
663
|
+
if (sub === "format" || args.includes("--fix")) {
|
|
664
|
+
const start = sub === "format" || sub === "check" ? 1 : 0;
|
|
665
|
+
for (const file of formatterFileArgs(args, start, new Set(["--config"])))
|
|
666
|
+
add(file);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else if (verb === "gofmt" && args.includes("-w")) {
|
|
670
|
+
for (const file of formatterFileArgs(args))
|
|
671
|
+
add(file);
|
|
672
|
+
}
|
|
673
|
+
else if (verb === "cargo" && args[0] === "fmt") {
|
|
674
|
+
// Bare cargo fmt is project-scoped and cannot be enumerated without a
|
|
675
|
+
// filesystem walk. Only explicit rustfmt operands after `--` are safe.
|
|
676
|
+
const separator = args.indexOf("--");
|
|
677
|
+
if (separator >= 0) {
|
|
678
|
+
for (const file of formatterFileArgs(args, separator + 1, new Set(["--edition", "--config-path"])))
|
|
679
|
+
add(file);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
else if (verb === "rustfmt") {
|
|
683
|
+
for (const file of formatterFileArgs(args, 0, new Set(["--edition", "--config-path"])))
|
|
684
|
+
add(file);
|
|
685
|
+
}
|
|
686
|
+
else if (verb === "black") {
|
|
687
|
+
for (const file of formatterFileArgs(args, 0, new Set([
|
|
688
|
+
"--config",
|
|
689
|
+
"--exclude",
|
|
690
|
+
"--include",
|
|
691
|
+
"--line-length",
|
|
692
|
+
"--target-version",
|
|
693
|
+
])))
|
|
694
|
+
add(file);
|
|
695
|
+
}
|
|
696
|
+
else if (verb === "clang-format" && args.includes("-i")) {
|
|
697
|
+
for (const file of formatterFileArgs(args, 0, new Set(["--style", "--fallback-style", "--assume-filename"])))
|
|
698
|
+
add(file);
|
|
699
|
+
}
|
|
700
|
+
else if (verb === "dotnet" && args[0] === "format") {
|
|
701
|
+
// dotnet format is normally solution-scoped. `--include` is the one
|
|
702
|
+
// invocation form that provides attributable source paths.
|
|
703
|
+
for (let i = 1; i < args.length; i += 1) {
|
|
704
|
+
if (args[i] === "--include" && args[i + 1]) {
|
|
705
|
+
for (const file of (args[i + 1] ?? "").split(","))
|
|
706
|
+
add(file);
|
|
707
|
+
i += 1;
|
|
708
|
+
}
|
|
709
|
+
else if (args[i]?.startsWith("--include=")) {
|
|
710
|
+
for (const file of (args[i] ?? "")
|
|
711
|
+
.slice("--include=".length)
|
|
712
|
+
.split(","))
|
|
713
|
+
add(file);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
509
717
|
else if (verb === "git") {
|
|
510
718
|
// git ops that REWRITE working-tree files with explicit paths:
|
|
511
719
|
// git checkout [<ref>] -- <files> git restore [opts] <files>
|
|
@@ -524,6 +732,75 @@ export function extractWrittenPathsFromCommand(command, cwd) {
|
|
|
524
732
|
for (const a of fileArgs)
|
|
525
733
|
add(a);
|
|
526
734
|
}
|
|
735
|
+
else if (sub === "mv") {
|
|
736
|
+
// git mv SRC... DEST — the destination is a write, exactly like
|
|
737
|
+
// plain `mv` above (#1668 review F2). The source side is a delete,
|
|
738
|
+
// handled by extractDeletedPathsFromCommand.
|
|
739
|
+
const files = args.slice(1).filter((a) => !a.startsWith("-"));
|
|
740
|
+
if (files.length >= 1)
|
|
741
|
+
add(files[files.length - 1]);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return Array.from(out);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Extract files a bash command likely DELETED, for the type-3 (Deleted)
|
|
749
|
+
* watched-files gap (#1668): `rm FILE...`, `git rm FILE...`, and the SOURCE
|
|
750
|
+
* side of `mv SRC DEST` / `git mv SRC DEST` (the destination is a write,
|
|
751
|
+
* covered by `extractWrittenPathsFromCommand` — including for `git mv`,
|
|
752
|
+
* #1668 review F2).
|
|
753
|
+
*
|
|
754
|
+
* Deliberately narrow: only commands naming an explicit file target are
|
|
755
|
+
* handled. A bare directory op (`rm -rf dir/`, `git clean`) is skipped —
|
|
756
|
+
* resolving what vanished inside a directory would mean listing it before
|
|
757
|
+
* and after, the "stat the world" cost this module exists to avoid. The
|
|
758
|
+
* caller confirms each candidate by checking it no longer exists on disk;
|
|
759
|
+
* this function only proposes what the command named.
|
|
760
|
+
*/
|
|
761
|
+
export function extractDeletedPathsFromCommand(command, cwd) {
|
|
762
|
+
const out = new Set();
|
|
763
|
+
const add = (token) => {
|
|
764
|
+
const abs = resolveCandidate(token, cwd);
|
|
765
|
+
if (abs)
|
|
766
|
+
out.add(abs);
|
|
767
|
+
};
|
|
768
|
+
// mv SRC... DEST / git mv SRC... DEST — every argument except the last is
|
|
769
|
+
// a source that vanishes from its original path once the move lands.
|
|
770
|
+
// Known miss (#1668 review F3): the same `-t DEST`/`--target-directory`
|
|
771
|
+
// form documented in `extractWrittenPathsFromCommand` misreads here too —
|
|
772
|
+
// this assumes source-before-destination order.
|
|
773
|
+
const addMoveSources = (args) => {
|
|
774
|
+
const files = args.filter((a) => !a.startsWith("-"));
|
|
775
|
+
if (files.length >= 2) {
|
|
776
|
+
for (const src of files.slice(0, -1))
|
|
777
|
+
add(src);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
for (const tokens of commandSegments(command)) {
|
|
781
|
+
if (tokens.length === 0)
|
|
782
|
+
continue;
|
|
783
|
+
const verb = path.basename(tokens[0] ?? "");
|
|
784
|
+
const args = tokens.slice(1);
|
|
785
|
+
if (verb === "rm") {
|
|
786
|
+
for (const a of args)
|
|
787
|
+
if (!a.startsWith("-"))
|
|
788
|
+
add(a);
|
|
789
|
+
}
|
|
790
|
+
else if (verb === "git" && args[0] === "rm") {
|
|
791
|
+
const rmArgs = args.slice(1);
|
|
792
|
+
const dashDash = rmArgs.indexOf("--");
|
|
793
|
+
const fileArgs = dashDash >= 0
|
|
794
|
+
? rmArgs.slice(dashDash + 1)
|
|
795
|
+
: rmArgs.filter((a) => !a.startsWith("-"));
|
|
796
|
+
for (const a of fileArgs)
|
|
797
|
+
add(a);
|
|
798
|
+
}
|
|
799
|
+
else if (verb === "git" && args[0] === "mv") {
|
|
800
|
+
addMoveSources(args.slice(1));
|
|
801
|
+
}
|
|
802
|
+
else if (verb === "mv") {
|
|
803
|
+
addMoveSources(args);
|
|
527
804
|
}
|
|
528
805
|
}
|
|
529
806
|
return Array.from(out);
|