@kr78/pi-coding-agent 17.2.10
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/CHANGELOG.md +14861 -0
- package/README.md +35 -0
- package/dist/CHANGELOG-chtdxh1z.md +14861 -0
- package/dist/cli.js +20483 -0
- package/dist/template-c2hyaytt.js +1653 -0
- package/dist/template-f8wx9vfn.css +1355 -0
- package/dist/template-qat058wr.html +55 -0
- package/dist/tool-views.generated-jdfmzwmn.js +35 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +104 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/extensions/README.md +142 -0
- package/examples/extensions/api-demo.ts +78 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +31 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +549 -0
- package/examples/extensions/reload-runtime.ts +38 -0
- package/examples/extensions/thinking-note.ts +13 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +17 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +46 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +82 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/12-redis-sessions.ts +54 -0
- package/examples/sdk/13-sql-sessions.ts +61 -0
- package/examples/sdk/README.md +169 -0
- package/package.json +587 -0
- package/scripts/bench-guard.ts +71 -0
- package/scripts/bench-title-models.ts +332 -0
- package/scripts/build-binary.ts +117 -0
- package/scripts/bundle-dist.ts +123 -0
- package/scripts/compile-binary.ts +69 -0
- package/scripts/embed-mupdf-wasm.ts +67 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-aria-snapshot.ts +134 -0
- package/scripts/generate-docs-index.ts +58 -0
- package/scripts/generate-share-viewer.ts +34 -0
- package/scripts/legacy-pi-virtual-module.ts +205 -0
- package/scripts/measure-prompt-tokens.ts +63 -0
- package/scripts/omp +51 -0
- package/scripts/omp.ts +19 -0
- package/scripts/security-compare.ts +40 -0
- package/src/advisor/advise-tool.ts +234 -0
- package/src/advisor/config.ts +341 -0
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +6 -0
- package/src/advisor/runtime.ts +1412 -0
- package/src/advisor/transcript-recorder.ts +215 -0
- package/src/advisor/watchdog.ts +135 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +847 -0
- package/src/auto-thinking/classifier.ts +220 -0
- package/src/autolearn/controller.ts +152 -0
- package/src/autolearn/managed-skills.ts +255 -0
- package/src/autoresearch/command-resume.md +14 -0
- package/src/autoresearch/dashboard.ts +436 -0
- package/src/autoresearch/git.ts +331 -0
- package/src/autoresearch/helpers.ts +218 -0
- package/src/autoresearch/index.ts +541 -0
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +103 -0
- package/src/autoresearch/resume-message.md +10 -0
- package/src/autoresearch/state.ts +273 -0
- package/src/autoresearch/storage.ts +700 -0
- package/src/autoresearch/tools/init-experiment.ts +268 -0
- package/src/autoresearch/tools/log-experiment.ts +520 -0
- package/src/autoresearch/tools/run-experiment.ts +407 -0
- package/src/autoresearch/tools/update-notes.ts +109 -0
- package/src/autoresearch/types.ts +168 -0
- package/src/capability/context-file.ts +44 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +117 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +467 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +115 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule-buckets.ts +66 -0
- package/src/capability/rule.ts +298 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +69 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +187 -0
- package/src/cleanse/agent.ts +226 -0
- package/src/cleanse/balance.ts +79 -0
- package/src/cleanse/checkers.ts +996 -0
- package/src/cleanse/index.ts +190 -0
- package/src/cleanse/loop.ts +51 -0
- package/src/cleanse/parsers.ts +726 -0
- package/src/cleanse/progress.ts +50 -0
- package/src/cleanse/prompts/assignment.md +47 -0
- package/src/cleanse/types.ts +72 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +368 -0
- package/src/cli/auth-broker-cli.ts +940 -0
- package/src/cli/auth-gateway-cli.ts +674 -0
- package/src/cli/bench-cli.ts +990 -0
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -0
- package/src/cli/command-help.ts +107 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/config-cli.ts +459 -0
- package/src/cli/dry-balance-cli.ts +864 -0
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/file-processor.ts +132 -0
- package/src/cli/flag-tables.ts +367 -0
- package/src/cli/gallery-cli.ts +272 -0
- package/src/cli/gallery-fixtures/agentic.ts +420 -0
- package/src/cli/gallery-fixtures/codeintel.ts +187 -0
- package/src/cli/gallery-fixtures/edit.ts +254 -0
- package/src/cli/gallery-fixtures/fs.ts +245 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +46 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +177 -0
- package/src/cli/gallery-fixtures/search.ts +135 -0
- package/src/cli/gallery-fixtures/shell.ts +241 -0
- package/src/cli/gallery-fixtures/types.ts +57 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli/gc-cli.ts +1566 -0
- package/src/cli/grep-cli.ts +161 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/models-cli.ts +385 -0
- package/src/cli/plugin-cli.ts +996 -0
- package/src/cli/profile-alias.ts +369 -0
- package/src/cli/profile-bootstrap.ts +233 -0
- package/src/cli/read-cli.ts +99 -0
- package/src/cli/session-picker.ts +110 -0
- package/src/cli/setup-cli.ts +312 -0
- package/src/cli/setup-model-picker.ts +43 -0
- package/src/cli/shell-cli.ts +176 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/startup-cwd.ts +58 -0
- package/src/cli/stats-cli.ts +229 -0
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/tiny-models-cli.ts +153 -0
- package/src/cli/ttsr-cli.ts +1013 -0
- package/src/cli/update-cli.ts +1184 -0
- package/src/cli/usage-cli.ts +1084 -0
- package/src/cli/usage-error.ts +7 -0
- package/src/cli/web-search-cli.ts +144 -0
- package/src/cli/worktree-cli.ts +311 -0
- package/src/cli-commands.ts +308 -0
- package/src/cli.ts +428 -0
- package/src/collab/crypto.ts +63 -0
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +764 -0
- package/src/collab/host.ts +692 -0
- package/src/collab/protocol.ts +296 -0
- package/src/collab/relay-client.ts +282 -0
- package/src/collab/replication-shrink.ts +111 -0
- package/src/commands/acp.ts +35 -0
- package/src/commands/agents.ts +58 -0
- package/src/commands/auth-broker.ts +100 -0
- package/src/commands/auth-gateway.ts +70 -0
- package/src/commands/bench.ts +64 -0
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +45 -0
- package/src/commands/commit.ts +47 -0
- package/src/commands/complete.ts +67 -0
- package/src/commands/completions.ts +61 -0
- package/src/commands/config.ts +52 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +61 -0
- package/src/commands/gc.ts +47 -0
- package/src/commands/grep.ts +49 -0
- package/src/commands/grievances.ts +52 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/join.ts +40 -0
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +34 -0
- package/src/commands/models.ts +61 -0
- package/src/commands/plugin.ts +79 -0
- package/src/commands/read.ts +39 -0
- package/src/commands/say.ts +146 -0
- package/src/commands/setup.ts +68 -0
- package/src/commands/share.ts +71 -0
- package/src/commands/shell.ts +30 -0
- package/src/commands/ssh.ts +61 -0
- package/src/commands/stats.ts +30 -0
- package/src/commands/tiny-models.ts +36 -0
- package/src/commands/token.ts +164 -0
- package/src/commands/ttsr.ts +125 -0
- package/src/commands/update.ts +34 -0
- package/src/commands/usage.ts +55 -0
- package/src/commands/web-search.ts +43 -0
- package/src/commands/worktree.ts +63 -0
- package/src/commit/agentic/agent.ts +322 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +383 -0
- package/src/commit/agentic/lock-files.ts +107 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +148 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +52 -0
- package/src/commit/agentic/tools/git-overview.ts +62 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +147 -0
- package/src/commit/agentic/tools/propose-commit.ts +109 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +11 -0
- package/src/commit/agentic/tools/split-commit.ts +241 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +64 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +107 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +101 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +49 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +95 -0
- package/src/commit/pipeline.ts +244 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/shared-llm.ts +70 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +58 -0
- package/src/config/api-key-resolver.ts +81 -0
- package/src/config/append-only-context-mode.ts +76 -0
- package/src/config/config-file.ts +347 -0
- package/src/config/inline-tool-descriptors-mode.ts +26 -0
- package/src/config/keybindings.ts +703 -0
- package/src/config/mcp-schema.json +252 -0
- package/src/config/model-discovery.ts +1077 -0
- package/src/config/model-registry.ts +2822 -0
- package/src/config/model-resolver.ts +2115 -0
- package/src/config/model-roles.ts +113 -0
- package/src/config/models-config-schema-bundle.ts +315 -0
- package/src/config/models-config-schema.ts +14 -0
- package/src/config/models-config.ts +130 -0
- package/src/config/prompt-templates.ts +205 -0
- package/src/config/provider-globals.ts +25 -0
- package/src/config/resolve-config-value.ts +95 -0
- package/src/config/service-tier.ts +146 -0
- package/src/config/settings-schema.ts +5887 -0
- package/src/config/settings.ts +2394 -0
- package/src/config.ts +242 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +960 -0
- package/src/dap/client.ts +1043 -0
- package/src/dap/config.ts +480 -0
- package/src/dap/defaults.json +212 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1841 -0
- package/src/dap/types.ts +611 -0
- package/src/debug/index.ts +584 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +966 -0
- package/src/debug/profiler.ts +168 -0
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/raw-sse-buffer.ts +421 -0
- package/src/debug/raw-sse.ts +312 -0
- package/src/debug/remote-debugger.ts +151 -0
- package/src/debug/report-bundle.ts +411 -0
- package/src/debug/system-info.ts +111 -0
- package/src/debug/terminal-info.ts +124 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +300 -0
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
- package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
- package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
- package/src/discovery/builtin-rules/go-ioutil.md +37 -0
- package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
- package/src/discovery/builtin-rules/go-new-expr.md +44 -0
- package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
- package/src/discovery/builtin-rules/go-range-int.md +45 -0
- package/src/discovery/builtin-rules/index.ts +74 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
- package/src/discovery/builtin-rules/rs-result-type.md +20 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
- package/src/discovery/builtin-rules/ts-import-type.md +43 -0
- package/src/discovery/builtin-rules/ts-no-any.md +66 -0
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/builtin.ts +945 -0
- package/src/discovery/claude-plugins.ts +640 -0
- package/src/discovery/claude.ts +591 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +553 -0
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/cursor.ts +223 -0
- package/src/discovery/gemini.ts +386 -0
- package/src/discovery/github.ts +337 -0
- package/src/discovery/helpers.ts +1183 -0
- package/src/discovery/index.ts +82 -0
- package/src/discovery/mcp-json.ts +182 -0
- package/src/discovery/omp-extension-roots.ts +272 -0
- package/src/discovery/omp-plugins.ts +409 -0
- package/src/discovery/opencode.ts +441 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +77 -0
- package/src/discovery/vscode.ts +106 -0
- package/src/discovery/windsurf.ts +149 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +1008 -0
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/file-snapshot-store.ts +148 -0
- package/src/edit/hashline/block-resolver.ts +33 -0
- package/src/edit/hashline/diff.ts +402 -0
- package/src/edit/hashline/execute.ts +313 -0
- package/src/edit/hashline/filesystem.ts +247 -0
- package/src/edit/hashline/index.ts +5 -0
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/hashline/params.ts +12 -0
- package/src/edit/index.ts +699 -0
- package/src/edit/modes/apply-patch.lark +19 -0
- package/src/edit/modes/apply-patch.ts +53 -0
- package/src/edit/modes/patch.ts +1958 -0
- package/src/edit/modes/replace.ts +1191 -0
- package/src/edit/normalize.ts +345 -0
- package/src/edit/notebook.ts +242 -0
- package/src/edit/read-file.ts +25 -0
- package/src/edit/renderer.ts +998 -0
- package/src/edit/snapshot-details.ts +77 -0
- package/src/edit/streaming.ts +710 -0
- package/src/eval/agent-bridge.ts +224 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/backend.ts +71 -0
- package/src/eval/bridge-timeout.ts +65 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +212 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/executor-base.ts +562 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +6 -0
- package/src/eval/jl/executor.ts +551 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +236 -0
- package/src/eval/jl/prelude.jl +736 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +666 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/context-manager.ts +872 -0
- package/src/eval/js/executor.ts +178 -0
- package/src/eval/js/index.ts +41 -0
- package/src/eval/js/process-entry.ts +31 -0
- package/src/eval/js/shared/helpers.ts +170 -0
- package/src/eval/js/shared/indirect-eval.ts +30 -0
- package/src/eval/js/shared/local-module-loader.ts +364 -0
- package/src/eval/js/shared/prelude.ts +2 -0
- package/src/eval/js/shared/prelude.txt +294 -0
- package/src/eval/js/shared/rewrite-imports.ts +550 -0
- package/src/eval/js/shared/runtime.ts +590 -0
- package/src/eval/js/shared/types.ts +18 -0
- package/src/eval/js/tool-bridge.ts +163 -0
- package/src/eval/js/worker-core.ts +380 -0
- package/src/eval/js/worker-entry.ts +37 -0
- package/src/eval/js/worker-protocol.ts +47 -0
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/display.ts +71 -0
- package/src/eval/py/executor.ts +643 -0
- package/src/eval/py/index.ts +57 -0
- package/src/eval/py/kernel.ts +235 -0
- package/src/eval/py/prelude.py +673 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1425 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +134 -0
- package/src/eval/py/tool-bridge.ts +201 -0
- package/src/eval/rb/executor.ts +511 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +231 -0
- package/src/eval/rb/prelude.rb +551 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +581 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/session-id.ts +8 -0
- package/src/eval/types.ts +48 -0
- package/src/exa/index.ts +2 -0
- package/src/exa/mcp-client.ts +370 -0
- package/src/exa/types.ts +69 -0
- package/src/exec/bash-executor.ts +627 -0
- package/src/exec/direnv.ts +145 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/non-interactive-env.ts +118 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +320 -0
- package/src/export/html/share-loader.js +102 -0
- package/src/export/html/template.css +1355 -0
- package/src/export/html/template.html +55 -0
- package/src/export/html/template.js +1653 -0
- package/src/export/html/tool-views.generated.js +35 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/html/web-palette.ts +126 -0
- package/src/export/share.ts +686 -0
- package/src/export/ttsr.ts +590 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +242 -0
- package/src/extensibility/custom-commands/types.ts +119 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +301 -0
- package/src/extensibility/custom-tools/types.ts +287 -0
- package/src/extensibility/custom-tools/wrapper.ts +50 -0
- package/src/extensibility/extensions/compact-handler.ts +40 -0
- package/src/extensibility/extensions/get-commands-handler.ts +78 -0
- package/src/extensibility/extensions/index.ts +16 -0
- package/src/extensibility/extensions/load-errors.ts +13 -0
- package/src/extensibility/extensions/loader.ts +663 -0
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/model-api.ts +39 -0
- package/src/extensibility/extensions/runner.ts +1396 -0
- package/src/extensibility/extensions/types.ts +1603 -0
- package/src/extensibility/extensions/wrapper.ts +395 -0
- package/src/extensibility/hooks/index.ts +6 -0
- package/src/extensibility/hooks/loader.ts +243 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +124 -0
- package/src/extensibility/hooks/types.ts +612 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
- package/src/extensibility/legacy-typebox.ts +101 -0
- package/src/extensibility/plugins/bun-git-cache.ts +91 -0
- package/src/extensibility/plugins/doctor.ts +65 -0
- package/src/extensibility/plugins/git-url.ts +367 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +201 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
- package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
- package/src/extensibility/plugins/loader.ts +535 -0
- package/src/extensibility/plugins/manager.ts +1142 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +927 -0
- package/src/extensibility/plugins/marketplace/registry.ts +192 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +192 -0
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/extensibility/plugins/parser.ts +107 -0
- package/src/extensibility/plugins/runtime-config.ts +9 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/extensibility/shared-events.ts +401 -0
- package/src/extensibility/skills.ts +531 -0
- package/src/extensibility/slash-commands.ts +131 -0
- package/src/extensibility/tool-event-input.ts +80 -0
- package/src/extensibility/tool-proxy.ts +28 -0
- package/src/extensibility/utils.ts +192 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +521 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +251 -0
- package/src/hindsight/backend.ts +354 -0
- package/src/hindsight/bank.ts +156 -0
- package/src/hindsight/client.ts +680 -0
- package/src/hindsight/config.ts +193 -0
- package/src/hindsight/content.ts +266 -0
- package/src/hindsight/index.ts +8 -0
- package/src/hindsight/mental-models.ts +429 -0
- package/src/hindsight/seeds.json +32 -0
- package/src/hindsight/state.ts +530 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/index.ts +66 -0
- package/src/internal-urls/agent-protocol.ts +180 -0
- package/src/internal-urls/artifact-protocol.ts +151 -0
- package/src/internal-urls/docs-index.ts +102 -0
- package/src/internal-urls/filesystem-resource.ts +34 -0
- package/src/internal-urls/history-protocol.ts +198 -0
- package/src/internal-urls/index.ts +28 -0
- package/src/internal-urls/issue-pr-protocol.ts +594 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +471 -0
- package/src/internal-urls/mcp-protocol.ts +168 -0
- package/src/internal-urls/memory-protocol.ts +380 -0
- package/src/internal-urls/omp-protocol.ts +94 -0
- package/src/internal-urls/parse.ts +103 -0
- package/src/internal-urls/registry-helpers.ts +132 -0
- package/src/internal-urls/router.ts +153 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/security-protocol.ts +261 -0
- package/src/internal-urls/skill-protocol.ts +131 -0
- package/src/internal-urls/ssh-protocol.ts +368 -0
- package/src/internal-urls/types.ts +196 -0
- package/src/internal-urls/vault-protocol.ts +940 -0
- package/src/internal-urls/xd-protocol.ts +46 -0
- package/src/irc/bus.ts +380 -0
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/launch/broker.ts +1368 -0
- package/src/launch/client.ts +521 -0
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +14 -0
- package/src/launch/presence.ts +82 -0
- package/src/launch/protocol.ts +448 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/launch/terminal-output.ts +46 -0
- package/src/lib/xai-http.ts +150 -0
- package/src/live/attestation.ts +91 -0
- package/src/live/controller.ts +517 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +422 -0
- package/src/live/visualizer.ts +221 -0
- package/src/live/voices.ts +18 -0
- package/src/lsp/client.ts +1465 -0
- package/src/lsp/clients/biome-client.ts +263 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +85 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +549 -0
- package/src/lsp/defaults.json +499 -0
- package/src/lsp/deferred-diagnostics.ts +66 -0
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/edits.ts +288 -0
- package/src/lsp/format-options.ts +119 -0
- package/src/lsp/index.ts +2821 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/render.ts +668 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +479 -0
- package/src/lsp/utils.ts +747 -0
- package/src/main.ts +1799 -0
- package/src/markit/NOTICE +32 -0
- package/src/markit/converters/docx.ts +56 -0
- package/src/markit/converters/epub.ts +136 -0
- package/src/markit/converters/pdf/columns.ts +103 -0
- package/src/markit/converters/pdf/extract.ts +598 -0
- package/src/markit/converters/pdf/grid.ts +780 -0
- package/src/markit/converters/pdf/headers.ts +106 -0
- package/src/markit/converters/pdf/index.ts +146 -0
- package/src/markit/converters/pdf/render.ts +501 -0
- package/src/markit/converters/pdf/types.ts +84 -0
- package/src/markit/converters/pptx.ts +325 -0
- package/src/markit/converters/xlsx.ts +173 -0
- package/src/markit/index.ts +2 -0
- package/src/markit/registry.ts +59 -0
- package/src/markit/types.ts +35 -0
- package/src/mcp/client.ts +511 -0
- package/src/mcp/config-writer.ts +377 -0
- package/src/mcp/config.ts +385 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +122 -0
- package/src/mcp/loader.ts +125 -0
- package/src/mcp/manager.ts +1524 -0
- package/src/mcp/oauth-credentials.ts +104 -0
- package/src/mcp/oauth-discovery.ts +587 -0
- package/src/mcp/oauth-flow.ts +830 -0
- package/src/mcp/render.ts +214 -0
- package/src/mcp/request-id.ts +24 -0
- package/src/mcp/smithery-auth.ts +108 -0
- package/src/mcp/smithery-connect.ts +154 -0
- package/src/mcp/smithery-registry.ts +500 -0
- package/src/mcp/startup-events.ts +116 -0
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/tool-bridge.ts +691 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +508 -0
- package/src/mcp/transports/index.ts +7 -0
- package/src/mcp/transports/sse.ts +372 -0
- package/src/mcp/transports/stdio.ts +905 -0
- package/src/mcp/types.ts +467 -0
- package/src/memories/index.ts +1434 -0
- package/src/memories/storage.ts +578 -0
- package/src/memory-backend/index.ts +19 -0
- package/src/memory-backend/local-backend.ts +47 -0
- package/src/memory-backend/messages.ts +19 -0
- package/src/memory-backend/off-backend.ts +25 -0
- package/src/memory-backend/resolve.ts +25 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/memory-backend/types.ts +166 -0
- package/src/mnemopi/backend.ts +629 -0
- package/src/mnemopi/config.ts +267 -0
- package/src/mnemopi/embed-client.ts +293 -0
- package/src/mnemopi/embed-protocol.ts +35 -0
- package/src/mnemopi/embed-worker.ts +114 -0
- package/src/mnemopi/index.ts +3 -0
- package/src/mnemopi/state.ts +940 -0
- package/src/modes/acp/acp-agent.ts +2595 -0
- package/src/modes/acp/acp-client-bridge.ts +154 -0
- package/src/modes/acp/acp-event-mapper.ts +1084 -0
- package/src/modes/acp/acp-mode.ts +48 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/advisor-config.ts +635 -0
- package/src/modes/components/advisor-message.ts +109 -0
- package/src/modes/components/agent-dashboard.ts +1252 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +1126 -0
- package/src/modes/components/agent-transcript-viewer.ts +647 -0
- package/src/modes/components/ask-dialog.ts +1017 -0
- package/src/modes/components/assistant-message.ts +978 -0
- package/src/modes/components/background-tan-message.ts +36 -0
- package/src/modes/components/bash-execution.ts +233 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/btw-panel.ts +161 -0
- package/src/modes/components/cache-invalidation-marker.ts +110 -0
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/chat-transcript-builder.ts +518 -0
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/collab-prompt-message.ts +32 -0
- package/src/modes/components/compaction-summary-message.ts +221 -0
- package/src/modes/components/copy-selector.ts +218 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +1036 -0
- package/src/modes/components/custom-message.ts +70 -0
- package/src/modes/components/diff.ts +254 -0
- package/src/modes/components/dynamic-border.ts +37 -0
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/eval-execution.ts +169 -0
- package/src/modes/components/execution-shared.ts +101 -0
- package/src/modes/components/extensions/extension-dashboard.ts +492 -0
- package/src/modes/components/extensions/extension-list.ts +507 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +326 -0
- package/src/modes/components/extensions/state-manager.ts +648 -0
- package/src/modes/components/extensions/types.ts +186 -0
- package/src/modes/components/footer.ts +274 -0
- package/src/modes/components/history-search.ts +268 -0
- package/src/modes/components/hook-editor.ts +213 -0
- package/src/modes/components/hook-input.ts +87 -0
- package/src/modes/components/hook-message.ts +67 -0
- package/src/modes/components/hook-selector.ts +691 -0
- package/src/modes/components/index.ts +42 -0
- package/src/modes/components/keybinding-hints.ts +56 -0
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/login-dialog.ts +197 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1413 -0
- package/src/modes/components/message-frame.ts +98 -0
- package/src/modes/components/model-browser.ts +888 -0
- package/src/modes/components/model-hub.ts +2014 -0
- package/src/modes/components/model-picker.ts +237 -0
- package/src/modes/components/move-overlay.ts +293 -0
- package/src/modes/components/oauth-selector.ts +474 -0
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/overlay-box.ts +109 -0
- package/src/modes/components/pause-screen.ts +208 -0
- package/src/modes/components/plan-review-overlay.ts +1226 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/plugin-selector.ts +100 -0
- package/src/modes/components/plugin-settings.ts +745 -0
- package/src/modes/components/queue-mode-selector.ts +61 -0
- package/src/modes/components/read-tool-group.ts +856 -0
- package/src/modes/components/reset-usage-selector.ts +161 -0
- package/src/modes/components/segment-track.ts +89 -0
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/session-account-selector.ts +62 -0
- package/src/modes/components/session-selector.ts +1035 -0
- package/src/modes/components/settings-defs.ts +267 -0
- package/src/modes/components/settings-selector.ts +1445 -0
- package/src/modes/components/show-images-selector.ts +50 -0
- package/src/modes/components/skill-message.ts +110 -0
- package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
- package/src/modes/components/snapcompact-shape-preview.ts +192 -0
- package/src/modes/components/status-line/component.ts +1843 -0
- package/src/modes/components/status-line/context-thresholds.ts +86 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/index.ts +5 -0
- package/src/modes/components/status-line/presets.ts +106 -0
- package/src/modes/components/status-line/segments.ts +710 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/types.ts +159 -0
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/theme-selector.ts +68 -0
- package/src/modes/components/thinking-selector.ts +57 -0
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +26 -0
- package/src/modes/components/todo-reminder.ts +43 -0
- package/src/modes/components/tool-execution.ts +1467 -0
- package/src/modes/components/transcript-container.ts +527 -0
- package/src/modes/components/tree-selector.ts +1006 -0
- package/src/modes/components/ttsr-notification.ts +123 -0
- package/src/modes/components/usage-row.ts +52 -0
- package/src/modes/components/user-message-selector.ts +227 -0
- package/src/modes/components/user-message.ts +167 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +578 -0
- package/src/modes/controllers/btw-controller.ts +246 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +2024 -0
- package/src/modes/controllers/event-controller.ts +2173 -0
- package/src/modes/controllers/extension-ui-controller.ts +1243 -0
- package/src/modes/controllers/input-controller.ts +2037 -0
- package/src/modes/controllers/live-command-controller.ts +261 -0
- package/src/modes/controllers/mcp-command-controller.ts +2552 -0
- package/src/modes/controllers/omfg-controller.ts +287 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +2058 -0
- package/src/modes/controllers/session-focus-controller.ts +117 -0
- package/src/modes/controllers/ssh-command-controller.ts +385 -0
- package/src/modes/controllers/streaming-reveal.ts +399 -0
- package/src/modes/controllers/tan-command-controller.ts +243 -0
- package/src/modes/controllers/todo-command-controller.ts +487 -0
- package/src/modes/controllers/tool-args-reveal.ts +591 -0
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/github-ref-autocomplete.ts +75 -0
- package/src/modes/gradient-highlight.ts +99 -0
- package/src/modes/image-references.ts +137 -0
- package/src/modes/index.ts +10 -0
- package/src/modes/interactive-mode.ts +5055 -0
- package/src/modes/internal-url-autocomplete.ts +158 -0
- package/src/modes/loop-limit.ts +192 -0
- package/src/modes/magic-keyword-boundary.ts +23 -0
- package/src/modes/magic-keywords.ts +42 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/oauth-manual-input.ts +69 -0
- package/src/modes/orchestrate.ts +43 -0
- package/src/modes/print-mode.ts +282 -0
- package/src/modes/prompt-action-autocomplete.ts +322 -0
- package/src/modes/queue-input.ts +132 -0
- package/src/modes/rpc/host-tools.ts +204 -0
- package/src/modes/rpc/host-uris.ts +241 -0
- package/src/modes/rpc/rpc-client.ts +1213 -0
- package/src/modes/rpc/rpc-frame.ts +316 -0
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +1519 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +544 -0
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/runtime-init.ts +144 -0
- package/src/modes/session-observer-registry.ts +223 -0
- package/src/modes/session-teardown.ts +82 -0
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +103 -0
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
- package/src/modes/setup-wizard/scenes/model.ts +132 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +105 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +330 -0
- package/src/modes/setup-wizard/scenes/types.ts +65 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
- package/src/modes/shared.ts +49 -0
- package/src/modes/skill-command.ts +91 -0
- package/src/modes/theme/dark.json +95 -0
- package/src/modes/theme/defaults/alabaster.json +93 -0
- package/src/modes/theme/defaults/amethyst.json +96 -0
- package/src/modes/theme/defaults/anthracite.json +93 -0
- package/src/modes/theme/defaults/basalt.json +91 -0
- package/src/modes/theme/defaults/birch.json +95 -0
- package/src/modes/theme/defaults/dark-abyss.json +91 -0
- package/src/modes/theme/defaults/dark-arctic.json +104 -0
- package/src/modes/theme/defaults/dark-aurora.json +95 -0
- package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
- package/src/modes/theme/defaults/dark-cavern.json +91 -0
- package/src/modes/theme/defaults/dark-copper.json +95 -0
- package/src/modes/theme/defaults/dark-cosmos.json +90 -0
- package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/modes/theme/defaults/dark-dracula.json +98 -0
- package/src/modes/theme/defaults/dark-eclipse.json +91 -0
- package/src/modes/theme/defaults/dark-ember.json +95 -0
- package/src/modes/theme/defaults/dark-equinox.json +90 -0
- package/src/modes/theme/defaults/dark-forest.json +96 -0
- package/src/modes/theme/defaults/dark-github.json +105 -0
- package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
- package/src/modes/theme/defaults/dark-lavender.json +95 -0
- package/src/modes/theme/defaults/dark-lunar.json +89 -0
- package/src/modes/theme/defaults/dark-midnight.json +95 -0
- package/src/modes/theme/defaults/dark-monochrome.json +94 -0
- package/src/modes/theme/defaults/dark-monokai.json +98 -0
- package/src/modes/theme/defaults/dark-nebula.json +90 -0
- package/src/modes/theme/defaults/dark-nord.json +97 -0
- package/src/modes/theme/defaults/dark-ocean.json +101 -0
- package/src/modes/theme/defaults/dark-one.json +100 -0
- package/src/modes/theme/defaults/dark-poimandres.json +143 -0
- package/src/modes/theme/defaults/dark-rainforest.json +91 -0
- package/src/modes/theme/defaults/dark-reef.json +91 -0
- package/src/modes/theme/defaults/dark-retro.json +92 -0
- package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
- package/src/modes/theme/defaults/dark-sakura.json +95 -0
- package/src/modes/theme/defaults/dark-slate.json +95 -0
- package/src/modes/theme/defaults/dark-solarized.json +97 -0
- package/src/modes/theme/defaults/dark-solstice.json +90 -0
- package/src/modes/theme/defaults/dark-starfall.json +91 -0
- package/src/modes/theme/defaults/dark-sunset.json +99 -0
- package/src/modes/theme/defaults/dark-swamp.json +90 -0
- package/src/modes/theme/defaults/dark-synthwave.json +103 -0
- package/src/modes/theme/defaults/dark-taiga.json +91 -0
- package/src/modes/theme/defaults/dark-terminal.json +95 -0
- package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/modes/theme/defaults/dark-tundra.json +91 -0
- package/src/modes/theme/defaults/dark-twilight.json +91 -0
- package/src/modes/theme/defaults/dark-volcanic.json +91 -0
- package/src/modes/theme/defaults/graphite.json +92 -0
- package/src/modes/theme/defaults/index.ts +199 -0
- package/src/modes/theme/defaults/light-arctic.json +107 -0
- package/src/modes/theme/defaults/light-aurora-day.json +91 -0
- package/src/modes/theme/defaults/light-canyon.json +91 -0
- package/src/modes/theme/defaults/light-catppuccin.json +106 -0
- package/src/modes/theme/defaults/light-cirrus.json +90 -0
- package/src/modes/theme/defaults/light-coral.json +95 -0
- package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
- package/src/modes/theme/defaults/light-dawn.json +90 -0
- package/src/modes/theme/defaults/light-dunes.json +91 -0
- package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
- package/src/modes/theme/defaults/light-forest.json +100 -0
- package/src/modes/theme/defaults/light-frost.json +95 -0
- package/src/modes/theme/defaults/light-github.json +115 -0
- package/src/modes/theme/defaults/light-glacier.json +91 -0
- package/src/modes/theme/defaults/light-gruvbox.json +108 -0
- package/src/modes/theme/defaults/light-haze.json +90 -0
- package/src/modes/theme/defaults/light-honeycomb.json +95 -0
- package/src/modes/theme/defaults/light-lagoon.json +91 -0
- package/src/modes/theme/defaults/light-lavender.json +95 -0
- package/src/modes/theme/defaults/light-meadow.json +91 -0
- package/src/modes/theme/defaults/light-mint.json +95 -0
- package/src/modes/theme/defaults/light-monochrome.json +101 -0
- package/src/modes/theme/defaults/light-ocean.json +99 -0
- package/src/modes/theme/defaults/light-one.json +99 -0
- package/src/modes/theme/defaults/light-opal.json +91 -0
- package/src/modes/theme/defaults/light-orchard.json +91 -0
- package/src/modes/theme/defaults/light-paper.json +95 -0
- package/src/modes/theme/defaults/light-poimandres.json +143 -0
- package/src/modes/theme/defaults/light-prism.json +90 -0
- package/src/modes/theme/defaults/light-retro.json +98 -0
- package/src/modes/theme/defaults/light-sand.json +95 -0
- package/src/modes/theme/defaults/light-savanna.json +91 -0
- package/src/modes/theme/defaults/light-solarized.json +102 -0
- package/src/modes/theme/defaults/light-soleil.json +90 -0
- package/src/modes/theme/defaults/light-sunset.json +99 -0
- package/src/modes/theme/defaults/light-synthwave.json +98 -0
- package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
- package/src/modes/theme/defaults/light-wetland.json +91 -0
- package/src/modes/theme/defaults/light-zenith.json +89 -0
- package/src/modes/theme/defaults/limestone.json +94 -0
- package/src/modes/theme/defaults/mahogany.json +97 -0
- package/src/modes/theme/defaults/marble.json +93 -0
- package/src/modes/theme/defaults/obsidian.json +91 -0
- package/src/modes/theme/defaults/onyx.json +91 -0
- package/src/modes/theme/defaults/pearl.json +93 -0
- package/src/modes/theme/defaults/porcelain.json +91 -0
- package/src/modes/theme/defaults/quartz.json +96 -0
- package/src/modes/theme/defaults/sandstone.json +95 -0
- package/src/modes/theme/defaults/titanium.json +90 -0
- package/src/modes/theme/light.json +93 -0
- package/src/modes/theme/mermaid-cache.ts +92 -0
- package/src/modes/theme/shimmer.ts +305 -0
- package/src/modes/theme/theme-schema.json +463 -0
- package/src/modes/theme/theme.ts +3171 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +494 -0
- package/src/modes/ultrathink.ts +42 -0
- package/src/modes/utils/context-usage.ts +518 -0
- package/src/modes/utils/copy-targets.ts +378 -0
- package/src/modes/utils/hotkeys-markdown.ts +65 -0
- package/src/modes/utils/interactive-context-helpers.ts +31 -0
- package/src/modes/utils/keybinding-matchers.ts +86 -0
- package/src/modes/utils/tools-markdown.ts +31 -0
- package/src/modes/utils/transcript-render-helpers.ts +253 -0
- package/src/modes/utils/ui-helpers.ts +976 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/modes/workflow.ts +55 -0
- package/src/plan-mode/approved-plan.ts +194 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/plan-mode/plan-protection.ts +31 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +60 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/advise-tool.md +3 -0
- package/src/prompts/advisor/context-files.md +8 -0
- package/src/prompts/advisor/system.md +98 -0
- package/src/prompts/agents/designer.md +74 -0
- package/src/prompts/agents/frontmatter.md +12 -0
- package/src/prompts/agents/init.md +33 -0
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/reviewer.md +139 -0
- package/src/prompts/agents/scout.md +58 -0
- package/src/prompts/agents/security-reviewer.md +75 -0
- package/src/prompts/agents/task.md +17 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/bench.md +6 -0
- package/src/prompts/ci-green-request.md +36 -0
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/goals/goal-mode-context.md +4 -0
- package/src/prompts/goals/goal-todo-context.md +12 -0
- package/src/prompts/goals/guided-goal-interview.md +43 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/consolidation_system.md +4 -0
- package/src/prompts/memories/read-path.md +17 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-custom-request.md +21 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +68 -0
- package/src/prompts/security/scan-coordinator.md +7 -0
- package/src/prompts/security/scan-request.md +21 -0
- package/src/prompts/security/validate-request.md +8 -0
- package/src/prompts/session/launch-completion.md +1 -0
- package/src/prompts/skills/autoload.md +8 -0
- package/src/prompts/skills/user-invocation.md +11 -0
- package/src/prompts/steering/parent-irc.md +5 -0
- package/src/prompts/steering/user-interjection.md +6 -0
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/agent-creation-architect.md +50 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-continue.md +1 -0
- package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
- package/src/prompts/system/auto-thinking-difficulty.md +14 -0
- package/src/prompts/system/autolearn-guidance-learn.md +1 -0
- package/src/prompts/system/autolearn-guidance.md +7 -0
- package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +14 -0
- package/src/prompts/system/computer-safety.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +64 -0
- package/src/prompts/system/eager-task.md +7 -0
- package/src/prompts/system/eager-todo.md +18 -0
- package/src/prompts/system/empty-stop-retry.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/irc-autoreply.md +6 -0
- package/src/prompts/system/irc-incoming.md +9 -0
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/mcp-xdev-guidance.md +11 -0
- package/src/prompts/system/memory-consolidation-system.md +8 -0
- package/src/prompts/system/memory-extraction-system.md +26 -0
- package/src/prompts/system/mid-run-todo-nudge.md +3 -0
- package/src/prompts/system/omfg-user.md +50 -0
- package/src/prompts/system/orchestrate-notice.md +40 -0
- package/src/prompts/system/personalities/default.md +18 -0
- package/src/prompts/system/personalities/friendly.md +17 -0
- package/src/prompts/system/personalities/pragmatic.md +15 -0
- package/src/prompts/system/plan-mode-active.md +125 -0
- package/src/prompts/system/plan-mode-approved.md +22 -0
- package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
- package/src/prompts/system/plan-mode-reference.md +10 -0
- package/src/prompts/system/plan-mode-subagent.md +33 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/plan-yolo-handoff.md +5 -0
- package/src/prompts/system/prewalk-checklist.md +7 -0
- package/src/prompts/system/prewalk-continue.md +1 -0
- package/src/prompts/system/prewalk-plan.md +13 -0
- package/src/prompts/system/project-prompt.md +61 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/resolve-device-reminder.md +3 -0
- package/src/prompts/system/rewind-report.md +6 -0
- package/src/prompts/system/side-channel-no-tools.md +3 -0
- package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-context-stub.md +1 -0
- package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-system-stub.md +1 -0
- package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
- package/src/prompts/system/speech-rewrite.md +15 -0
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/subagent-system-prompt.md +73 -0
- package/src/prompts/system/subagent-user-prompt.md +3 -0
- package/src/prompts/system/subagent-yield-reminder.md +23 -0
- package/src/prompts/system/system-prompt.md +263 -0
- package/src/prompts/system/tan-context-switch.md +17 -0
- package/src/prompts/system/task-label.md +23 -0
- package/src/prompts/system/thinking-loop-redirect.md +10 -0
- package/src/prompts/system/title-marker-instruction.md +1 -0
- package/src/prompts/system/title-system.md +16 -0
- package/src/prompts/system/tool-call-loop-redirect.md +8 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/system/ultrathink-notice.md +3 -0
- package/src/prompts/system/unexpected-stop-classifier.md +17 -0
- package/src/prompts/system/unexpected-stop-retry.md +4 -0
- package/src/prompts/system/vibe-mode-active.md +26 -0
- package/src/prompts/system/web-search.md +25 -0
- package/src/prompts/system/workflow-notice.md +112 -0
- package/src/prompts/system/xdev-mount-notice.md +20 -0
- package/src/prompts/tools/apply-patch.md +65 -0
- package/src/prompts/tools/ask.md +22 -0
- package/src/prompts/tools/ast-edit.md +11 -0
- package/src/prompts/tools/ast-grep.md +19 -0
- package/src/prompts/tools/async-result.md +8 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/browser.md +29 -0
- package/src/prompts/tools/checkpoint.md +15 -0
- package/src/prompts/tools/computer.md +26 -0
- package/src/prompts/tools/debug.md +3 -0
- package/src/prompts/tools/eval.md +45 -0
- package/src/prompts/tools/github.md +22 -0
- package/src/prompts/tools/glob.md +16 -0
- package/src/prompts/tools/goal.md +11 -0
- package/src/prompts/tools/grep.md +13 -0
- package/src/prompts/tools/hub.md +34 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/prompts/tools/image-gen.md +7 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +22 -0
- package/src/prompts/tools/learn.md +7 -0
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/lsp.md +19 -0
- package/src/prompts/tools/manage-skill.md +9 -0
- package/src/prompts/tools/memory-edit.md +12 -0
- package/src/prompts/tools/patch.md +57 -0
- package/src/prompts/tools/read.md +27 -0
- package/src/prompts/tools/recall.md +7 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/replace.md +30 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +14 -0
- package/src/prompts/tools/security-publish.md +1 -0
- package/src/prompts/tools/security-scan.md +1 -0
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task-summary.md +20 -0
- package/src/prompts/tools/task.md +83 -0
- package/src/prompts/tools/todo.md +42 -0
- package/src/prompts/tools/vibe-kill.md +3 -0
- package/src/prompts/tools/vibe-list.md +3 -0
- package/src/prompts/tools/vibe-send.md +9 -0
- package/src/prompts/tools/vibe-spawn.md +10 -0
- package/src/prompts/tools/vibe-turn-result.md +19 -0
- package/src/prompts/tools/vibe-wait.md +8 -0
- package/src/prompts/tools/web-search.md +8 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/registry/agent-lifecycle.ts +505 -0
- package/src/registry/agent-registry.ts +287 -0
- package/src/registry/persisted-agents.ts +423 -0
- package/src/sdk.ts +3820 -0
- package/src/secrets/index.ts +375 -0
- package/src/secrets/obfuscator.ts +2629 -0
- package/src/secrets/regex.ts +21 -0
- package/src/security/auth.ts +98 -0
- package/src/security/cloud.ts +686 -0
- package/src/security/comparison.ts +255 -0
- package/src/security/contracts/ids.ts +111 -0
- package/src/security/contracts/index.ts +4 -0
- package/src/security/contracts/schemas.ts +219 -0
- package/src/security/contracts/types.ts +254 -0
- package/src/security/contracts/validation.ts +69 -0
- package/src/security/coordinator.ts +708 -0
- package/src/security/importers/codex-security.ts +387 -0
- package/src/security/importers/index.ts +2 -0
- package/src/security/importers/sarif.ts +357 -0
- package/src/security/index.ts +13 -0
- package/src/security/preflight.ts +405 -0
- package/src/security/provenance.ts +106 -0
- package/src/security/publication.ts +326 -0
- package/src/security/remediation.ts +93 -0
- package/src/security/resource-output.ts +50 -0
- package/src/security/sarif.ts +78 -0
- package/src/security/store.ts +430 -0
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-events.ts +67 -0
- package/src/session/agent-session-types.ts +407 -0
- package/src/session/agent-session.ts +9000 -0
- package/src/session/agent-storage.ts +807 -0
- package/src/session/artifacts.ts +154 -0
- package/src/session/async-job-delivery.ts +82 -0
- package/src/session/auth-broker-config.ts +92 -0
- package/src/session/auth-storage.ts +25 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.ts +295 -0
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/client-bridge.ts +85 -0
- package/src/session/codex-auto-reset.ts +673 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/compact-modes.ts +105 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/eval-runner.ts +219 -0
- package/src/session/exit-diagnostics.ts +310 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/history-storage.ts +329 -0
- package/src/session/indexed-session-storage.ts +553 -0
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/launch-completion.ts +37 -0
- package/src/session/messages.ts +1304 -0
- package/src/session/model-controls.ts +757 -0
- package/src/session/prewalk.ts +279 -0
- package/src/session/provider-image-budget.ts +86 -0
- package/src/session/queued-messages.ts +99 -0
- package/src/session/redis-session-storage.ts +257 -0
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1896 -0
- package/src/session/session-context.ts +584 -0
- package/src/session/session-dump-format.ts +216 -0
- package/src/session/session-entries.ts +306 -0
- package/src/session/session-handoff.ts +325 -0
- package/src/session/session-history-format.ts +459 -0
- package/src/session/session-listing.ts +715 -0
- package/src/session/session-loader.ts +362 -0
- package/src/session/session-maintenance.ts +3115 -0
- package/src/session/session-manager.ts +2747 -0
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-metadata.ts +53 -0
- package/src/session/session-migrations.ts +78 -0
- package/src/session/session-paths.ts +280 -0
- package/src/session/session-persistence.ts +293 -0
- package/src/session/session-provider-boundary.ts +306 -0
- package/src/session/session-stats.ts +349 -0
- package/src/session/session-storage.ts +774 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/session-tools.ts +1385 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/settings-stream-fn.ts +79 -0
- package/src/session/shake-types.ts +43 -0
- package/src/session/snapcompact-inline.ts +545 -0
- package/src/session/snapcompact-savings-journal.ts +113 -0
- package/src/session/sql-session-storage.ts +374 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/streaming-output.ts +1459 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/tool-choice-queue.ts +305 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-persistence.ts +142 -0
- package/src/session/turn-recovery.ts +2005 -0
- package/src/session/unexpected-stop-classifier.ts +141 -0
- package/src/session/yield-queue.ts +288 -0
- package/src/slash-commands/acp-builtins.ts +70 -0
- package/src/slash-commands/available-commands.ts +105 -0
- package/src/slash-commands/builtin-registry.ts +3149 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
- package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
- package/src/slash-commands/helpers/context-report.ts +66 -0
- package/src/slash-commands/helpers/format.ts +46 -0
- package/src/slash-commands/helpers/logout.ts +108 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +533 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/reset-usage.ts +68 -0
- package/src/slash-commands/helpers/security.ts +451 -0
- package/src/slash-commands/helpers/session-pin.ts +44 -0
- package/src/slash-commands/helpers/ssh.ts +196 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
- package/src/slash-commands/helpers/todo.ts +285 -0
- package/src/slash-commands/helpers/usage-report.ts +198 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/types.ts +139 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +667 -0
- package/src/ssh/file-transfer.ts +214 -0
- package/src/ssh/sshfs-mount.ts +163 -0
- package/src/ssh/utils.ts +51 -0
- package/src/startup-splash.ts +19 -0
- package/src/stt/asr-client.ts +401 -0
- package/src/stt/asr-protocol.ts +65 -0
- package/src/stt/asr-worker.ts +603 -0
- package/src/stt/downloader.ts +142 -0
- package/src/stt/endpointer.ts +259 -0
- package/src/stt/index.ts +6 -0
- package/src/stt/models.ts +150 -0
- package/src/stt/sherpa-runtime.ts +71 -0
- package/src/stt/stt-controller.ts +320 -0
- package/src/stt/submit-trigger.ts +74 -0
- package/src/subprocess/worker-client.ts +463 -0
- package/src/subprocess/worker-runtime.ts +494 -0
- package/src/system-prompt.ts +921 -0
- package/src/task/agents.ts +170 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +145 -0
- package/src/task/executor.ts +3431 -0
- package/src/task/index.ts +1515 -0
- package/src/task/isolation-ownership.ts +106 -0
- package/src/task/isolation-runner.ts +444 -0
- package/src/task/label.ts +40 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/omp-command.ts +26 -0
- package/src/task/output-manager.ts +115 -0
- package/src/task/parallel.ts +221 -0
- package/src/task/persisted-revive.ts +173 -0
- package/src/task/prewalk.ts +6 -0
- package/src/task/prompt-policy.ts +8 -0
- package/src/task/provider-concurrency.ts +100 -0
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/render.ts +1830 -0
- package/src/task/renderer.ts +14 -0
- package/src/task/repair-args.ts +118 -0
- package/src/task/spawn-policy.ts +72 -0
- package/src/task/structured-subagent.ts +676 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +555 -0
- package/src/task/worktree.ts +967 -0
- package/src/task/yield-assembly.ts +198 -0
- package/src/telemetry-export.ts +504 -0
- package/src/thinking.ts +377 -0
- package/src/tiny/device.ts +111 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/message-preproc.ts +155 -0
- package/src/tiny/models.ts +268 -0
- package/src/tiny/text.ts +290 -0
- package/src/tiny/title-client.ts +460 -0
- package/src/tiny/title-protocol.ts +56 -0
- package/src/tiny/worker.ts +354 -0
- package/src/tools/acp-bridge.ts +125 -0
- package/src/tools/approval.ts +245 -0
- package/src/tools/ask.ts +1459 -0
- package/src/tools/ast-edit.ts +718 -0
- package/src/tools/ast-grep.ts +526 -0
- package/src/tools/auto-generated-guard.ts +335 -0
- package/src/tools/bash-interactive.ts +435 -0
- package/src/tools/bash-interceptor.ts +148 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +350 -0
- package/src/tools/bash.ts +1826 -0
- package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
- package/src/tools/browser/aria/aria-snapshot.ts +131 -0
- package/src/tools/browser/attach.ts +219 -0
- package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
- package/src/tools/browser/cmux/rpc.ts +200 -0
- package/src/tools/browser/cmux/socket-client.ts +445 -0
- package/src/tools/browser/launch.ts +939 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +447 -0
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +229 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/shared-daemon.ts +139 -0
- package/src/tools/browser/tab-protocol.ts +123 -0
- package/src/tools/browser/tab-supervisor.ts +1077 -0
- package/src/tools/browser/tab-worker-entry.ts +29 -0
- package/src/tools/browser/tab-worker.ts +1982 -0
- package/src/tools/browser.ts +487 -0
- package/src/tools/builtin-names.ts +67 -0
- package/src/tools/checkpoint.ts +137 -0
- package/src/tools/computer/exposure.ts +14 -0
- package/src/tools/computer/protocol.ts +69 -0
- package/src/tools/computer/supervisor.ts +409 -0
- package/src/tools/computer/worker-entry.ts +39 -0
- package/src/tools/computer/worker.ts +745 -0
- package/src/tools/computer-renderer.ts +147 -0
- package/src/tools/computer.ts +222 -0
- package/src/tools/conflict-detect.ts +815 -0
- package/src/tools/context.ts +49 -0
- package/src/tools/debug.ts +1121 -0
- package/src/tools/default-renderer.ts +154 -0
- package/src/tools/essential-tools.ts +46 -0
- package/src/tools/eval-backends.ts +34 -0
- package/src/tools/eval-format/index.ts +24 -0
- package/src/tools/eval-format/javascript.ts +952 -0
- package/src/tools/eval-format/julia.ts +446 -0
- package/src/tools/eval-format/python.ts +544 -0
- package/src/tools/eval-format/ruby.ts +380 -0
- package/src/tools/eval-render.ts +784 -0
- package/src/tools/eval.ts +774 -0
- package/src/tools/fetch.ts +1889 -0
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gh-cache-invalidation.ts +175 -0
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +484 -0
- package/src/tools/gh.ts +3958 -0
- package/src/tools/github-cache.ts +663 -0
- package/src/tools/glob.ts +691 -0
- package/src/tools/grep.ts +1920 -0
- package/src/tools/grouped-file-output.ts +210 -0
- package/src/tools/hub/index.ts +579 -0
- package/src/tools/hub/jobs.ts +714 -0
- package/src/tools/hub/launch.ts +690 -0
- package/src/tools/hub/messaging.ts +735 -0
- package/src/tools/hub/types.ts +117 -0
- package/src/tools/image-gen.ts +1689 -0
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +736 -0
- package/src/tools/inspect-image-renderer.ts +133 -0
- package/src/tools/inspect-image.ts +316 -0
- package/src/tools/json-tree.ts +260 -0
- package/src/tools/jtd-to-json-schema.ts +219 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/learn.ts +141 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/manage-skill.ts +102 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/memory-edit.ts +61 -0
- package/src/tools/memory-recall.ts +102 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +211 -0
- package/src/tools/memory-retain.ts +89 -0
- package/src/tools/output-meta.ts +860 -0
- package/src/tools/output-schema-validator.ts +307 -0
- package/src/tools/path-utils.ts +1489 -0
- package/src/tools/plan-mode-guard.ts +155 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
- package/src/tools/read.ts +3762 -0
- package/src/tools/render-utils.ts +926 -0
- package/src/tools/renderers.ts +133 -0
- package/src/tools/report-tool-issue.ts +568 -0
- package/src/tools/resolve.ts +423 -0
- package/src/tools/review.ts +103 -0
- package/src/tools/run-scope.ts +417 -0
- package/src/tools/security-scan.ts +287 -0
- package/src/tools/shell-tokenize.ts +213 -0
- package/src/tools/sqlite-reader.ts +884 -0
- package/src/tools/terminal-output.ts +141 -0
- package/src/tools/todo.ts +1226 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +102 -0
- package/src/tools/tool-timeouts.ts +38 -0
- package/src/tools/tts.ts +266 -0
- package/src/tools/vibe.ts +608 -0
- package/src/tools/write.ts +1646 -0
- package/src/tools/xdev.ts +560 -0
- package/src/tools/yield.ts +486 -0
- package/src/tts/downloader.ts +64 -0
- package/src/tts/index.ts +10 -0
- package/src/tts/models.ts +137 -0
- package/src/tts/runtime.ts +21 -0
- package/src/tts/speakable.ts +392 -0
- package/src/tts/speech-enhancer.ts +206 -0
- package/src/tts/streaming-player.ts +120 -0
- package/src/tts/tts-client.ts +475 -0
- package/src/tts/tts-protocol.ts +69 -0
- package/src/tts/tts-worker.ts +434 -0
- package/src/tts/vocalizer.ts +419 -0
- package/src/tts/wav.ts +58 -0
- package/src/tui/code-cell.ts +268 -0
- package/src/tui/file-list.ts +55 -0
- package/src/tui/hyperlink.ts +178 -0
- package/src/tui/index.ts +13 -0
- package/src/tui/output-block.ts +268 -0
- package/src/tui/status-line.ts +54 -0
- package/src/tui/tree-list.ts +172 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/block-context.ts +312 -0
- package/src/utils/changelog.ts +373 -0
- package/src/utils/clipboard.ts +361 -0
- package/src/utils/command-args.ts +74 -0
- package/src/utils/commit-message-generator.ts +148 -0
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/edit-mode.ts +61 -0
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +80 -0
- package/src/utils/fetch-timeout.ts +10 -0
- package/src/utils/file-display-mode.ts +44 -0
- package/src/utils/file-mentions.ts +293 -0
- package/src/utils/git.ts +2472 -0
- package/src/utils/image-loading.ts +236 -0
- package/src/utils/image-resize.ts +420 -0
- package/src/utils/image-vision-fallback.ts +196 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/ipc.ts +38 -0
- package/src/utils/jj.ts +422 -0
- package/src/utils/lang-from-path.ts +251 -0
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/local-date.ts +7 -0
- package/src/utils/mac-file-urls.applescript +37 -0
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +223 -0
- package/src/utils/mupdf-wasm-embed.ts +12 -0
- package/src/utils/open.ts +126 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/qrcode.ts +535 -0
- package/src/utils/sample-profile.ts +437 -0
- package/src/utils/session-color.ts +142 -0
- package/src/utils/shell-snapshot-fn-env.sh +63 -0
- package/src/utils/shell-snapshot.ts +326 -0
- package/src/utils/sixel.ts +69 -0
- package/src/utils/thinking-display.ts +163 -0
- package/src/utils/title-generator.ts +597 -0
- package/src/utils/token-rate.ts +72 -0
- package/src/utils/tool-choice.ts +50 -0
- package/src/utils/tools-manager.ts +411 -0
- package/src/utils/turndown.ts +82 -0
- package/src/utils/zip.ts +1106 -0
- package/src/vibe/runtime.ts +1684 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/kagi.ts +305 -0
- package/src/web/parallel.ts +354 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +97 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +663 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +800 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +129 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +173 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +121 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +189 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +94 -0
- package/src/web/scrapers/types.ts +354 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +325 -0
- package/src/web/search/index.ts +393 -0
- package/src/web/search/provider.ts +272 -0
- package/src/web/search/providers/anthropic.ts +406 -0
- package/src/web/search/providers/base.ts +112 -0
- package/src/web/search/providers/brave.ts +181 -0
- package/src/web/search/providers/browser-headers.ts +82 -0
- package/src/web/search/providers/browser-page.ts +125 -0
- package/src/web/search/providers/codex.ts +788 -0
- package/src/web/search/providers/duckduckgo.ts +382 -0
- package/src/web/search/providers/ecosia.ts +183 -0
- package/src/web/search/providers/exa.ts +513 -0
- package/src/web/search/providers/firecrawl.ts +213 -0
- package/src/web/search/providers/gemini.ts +634 -0
- package/src/web/search/providers/google.ts +195 -0
- package/src/web/search/providers/jina.ts +144 -0
- package/src/web/search/providers/kagi.ts +98 -0
- package/src/web/search/providers/kimi.ts +221 -0
- package/src/web/search/providers/mojeek.ts +220 -0
- package/src/web/search/providers/parallel.ts +186 -0
- package/src/web/search/providers/perplexity-auth.ts +142 -0
- package/src/web/search/providers/perplexity.ts +997 -0
- package/src/web/search/providers/public.ts +199 -0
- package/src/web/search/providers/searxng.ts +467 -0
- package/src/web/search/providers/startpage.ts +225 -0
- package/src/web/search/providers/synthetic.ts +126 -0
- package/src/web/search/providers/tavily.ts +244 -0
- package/src/web/search/providers/tinyfish.ts +166 -0
- package/src/web/search/providers/utils.ts +130 -0
- package/src/web/search/providers/xai.ts +372 -0
- package/src/web/search/providers/zai.ts +452 -0
- package/src/web/search/query.ts +850 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +518 -0
- package/src/web/search/utils.ts +17 -0
- package/src/workspace-tree.ts +326 -0
|
@@ -0,0 +1,1920 @@
|
|
|
1
|
+
import { mkdtemp, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { formatHashlineHeader } from "@oh-my-pi/hashline";
|
|
5
|
+
import { type } from "@oh-my-pi/omptype";
|
|
6
|
+
import type {
|
|
7
|
+
AgentTool,
|
|
8
|
+
AgentToolContext,
|
|
9
|
+
AgentToolResult,
|
|
10
|
+
AgentToolUpdateCallback,
|
|
11
|
+
ToolTier,
|
|
12
|
+
} from "@oh-my-pi/pi-agent-core";
|
|
13
|
+
import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi/pi-natives";
|
|
14
|
+
import type { Component } from "@oh-my-pi/pi-tui";
|
|
15
|
+
import { Text } from "@oh-my-pi/pi-tui";
|
|
16
|
+
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
17
|
+
import { recordFileSnapshot, recordSeenLinesFromBody } from "../edit/file-snapshot-store";
|
|
18
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
19
|
+
import type { LocalProtocolOptions } from "../internal-urls/local-protocol";
|
|
20
|
+
import { InternalUrlRouter } from "../internal-urls/router";
|
|
21
|
+
import type { InternalResource, ResolveContext } from "../internal-urls/types";
|
|
22
|
+
import type { Theme } from "../modes/theme/theme";
|
|
23
|
+
import grepDescription from "../prompts/tools/grep.md" with { type: "text" };
|
|
24
|
+
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead, truncateLine } from "../session/streaming-output";
|
|
25
|
+
import { isScoutSpawnable } from "../task/spawn-policy";
|
|
26
|
+
import {
|
|
27
|
+
Ellipsis,
|
|
28
|
+
fileHyperlink,
|
|
29
|
+
getTreeBranch,
|
|
30
|
+
getTreeContinuePrefix,
|
|
31
|
+
renderStatusLine,
|
|
32
|
+
renderTreeList,
|
|
33
|
+
truncateToWidth,
|
|
34
|
+
tryResolveInternalUrlSync,
|
|
35
|
+
uriHyperlink,
|
|
36
|
+
} from "../tui";
|
|
37
|
+
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
38
|
+
import { type ArchiveReader, type ExtractedArchiveFile, openArchive, parseArchivePathCandidates } from "../utils/zip";
|
|
39
|
+
import type { ToolSession } from ".";
|
|
40
|
+
import { materializeReadUrlToFile, parseReadUrlTarget } from "./fetch";
|
|
41
|
+
import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
42
|
+
import { classifyGroupedLines, formatGroupedFiles, groupLineIndicesByBlank } from "./grouped-file-output";
|
|
43
|
+
import { formatMatchLine } from "./match-line-format";
|
|
44
|
+
import type { OutputMeta } from "./output-meta";
|
|
45
|
+
import {
|
|
46
|
+
expandDelimitedPathEntries,
|
|
47
|
+
hasGlobPathChars,
|
|
48
|
+
isLineInRanges,
|
|
49
|
+
type LineRange,
|
|
50
|
+
parseLineRanges,
|
|
51
|
+
pathTargetsSsh,
|
|
52
|
+
type ResolvedSearchTarget,
|
|
53
|
+
resolveReadPath,
|
|
54
|
+
resolveToolSearchScope,
|
|
55
|
+
selectorLineRanges,
|
|
56
|
+
splitInternalUrlSel,
|
|
57
|
+
splitPathAndSel,
|
|
58
|
+
splitPathAndSelPreferringLiteral,
|
|
59
|
+
toPathList,
|
|
60
|
+
} from "./path-utils";
|
|
61
|
+
import {
|
|
62
|
+
createCachedComponent,
|
|
63
|
+
formatCodeFrameLine,
|
|
64
|
+
formatCount,
|
|
65
|
+
formatEmptyMessage,
|
|
66
|
+
formatErrorMessage,
|
|
67
|
+
formatMoreItems,
|
|
68
|
+
PREVIEW_LIMITS,
|
|
69
|
+
replaceTabs,
|
|
70
|
+
} from "./render-utils";
|
|
71
|
+
import { ToolError } from "./tool-errors";
|
|
72
|
+
import { toolResult } from "./tool-result";
|
|
73
|
+
|
|
74
|
+
const searchPathEntry = type("string").describe(
|
|
75
|
+
'file, directory, glob, internal URL, or "<file>:<lines>" selector to search (e.g. "src/foo.ts:50-100", "src/foo.ts:50+10", "src/foo.ts:50-100,200-300")',
|
|
76
|
+
);
|
|
77
|
+
const searchSchema = type({
|
|
78
|
+
pattern: type("string").describe("regex pattern"),
|
|
79
|
+
"path?": searchPathEntry.describe(
|
|
80
|
+
'file, directory, glob, internal URL, or "<file>:<lines>" selector to search; pass several as a semicolon-delimited list ("src; tests"). Omitted -> searches the workspace root (".")',
|
|
81
|
+
),
|
|
82
|
+
"case?": type("boolean").describe("case-sensitive search"),
|
|
83
|
+
"gitignore?": type("boolean").describe("respect gitignore"),
|
|
84
|
+
"skip?": type("number")
|
|
85
|
+
.or("null")
|
|
86
|
+
.describe("files to skip before collecting results — use to paginate when the prior call hit the file limit"),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export type GrepToolInput = typeof searchSchema.infer;
|
|
90
|
+
|
|
91
|
+
/** Maximum number of distinct files surfaced in a single response. The
|
|
92
|
+
* agent paginates further pages via `skip`. */
|
|
93
|
+
export const DEFAULT_FILE_LIMIT = 20;
|
|
94
|
+
/** Per-file match cap for multi-file searches — keeps a single hot file
|
|
95
|
+
* from crowding out diverse hits. Applied in JS after grep returns. */
|
|
96
|
+
export const MULTI_FILE_PER_FILE_MATCHES = 20;
|
|
97
|
+
/** Per-file match cap for single-file searches — there's no diversity
|
|
98
|
+
* concern when the scope is one file. */
|
|
99
|
+
export const SINGLE_FILE_MATCHES = 200;
|
|
100
|
+
/** Hard safety ceiling on how many matches we fetch from native grep
|
|
101
|
+
* before JS-side grouping. Sized to comfortably cover the file window
|
|
102
|
+
* (DEFAULT_FILE_LIMIT files × MULTI_FILE_PER_FILE_MATCHES matches) plus
|
|
103
|
+
* pagination headroom so the caller can see total file count. */
|
|
104
|
+
const INTERNAL_TOTAL_CAP = 2000;
|
|
105
|
+
/** Mirrors `MAX_FILE_BYTES` in `crates/pi-natives/src/grep.rs`. Native grep
|
|
106
|
+
* searches only the first `MAX_FILE_BYTES` of a larger file (a leading mmap
|
|
107
|
+
* window) and drops the rest; matches beyond the window are not returned. We
|
|
108
|
+
* surface a partial-coverage note when the caller explicitly targeted such a
|
|
109
|
+
* file so they know matches past the window are not shown. */
|
|
110
|
+
const NATIVE_GREP_MAX_FILE_BYTES = 4 * 1024 * 1024;
|
|
111
|
+
/** Wall-clock budget for a single native grep invocation. Without it, an
|
|
112
|
+
* aborted or runaway search (huge tree, network mount) keeps burning CPU on
|
|
113
|
+
* the native thread pool after the JS promise is abandoned. */
|
|
114
|
+
const SEARCH_GREP_TIMEOUT_MS = 30_000;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Parsed `paths` entry — a path (possibly archive-shaped) plus an optional
|
|
118
|
+
* line-range selector peeled off the trailing `:N-M` (or `:N+K`, `:N,M`, …)
|
|
119
|
+
* chunk via {@link splitPathAndSel}.
|
|
120
|
+
*/
|
|
121
|
+
interface GrepPathSpec {
|
|
122
|
+
original: string;
|
|
123
|
+
clean: string;
|
|
124
|
+
literalFilesystemMatch?: boolean;
|
|
125
|
+
ranges?: [LineRange, ...LineRange[]];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Mirror of read's `parseSel` selector grammar (`read.ts`) so `grep` accepts
|
|
130
|
+
* exactly the internal-URL selectors `read` accepts: a single chunk that is a
|
|
131
|
+
* line range, `raw`, or `conflicts`; or a two-chunk compound of exactly one `raw`
|
|
132
|
+
* plus one line range. Everything else (`:-10`, `:1-1:1-2`, `:conflicts:1-1`,
|
|
133
|
+
* `:raw:conflicts`) is rejected.
|
|
134
|
+
*
|
|
135
|
+
* This mirrors the *accepted set* of `parseSel`; `read` rejects the same shapes
|
|
136
|
+
* caller-side when a peeled internal-URL selector parses as `none`, so neither
|
|
137
|
+
* tool silently widens on a malformed compound. Keep in sync with `read.parseSel`.
|
|
138
|
+
*/
|
|
139
|
+
function isReadSelectorGrammar(sel: string): boolean {
|
|
140
|
+
if (sel.includes(":")) {
|
|
141
|
+
const chunks = sel.split(":");
|
|
142
|
+
if (chunks.length !== 2) return false;
|
|
143
|
+
const [a, b] = chunks as [string, string];
|
|
144
|
+
const aIsRaw = a.toLowerCase() === "raw";
|
|
145
|
+
const bIsRaw = b.toLowerCase() === "raw";
|
|
146
|
+
const rangeChunk = aIsRaw ? b : bIsRaw ? a : null;
|
|
147
|
+
return rangeChunk !== null && parseLineRanges(rangeChunk) !== null;
|
|
148
|
+
}
|
|
149
|
+
const lower = sel.toLowerCase();
|
|
150
|
+
return lower === "raw" || lower === "conflicts" || parseLineRanges(sel) !== null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function parsePathSpecs(rawEntries: readonly string[], cwd: string): Promise<GrepPathSpec[]> {
|
|
154
|
+
const specs: GrepPathSpec[] = [];
|
|
155
|
+
for (const entry of rawEntries) {
|
|
156
|
+
// Internal URLs (`artifact://`, `skill://`, …) use the URL-aware splitter,
|
|
157
|
+
// which peels selector-shaped tails only for selector-capable schemes and
|
|
158
|
+
// leaves opaque ones (`mcp://`) intact. Unlike filesystem paths, their
|
|
159
|
+
// verbatim/index display modes (`raw`, `conflicts`) carry no meaning for
|
|
160
|
+
// content search, so we accept them — searching the whole resource — and
|
|
161
|
+
// still honor any embedded line range as a match filter.
|
|
162
|
+
const internalSplit = splitInternalUrlSel(entry);
|
|
163
|
+
if (internalSplit.sel !== undefined) {
|
|
164
|
+
// Reject selectors read's parseSel would reject (`:-10`, `:1-1:1-2`,
|
|
165
|
+
// `:conflicts:1-1`) instead of silently widening the search or dropping a chunk.
|
|
166
|
+
if (!isReadSelectorGrammar(internalSplit.sel)) {
|
|
167
|
+
throw new ToolError(
|
|
168
|
+
`path entry "${entry}" has an invalid selector ":${internalSplit.sel}" — use ":N-M" line ranges, ":raw"/":conflicts", a range plus ":raw", or percent-encode a literal ":" as %3A`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
specs.push({ original: entry, clean: internalSplit.path, ranges: selectorLineRanges(internalSplit.sel) });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
// Prefer a literal filesystem match when one exists — a real file named
|
|
175
|
+
// `test:1-2` outranks the `:1-2` selector interpretation (issue #4618).
|
|
176
|
+
const strictSplit = splitPathAndSel(entry);
|
|
177
|
+
const split = await splitPathAndSelPreferringLiteral(entry, cwd);
|
|
178
|
+
const literalFilesystemMatch = strictSplit.sel !== undefined && split.sel === undefined;
|
|
179
|
+
let clean = literalFilesystemMatch ? resolveReadPath(entry, cwd) : entry;
|
|
180
|
+
let ranges: [LineRange, ...LineRange[]] | undefined;
|
|
181
|
+
if (!literalFilesystemMatch && split.sel) {
|
|
182
|
+
const parsed = parseLineRanges(split.sel);
|
|
183
|
+
if (!parsed) {
|
|
184
|
+
throw new ToolError(
|
|
185
|
+
`path entry "${entry}" — only line-range selectors like ":50-100" are supported (no ":raw"/":conflicts")`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (hasGlobPathChars(split.path)) {
|
|
189
|
+
throw new ToolError(`Line-range selector requires a single file, not a glob: ${entry}`);
|
|
190
|
+
}
|
|
191
|
+
clean = split.path;
|
|
192
|
+
ranges = parsed;
|
|
193
|
+
}
|
|
194
|
+
specs.push({
|
|
195
|
+
original: entry,
|
|
196
|
+
clean,
|
|
197
|
+
literalFilesystemMatch,
|
|
198
|
+
ranges,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return specs;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function mergeRangesInto(map: Map<string, LineRange[]>, absKey: string, ranges: readonly LineRange[]): void {
|
|
205
|
+
// Concat-without-merge is correct: `isLineInRanges` scans linearly, so
|
|
206
|
+
// duplicates/overlaps only cost a few extra comparisons per match.
|
|
207
|
+
const existing = map.get(absKey);
|
|
208
|
+
if (existing) {
|
|
209
|
+
existing.push(...ranges);
|
|
210
|
+
} else {
|
|
211
|
+
map.set(absKey, [...ranges]);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function matchAbsolutePath(matchPath: string, searchPath: string): string {
|
|
216
|
+
if (matchPath === "") return searchPath;
|
|
217
|
+
if (path.isAbsolute(matchPath)) return matchPath;
|
|
218
|
+
return path.resolve(searchPath, matchPath);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Pre-resolve any `paths` entries that point at a member inside an archive
|
|
223
|
+
* (e.g. `bundle.zip:src/foo.ts`, `release.tar.gz:notes.md`). Native grep
|
|
224
|
+
* cannot read archive members, so we materialize each text member to a
|
|
225
|
+
* temp scratch file and substitute that path into the search inputs. After
|
|
226
|
+
* grep returns, callers remap `match.path` back to the original
|
|
227
|
+
* `archive:member` selector so it round-trips through the `read` tool.
|
|
228
|
+
*
|
|
229
|
+
* Returns the rewritten paths array (same length/order as input), a map
|
|
230
|
+
* from absolute scratch path → original selector, a list of entries we
|
|
231
|
+
* could not materialize (binary member, missing archive, etc.), and a
|
|
232
|
+
* cleanup hook the caller MUST invoke in a `finally`.
|
|
233
|
+
*/
|
|
234
|
+
async function resolveArchiveSearchPaths(
|
|
235
|
+
pathSpecs: readonly GrepPathSpec[],
|
|
236
|
+
cwd: string,
|
|
237
|
+
): Promise<{
|
|
238
|
+
resolvedPaths: string[];
|
|
239
|
+
displayMap: Map<string, string>;
|
|
240
|
+
displaySet: Set<string>;
|
|
241
|
+
unreadable: string[];
|
|
242
|
+
cleanup: () => Promise<void>;
|
|
243
|
+
}> {
|
|
244
|
+
const resolvedPaths = pathSpecs.map(spec => spec.clean);
|
|
245
|
+
const displayMap = new Map<string, string>();
|
|
246
|
+
const displaySet = new Set<string>();
|
|
247
|
+
const unreadable: string[] = [];
|
|
248
|
+
let tempDir: string | undefined;
|
|
249
|
+
const archiveCache = new Map<string, ArchiveReader>();
|
|
250
|
+
|
|
251
|
+
for (let idx = 0; idx < pathSpecs.length; idx++) {
|
|
252
|
+
const spec = pathSpecs[idx];
|
|
253
|
+
if (!spec || spec.literalFilesystemMatch) continue;
|
|
254
|
+
const entry = spec.clean;
|
|
255
|
+
const candidates = parseArchivePathCandidates(entry);
|
|
256
|
+
const member = candidates.find(c => c.subPath !== "" && c.archivePath !== entry);
|
|
257
|
+
if (!member) continue;
|
|
258
|
+
|
|
259
|
+
const archiveAbs = resolveReadPath(member.archivePath, cwd);
|
|
260
|
+
let archive = archiveCache.get(archiveAbs);
|
|
261
|
+
if (!archive) {
|
|
262
|
+
try {
|
|
263
|
+
archive = await openArchive(archiveAbs);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
unreadable.push(`${entry} (cannot open archive: ${(err as Error).message})`);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
archiveCache.set(archiveAbs, archive);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
let extracted: ExtractedArchiveFile;
|
|
272
|
+
try {
|
|
273
|
+
extracted = await archive.readFile(member.subPath);
|
|
274
|
+
} catch (err) {
|
|
275
|
+
unreadable.push(`${entry} (${(err as Error).message})`);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// UTF-8 only — binary members would just produce noise through ripgrep.
|
|
279
|
+
if (extracted.bytes.some(byte => byte === 0)) {
|
|
280
|
+
unreadable.push(`${entry} (binary archive entry)`);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
let text: string;
|
|
284
|
+
try {
|
|
285
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(extracted.bytes);
|
|
286
|
+
} catch {
|
|
287
|
+
unreadable.push(`${entry} (non-UTF-8 archive entry)`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!tempDir) {
|
|
292
|
+
tempDir = await mkdtemp(path.join(tmpdir(), "omp-search-archive-"));
|
|
293
|
+
}
|
|
294
|
+
// Per-entry filename keeps the scratch path unique even when two selectors
|
|
295
|
+
// resolve to members with the same basename.
|
|
296
|
+
const safeBase = path.basename(member.subPath).replace(/[^\w.-]+/g, "_") || "entry";
|
|
297
|
+
const tempPath = path.join(tempDir, `${idx}-${safeBase}`);
|
|
298
|
+
await writeFile(tempPath, text);
|
|
299
|
+
resolvedPaths[idx] = tempPath;
|
|
300
|
+
displayMap.set(tempPath, entry);
|
|
301
|
+
displaySet.add(entry);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const cleanup = async () => {
|
|
305
|
+
if (tempDir) {
|
|
306
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
return { resolvedPaths, displayMap, displaySet, unreadable, cleanup };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
interface VirtualSearchResource {
|
|
313
|
+
path: string;
|
|
314
|
+
content: string;
|
|
315
|
+
ranges?: readonly LineRange[];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
interface InternalSearchInputResolution {
|
|
319
|
+
paths: string[];
|
|
320
|
+
resolvedPathsByInput: string[];
|
|
321
|
+
virtualResources: VirtualSearchResource[];
|
|
322
|
+
virtualPathSet: Set<string>;
|
|
323
|
+
virtualInputIndexes: Set<number>;
|
|
324
|
+
immutableSourcePaths: Set<string>;
|
|
325
|
+
virtualScopePath?: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function isImmutableSourcePath(filePath: string, immutableSourcePaths: ReadonlySet<string>): boolean {
|
|
329
|
+
for (const immutablePath of immutableSourcePaths) {
|
|
330
|
+
if (filePath === immutablePath || filePath.startsWith(`${immutablePath}${path.sep}`)) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
interface IndexedContentLines {
|
|
338
|
+
lines: string[];
|
|
339
|
+
starts: number[];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const OMP_ROOT_URL_RE = /^omp:\/\/(?:\/?|docs\/?)$/i;
|
|
343
|
+
|
|
344
|
+
function normalizeSearchLine(line: string): string {
|
|
345
|
+
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function splitSearchLines(content: string): string[] {
|
|
349
|
+
const lines = content.split("\n");
|
|
350
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
351
|
+
lines.pop();
|
|
352
|
+
}
|
|
353
|
+
return lines.map(normalizeSearchLine);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function indexSearchLines(content: string): IndexedContentLines {
|
|
357
|
+
const rawLines = content.split("\n");
|
|
358
|
+
if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") {
|
|
359
|
+
rawLines.pop();
|
|
360
|
+
}
|
|
361
|
+
const lines: string[] = [];
|
|
362
|
+
const starts: number[] = [];
|
|
363
|
+
let offset = 0;
|
|
364
|
+
for (const rawLine of rawLines) {
|
|
365
|
+
starts.push(offset);
|
|
366
|
+
lines.push(normalizeSearchLine(rawLine));
|
|
367
|
+
offset += rawLine.length + 1;
|
|
368
|
+
}
|
|
369
|
+
return { lines, starts };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function lineAllowed(lineNumber: number, ranges: readonly LineRange[] | undefined): boolean {
|
|
373
|
+
return !ranges || isLineInRanges(lineNumber, ranges);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Per-file native fetch budget that guarantees the JS range filter can still
|
|
378
|
+
* surface `perFileKeep` in-range hits. Matches arrive one entry per matched
|
|
379
|
+
* line in line order, so a bounded range's hits all sit within the first
|
|
380
|
+
* `endLine` entries, and an open-ended range starting at S is preceded by at
|
|
381
|
+
* most S-1 out-of-range entries — S-1+perFileKeep entries cover the kept
|
|
382
|
+
* window or exhaust the file. Clamped to the native file-size ceiling (a
|
|
383
|
+
* ≤4 MiB file cannot have more matched lines than bytes), which also keeps
|
|
384
|
+
* the scaled global budget inside the native layer's u32 bounds.
|
|
385
|
+
*/
|
|
386
|
+
function lineRangeFetchCap(pathSpecs: readonly GrepPathSpec[], perFileKeep: number): number {
|
|
387
|
+
let cap = 0;
|
|
388
|
+
for (const spec of pathSpecs) {
|
|
389
|
+
if (!spec.ranges) continue;
|
|
390
|
+
for (const range of spec.ranges) {
|
|
391
|
+
cap = Math.max(cap, range.endLine ?? range.startLine - 1 + perFileKeep);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return Math.min(cap, NATIVE_GREP_MAX_FILE_BYTES);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Binary search for the index of the line containing byte `offset`. */
|
|
398
|
+
function findLineIndex(starts: readonly number[], offset: number): number {
|
|
399
|
+
if (starts.length === 0) return -1;
|
|
400
|
+
let low = 0;
|
|
401
|
+
let high = starts.length - 1;
|
|
402
|
+
while (low <= high) {
|
|
403
|
+
const mid = Math.floor((low + high) / 2);
|
|
404
|
+
if (starts[mid] <= offset) {
|
|
405
|
+
low = mid + 1;
|
|
406
|
+
} else {
|
|
407
|
+
high = mid - 1;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return Math.max(0, high);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* JS-`RegExp` fallback returning matched line indexes for a virtual resource too
|
|
415
|
+
* large for native grep (>`NATIVE_GREP_MAX_FILE_BYTES`, which native grep silently
|
|
416
|
+
* skips). Mirrors the native probe's output (sorted, deduped indexes) so
|
|
417
|
+
* `buildVirtualMatches` rebuilds context/ranges identically; only the regex dialect
|
|
418
|
+
* differs for these oversized inputs (the pre-RE2-parity behavior).
|
|
419
|
+
*/
|
|
420
|
+
function jsMatchedLineIndexes(
|
|
421
|
+
content: string,
|
|
422
|
+
lines: readonly string[],
|
|
423
|
+
pattern: string,
|
|
424
|
+
ignoreCase: boolean,
|
|
425
|
+
multiline: boolean,
|
|
426
|
+
): number[] {
|
|
427
|
+
const flags = `${ignoreCase ? "i" : ""}${multiline ? "gm" : ""}`;
|
|
428
|
+
let regex: RegExp;
|
|
429
|
+
try {
|
|
430
|
+
regex = new RegExp(pattern, flags);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
433
|
+
throw new ToolError(`Invalid regex: ${message.replace(/^Invalid regular expression:\s*/i, "")}`);
|
|
434
|
+
}
|
|
435
|
+
if (!multiline) {
|
|
436
|
+
const out: number[] = [];
|
|
437
|
+
for (let i = 0; i < lines.length; i++) {
|
|
438
|
+
regex.lastIndex = 0;
|
|
439
|
+
if (regex.test(lines[i] ?? "")) out.push(i);
|
|
440
|
+
}
|
|
441
|
+
return out;
|
|
442
|
+
}
|
|
443
|
+
const { starts } = indexSearchLines(content);
|
|
444
|
+
const seen = new Set<number>();
|
|
445
|
+
const out: number[] = [];
|
|
446
|
+
let match = regex.exec(content);
|
|
447
|
+
while (match !== null) {
|
|
448
|
+
const lineIndex = findLineIndex(starts, match.index);
|
|
449
|
+
if (lineIndex >= 0 && !seen.has(lineIndex)) {
|
|
450
|
+
seen.add(lineIndex);
|
|
451
|
+
out.push(lineIndex);
|
|
452
|
+
}
|
|
453
|
+
if (match[0].length === 0) regex.lastIndex++;
|
|
454
|
+
match = regex.exec(content);
|
|
455
|
+
}
|
|
456
|
+
out.sort((a, b) => a - b);
|
|
457
|
+
return out;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Native-grep an oversized (>NATIVE_GREP_MAX_FILE_BYTES) line-mode virtual resource
|
|
462
|
+
* in line-boundary chunks (each <= the cap) so it keeps RE2 dialect parity instead of
|
|
463
|
+
* the JS fallback. Each chunk's matched line numbers are offset by its starting line
|
|
464
|
+
* index. A single line larger than the cap can't be native-grepped, so that one line
|
|
465
|
+
* is JS-tested. Returns sorted 0-based line indexes.
|
|
466
|
+
*/
|
|
467
|
+
async function nativeChunkedLineIndexes(
|
|
468
|
+
dir: string,
|
|
469
|
+
resourceIdx: number,
|
|
470
|
+
content: string,
|
|
471
|
+
pattern: string,
|
|
472
|
+
ignoreCase: boolean,
|
|
473
|
+
signal: AbortSignal | undefined,
|
|
474
|
+
): Promise<number[]> {
|
|
475
|
+
const rawLines = content.split("\n");
|
|
476
|
+
if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") rawLines.pop();
|
|
477
|
+
const indexes: number[] = [];
|
|
478
|
+
let chunkStart = 0;
|
|
479
|
+
let chunkBytes = 0;
|
|
480
|
+
let chunkLines: string[] = [];
|
|
481
|
+
let chunkSeq = 0;
|
|
482
|
+
const flush = async (): Promise<void> => {
|
|
483
|
+
if (chunkLines.length === 0) return;
|
|
484
|
+
const scratch = path.resolve(dir, `${resourceIdx}-chunk-${chunkSeq++}`);
|
|
485
|
+
await writeFile(scratch, chunkLines.join("\n"));
|
|
486
|
+
const probe = await grep(
|
|
487
|
+
{
|
|
488
|
+
pattern,
|
|
489
|
+
path: scratch,
|
|
490
|
+
ignoreCase,
|
|
491
|
+
multiline: false,
|
|
492
|
+
hidden: true,
|
|
493
|
+
gitignore: false,
|
|
494
|
+
maxCount: chunkLines.length,
|
|
495
|
+
contextBefore: 0,
|
|
496
|
+
contextAfter: 0,
|
|
497
|
+
maxColumns: DEFAULT_MAX_COLUMN,
|
|
498
|
+
mode: GrepOutputMode.Content,
|
|
499
|
+
signal,
|
|
500
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
501
|
+
},
|
|
502
|
+
undefined,
|
|
503
|
+
);
|
|
504
|
+
for (const match of probe.matches) indexes.push(chunkStart + match.lineNumber - 1);
|
|
505
|
+
chunkLines = [];
|
|
506
|
+
chunkBytes = 0;
|
|
507
|
+
};
|
|
508
|
+
let lineRegex: RegExp | undefined;
|
|
509
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
510
|
+
const line = rawLines[i];
|
|
511
|
+
const lineBytes = Buffer.byteLength(line, "utf8") + 1;
|
|
512
|
+
if (lineBytes > NATIVE_GREP_MAX_FILE_BYTES) {
|
|
513
|
+
await flush();
|
|
514
|
+
if (!lineRegex) {
|
|
515
|
+
try {
|
|
516
|
+
lineRegex = new RegExp(pattern, ignoreCase ? "i" : "");
|
|
517
|
+
} catch (err) {
|
|
518
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
519
|
+
throw new ToolError(`Invalid regex: ${message.replace(/^Invalid regular expression:\s*/i, "")}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
lineRegex.lastIndex = 0;
|
|
523
|
+
if (lineRegex.test(line)) indexes.push(i);
|
|
524
|
+
chunkStart = i + 1;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
if (chunkLines.length > 0 && chunkBytes + lineBytes > NATIVE_GREP_MAX_FILE_BYTES) {
|
|
528
|
+
await flush();
|
|
529
|
+
chunkStart = i;
|
|
530
|
+
}
|
|
531
|
+
if (chunkLines.length === 0) chunkStart = i;
|
|
532
|
+
chunkLines.push(line);
|
|
533
|
+
chunkBytes += lineBytes;
|
|
534
|
+
}
|
|
535
|
+
await flush();
|
|
536
|
+
indexes.sort((a, b) => a - b);
|
|
537
|
+
return indexes;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function makeContextLine(lines: readonly string[], lineIndex: number): { lineNumber: number; line: string } {
|
|
541
|
+
const { text } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
|
|
542
|
+
return { lineNumber: lineIndex + 1, line: text };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function makeVirtualMatch(
|
|
546
|
+
resource: VirtualSearchResource,
|
|
547
|
+
lines: readonly string[],
|
|
548
|
+
lineIndex: number,
|
|
549
|
+
contextBefore: number,
|
|
550
|
+
contextAfter: number,
|
|
551
|
+
lastEmittedLine: number,
|
|
552
|
+
nextMatchLine: number,
|
|
553
|
+
): GrepMatch {
|
|
554
|
+
const lineNumber = lineIndex + 1;
|
|
555
|
+
const { text, wasTruncated } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
|
|
556
|
+
const match: GrepMatch = {
|
|
557
|
+
path: resource.path,
|
|
558
|
+
lineNumber,
|
|
559
|
+
line: text,
|
|
560
|
+
};
|
|
561
|
+
if (wasTruncated) match.truncated = true;
|
|
562
|
+
|
|
563
|
+
if (contextBefore > 0) {
|
|
564
|
+
const before: NonNullable<GrepMatch["contextBefore"]> = [];
|
|
565
|
+
// Start after the previous match's last emitted line so adjacent matches
|
|
566
|
+
// never repeat or rewind context lines (mirrors native grep's sink).
|
|
567
|
+
const start = Math.max(0, lineIndex - contextBefore, lastEmittedLine);
|
|
568
|
+
for (let idx = start; idx < lineIndex; idx++) {
|
|
569
|
+
const contextLineNumber = idx + 1;
|
|
570
|
+
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
571
|
+
before.push(makeContextLine(lines, idx));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (before.length > 0) match.contextBefore = before;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (contextAfter > 0) {
|
|
578
|
+
const after: NonNullable<GrepMatch["contextAfter"]> = [];
|
|
579
|
+
// Stop before the next match line; it is emitted as a match itself.
|
|
580
|
+
const end = Math.min(lines.length - 1, lineIndex + contextAfter, nextMatchLine - 2);
|
|
581
|
+
for (let idx = lineIndex + 1; idx <= end; idx++) {
|
|
582
|
+
const contextLineNumber = idx + 1;
|
|
583
|
+
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
584
|
+
after.push(makeContextLine(lines, idx));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (after.length > 0) match.contextAfter = after;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return match;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** Build matches for ascending matched line indexes with forward-only,
|
|
594
|
+
* deduplicated context windows (line numbers never repeat or go backwards
|
|
595
|
+
* within one resource). */
|
|
596
|
+
function buildVirtualMatches(
|
|
597
|
+
resource: VirtualSearchResource,
|
|
598
|
+
lines: readonly string[],
|
|
599
|
+
matchedIndexes: readonly number[],
|
|
600
|
+
contextBefore: number,
|
|
601
|
+
contextAfter: number,
|
|
602
|
+
maxCount: number,
|
|
603
|
+
): GrepMatch[] {
|
|
604
|
+
const matches: GrepMatch[] = [];
|
|
605
|
+
let lastEmittedLine = 0;
|
|
606
|
+
for (let i = 0; i < matchedIndexes.length && matches.length < maxCount; i++) {
|
|
607
|
+
const lineIndex = matchedIndexes[i];
|
|
608
|
+
const nextMatchLine = i + 1 < matchedIndexes.length ? matchedIndexes[i + 1] + 1 : Number.POSITIVE_INFINITY;
|
|
609
|
+
const match = makeVirtualMatch(
|
|
610
|
+
resource,
|
|
611
|
+
lines,
|
|
612
|
+
lineIndex,
|
|
613
|
+
contextBefore,
|
|
614
|
+
contextAfter,
|
|
615
|
+
lastEmittedLine,
|
|
616
|
+
nextMatchLine,
|
|
617
|
+
);
|
|
618
|
+
const after = match.contextAfter;
|
|
619
|
+
lastEmittedLine = after && after.length > 0 ? after[after.length - 1].lineNumber : match.lineNumber;
|
|
620
|
+
matches.push(match);
|
|
621
|
+
}
|
|
622
|
+
return matches;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async function searchVirtualResources(
|
|
626
|
+
resources: readonly VirtualSearchResource[],
|
|
627
|
+
pattern: string,
|
|
628
|
+
ignoreCase: boolean,
|
|
629
|
+
multiline: boolean,
|
|
630
|
+
contextBefore: number,
|
|
631
|
+
contextAfter: number,
|
|
632
|
+
maxCount: number,
|
|
633
|
+
signal?: AbortSignal,
|
|
634
|
+
): Promise<GrepResult> {
|
|
635
|
+
if (resources.length === 0) {
|
|
636
|
+
return { matches: [], totalMatches: 0, filesWithMatches: 0, filesSearched: 0, limitReached: false };
|
|
637
|
+
}
|
|
638
|
+
const matches: GrepMatch[] = [];
|
|
639
|
+
const filesWithMatches = new Set<string>();
|
|
640
|
+
let totalMatches = 0;
|
|
641
|
+
let limitReached = false;
|
|
642
|
+
// Detect matched line numbers with native grep (RE2) — the SAME matcher local
|
|
643
|
+
// search uses — so a pattern valid for local grep but not JS `RegExp` (`(?i)x`,
|
|
644
|
+
// `[[:digit:]]`) behaves identically on virtual/remote resources. The JS helpers
|
|
645
|
+
// below then rebuild the exact forward-only, range-trimmed context windows the
|
|
646
|
+
// virtual-search contract requires.
|
|
647
|
+
const dir = await mkdtemp(path.join(tmpdir(), "omp-search-virtual-"));
|
|
648
|
+
try {
|
|
649
|
+
for (let idx = 0; idx < resources.length; idx++) {
|
|
650
|
+
const resource = resources[idx];
|
|
651
|
+
const remaining = Math.max(maxCount - matches.length, 0);
|
|
652
|
+
if (remaining === 0) {
|
|
653
|
+
limitReached = true;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
const lines = multiline ? indexSearchLines(resource.content).lines : splitSearchLines(resource.content);
|
|
657
|
+
let matchedIndexes: number[];
|
|
658
|
+
if (Buffer.byteLength(resource.content, "utf8") > NATIVE_GREP_MAX_FILE_BYTES) {
|
|
659
|
+
// Native grep skips files above its 4 MiB cap. Search oversized content in
|
|
660
|
+
// line-boundary chunks so line-mode keeps RE2 parity; multiline can't be chunked
|
|
661
|
+
// without missing matches that span a chunk boundary, so it falls back to JS
|
|
662
|
+
// (dialect-as-JS only for these oversized multiline inputs).
|
|
663
|
+
matchedIndexes = (
|
|
664
|
+
multiline
|
|
665
|
+
? jsMatchedLineIndexes(resource.content, lines, pattern, ignoreCase, true)
|
|
666
|
+
: await nativeChunkedLineIndexes(dir, idx, resource.content, pattern, ignoreCase, signal)
|
|
667
|
+
).filter(lineIndex => lineAllowed(lineIndex + 1, resource.ranges));
|
|
668
|
+
} else {
|
|
669
|
+
const scratch = path.resolve(dir, `${idx}`);
|
|
670
|
+
await writeFile(scratch, resource.content);
|
|
671
|
+
const probe = await grep(
|
|
672
|
+
{
|
|
673
|
+
pattern,
|
|
674
|
+
path: scratch,
|
|
675
|
+
ignoreCase,
|
|
676
|
+
multiline,
|
|
677
|
+
hidden: true,
|
|
678
|
+
gitignore: false,
|
|
679
|
+
// A ranged selector must see every match so the range filter below never
|
|
680
|
+
// drops in-range hits that fall after the cap; matches can't exceed the
|
|
681
|
+
// line count. Unranged search keeps the overall result cap.
|
|
682
|
+
maxCount: resource.ranges ? Math.max(lines.length, 1) : INTERNAL_TOTAL_CAP,
|
|
683
|
+
contextBefore: 0,
|
|
684
|
+
contextAfter: 0,
|
|
685
|
+
maxColumns: DEFAULT_MAX_COLUMN,
|
|
686
|
+
mode: GrepOutputMode.Content,
|
|
687
|
+
signal,
|
|
688
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
689
|
+
},
|
|
690
|
+
undefined,
|
|
691
|
+
);
|
|
692
|
+
matchedIndexes = [...new Set(probe.matches.map(match => match.lineNumber - 1))]
|
|
693
|
+
.filter(lineIndex => lineAllowed(lineIndex + 1, resource.ranges))
|
|
694
|
+
.sort((a, b) => a - b);
|
|
695
|
+
}
|
|
696
|
+
const resourceMatches = buildVirtualMatches(
|
|
697
|
+
resource,
|
|
698
|
+
lines,
|
|
699
|
+
matchedIndexes,
|
|
700
|
+
contextBefore,
|
|
701
|
+
contextAfter,
|
|
702
|
+
remaining,
|
|
703
|
+
);
|
|
704
|
+
if (matchedIndexes.length > 0) filesWithMatches.add(resource.path);
|
|
705
|
+
totalMatches += matchedIndexes.length;
|
|
706
|
+
limitReached = limitReached || matchedIndexes.length > resourceMatches.length;
|
|
707
|
+
matches.push(...resourceMatches);
|
|
708
|
+
}
|
|
709
|
+
} finally {
|
|
710
|
+
await rm(dir, { recursive: true, force: true }).catch(() => {});
|
|
711
|
+
}
|
|
712
|
+
return {
|
|
713
|
+
matches,
|
|
714
|
+
totalMatches,
|
|
715
|
+
filesWithMatches: filesWithMatches.size,
|
|
716
|
+
filesSearched: resources.length,
|
|
717
|
+
limitReached,
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function mergeGrepResults(left: GrepResult, right: GrepResult, maxCount: number): GrepResult {
|
|
722
|
+
if (left.matches.length === 0) return right;
|
|
723
|
+
if (right.matches.length === 0) return left;
|
|
724
|
+
const combinedMatches = [...left.matches, ...right.matches];
|
|
725
|
+
const matches = combinedMatches.length > maxCount ? combinedMatches.slice(0, maxCount) : combinedMatches;
|
|
726
|
+
return {
|
|
727
|
+
matches,
|
|
728
|
+
totalMatches: left.totalMatches + right.totalMatches,
|
|
729
|
+
filesWithMatches: new Set(matches.map(match => match.path)).size,
|
|
730
|
+
filesSearched: left.filesSearched + right.filesSearched,
|
|
731
|
+
limitReached: left.limitReached || right.limitReached || matches.length < combinedMatches.length,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
async function expandVirtualInternalResource(
|
|
736
|
+
rawPath: string,
|
|
737
|
+
resource: InternalResource,
|
|
738
|
+
internalRouter: InternalUrlRouter,
|
|
739
|
+
context: ResolveContext,
|
|
740
|
+
ranges: readonly LineRange[] | undefined,
|
|
741
|
+
): Promise<VirtualSearchResource[]> {
|
|
742
|
+
if (OMP_ROOT_URL_RE.test(rawPath)) {
|
|
743
|
+
const completions = await internalRouter.complete("omp", "");
|
|
744
|
+
if (completions && completions.length > 0) {
|
|
745
|
+
const resources: VirtualSearchResource[] = [];
|
|
746
|
+
const seen = new Set<string>();
|
|
747
|
+
for (const completion of completions) {
|
|
748
|
+
if (seen.has(completion.value)) continue;
|
|
749
|
+
seen.add(completion.value);
|
|
750
|
+
const docUrl = `omp://${completion.value}`;
|
|
751
|
+
const doc = await internalRouter.resolve(docUrl, context);
|
|
752
|
+
if (!doc.sourcePath) {
|
|
753
|
+
resources.push({ path: docUrl, content: doc.content, ranges });
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (resources.length > 0) return resources;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return [{ path: rawPath, content: resource.content, ranges }];
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
async function resolveInternalSearchInputs(opts: {
|
|
764
|
+
pathSpecs: readonly GrepPathSpec[];
|
|
765
|
+
resolvedPaths: string[];
|
|
766
|
+
cwd: string;
|
|
767
|
+
settings: unknown;
|
|
768
|
+
signal?: AbortSignal;
|
|
769
|
+
archiveDisplayMap: ReadonlyMap<string, string>;
|
|
770
|
+
localProtocolOptions?: LocalProtocolOptions;
|
|
771
|
+
skills?: ResolveContext["skills"];
|
|
772
|
+
}): Promise<InternalSearchInputResolution> {
|
|
773
|
+
const internalRouter = InternalUrlRouter.instance();
|
|
774
|
+
const paths = opts.resolvedPaths.slice();
|
|
775
|
+
const virtualResources: VirtualSearchResource[] = [];
|
|
776
|
+
const virtualPathSet = new Set<string>();
|
|
777
|
+
const virtualInputIndexes = new Set<number>();
|
|
778
|
+
const immutableSourcePaths = new Set<string>();
|
|
779
|
+
let virtualScopePath: string | undefined;
|
|
780
|
+
const context: ResolveContext = {
|
|
781
|
+
cwd: opts.cwd,
|
|
782
|
+
settings: opts.settings,
|
|
783
|
+
signal: opts.signal,
|
|
784
|
+
localProtocolOptions: opts.localProtocolOptions,
|
|
785
|
+
skills: opts.skills,
|
|
786
|
+
skipDirectoryListing: true,
|
|
787
|
+
// Try path-only first so large artifacts (and any other handler that
|
|
788
|
+
// separates path from content) resolve without materializing bytes.
|
|
789
|
+
// Handlers that ignore the flag still return content, and virtual
|
|
790
|
+
// resources without a sourcePath fall through to a second resolve.
|
|
791
|
+
pathOnly: true,
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
for (let idx = 0; idx < paths.length; idx++) {
|
|
795
|
+
const rawPath = paths[idx];
|
|
796
|
+
if (!rawPath || opts.archiveDisplayMap.has(rawPath) || !internalRouter.canHandle(rawPath)) {
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
// `ssh://[::1]/path` carries `[`/`]` in the IPv6 authority — glob metacharacters
|
|
800
|
+
// — so check only the path portion for ssh:// (the SSH handler reads a single
|
|
801
|
+
// remote file; there is no glob expansion). A glob in the remote path still trips.
|
|
802
|
+
const globTarget = /^ssh:\/\//i.test(rawPath) ? rawPath.replace(/^ssh:\/\/[^/]*/i, "") : rawPath;
|
|
803
|
+
if (hasGlobPathChars(globTarget)) {
|
|
804
|
+
throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
|
|
805
|
+
}
|
|
806
|
+
let resource = await internalRouter.resolve(rawPath, context);
|
|
807
|
+
// A directory listing with no backing local path (e.g. a remote ssh:// dir)
|
|
808
|
+
// has no real contents to grep — searching its listing text would be
|
|
809
|
+
// misleading. Local/skill/vault dir resources set `sourcePath` and skip this.
|
|
810
|
+
if (resource.isDirectory && !resource.sourcePath) {
|
|
811
|
+
throw new ToolError(
|
|
812
|
+
`search cannot recurse the directory listing at ${rawPath}; search a specific file under it (e.g. ${rawPath.replace(/\/+$/, "")}/<file>) or read ${rawPath} to list its entries`,
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
if (resource.sourcePath) {
|
|
816
|
+
paths[idx] = resource.sourcePath;
|
|
817
|
+
if (resource.immutable) {
|
|
818
|
+
immutableSourcePaths.add(path.resolve(resource.sourcePath));
|
|
819
|
+
}
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// No sourcePath: this handler needs its content materialized so the
|
|
824
|
+
// virtual expansion can search it. Re-resolve without pathOnly.
|
|
825
|
+
if (context.pathOnly) {
|
|
826
|
+
resource = await internalRouter.resolve(rawPath, { ...context, pathOnly: false });
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const ranges = opts.pathSpecs[idx]?.ranges;
|
|
830
|
+
const expanded = await expandVirtualInternalResource(
|
|
831
|
+
rawPath,
|
|
832
|
+
resource,
|
|
833
|
+
internalRouter,
|
|
834
|
+
{ ...context, pathOnly: false },
|
|
835
|
+
ranges,
|
|
836
|
+
);
|
|
837
|
+
virtualInputIndexes.add(idx);
|
|
838
|
+
for (const virtual of expanded) {
|
|
839
|
+
virtualResources.push(virtual);
|
|
840
|
+
virtualPathSet.add(virtual.path);
|
|
841
|
+
}
|
|
842
|
+
virtualScopePath = virtualScopePath ? `${virtualScopePath}, ${rawPath}` : rawPath;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
return {
|
|
846
|
+
resolvedPathsByInput: paths,
|
|
847
|
+
paths: paths.filter((_, idx) => !virtualInputIndexes.has(idx)),
|
|
848
|
+
virtualResources,
|
|
849
|
+
virtualPathSet,
|
|
850
|
+
virtualInputIndexes,
|
|
851
|
+
immutableSourcePaths,
|
|
852
|
+
virtualScopePath,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export interface GrepToolDetails {
|
|
857
|
+
truncation?: TruncationResult;
|
|
858
|
+
fileLimitReached?: number;
|
|
859
|
+
perFileLimitReached?: number;
|
|
860
|
+
linesTruncated?: boolean;
|
|
861
|
+
meta?: OutputMeta;
|
|
862
|
+
scopePath?: string;
|
|
863
|
+
matchCount?: number;
|
|
864
|
+
fileCount?: number;
|
|
865
|
+
files?: string[];
|
|
866
|
+
fileMatches?: Array<{ path: string; count: number }>;
|
|
867
|
+
truncated?: boolean;
|
|
868
|
+
error?: string;
|
|
869
|
+
/** Pre-formatted text for the user-visible TUI render. Mirrors the model-facing
|
|
870
|
+
* `result.text` lines but uses a `│` gutter and `*` to mark match lines (vs space for
|
|
871
|
+
* context). The TUI uses this directly so it never parses model-facing hashline anchors. */
|
|
872
|
+
displayContent?: string;
|
|
873
|
+
/** Absolute base directory used during search. Used by the renderer to resolve
|
|
874
|
+
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
875
|
+
searchPath?: string;
|
|
876
|
+
/** Session cwd at search time. The renderer resolves the display-relative
|
|
877
|
+
* (cwd-relative) header/match paths against this for OSC 8 hyperlinks;
|
|
878
|
+
* `searchPath` is the scope label target, not the display-path base. */
|
|
879
|
+
cwd?: string;
|
|
880
|
+
/** User-supplied paths whose base directory was missing on disk. The tool
|
|
881
|
+
* skipped these and continued with the surviving entries; surfaced as a
|
|
882
|
+
* non-fatal warning in the renderer and in the model-facing text. */
|
|
883
|
+
missingPaths?: string[];
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
type SearchParams = typeof searchSchema.infer;
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Construction-time overrides for callers that are not the model.
|
|
890
|
+
*
|
|
891
|
+
* The model-facing schema deliberately does not grow these: they exist for
|
|
892
|
+
* wire bridges (the Cursor `pi_grep` frame) whose protocol carries an explicit
|
|
893
|
+
* context width and total match cap, and which would otherwise have to drop
|
|
894
|
+
* them. Unset means "use the session settings / built-in caps" — the behavior
|
|
895
|
+
* every model-issued call keeps.
|
|
896
|
+
*/
|
|
897
|
+
export interface GrepToolOptions {
|
|
898
|
+
/** Overrides `grep.contextBefore`/`grep.contextAfter` for every call on this instance. */
|
|
899
|
+
context?: number;
|
|
900
|
+
/** Caps total surfaced matches. Applied on top of the built-in per-file and file-window caps, never above them. */
|
|
901
|
+
totalMatchLimit?: number;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
export class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails> {
|
|
905
|
+
readonly name = "grep";
|
|
906
|
+
readonly approval = (args: unknown): ToolTier => {
|
|
907
|
+
const a = args as { path?: string | string[]; paths?: string | string[] };
|
|
908
|
+
return toPathList(a.path ?? a.paths).some(pathTargetsSsh) ? "exec" : "read";
|
|
909
|
+
};
|
|
910
|
+
readonly label = "Grep";
|
|
911
|
+
readonly loadMode = "discoverable";
|
|
912
|
+
readonly summary = "Grep file contents using ripgrep (fast regex search)";
|
|
913
|
+
get description(): string {
|
|
914
|
+
const displayMode = resolveFileDisplayMode(this.session);
|
|
915
|
+
return prompt.render(grepDescription, {
|
|
916
|
+
IS_HL_MODE: displayMode.hashLines,
|
|
917
|
+
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
918
|
+
scoutAvailable: isScoutSpawnable(
|
|
919
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
920
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
921
|
+
),
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
readonly parameters = searchSchema;
|
|
925
|
+
readonly strict = true;
|
|
926
|
+
|
|
927
|
+
readonly #contextOverride?: number;
|
|
928
|
+
readonly #totalMatchLimit?: number;
|
|
929
|
+
|
|
930
|
+
constructor(
|
|
931
|
+
private readonly session: ToolSession,
|
|
932
|
+
options?: GrepToolOptions,
|
|
933
|
+
) {
|
|
934
|
+
const context = options?.context;
|
|
935
|
+
this.#contextOverride = context !== undefined ? Math.max(0, Math.floor(context)) : undefined;
|
|
936
|
+
const total = options?.totalMatchLimit;
|
|
937
|
+
this.#totalMatchLimit = total !== undefined ? Math.max(1, Math.floor(total)) : undefined;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
async execute(
|
|
941
|
+
_toolCallId: string,
|
|
942
|
+
params: SearchParams,
|
|
943
|
+
signal?: AbortSignal,
|
|
944
|
+
_onUpdate?: AgentToolUpdateCallback<GrepToolDetails>,
|
|
945
|
+
_toolContext?: AgentToolContext,
|
|
946
|
+
): Promise<AgentToolResult<GrepToolDetails>> {
|
|
947
|
+
const { pattern, path: rawPath, case: caseSensitive, gitignore, skip } = params;
|
|
948
|
+
|
|
949
|
+
return untilAborted(signal, async () => {
|
|
950
|
+
// Preserve the pattern verbatim — leading/trailing whitespace is
|
|
951
|
+
// meaningful in regexes (indentation anchors, trailing-space matches).
|
|
952
|
+
if (!pattern.trim()) {
|
|
953
|
+
throw new ToolError("Pattern must not be empty");
|
|
954
|
+
}
|
|
955
|
+
const normalizedPattern = pattern;
|
|
956
|
+
|
|
957
|
+
const normalizedSkip =
|
|
958
|
+
skip === undefined || skip === null ? 0 : Number.isFinite(skip) ? Math.floor(skip) : Number.NaN;
|
|
959
|
+
if (normalizedSkip < 0 || !Number.isFinite(normalizedSkip)) {
|
|
960
|
+
throw new ToolError("Skip must be a non-negative number");
|
|
961
|
+
}
|
|
962
|
+
const scopedPaths = toPathList(rawPath);
|
|
963
|
+
const effectivePaths = scopedPaths.length > 0 ? scopedPaths : ["."];
|
|
964
|
+
const rawEntries = await expandDelimitedPathEntries(effectivePaths, this.session.cwd);
|
|
965
|
+
const pathSpecs = await parsePathSpecs(rawEntries, this.session.cwd);
|
|
966
|
+
const materializedExternalPaths = new Map<string, string>();
|
|
967
|
+
const materializeExternalUrlForSearch = async (rawPath: string) => {
|
|
968
|
+
const target = parseReadUrlTarget(rawPath);
|
|
969
|
+
if (!target) return undefined;
|
|
970
|
+
const materialized = await materializeReadUrlToFile(
|
|
971
|
+
this.session,
|
|
972
|
+
{ path: target.path, raw: target.raw },
|
|
973
|
+
signal,
|
|
974
|
+
);
|
|
975
|
+
materializedExternalPaths.set(rawPath, materialized.path);
|
|
976
|
+
return { sourcePath: materialized.path, immutable: true };
|
|
977
|
+
};
|
|
978
|
+
const {
|
|
979
|
+
resolvedPaths,
|
|
980
|
+
displayMap: archiveDisplayMap,
|
|
981
|
+
displaySet: archiveDisplaySet,
|
|
982
|
+
unreadable: archiveUnreadable,
|
|
983
|
+
cleanup: cleanupArchiveScratch,
|
|
984
|
+
} = await resolveArchiveSearchPaths(pathSpecs, this.session.cwd);
|
|
985
|
+
try {
|
|
986
|
+
const internalResolution = await resolveInternalSearchInputs({
|
|
987
|
+
pathSpecs,
|
|
988
|
+
resolvedPaths,
|
|
989
|
+
cwd: this.session.cwd,
|
|
990
|
+
settings: this.session.settings,
|
|
991
|
+
signal,
|
|
992
|
+
archiveDisplayMap,
|
|
993
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
994
|
+
skills: this.session.skills,
|
|
995
|
+
});
|
|
996
|
+
const searchablePaths = internalResolution.paths;
|
|
997
|
+
const { virtualResources, virtualPathSet, virtualInputIndexes } = internalResolution;
|
|
998
|
+
const rangesByAbsPath = new Map<string, LineRange[]>();
|
|
999
|
+
|
|
1000
|
+
if (
|
|
1001
|
+
archiveUnreadable.length > 0 &&
|
|
1002
|
+
searchablePaths.length === archiveUnreadable.length &&
|
|
1003
|
+
virtualResources.length === 0
|
|
1004
|
+
) {
|
|
1005
|
+
// All inputs were archive selectors we couldn't materialize; surface the
|
|
1006
|
+
// reason instead of a downstream "path not found" from the scope resolver.
|
|
1007
|
+
throw new ToolError(
|
|
1008
|
+
`Cannot search archive member(s): ${archiveUnreadable.join(", ")}. ` +
|
|
1009
|
+
`Read the member with \`read <archive>:<member>\` and inspect the returned text, ` +
|
|
1010
|
+
`or pass a UTF-8 text member.`,
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
const normalizedContextBefore = this.#contextOverride ?? this.session.settings.get("grep.contextBefore");
|
|
1014
|
+
const normalizedContextAfter = this.#contextOverride ?? this.session.settings.get("grep.contextAfter");
|
|
1015
|
+
const ignoreCase = !(caseSensitive ?? true);
|
|
1016
|
+
const useGitignore = gitignore ?? true;
|
|
1017
|
+
const patternHasNewline = normalizedPattern.includes("\n") || normalizedPattern.includes("\\n");
|
|
1018
|
+
const effectiveMultiline = patternHasNewline;
|
|
1019
|
+
|
|
1020
|
+
let searchPath: string;
|
|
1021
|
+
let scopePath: string;
|
|
1022
|
+
let globFilter: string | undefined;
|
|
1023
|
+
let isDirectory: boolean;
|
|
1024
|
+
let multiTargets: ResolvedSearchTarget[] | undefined;
|
|
1025
|
+
let exactFilePaths: string[] | undefined;
|
|
1026
|
+
let missingPaths: string[];
|
|
1027
|
+
const immutableSourcePaths = new Set(internalResolution.immutableSourcePaths);
|
|
1028
|
+
if (searchablePaths.length > 0) {
|
|
1029
|
+
const scope = await resolveToolSearchScope({
|
|
1030
|
+
rawPaths: searchablePaths,
|
|
1031
|
+
cwd: this.session.cwd,
|
|
1032
|
+
internalUrlAction: "search",
|
|
1033
|
+
trackImmutableSources: true,
|
|
1034
|
+
surfaceExactFilePaths: true,
|
|
1035
|
+
fanOutFileTargets: true,
|
|
1036
|
+
multipathStatHint: " (`path` list entries must each exist relative to cwd)",
|
|
1037
|
+
settings: this.session.settings,
|
|
1038
|
+
signal,
|
|
1039
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
1040
|
+
skills: this.session.skills,
|
|
1041
|
+
resolveExternalUrl: materializeExternalUrlForSearch,
|
|
1042
|
+
});
|
|
1043
|
+
searchPath = scope.searchPath;
|
|
1044
|
+
isDirectory = scope.isDirectory;
|
|
1045
|
+
multiTargets = scope.multiTargets;
|
|
1046
|
+
exactFilePaths = scope.exactFilePaths;
|
|
1047
|
+
missingPaths = scope.missingPaths;
|
|
1048
|
+
globFilter = scope.globFilter;
|
|
1049
|
+
for (const immutablePath of scope.immutableSourcePaths) {
|
|
1050
|
+
immutableSourcePaths.add(immutablePath);
|
|
1051
|
+
}
|
|
1052
|
+
// Build the per-file line-range filter after URL materialization has run:
|
|
1053
|
+
// archive entries are keyed by scratch path, URL entries by read-cache
|
|
1054
|
+
// content path, and ordinary files by their resolved filesystem path.
|
|
1055
|
+
for (let idx = 0; idx < pathSpecs.length; idx++) {
|
|
1056
|
+
const spec = pathSpecs[idx];
|
|
1057
|
+
if (!spec.ranges) continue;
|
|
1058
|
+
if (virtualInputIndexes.has(idx)) continue;
|
|
1059
|
+
const resolved = internalResolution.resolvedPathsByInput[idx];
|
|
1060
|
+
if (!resolved) continue;
|
|
1061
|
+
const materializedExternalPath = materializedExternalPaths.get(spec.clean);
|
|
1062
|
+
if (materializedExternalPath) {
|
|
1063
|
+
mergeRangesInto(rangesByAbsPath, path.resolve(materializedExternalPath), spec.ranges);
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
if (resolved === spec.clean && !archiveDisplayMap.has(resolved)) {
|
|
1067
|
+
// Non-archive entry; ensure the cleaned path resolves to a regular file.
|
|
1068
|
+
const absKey = path.resolve(resolveReadPath(resolved, this.session.cwd));
|
|
1069
|
+
const stats = await stat(absKey).catch(() => null);
|
|
1070
|
+
if (!stats) {
|
|
1071
|
+
throw new ToolError(`Path not found for line-range selector: ${spec.original}`);
|
|
1072
|
+
}
|
|
1073
|
+
if (!stats.isFile()) {
|
|
1074
|
+
throw new ToolError(
|
|
1075
|
+
`Line-range selector requires a single file: ${spec.original} is a directory`,
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
mergeRangesInto(rangesByAbsPath, absKey, spec.ranges);
|
|
1079
|
+
} else {
|
|
1080
|
+
mergeRangesInto(rangesByAbsPath, path.resolve(resolved), spec.ranges);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
// When the only input was an archive selector, surface that selector instead
|
|
1084
|
+
// of the temp scratch path the resolver substituted in.
|
|
1085
|
+
const physicalScopePath =
|
|
1086
|
+
searchablePaths.length === 1 && archiveDisplayMap.get(searchPath)
|
|
1087
|
+
? (archiveDisplayMap.get(searchPath) as string)
|
|
1088
|
+
: scope.scopePath;
|
|
1089
|
+
scopePath = internalResolution.virtualScopePath
|
|
1090
|
+
? `${physicalScopePath}, ${internalResolution.virtualScopePath}`
|
|
1091
|
+
: physicalScopePath;
|
|
1092
|
+
} else {
|
|
1093
|
+
searchPath = this.session.cwd;
|
|
1094
|
+
scopePath = internalResolution.virtualScopePath ?? ".";
|
|
1095
|
+
globFilter = undefined;
|
|
1096
|
+
isDirectory = false;
|
|
1097
|
+
multiTargets = undefined;
|
|
1098
|
+
exactFilePaths = undefined;
|
|
1099
|
+
missingPaths = [];
|
|
1100
|
+
}
|
|
1101
|
+
if (
|
|
1102
|
+
missingPaths.length > 0 &&
|
|
1103
|
+
missingPaths.length === searchablePaths.length &&
|
|
1104
|
+
virtualResources.length === 0
|
|
1105
|
+
) {
|
|
1106
|
+
const archiveHint =
|
|
1107
|
+
archiveUnreadable.length > 0
|
|
1108
|
+
? ` (archive members were not searchable: ${archiveUnreadable.join(", ")})`
|
|
1109
|
+
: "";
|
|
1110
|
+
throw new ToolError(
|
|
1111
|
+
`Path not found: ${missingPaths.join(", ")}; list each target in the semicolon-delimited \`path\`${archiveHint}`,
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
const baseDisplayMode = resolveFileDisplayMode(this.session);
|
|
1115
|
+
|
|
1116
|
+
const effectiveOutputMode = GrepOutputMode.Content;
|
|
1117
|
+
const isMultiScope =
|
|
1118
|
+
isDirectory ||
|
|
1119
|
+
Boolean(exactFilePaths) ||
|
|
1120
|
+
Boolean(multiTargets) ||
|
|
1121
|
+
(virtualResources.length > 0 && (virtualResources.length > 1 || searchablePaths.length > 0));
|
|
1122
|
+
const perFileMatchCap = isMultiScope ? MULTI_FILE_PER_FILE_MATCHES : SINGLE_FILE_MATCHES;
|
|
1123
|
+
// Range filtering happens in JS after the native fetch, so out-of-range
|
|
1124
|
+
// matches consume fetch budget. Widen the per-file budget just enough
|
|
1125
|
+
// that filtering can still yield `perFileMatchCap` in-range hits, and
|
|
1126
|
+
// scale the global safety ceiling by the same amplification so ranged
|
|
1127
|
+
// searches keep the baseline file coverage while staying finite.
|
|
1128
|
+
const hasLineRangeFilters = pathSpecs.some(spec => spec.ranges);
|
|
1129
|
+
const nativeMaxCountPerFile = hasLineRangeFilters
|
|
1130
|
+
? Math.max(perFileMatchCap + 1, lineRangeFetchCap(pathSpecs, perFileMatchCap + 1))
|
|
1131
|
+
: perFileMatchCap + 1;
|
|
1132
|
+
const nativeMaxCount = hasLineRangeFilters
|
|
1133
|
+
? Math.ceil(INTERNAL_TOTAL_CAP / (perFileMatchCap + 1)) * nativeMaxCountPerFile
|
|
1134
|
+
: INTERNAL_TOTAL_CAP;
|
|
1135
|
+
|
|
1136
|
+
// Run grep
|
|
1137
|
+
let result: GrepResult = {
|
|
1138
|
+
matches: [],
|
|
1139
|
+
totalMatches: 0,
|
|
1140
|
+
filesWithMatches: 0,
|
|
1141
|
+
filesSearched: 0,
|
|
1142
|
+
limitReached: false,
|
|
1143
|
+
};
|
|
1144
|
+
let skippedOversizedCount = 0;
|
|
1145
|
+
try {
|
|
1146
|
+
if (searchablePaths.length > 0) {
|
|
1147
|
+
if (exactFilePaths || multiTargets) {
|
|
1148
|
+
const matches: GrepMatch[] = [];
|
|
1149
|
+
const seenMatchKeys = new Set<string>();
|
|
1150
|
+
let limitReached = false;
|
|
1151
|
+
let totalMatches = 0;
|
|
1152
|
+
let filesSearched = 0;
|
|
1153
|
+
const targets = exactFilePaths
|
|
1154
|
+
? exactFilePaths.map(filePath => ({
|
|
1155
|
+
basePath: filePath,
|
|
1156
|
+
glob: undefined as string | undefined,
|
|
1157
|
+
}))
|
|
1158
|
+
: (multiTargets ?? []);
|
|
1159
|
+
for (const target of targets) {
|
|
1160
|
+
const targetResult = await grep(
|
|
1161
|
+
{
|
|
1162
|
+
pattern: normalizedPattern,
|
|
1163
|
+
path: target.basePath,
|
|
1164
|
+
glob: target.glob,
|
|
1165
|
+
ignoreCase,
|
|
1166
|
+
multiline: effectiveMultiline,
|
|
1167
|
+
hidden: true,
|
|
1168
|
+
gitignore: useGitignore,
|
|
1169
|
+
maxCount: nativeMaxCount,
|
|
1170
|
+
contextBefore: normalizedContextBefore,
|
|
1171
|
+
contextAfter: normalizedContextAfter,
|
|
1172
|
+
maxColumns: DEFAULT_MAX_COLUMN,
|
|
1173
|
+
mode: effectiveOutputMode,
|
|
1174
|
+
maxCountPerFile: nativeMaxCountPerFile,
|
|
1175
|
+
signal,
|
|
1176
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
1177
|
+
},
|
|
1178
|
+
undefined,
|
|
1179
|
+
);
|
|
1180
|
+
skippedOversizedCount += targetResult.skippedOversized ?? 0;
|
|
1181
|
+
limitReached = limitReached || Boolean(targetResult.limitReached);
|
|
1182
|
+
totalMatches += targetResult.totalMatches;
|
|
1183
|
+
filesSearched += targetResult.filesSearched;
|
|
1184
|
+
for (const match of targetResult.matches) {
|
|
1185
|
+
const absolute = path.resolve(target.basePath, match.path);
|
|
1186
|
+
// Overlapping targets (a directory plus a file nested
|
|
1187
|
+
// inside it) surface the same physical line twice;
|
|
1188
|
+
// keep the first occurrence.
|
|
1189
|
+
const matchKey = `${absolute}\0${match.lineNumber}`;
|
|
1190
|
+
if (seenMatchKeys.has(matchKey)) {
|
|
1191
|
+
totalMatches = Math.max(0, totalMatches - 1);
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
seenMatchKeys.add(matchKey);
|
|
1195
|
+
const rebased = path.relative(searchPath, absolute).replace(/\\/g, "/");
|
|
1196
|
+
matches.push({ ...match, path: rebased });
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
result = {
|
|
1200
|
+
matches,
|
|
1201
|
+
totalMatches: exactFilePaths ? matches.length : totalMatches,
|
|
1202
|
+
filesWithMatches: new Set(matches.map(match => match.path)).size,
|
|
1203
|
+
filesSearched: exactFilePaths ? exactFilePaths.length : filesSearched,
|
|
1204
|
+
limitReached,
|
|
1205
|
+
};
|
|
1206
|
+
} else {
|
|
1207
|
+
result = await grep(
|
|
1208
|
+
{
|
|
1209
|
+
pattern: normalizedPattern,
|
|
1210
|
+
path: searchPath,
|
|
1211
|
+
glob: globFilter,
|
|
1212
|
+
ignoreCase,
|
|
1213
|
+
multiline: effectiveMultiline,
|
|
1214
|
+
hidden: true,
|
|
1215
|
+
gitignore: useGitignore,
|
|
1216
|
+
maxCount: nativeMaxCount,
|
|
1217
|
+
contextBefore: normalizedContextBefore,
|
|
1218
|
+
contextAfter: normalizedContextAfter,
|
|
1219
|
+
maxColumns: DEFAULT_MAX_COLUMN,
|
|
1220
|
+
mode: effectiveOutputMode,
|
|
1221
|
+
maxCountPerFile: nativeMaxCountPerFile,
|
|
1222
|
+
signal,
|
|
1223
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
1224
|
+
},
|
|
1225
|
+
undefined,
|
|
1226
|
+
);
|
|
1227
|
+
skippedOversizedCount = result.skippedOversized ?? 0;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
|
|
1232
|
+
throw new ToolError(err.message.replace(/^regex(?: parse)? error:?\s*/i, "Invalid regex: "));
|
|
1233
|
+
}
|
|
1234
|
+
if (err instanceof Error && err.message.includes("Aborted: Timeout")) {
|
|
1235
|
+
throw new ToolError(
|
|
1236
|
+
`Grep timed out after ${SEARCH_GREP_TIMEOUT_MS / 1000}s; narrow paths or pattern, or scope with \`glob\` first`,
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
throw err;
|
|
1240
|
+
}
|
|
1241
|
+
let virtualResult: GrepResult;
|
|
1242
|
+
try {
|
|
1243
|
+
virtualResult = await searchVirtualResources(
|
|
1244
|
+
virtualResources,
|
|
1245
|
+
normalizedPattern,
|
|
1246
|
+
ignoreCase,
|
|
1247
|
+
effectiveMultiline,
|
|
1248
|
+
normalizedContextBefore,
|
|
1249
|
+
normalizedContextAfter,
|
|
1250
|
+
INTERNAL_TOTAL_CAP,
|
|
1251
|
+
signal,
|
|
1252
|
+
);
|
|
1253
|
+
} catch (err) {
|
|
1254
|
+
if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
|
|
1255
|
+
throw new ToolError(err.message.replace(/^regex(?: parse)? error:?\s*/i, "Invalid regex: "));
|
|
1256
|
+
}
|
|
1257
|
+
if (err instanceof SyntaxError) {
|
|
1258
|
+
throw new ToolError(`Invalid regex: ${err.message}`);
|
|
1259
|
+
}
|
|
1260
|
+
throw err;
|
|
1261
|
+
}
|
|
1262
|
+
result = mergeGrepResults(result, virtualResult, nativeMaxCount);
|
|
1263
|
+
if (rangesByAbsPath.size > 0) {
|
|
1264
|
+
const filteredMatches: GrepMatch[] = [];
|
|
1265
|
+
for (const match of result.matches) {
|
|
1266
|
+
const abs = matchAbsolutePath(match.path, searchPath);
|
|
1267
|
+
const ranges = rangesByAbsPath.get(abs);
|
|
1268
|
+
if (!ranges) {
|
|
1269
|
+
// Path has no line-range constraint (e.g. a peer entry without `:N-M`).
|
|
1270
|
+
filteredMatches.push(match);
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
if (!isLineInRanges(match.lineNumber, ranges)) continue;
|
|
1274
|
+
// Drop context lines that fall outside the allowed ranges; they would
|
|
1275
|
+
// otherwise leak content the caller explicitly excluded.
|
|
1276
|
+
const trimBefore = match.contextBefore?.filter(c => isLineInRanges(c.lineNumber, ranges));
|
|
1277
|
+
const trimAfter = match.contextAfter?.filter(c => isLineInRanges(c.lineNumber, ranges));
|
|
1278
|
+
filteredMatches.push({
|
|
1279
|
+
...match,
|
|
1280
|
+
contextBefore: trimBefore && trimBefore.length > 0 ? trimBefore : undefined,
|
|
1281
|
+
contextAfter: trimAfter && trimAfter.length > 0 ? trimAfter : undefined,
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
result = {
|
|
1285
|
+
matches: filteredMatches,
|
|
1286
|
+
totalMatches: filteredMatches.length,
|
|
1287
|
+
filesWithMatches: new Set(filteredMatches.map(match => match.path)).size,
|
|
1288
|
+
filesSearched: result.filesSearched,
|
|
1289
|
+
limitReached: result.limitReached,
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
if (archiveDisplayMap.size > 0) {
|
|
1293
|
+
for (const match of result.matches) {
|
|
1294
|
+
const abs = matchAbsolutePath(match.path, searchPath);
|
|
1295
|
+
const display = archiveDisplayMap.get(abs);
|
|
1296
|
+
if (display) match.path = display;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
const formatPath = (filePath: string): string =>
|
|
1301
|
+
archiveDisplaySet.has(filePath) || virtualPathSet.has(filePath)
|
|
1302
|
+
? filePath
|
|
1303
|
+
: formatResultPath(filePath, isDirectory, searchPath, this.session.cwd);
|
|
1304
|
+
|
|
1305
|
+
// Group matches by file in encounter order. Detect per-file overflow
|
|
1306
|
+
// BEFORE truncation so the renderer can surface that a hot file was
|
|
1307
|
+
// trimmed for diversity.
|
|
1308
|
+
const fileOrder: string[] = [];
|
|
1309
|
+
const matchesByPath = new Map<string, GrepMatch[]>();
|
|
1310
|
+
for (const match of result.matches) {
|
|
1311
|
+
if (!matchesByPath.has(match.path)) {
|
|
1312
|
+
fileOrder.push(match.path);
|
|
1313
|
+
matchesByPath.set(match.path, []);
|
|
1314
|
+
}
|
|
1315
|
+
matchesByPath.get(match.path)!.push(match);
|
|
1316
|
+
}
|
|
1317
|
+
let perFileLimitReached = false;
|
|
1318
|
+
for (const file of fileOrder) {
|
|
1319
|
+
const list = matchesByPath.get(file)!;
|
|
1320
|
+
if (list.length > perFileMatchCap) {
|
|
1321
|
+
perFileLimitReached = true;
|
|
1322
|
+
list.length = perFileMatchCap;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
const totalFiles = fileOrder.length;
|
|
1326
|
+
// When native grep stopped at its internal cap, files past the cap were
|
|
1327
|
+
// never surfaced — the file total is only a lower bound.
|
|
1328
|
+
const totalFilesLabel = result.limitReached ? `${totalFiles}+` : `${totalFiles}`;
|
|
1329
|
+
// Single-file scopes can't paginate — there is one file by definition.
|
|
1330
|
+
const canPaginate = isMultiScope;
|
|
1331
|
+
const skipFiles = canPaginate ? Math.min(normalizedSkip, totalFiles) : 0;
|
|
1332
|
+
// A caller with a total match cap is not paginating: the cap bounds the
|
|
1333
|
+
// output, and the only consumer that sets one (`pi_grep`) has no `skip`
|
|
1334
|
+
// field to follow a "use skip=N" suggestion with. Windowing it to the
|
|
1335
|
+
// first 20 files would silently return fewer matches than it asked for
|
|
1336
|
+
// while reporting the cap as unreached.
|
|
1337
|
+
//
|
|
1338
|
+
// The window is cap+1 files, not cap: with one match per file, a cap
|
|
1339
|
+
// of N over exactly N files is complete, while over N+1 files it is
|
|
1340
|
+
// clipped — and only reading that extra file distinguishes the two.
|
|
1341
|
+
// The cap below then does the trimming and records that it bit, so
|
|
1342
|
+
// `match_limit_reached` reaches the frame set.
|
|
1343
|
+
const fileWindow = this.#totalMatchLimit !== undefined ? this.#totalMatchLimit + 1 : DEFAULT_FILE_LIMIT;
|
|
1344
|
+
const windowFiles = canPaginate ? fileOrder.slice(skipFiles, skipFiles + fileWindow) : fileOrder;
|
|
1345
|
+
const fileLimitReached = canPaginate && totalFiles > skipFiles + fileWindow;
|
|
1346
|
+
const selectedMatches: GrepMatch[] = [];
|
|
1347
|
+
let totalMatchLimitReached = false;
|
|
1348
|
+
if (windowFiles.length > 0) {
|
|
1349
|
+
const lists = windowFiles.map(file => matchesByPath.get(file) ?? []);
|
|
1350
|
+
const cursors = new Array<number>(lists.length).fill(0);
|
|
1351
|
+
let anyAdded = true;
|
|
1352
|
+
while (anyAdded) {
|
|
1353
|
+
anyAdded = false;
|
|
1354
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1355
|
+
if (cursors[i] < lists[i].length) {
|
|
1356
|
+
selectedMatches.push(lists[i][cursors[i]++]);
|
|
1357
|
+
anyAdded = true;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
// Round-robin above interleaves files for diversity, so the cap is
|
|
1362
|
+
// applied after selection rather than as a per-list bound: trimming
|
|
1363
|
+
// mid-rotation would silently favour whichever files sort first.
|
|
1364
|
+
const cap = this.#totalMatchLimit;
|
|
1365
|
+
if (cap !== undefined && selectedMatches.length > cap) {
|
|
1366
|
+
selectedMatches.length = cap;
|
|
1367
|
+
totalMatchLimitReached = true;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const nextSkip = skipFiles + windowFiles.length;
|
|
1371
|
+
const limitMessage = fileLimitReached
|
|
1372
|
+
? `Showing files ${skipFiles + 1}-${nextSkip} of ${totalFilesLabel}. Use skip=${nextSkip} for the next page, or narrow paths/pattern.`
|
|
1373
|
+
: "";
|
|
1374
|
+
const { record: recordFile, list: fileList } = createFileRecorder();
|
|
1375
|
+
const fileMatchCounts = new Map<string, number>();
|
|
1376
|
+
// Detect explicit file targets that exceed the native grep size cap.
|
|
1377
|
+
// Native searches only their first NATIVE_GREP_MAX_FILE_BYTES; without
|
|
1378
|
+
// this note the caller might miss that matches beyond the window
|
|
1379
|
+
// (or "no matches") reflect partial coverage, not the whole file.
|
|
1380
|
+
const oversizedNote = await (async (): Promise<string | undefined> => {
|
|
1381
|
+
const explicitFileTargets: string[] = [];
|
|
1382
|
+
if (exactFilePaths) {
|
|
1383
|
+
explicitFileTargets.push(...exactFilePaths);
|
|
1384
|
+
} else if (searchablePaths.length > 0 && !isDirectory && !multiTargets) {
|
|
1385
|
+
explicitFileTargets.push(searchPath);
|
|
1386
|
+
}
|
|
1387
|
+
if (explicitFileTargets.length === 0) return undefined;
|
|
1388
|
+
const oversized: string[] = [];
|
|
1389
|
+
await Promise.all(
|
|
1390
|
+
explicitFileTargets.map(async target => {
|
|
1391
|
+
try {
|
|
1392
|
+
const st = await stat(target);
|
|
1393
|
+
if (st.isFile() && st.size > NATIVE_GREP_MAX_FILE_BYTES) {
|
|
1394
|
+
oversized.push(path.relative(this.session.cwd, target) || target);
|
|
1395
|
+
}
|
|
1396
|
+
} catch {
|
|
1397
|
+
// Stat failures here are surfaced by other code paths.
|
|
1398
|
+
}
|
|
1399
|
+
}),
|
|
1400
|
+
);
|
|
1401
|
+
if (oversized.length === 0) return undefined;
|
|
1402
|
+
const limitMb = Math.floor(NATIVE_GREP_MAX_FILE_BYTES / (1024 * 1024));
|
|
1403
|
+
return `Searched only the first ${limitMb}MB of large files (matches past the ${limitMb}MB window are not shown; use \`read\` for the rest): ${oversized.join(", ")}`;
|
|
1404
|
+
})();
|
|
1405
|
+
// Directory/multi-target scopes: native counts files it could not map
|
|
1406
|
+
// even a prefix of (rare mmap failures), but cannot name them.
|
|
1407
|
+
const oversizedScanNote =
|
|
1408
|
+
!oversizedNote && skippedOversizedCount > 0
|
|
1409
|
+
? `Skipped ${skippedOversizedCount} unreadable large file(s); target them directly with \`read\``
|
|
1410
|
+
: undefined;
|
|
1411
|
+
const archiveNote =
|
|
1412
|
+
archiveUnreadable.length > 0
|
|
1413
|
+
? `Skipped archive entries (search supports text members only): ${archiveUnreadable.join(", ")}`
|
|
1414
|
+
: undefined;
|
|
1415
|
+
// Suppress entries we already explained via archiveNote — they would otherwise
|
|
1416
|
+
// double up (the unreadable selector also failed the scope's existence check).
|
|
1417
|
+
const archiveUnreadablePaths = new Set(archiveUnreadable.map(s => s.replace(/ \(.*\)$/, "")));
|
|
1418
|
+
const missingPathsForNote = missingPaths.filter(p => !archiveUnreadablePaths.has(p));
|
|
1419
|
+
const missingPathsNote =
|
|
1420
|
+
missingPathsForNote.length > 0 ? `Skipped missing paths: ${missingPathsForNote.join(", ")}` : undefined;
|
|
1421
|
+
const warningNote =
|
|
1422
|
+
[missingPathsNote, archiveNote, oversizedNote, oversizedScanNote]
|
|
1423
|
+
.filter((s): s is string => Boolean(s))
|
|
1424
|
+
.join("\n") || undefined;
|
|
1425
|
+
if (selectedMatches.length === 0) {
|
|
1426
|
+
const details: GrepToolDetails = {
|
|
1427
|
+
scopePath,
|
|
1428
|
+
searchPath,
|
|
1429
|
+
cwd: this.session.cwd,
|
|
1430
|
+
matchCount: 0,
|
|
1431
|
+
fileCount: 0,
|
|
1432
|
+
files: [],
|
|
1433
|
+
truncated: false,
|
|
1434
|
+
missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
|
|
1435
|
+
};
|
|
1436
|
+
const skipPastEnd = canPaginate && normalizedSkip > 0 && totalFiles > 0 && skipFiles >= totalFiles;
|
|
1437
|
+
const noMatchText = skipPastEnd
|
|
1438
|
+
? `No more results (${totalFilesLabel} files total; skip=${normalizedSkip} is past the end)`
|
|
1439
|
+
: "No matches found";
|
|
1440
|
+
const text = warningNote ? `${noMatchText}\n${warningNote}` : noMatchText;
|
|
1441
|
+
// Zero matches is useless regardless of warnings: by the time
|
|
1442
|
+
// compaction runs, the follow-up call has already corrected course.
|
|
1443
|
+
return toolResult(details).text(text).useless().done();
|
|
1444
|
+
}
|
|
1445
|
+
const outputLines: string[] = [];
|
|
1446
|
+
let linesTruncated = false;
|
|
1447
|
+
const matchesByFile = new Map<string, GrepMatch[]>();
|
|
1448
|
+
for (const match of selectedMatches) {
|
|
1449
|
+
const relativePath = formatPath(match.path);
|
|
1450
|
+
recordFile(relativePath);
|
|
1451
|
+
if (!matchesByFile.has(relativePath)) {
|
|
1452
|
+
matchesByFile.set(relativePath, []);
|
|
1453
|
+
}
|
|
1454
|
+
matchesByFile.get(relativePath)!.push(match);
|
|
1455
|
+
}
|
|
1456
|
+
const displayLines: string[] = [];
|
|
1457
|
+
const hashContexts = new Map<string, { tag: string }>();
|
|
1458
|
+
if (baseDisplayMode.hashLines) {
|
|
1459
|
+
for (const relativePath of fileList) {
|
|
1460
|
+
if (archiveDisplaySet.has(relativePath) || virtualPathSet.has(relativePath)) continue;
|
|
1461
|
+
const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
|
|
1462
|
+
if (isImmutableSourcePath(absoluteFilePath, immutableSourcePaths)) continue;
|
|
1463
|
+
// Mint a whole-file content tag so any anchor validates while the
|
|
1464
|
+
// file is unchanged; over-cap / unreadable files get no tag (and
|
|
1465
|
+
// therefore plain, non-editable line output).
|
|
1466
|
+
const tag = await recordFileSnapshot(this.session, absoluteFilePath);
|
|
1467
|
+
if (tag) hashContexts.set(relativePath, { tag });
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const renderMatchesForFile = (relativePath: string): { model: string[]; display: string[] } => {
|
|
1471
|
+
const modelOut: string[] = [];
|
|
1472
|
+
const displayOut: string[] = [];
|
|
1473
|
+
const fileMatches = matchesByFile.get(relativePath) ?? [];
|
|
1474
|
+
const hashContext = hashContexts.get(relativePath);
|
|
1475
|
+
const useHashLines = hashContext !== undefined;
|
|
1476
|
+
const lineNumberWidth = fileMatches.reduce((width, match) => {
|
|
1477
|
+
let nextWidth = Math.max(width, String(match.lineNumber).length);
|
|
1478
|
+
for (const ctx of match.contextBefore ?? []) {
|
|
1479
|
+
nextWidth = Math.max(nextWidth, String(ctx.lineNumber).length);
|
|
1480
|
+
}
|
|
1481
|
+
for (const ctx of match.contextAfter ?? []) {
|
|
1482
|
+
nextWidth = Math.max(nextWidth, String(ctx.lineNumber).length);
|
|
1483
|
+
}
|
|
1484
|
+
return nextWidth;
|
|
1485
|
+
}, 0);
|
|
1486
|
+
let lastEmittedLine: number | undefined;
|
|
1487
|
+
const gutterPad = " ".repeat(lineNumberWidth + 1);
|
|
1488
|
+
for (const match of fileMatches) {
|
|
1489
|
+
const pushLine = (lineNumber: number, line: string, isMatch: boolean) => {
|
|
1490
|
+
if (lastEmittedLine !== undefined && lineNumber > lastEmittedLine + 1) {
|
|
1491
|
+
modelOut.push("...");
|
|
1492
|
+
displayOut.push(`${gutterPad}│...`);
|
|
1493
|
+
}
|
|
1494
|
+
modelOut.push(formatMatchLine(lineNumber, line, isMatch, { useHashLines }));
|
|
1495
|
+
displayOut.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
|
|
1496
|
+
lastEmittedLine = lineNumber;
|
|
1497
|
+
};
|
|
1498
|
+
if (match.contextBefore) {
|
|
1499
|
+
for (const ctx of match.contextBefore) {
|
|
1500
|
+
pushLine(ctx.lineNumber, ctx.line, false);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
pushLine(match.lineNumber, match.line, true);
|
|
1504
|
+
if (match.truncated) linesTruncated = true;
|
|
1505
|
+
if (match.contextAfter) {
|
|
1506
|
+
for (const ctx of match.contextAfter) {
|
|
1507
|
+
pushLine(ctx.lineNumber, ctx.line, false);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
|
|
1511
|
+
}
|
|
1512
|
+
if (hashContext?.tag) {
|
|
1513
|
+
const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
|
|
1514
|
+
recordSeenLinesFromBody(this.session, absoluteFilePath, hashContext.tag, modelOut.join("\n"));
|
|
1515
|
+
}
|
|
1516
|
+
return { model: modelOut, display: displayOut };
|
|
1517
|
+
};
|
|
1518
|
+
const useGroupedOutput = isDirectory || isMultiScope;
|
|
1519
|
+
if (useGroupedOutput) {
|
|
1520
|
+
const grouped = formatGroupedFiles(fileList, relativePath => {
|
|
1521
|
+
const rendered = renderMatchesForFile(relativePath);
|
|
1522
|
+
const hashContext = hashContexts.get(relativePath);
|
|
1523
|
+
return {
|
|
1524
|
+
modelLines: rendered.model,
|
|
1525
|
+
displayLines: rendered.display,
|
|
1526
|
+
headerSuffix: hashContext?.tag ? `#${hashContext.tag}` : "",
|
|
1527
|
+
skip: rendered.model.length === 0,
|
|
1528
|
+
};
|
|
1529
|
+
});
|
|
1530
|
+
outputLines.push(...grouped.model);
|
|
1531
|
+
displayLines.push(...grouped.display);
|
|
1532
|
+
} else {
|
|
1533
|
+
for (const relativePath of fileList) {
|
|
1534
|
+
const rendered = renderMatchesForFile(relativePath);
|
|
1535
|
+
if (rendered.model.length === 0) continue;
|
|
1536
|
+
if (outputLines.length > 0) {
|
|
1537
|
+
outputLines.push("");
|
|
1538
|
+
displayLines.push("");
|
|
1539
|
+
}
|
|
1540
|
+
const hashContext = hashContexts.get(relativePath);
|
|
1541
|
+
if (hashContext?.tag) {
|
|
1542
|
+
outputLines.push(formatHashlineHeader(relativePath, hashContext.tag));
|
|
1543
|
+
}
|
|
1544
|
+
outputLines.push(...rendered.model);
|
|
1545
|
+
displayLines.push(...rendered.display);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
if (limitMessage) {
|
|
1549
|
+
outputLines.push("", limitMessage);
|
|
1550
|
+
}
|
|
1551
|
+
if (warningNote) {
|
|
1552
|
+
outputLines.push("", warningNote);
|
|
1553
|
+
}
|
|
1554
|
+
const rawOutput = outputLines.join("\n");
|
|
1555
|
+
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
1556
|
+
const output = truncation.content;
|
|
1557
|
+
const displayText = displayLines.join("\n");
|
|
1558
|
+
const truncated = Boolean(
|
|
1559
|
+
fileLimitReached ||
|
|
1560
|
+
perFileLimitReached ||
|
|
1561
|
+
totalMatchLimitReached ||
|
|
1562
|
+
result.limitReached ||
|
|
1563
|
+
truncation.truncated ||
|
|
1564
|
+
linesTruncated,
|
|
1565
|
+
);
|
|
1566
|
+
const details: GrepToolDetails = {
|
|
1567
|
+
scopePath,
|
|
1568
|
+
searchPath,
|
|
1569
|
+
cwd: this.session.cwd,
|
|
1570
|
+
matchCount: selectedMatches.length,
|
|
1571
|
+
fileCount: fileList.length,
|
|
1572
|
+
files: fileList,
|
|
1573
|
+
fileMatches: fileList.map(path => ({
|
|
1574
|
+
path,
|
|
1575
|
+
count: fileMatchCounts.get(path) ?? 0,
|
|
1576
|
+
})),
|
|
1577
|
+
truncated,
|
|
1578
|
+
fileLimitReached: fileLimitReached ? fileWindow : undefined,
|
|
1579
|
+
perFileLimitReached: totalMatchLimitReached
|
|
1580
|
+
? this.#totalMatchLimit
|
|
1581
|
+
: perFileLimitReached
|
|
1582
|
+
? perFileMatchCap
|
|
1583
|
+
: undefined,
|
|
1584
|
+
displayContent: displayText,
|
|
1585
|
+
missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
|
|
1586
|
+
};
|
|
1587
|
+
if (truncation.truncated) details.truncation = truncation;
|
|
1588
|
+
if (linesTruncated) details.linesTruncated = true;
|
|
1589
|
+
const resultBuilder = toolResult(details)
|
|
1590
|
+
.text(output)
|
|
1591
|
+
.limits({ columnMax: linesTruncated ? DEFAULT_MAX_COLUMN : undefined });
|
|
1592
|
+
if (truncation.truncated) {
|
|
1593
|
+
resultBuilder.truncation(truncation, { direction: "head" });
|
|
1594
|
+
}
|
|
1595
|
+
return resultBuilder.done();
|
|
1596
|
+
} finally {
|
|
1597
|
+
await cleanupArchiveScratch();
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// =============================================================================
|
|
1604
|
+
// TUI Renderer
|
|
1605
|
+
// =============================================================================
|
|
1606
|
+
|
|
1607
|
+
interface GrepRenderArgs {
|
|
1608
|
+
pattern: string;
|
|
1609
|
+
path?: string | string[];
|
|
1610
|
+
/** Legacy pre-`path` argument name; kept so historical transcripts still render a scope. */
|
|
1611
|
+
paths?: string | string[];
|
|
1612
|
+
case?: boolean;
|
|
1613
|
+
gitignore?: boolean;
|
|
1614
|
+
skip?: number;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
1618
|
+
/** Line budget for the expanded view. Larger than collapsed so expanding
|
|
1619
|
+
* reveals more matches with context, but still bounded so a single hot file
|
|
1620
|
+
* whose matches span the whole file can't dump its entire length. */
|
|
1621
|
+
const EXPANDED_TEXT_LIMIT = PREVIEW_LIMITS.EXPANDED_LINES * 2;
|
|
1622
|
+
|
|
1623
|
+
const SEARCH_CODE_FRAME_LINE_RE = /^\s*\*?(\d+)│/;
|
|
1624
|
+
|
|
1625
|
+
function searchScopeMeta(details: GrepToolDetails | undefined): string | undefined {
|
|
1626
|
+
if (!details?.scopePath) return undefined;
|
|
1627
|
+
const label = details.searchPath ? fileHyperlink(details.searchPath, details.scopePath) : details.scopePath;
|
|
1628
|
+
return `in ${label}`;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
function linkUrlLikeSearchHeader(raw: string, styled: string): { line: string; absPath?: string } {
|
|
1632
|
+
const resolvedPath = tryResolveInternalUrlSync(raw);
|
|
1633
|
+
if (resolvedPath) return { line: fileHyperlink(resolvedPath, styled), absPath: resolvedPath };
|
|
1634
|
+
return { line: uriHyperlink(raw, styled) };
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
function parseSearchDisplayLineNumber(line: string): number | undefined {
|
|
1638
|
+
const match = SEARCH_CODE_FRAME_LINE_RE.exec(line);
|
|
1639
|
+
if (!match) return undefined;
|
|
1640
|
+
return Number.parseInt(match[1]!, 10);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
const SEARCH_MATCH_LINE_RE = /^\s*\*\d+(?:│|[:|])/;
|
|
1644
|
+
|
|
1645
|
+
interface RenderedSearchLine {
|
|
1646
|
+
raw: string;
|
|
1647
|
+
styled: string;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function isSearchMatchLine(line: string): boolean {
|
|
1651
|
+
return SEARCH_MATCH_LINE_RE.test(line);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function isSearchHeaderLine(line: string): boolean {
|
|
1655
|
+
return /^#+ /.test(line);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
const URL_HEADER_PREFIX_RE = /^#+\s+/;
|
|
1659
|
+
|
|
1660
|
+
function renderSearchDisplayLines(
|
|
1661
|
+
lines: readonly string[],
|
|
1662
|
+
headerBase: string | undefined,
|
|
1663
|
+
fileScope: string | undefined,
|
|
1664
|
+
uiTheme: Theme,
|
|
1665
|
+
): RenderedSearchLine[] {
|
|
1666
|
+
const contexts = classifyGroupedLines(lines, headerBase, fileScope);
|
|
1667
|
+
// `classifyGroupedLines` can't resolve internal URLs (TUI-only), so track the
|
|
1668
|
+
// resolved URL target here and use it for the body lines that follow.
|
|
1669
|
+
let urlFile: string | undefined;
|
|
1670
|
+
return lines.map((line, index) => {
|
|
1671
|
+
const ctx = contexts[index]!;
|
|
1672
|
+
if (ctx.kind === "dir") {
|
|
1673
|
+
urlFile = undefined;
|
|
1674
|
+
const styled = uiTheme.fg("accent", line);
|
|
1675
|
+
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
1676
|
+
}
|
|
1677
|
+
if (ctx.kind === "file") {
|
|
1678
|
+
if (ctx.isUrl) {
|
|
1679
|
+
const raw = line
|
|
1680
|
+
.replace(URL_HEADER_PREFIX_RE, "")
|
|
1681
|
+
.trimEnd()
|
|
1682
|
+
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
1683
|
+
const linked = linkUrlLikeSearchHeader(raw, uiTheme.fg("accent", line));
|
|
1684
|
+
urlFile = linked.absPath;
|
|
1685
|
+
return { raw: line, styled: linked.line };
|
|
1686
|
+
}
|
|
1687
|
+
urlFile = undefined;
|
|
1688
|
+
// Root-level files keep the bright accent; nested file headers are dimmed.
|
|
1689
|
+
const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
1690
|
+
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
1691
|
+
}
|
|
1692
|
+
const styled = uiTheme.fg("toolOutput", line);
|
|
1693
|
+
const lineNumber = parseSearchDisplayLineNumber(line);
|
|
1694
|
+
const filePath = ctx.filePath ?? urlFile;
|
|
1695
|
+
return {
|
|
1696
|
+
raw: line,
|
|
1697
|
+
styled: filePath && lineNumber !== undefined ? fileHyperlink(filePath, styled, { line: lineNumber }) : styled,
|
|
1698
|
+
};
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
function compactSearchPreviewGroup(group: RenderedSearchLine[]): RenderedSearchLine[] {
|
|
1703
|
+
const compact = group.filter(line => isSearchHeaderLine(line.raw) || isSearchMatchLine(line.raw));
|
|
1704
|
+
return compact.length > 0 ? compact : group;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
function countPreviewMatches(lines: readonly RenderedSearchLine[], hasMarkedMatches: boolean): number {
|
|
1708
|
+
if (hasMarkedMatches) return lines.reduce((count, line) => count + (isSearchMatchLine(line.raw) ? 1 : 0), 0);
|
|
1709
|
+
return lines.reduce((count, line) => count + (!isSearchHeaderLine(line.raw) && line.raw.length > 0 ? 1 : 0), 0);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function renderBudgetedSearchGroups(
|
|
1713
|
+
groups: RenderedSearchLine[][],
|
|
1714
|
+
maxLines: number,
|
|
1715
|
+
matchCount: number,
|
|
1716
|
+
uiTheme: Theme,
|
|
1717
|
+
compact: boolean,
|
|
1718
|
+
): string[] {
|
|
1719
|
+
if (maxLines <= 0) return [];
|
|
1720
|
+
const renderedGroups = groups
|
|
1721
|
+
.map(group => (compact ? compactSearchPreviewGroup(group) : group))
|
|
1722
|
+
.filter(group => group.length > 0);
|
|
1723
|
+
if (renderedGroups.length === 0) return [];
|
|
1724
|
+
|
|
1725
|
+
let totalLines = 0;
|
|
1726
|
+
let totalMarkedMatches = 0;
|
|
1727
|
+
let totalFallbackMatches = 0;
|
|
1728
|
+
for (const group of renderedGroups) {
|
|
1729
|
+
totalLines += group.length;
|
|
1730
|
+
totalMarkedMatches += countPreviewMatches(group, true);
|
|
1731
|
+
totalFallbackMatches += countPreviewMatches(group, false);
|
|
1732
|
+
}
|
|
1733
|
+
const hasMarkedMatches = totalMarkedMatches > 0;
|
|
1734
|
+
const needsSummary = totalLines > maxLines;
|
|
1735
|
+
const contentBudget = needsSummary ? Math.max(maxLines - 1, 0) : maxLines;
|
|
1736
|
+
const visibleGroups: RenderedSearchLine[][] = [];
|
|
1737
|
+
let visibleLineCount = 0;
|
|
1738
|
+
let visibleMatches = 0;
|
|
1739
|
+
for (const group of renderedGroups) {
|
|
1740
|
+
if (visibleLineCount >= contentBudget) break;
|
|
1741
|
+
const available = contentBudget - visibleLineCount;
|
|
1742
|
+
const take = Math.min(group.length, available);
|
|
1743
|
+
if (take <= 0) break;
|
|
1744
|
+
const visibleGroup = group.slice(0, take);
|
|
1745
|
+
visibleGroups.push(visibleGroup);
|
|
1746
|
+
visibleLineCount += visibleGroup.length;
|
|
1747
|
+
visibleMatches += countPreviewMatches(visibleGroup, hasMarkedMatches);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
const totalMatches = hasMarkedMatches ? totalMarkedMatches : Math.max(matchCount, totalFallbackMatches);
|
|
1751
|
+
const hiddenMatches = Math.max(totalMatches - visibleMatches, 0);
|
|
1752
|
+
const hiddenLines = Math.max(totalLines - visibleLineCount, 0);
|
|
1753
|
+
const hasSummary = needsSummary && (hiddenMatches > 0 || hiddenLines > 0);
|
|
1754
|
+
const lines: string[] = [];
|
|
1755
|
+
for (let i = 0; i < visibleGroups.length; i++) {
|
|
1756
|
+
const group = visibleGroups[i]!;
|
|
1757
|
+
const isLast = !hasSummary && i === visibleGroups.length - 1;
|
|
1758
|
+
const prefix = `${uiTheme.fg("dim", getTreeBranch(isLast, uiTheme))} `;
|
|
1759
|
+
const continuePrefix = uiTheme.fg("dim", getTreeContinuePrefix(isLast, uiTheme));
|
|
1760
|
+
lines.push(`${prefix}${replaceTabs(group[0]!.styled)}`);
|
|
1761
|
+
for (let j = 1; j < group.length; j++) {
|
|
1762
|
+
lines.push(`${continuePrefix}${replaceTabs(group[j]!.styled)}`);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (hasSummary) {
|
|
1766
|
+
const hiddenLabel =
|
|
1767
|
+
hiddenMatches > 0 ? formatMoreItems(hiddenMatches, "match") : formatMoreItems(hiddenLines, "line");
|
|
1768
|
+
lines.push(`${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", hiddenLabel)}`);
|
|
1769
|
+
}
|
|
1770
|
+
return lines;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function grepStatusIcon(uiTheme: Theme): string {
|
|
1774
|
+
return uiTheme.fg("toolTitle", uiTheme.symbol("icon.search"));
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
export const grepToolRenderer = {
|
|
1778
|
+
inline: true,
|
|
1779
|
+
renderCall(args: GrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
1780
|
+
const paths = toPathList(args.path ?? args.paths);
|
|
1781
|
+
const meta: string[] = [];
|
|
1782
|
+
if (paths.length) meta.push(`in ${paths.join(", ")}`);
|
|
1783
|
+
if (args.case === false) meta.push("case:insensitive");
|
|
1784
|
+
if (args.gitignore === false) meta.push("gitignore:false");
|
|
1785
|
+
if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
|
|
1786
|
+
|
|
1787
|
+
const text = renderStatusLine(
|
|
1788
|
+
{ icon: "pending", title: "Grep", titleColor: "toolTitle", description: args.pattern || "?", meta },
|
|
1789
|
+
uiTheme,
|
|
1790
|
+
);
|
|
1791
|
+
return new Text(text, 1, 0);
|
|
1792
|
+
},
|
|
1793
|
+
|
|
1794
|
+
renderResult(
|
|
1795
|
+
result: { content: Array<{ type: string; text?: string }>; details?: GrepToolDetails; isError?: boolean },
|
|
1796
|
+
options: RenderResultOptions,
|
|
1797
|
+
uiTheme: Theme,
|
|
1798
|
+
args?: GrepRenderArgs,
|
|
1799
|
+
): Component {
|
|
1800
|
+
const details = result.details;
|
|
1801
|
+
|
|
1802
|
+
if (result.isError || details?.error) {
|
|
1803
|
+
const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
1804
|
+
return new Text(formatErrorMessage(errorText, uiTheme), 1, 0);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined;
|
|
1808
|
+
|
|
1809
|
+
if (!hasDetailedData) {
|
|
1810
|
+
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text;
|
|
1811
|
+
if (!textContent || textContent === "No matches found") {
|
|
1812
|
+
return new Text(formatEmptyMessage("No matches found", uiTheme), 1, 0);
|
|
1813
|
+
}
|
|
1814
|
+
const lines = textContent.split("\n").filter(line => line.trim() !== "");
|
|
1815
|
+
const description = args?.pattern ?? undefined;
|
|
1816
|
+
const header = renderStatusLine(
|
|
1817
|
+
{
|
|
1818
|
+
iconOverride: grepStatusIcon(uiTheme),
|
|
1819
|
+
title: "Grep",
|
|
1820
|
+
titleColor: "toolTitle",
|
|
1821
|
+
description,
|
|
1822
|
+
meta: [formatCount("item", lines.length)],
|
|
1823
|
+
},
|
|
1824
|
+
uiTheme,
|
|
1825
|
+
);
|
|
1826
|
+
return createCachedComponent(
|
|
1827
|
+
() => options.expanded,
|
|
1828
|
+
width => {
|
|
1829
|
+
const listLines = renderTreeList(
|
|
1830
|
+
{
|
|
1831
|
+
items: lines,
|
|
1832
|
+
expanded: options.expanded,
|
|
1833
|
+
maxCollapsed: COLLAPSED_TEXT_LIMIT,
|
|
1834
|
+
maxCollapsedLines: COLLAPSED_TEXT_LIMIT,
|
|
1835
|
+
itemType: "item",
|
|
1836
|
+
renderItem: line => uiTheme.fg("toolOutput", line),
|
|
1837
|
+
},
|
|
1838
|
+
uiTheme,
|
|
1839
|
+
);
|
|
1840
|
+
return [header, ...listLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
1841
|
+
},
|
|
1842
|
+
{ paddingX: 1 },
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
const matchCount = details?.matchCount ?? 0;
|
|
1847
|
+
const fileCount = details?.fileCount ?? 0;
|
|
1848
|
+
const truncation = details?.meta?.truncation;
|
|
1849
|
+
const limits = details?.meta?.limits;
|
|
1850
|
+
const truncated = Boolean(details?.truncated || truncation || limits?.columnTruncated);
|
|
1851
|
+
|
|
1852
|
+
const missingPathsList = details?.missingPaths ?? [];
|
|
1853
|
+
const missingNote =
|
|
1854
|
+
missingPathsList.length > 0
|
|
1855
|
+
? uiTheme.fg("warning", `skipped missing: ${missingPathsList.join(", ")}`)
|
|
1856
|
+
: undefined;
|
|
1857
|
+
|
|
1858
|
+
if (matchCount === 0) {
|
|
1859
|
+
const meta = ["0 matches"];
|
|
1860
|
+
const scopeMeta = searchScopeMeta(details);
|
|
1861
|
+
if (scopeMeta) meta.push(scopeMeta);
|
|
1862
|
+
const header = renderStatusLine(
|
|
1863
|
+
{ icon: "warning", title: "Grep", titleColor: "toolTitle", description: args?.pattern, meta },
|
|
1864
|
+
uiTheme,
|
|
1865
|
+
);
|
|
1866
|
+
const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
|
|
1867
|
+
if (missingNote) lines.push(missingNote);
|
|
1868
|
+
return new Text(lines.join("\n"), 1, 0);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
|
|
1872
|
+
const meta = [...summaryParts];
|
|
1873
|
+
const scopeMeta = searchScopeMeta(details);
|
|
1874
|
+
if (scopeMeta) meta.push(scopeMeta);
|
|
1875
|
+
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
1876
|
+
const description = args?.pattern ?? undefined;
|
|
1877
|
+
const header = renderStatusLine(
|
|
1878
|
+
{
|
|
1879
|
+
...(truncated ? { icon: "warning" as const } : { iconOverride: grepStatusIcon(uiTheme) }),
|
|
1880
|
+
title: "Grep",
|
|
1881
|
+
titleColor: "toolTitle",
|
|
1882
|
+
description,
|
|
1883
|
+
meta,
|
|
1884
|
+
},
|
|
1885
|
+
uiTheme,
|
|
1886
|
+
);
|
|
1887
|
+
|
|
1888
|
+
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1889
|
+
const allLines = textContent.split("\n");
|
|
1890
|
+
// Resolve hyperlinks once over the whole output so a nested directory stack
|
|
1891
|
+
// reconstructs correctly across blank-line group boundaries.
|
|
1892
|
+
// Header/match display paths are cwd-relative, so resolve them against cwd
|
|
1893
|
+
// (falling back to searchPath for legacy results that predate `cwd`); the
|
|
1894
|
+
// scoped file's absolute path seeds body lines in single-file searches.
|
|
1895
|
+
const renderedLines = renderSearchDisplayLines(
|
|
1896
|
+
allLines,
|
|
1897
|
+
details?.cwd ?? details?.searchPath,
|
|
1898
|
+
details?.searchPath,
|
|
1899
|
+
uiTheme,
|
|
1900
|
+
);
|
|
1901
|
+
const matchGroups = groupLineIndicesByBlank(allLines).map(indices => indices.map(i => renderedLines[i]!));
|
|
1902
|
+
|
|
1903
|
+
const extraLines: string[] = [];
|
|
1904
|
+
if (missingNote) extraLines.push(missingNote);
|
|
1905
|
+
|
|
1906
|
+
return createCachedComponent(
|
|
1907
|
+
() => options.expanded,
|
|
1908
|
+
width => {
|
|
1909
|
+
const budget = Math.max(
|
|
1910
|
+
(options.expanded ? EXPANDED_TEXT_LIMIT : COLLAPSED_TEXT_LIMIT) - extraLines.length,
|
|
1911
|
+
0,
|
|
1912
|
+
);
|
|
1913
|
+
const matchLines = renderBudgetedSearchGroups(matchGroups, budget, matchCount, uiTheme, !options.expanded);
|
|
1914
|
+
return [header, ...matchLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
1915
|
+
},
|
|
1916
|
+
{ paddingX: 1 },
|
|
1917
|
+
);
|
|
1918
|
+
},
|
|
1919
|
+
mergeCallAndResult: true,
|
|
1920
|
+
};
|