@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,1646 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { formatHashlineHeader, stripHashlinePrefixes } from "@oh-my-pi/hashline";
|
|
6
|
+
import { type } from "@oh-my-pi/omptype";
|
|
7
|
+
import type {
|
|
8
|
+
AgentTool,
|
|
9
|
+
AgentToolContext,
|
|
10
|
+
AgentToolResult,
|
|
11
|
+
AgentToolUpdateCallback,
|
|
12
|
+
ToolTier,
|
|
13
|
+
} from "@oh-my-pi/pi-agent-core";
|
|
14
|
+
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
15
|
+
import { isEnoent, isRecord, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
16
|
+
|
|
17
|
+
import { canonicalSnapshotKey, getFileSnapshotStore } from "../edit/file-snapshot-store";
|
|
18
|
+
import { normalizeToLF } from "../edit/normalize";
|
|
19
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
20
|
+
import { InternalUrlRouter } from "../internal-urls";
|
|
21
|
+
import { parseInternalUrl } from "../internal-urls/parse";
|
|
22
|
+
import { couldBecomeXdUrl, parseXdUrl } from "../internal-urls/xd-protocol";
|
|
23
|
+
import { createLspWritethrough, type FileDiagnosticsResult, type WritethroughCallback, writethroughNoop } from "../lsp";
|
|
24
|
+
import { DeferredDiagnostics } from "../lsp/deferred-diagnostics";
|
|
25
|
+
import { getDiagnosticsLedger } from "../lsp/diagnostics-ledger";
|
|
26
|
+
import { getLanguageFromPath, highlightCode, type Theme } from "../modes/theme/theme";
|
|
27
|
+
import writeDescription from "../prompts/tools/write.md" with { type: "text" };
|
|
28
|
+
import type { ToolSession } from "../sdk";
|
|
29
|
+
import { fileHyperlink, framedBlock, renderStatusLine } from "../tui";
|
|
30
|
+
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
31
|
+
import {
|
|
32
|
+
type ArchiveMemberContent,
|
|
33
|
+
archiveFormatFromPath,
|
|
34
|
+
parseArchivePathCandidates,
|
|
35
|
+
readArchiveEntries,
|
|
36
|
+
writeArchive,
|
|
37
|
+
} from "../utils/zip";
|
|
38
|
+
import { routeWriteThroughBridge } from "./acp-bridge";
|
|
39
|
+
import { resolveToolTier, truncateForPrompt } from "./approval";
|
|
40
|
+
import { assertEditableFile } from "./auto-generated-guard";
|
|
41
|
+
import {
|
|
42
|
+
type ConflictEntry,
|
|
43
|
+
conflictRegionPresent,
|
|
44
|
+
conflictRegionsEqual,
|
|
45
|
+
expandContentTokens,
|
|
46
|
+
getConflictHistory,
|
|
47
|
+
parseConflictUri,
|
|
48
|
+
spliceConflict,
|
|
49
|
+
} from "./conflict-detect";
|
|
50
|
+
import { invalidateFsScanAfterWrite } from "./fs-cache-invalidation";
|
|
51
|
+
import { type OutputMeta, outputMeta } from "./output-meta";
|
|
52
|
+
import {
|
|
53
|
+
formatPathRelativeToCwd,
|
|
54
|
+
isInternalUrlPath,
|
|
55
|
+
pathTargetsSsh,
|
|
56
|
+
peelWriteUrlSelector,
|
|
57
|
+
probeLiteralPathExists,
|
|
58
|
+
splitPathAndSel,
|
|
59
|
+
} from "./path-utils";
|
|
60
|
+
import { enforcePlanModeWrite, resolvePlanPath, unwrapHashlineHeaderPath } from "./plan-mode-guard";
|
|
61
|
+
import {
|
|
62
|
+
cachedRenderedString,
|
|
63
|
+
createRenderedStringCache,
|
|
64
|
+
Ellipsis,
|
|
65
|
+
formatDiagnostics,
|
|
66
|
+
formatErrorDetail,
|
|
67
|
+
formatExpandHint,
|
|
68
|
+
formatMoreItems,
|
|
69
|
+
formatStatusIcon,
|
|
70
|
+
getLspBatchRequest,
|
|
71
|
+
type RenderedStringCache,
|
|
72
|
+
replaceTabs,
|
|
73
|
+
shortenPath,
|
|
74
|
+
TRUNCATE_LENGTHS,
|
|
75
|
+
truncateToWidth,
|
|
76
|
+
} from "./render-utils";
|
|
77
|
+
import { dispatchReportIssueDevice, REPORT_ISSUE_DEVICE_NAME, renderReportIssueDeviceCall } from "./report-tool-issue";
|
|
78
|
+
import { dispatchResolutionDevice, isResolutionDeviceName, renderResolutionDeviceCall } from "./resolve";
|
|
79
|
+
import {
|
|
80
|
+
deleteRowByKey,
|
|
81
|
+
deleteRowByRowId,
|
|
82
|
+
insertRow,
|
|
83
|
+
isSqliteFile,
|
|
84
|
+
parseSqlitePathCandidates,
|
|
85
|
+
resolveTableRowLookup,
|
|
86
|
+
updateRowByKey,
|
|
87
|
+
updateRowByRowId,
|
|
88
|
+
} from "./sqlite-reader";
|
|
89
|
+
import { ToolError } from "./tool-errors";
|
|
90
|
+
import { toolResult } from "./tool-result";
|
|
91
|
+
import {
|
|
92
|
+
dispatchXdevTool,
|
|
93
|
+
renderXdevCall,
|
|
94
|
+
renderXdevResult,
|
|
95
|
+
resolveXdevTool,
|
|
96
|
+
type XdevDispatch,
|
|
97
|
+
xdevListing,
|
|
98
|
+
} from "./xdev";
|
|
99
|
+
|
|
100
|
+
const LOOSE_HASHLINE_HEADER_RE = /^\s*\[[^#\r\n]+#[^ \t\r\n]*\]\s*$/;
|
|
101
|
+
const EXECUTABLE_NOTICE = "[Notice: Made executable via chmod +x]";
|
|
102
|
+
const URI_LIKE_WRITE_PATH_RE = /^([a-z][a-z0-9+.-]*):\/{1,2}(.*)$/i;
|
|
103
|
+
const XD_MISSING_DELIMITER_RE = /^xd\/+(.*)$/i;
|
|
104
|
+
const XD_SCHEME_NEAR_MISSES: Record<string, true> = { dx: true, xdd: true, xdt: true };
|
|
105
|
+
|
|
106
|
+
function assertWriteTargetAddressable(target: string, router: InternalUrlRouter): void {
|
|
107
|
+
const trimmed = target.trim();
|
|
108
|
+
if (path.win32.isAbsolute(trimmed) || router.canHandle(trimmed)) return;
|
|
109
|
+
|
|
110
|
+
const missingDelimiter = trimmed.match(XD_MISSING_DELIMITER_RE);
|
|
111
|
+
if (missingDelimiter) {
|
|
112
|
+
throw new ToolError(
|
|
113
|
+
`Unknown URI-like write target '${trimmed}'. Did you mean 'xd://${missingDelimiter[1]}'? Prefix the path with './' to write it as a filesystem path.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const uriLike = trimmed.match(URI_LIKE_WRITE_PATH_RE);
|
|
118
|
+
if (!uriLike) return;
|
|
119
|
+
|
|
120
|
+
const scheme = uriLike[1]!.toLowerCase();
|
|
121
|
+
// conflict:// has no router handler but is spliced downstream by
|
|
122
|
+
// parseConflictUri (which emits its own precise id/scope errors); let it pass.
|
|
123
|
+
if (scheme === "conflict") return;
|
|
124
|
+
const canonicalScheme = router.getHandler(scheme) ? scheme : XD_SCHEME_NEAR_MISSES[scheme] ? "xd" : undefined;
|
|
125
|
+
const suggestion = canonicalScheme
|
|
126
|
+
? ` Did you mean '${canonicalScheme}://${uriLike[2]}'?`
|
|
127
|
+
: " Tool devices use 'xd://<tool>'.";
|
|
128
|
+
throw new ToolError(
|
|
129
|
+
`Unknown URI-like write target '${trimmed}'.${suggestion} Prefix the path with './' to write it as a filesystem path.`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Fail closed when a local write target looks like a mis-dispatched read.
|
|
135
|
+
*
|
|
136
|
+
* A read-only step that selects `write` instead of `read` passes the full read
|
|
137
|
+
* expression (`src/foo.tsx:1-260:raw`) as the target. Because a literal colon
|
|
138
|
+
* filename is legal on POSIX (issue #4618), that request otherwise resolves to
|
|
139
|
+
* filesystem creation and reports success, leaving a stray zero-byte file the
|
|
140
|
+
* model cannot recover from — the local analogue of the `xd://` near-miss guard
|
|
141
|
+
* ({@link assertWriteTargetAddressable}, issue #6123).
|
|
142
|
+
*
|
|
143
|
+
* Fires only on the high-confidence combination the report identifies: the tail
|
|
144
|
+
* parses as a read-tool selector, the literal target is missing, and no content
|
|
145
|
+
* was supplied. Non-empty content is the escape hatch — it is never blocked, so
|
|
146
|
+
* a deliberate write to a selector-shaped filename still succeeds. An existing
|
|
147
|
+
* literal path or an ambiguous stat (`"unknown"`: EACCES, transient I/O) also
|
|
148
|
+
* passes through so a real file is never shadowed by the guard.
|
|
149
|
+
*/
|
|
150
|
+
function readSelectorForEmptyWrite(target: string, content: string): string | undefined {
|
|
151
|
+
if (content.length > 0) return undefined;
|
|
152
|
+
return splitPathAndSel(target).sel;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function throwReadSelectorMisfire(target: string, sel: string): never {
|
|
156
|
+
throw new ToolError(
|
|
157
|
+
`write target '${target}' ends with a read-tool selector ':${sel}' and no such file exists — refusing to create a literal file by that name. ` +
|
|
158
|
+
`If you meant to read it, use read({ path: "${target}" }). ` +
|
|
159
|
+
`If you truly intend to create this file, pass its contents in \`content\` (a non-empty write is never blocked).`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Recognize a semicolon-joined list of read-tool selectors mis-dispatched as a
|
|
165
|
+
* single write target — the multi-file read expression the scout emitted in
|
|
166
|
+
* issue #6809 (`a.txt:1-2;b/c.txt:3-4`). Every `;`-segment must be non-empty and
|
|
167
|
+
* carry its own read selector ({@link splitPathAndSel} peels a `:N-M`, `:raw`,
|
|
168
|
+
* or `:conflicts` tail). No real call targets such a list: `read` accepts one
|
|
169
|
+
* path, `write` writes one file. Unlike {@link readSelectorForEmptyWrite} this
|
|
170
|
+
* fires regardless of `content` — the non-empty-content escape hatch exists for
|
|
171
|
+
* a lone selector-shaped *filename*, never a `;`-list, and honoring it here
|
|
172
|
+
* silently creates a nested directory tree (`a.txt:1-2;b/`) in the workspace.
|
|
173
|
+
* The caller still probes the literal target first, so an existing POSIX file
|
|
174
|
+
* by that exact name stays writable (same escape as the single-selector guard).
|
|
175
|
+
*/
|
|
176
|
+
function readSelectorListMisfire(target: string): number | undefined {
|
|
177
|
+
if (!target.includes(";")) return undefined;
|
|
178
|
+
const segments = target.split(";");
|
|
179
|
+
if (segments.length < 2) return undefined;
|
|
180
|
+
for (const segment of segments) {
|
|
181
|
+
const trimmed = segment.trim();
|
|
182
|
+
if (trimmed.length === 0 || splitPathAndSel(trimmed).sel === undefined) return undefined;
|
|
183
|
+
}
|
|
184
|
+
return segments.length;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function throwReadSelectorListMisfire(target: string, count: number): never {
|
|
188
|
+
throw new ToolError(
|
|
189
|
+
`write target '${target}' is a semicolon-joined list of ${count} read-tool selectors, not a filesystem path — refusing to create it. ` +
|
|
190
|
+
`write creates a single file; issue one read() per path to read these ranges (e.g. read({ path: "<one path>:<range>" })).`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function assertNotReadSelectorMisfire(target: string, content: string, cwd: string): Promise<void> {
|
|
195
|
+
const listCount = readSelectorListMisfire(target);
|
|
196
|
+
if (listCount !== undefined && (await probeLiteralPathExists(target, cwd)) === "missing") {
|
|
197
|
+
throwReadSelectorListMisfire(target, listCount);
|
|
198
|
+
}
|
|
199
|
+
const sel = readSelectorForEmptyWrite(target, content);
|
|
200
|
+
if (sel === undefined) return;
|
|
201
|
+
if ((await probeLiteralPathExists(target, cwd)) !== "missing") return;
|
|
202
|
+
throwReadSelectorMisfire(target, sel);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const BULK_DIRECTIVE_RE = /^#?(\d+)\s*[:=]\s*(@ours|@theirs|@base|@both)$/;
|
|
206
|
+
/**
|
|
207
|
+
* The head of a per-id directive line — `<id>:` / `<id>=` (optionally `#`-prefixed),
|
|
208
|
+
* regardless of whether its value is a valid `@side` token. Used only to sharpen the
|
|
209
|
+
* error message when a directive block is malformed (e.g. `15: some literal text`).
|
|
210
|
+
*/
|
|
211
|
+
const BULK_DIRECTIVE_HEAD_RE = /^#?\d+\s*[:=]/;
|
|
212
|
+
|
|
213
|
+
function truncateDirectiveLine(line: string): string {
|
|
214
|
+
return line.length > 60 ? `${line.slice(0, 57)}…` : line;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Parse `conflict://*` per-id directive content: every non-empty line must be
|
|
219
|
+
* `<id>: @side` (also accepted: `#<id> = @side`), where `@side` is one of
|
|
220
|
+
* `@ours` / `@theirs` / `@base` / `@both`.
|
|
221
|
+
*
|
|
222
|
+
* Returns `null` only when NO line is directive-shaped (→ uniform bulk mode).
|
|
223
|
+
* Throws on duplicate ids, and — critically — on a *partial* directive block:
|
|
224
|
+
* content that mixes valid `<id>: @side` lines with lines that aren't. Without
|
|
225
|
+
* that guard a per-id write carrying any non-token value (a literal or
|
|
226
|
+
* multi-line replacement, e.g. `15: <multi-line content>`) fell through to
|
|
227
|
+
* uniform bulk mode, which pasted the raw directive text verbatim into every
|
|
228
|
+
* block and still reported success. Per-id bulk is token-only; literal or
|
|
229
|
+
* multi-line replacements must go through individual `conflict://<N>` writes.
|
|
230
|
+
*/
|
|
231
|
+
function parseBulkDirectives(content: string): Map<number, string> | null {
|
|
232
|
+
const map = new Map<number, string>();
|
|
233
|
+
const stray: string[] = [];
|
|
234
|
+
let sawDirective = false;
|
|
235
|
+
for (const raw of content.split("\n")) {
|
|
236
|
+
const line = raw.trim();
|
|
237
|
+
if (line.length === 0) continue;
|
|
238
|
+
const match = line.match(BULK_DIRECTIVE_RE);
|
|
239
|
+
if (!match) {
|
|
240
|
+
stray.push(line);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
sawDirective = true;
|
|
244
|
+
const id = Number.parseInt(match[1], 10);
|
|
245
|
+
if (map.has(id)) {
|
|
246
|
+
throw new ToolError(`Bulk directive lists conflict #${id} twice — each id may appear once.`);
|
|
247
|
+
}
|
|
248
|
+
map.set(id, match[2]);
|
|
249
|
+
}
|
|
250
|
+
// No directive lines at all → not a per-id block; caller uses uniform mode.
|
|
251
|
+
if (!sawDirective) return null;
|
|
252
|
+
if (stray.length > 0) {
|
|
253
|
+
const sample = stray[0]!;
|
|
254
|
+
const tokenHint = BULK_DIRECTIVE_HEAD_RE.test(sample)
|
|
255
|
+
? `Per-id bulk only accepts the tokens @ours/@theirs/@base/@both — one side per id, single line. `
|
|
256
|
+
: "";
|
|
257
|
+
throw new ToolError(
|
|
258
|
+
`Malformed \`conflict://*\` per-id block: ${stray.length} line(s) are not \`<id>: @side\` directives (first: \`${truncateDirectiveLine(sample)}\`). ` +
|
|
259
|
+
tokenHint +
|
|
260
|
+
`Literal or multi-line replacement content isn't supported in a per-id block — resolve those blocks with individual \`write({ path: "conflict://<N>", content })\` calls (you can issue several at once). ` +
|
|
261
|
+
`For a pure pick-a-side pass, make every non-empty line \`<id>: @ours\` (or @theirs/@base/@both).`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return map;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Resolve per-id directives, preferring the pre-strip `raw` content and falling
|
|
269
|
+
* back to the hashline-stripped `stripped` content.
|
|
270
|
+
*
|
|
271
|
+
* Raw is preferred because the `<id>:` directive heads look exactly like
|
|
272
|
+
* hashline `LINE:` prefixes and would be eaten by stripping. When the two
|
|
273
|
+
* contents are identical (hashline mode off) a single parse decides everything,
|
|
274
|
+
* so a malformed-block error propagates straight through — the previous
|
|
275
|
+
* `?? parseBulkDirectives(...)` chain would have swallowed it and silently
|
|
276
|
+
* degraded to uniform bulk mode, pasting the raw directive text into every
|
|
277
|
+
* block. When they differ, a malformed raw block still defers to a *clean*
|
|
278
|
+
* stripped block, but otherwise surfaces its error rather than degrading.
|
|
279
|
+
*/
|
|
280
|
+
function resolveBulkDirectives(raw: string, stripped: string): Map<number, string> | null {
|
|
281
|
+
if (raw === stripped) return parseBulkDirectives(raw);
|
|
282
|
+
let rawResult: Map<number, string> | null;
|
|
283
|
+
try {
|
|
284
|
+
rawResult = parseBulkDirectives(raw);
|
|
285
|
+
} catch (rawError) {
|
|
286
|
+
let fallback: Map<number, string> | null = null;
|
|
287
|
+
try {
|
|
288
|
+
fallback = parseBulkDirectives(stripped);
|
|
289
|
+
} catch {
|
|
290
|
+
fallback = null;
|
|
291
|
+
}
|
|
292
|
+
if (fallback) return fallback;
|
|
293
|
+
throw rawError;
|
|
294
|
+
}
|
|
295
|
+
return rawResult ?? parseBulkDirectives(stripped);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const writeSchema = type({
|
|
299
|
+
path: type("string").describe("file path"),
|
|
300
|
+
content: type("string").describe("file content"),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
export type WriteToolInput = typeof writeSchema.infer;
|
|
304
|
+
|
|
305
|
+
/** Details returned by the write tool for TUI rendering */
|
|
306
|
+
export interface WriteToolDetails {
|
|
307
|
+
diagnostics?: FileDiagnosticsResult;
|
|
308
|
+
meta?: OutputMeta;
|
|
309
|
+
/** Set when the file was auto-chmod'd because content begins with a `#!` shebang. */
|
|
310
|
+
madeExecutable?: boolean;
|
|
311
|
+
/** Absolute filesystem path the write resolved to. Used by the renderer to wrap
|
|
312
|
+
* the (possibly cwd-relative) header path in an OSC 8 `file://` hyperlink. */
|
|
313
|
+
resolvedPath?: string;
|
|
314
|
+
/** Set when the write dispatched an `xd://` tool device; drives renderer delegation. */
|
|
315
|
+
xdev?: XdevDispatch;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Strip hashline display prefixes from write content.
|
|
320
|
+
*
|
|
321
|
+
* Includes a fallback for loosely-formed section headers that still carry
|
|
322
|
+
* line-number prefixes (for example legacy or malformed hashline echoes).
|
|
323
|
+
*/
|
|
324
|
+
function stripWriteContentWithPotentialLooseHeader(lines: string[]): { text: string; stripped: boolean } {
|
|
325
|
+
const cleaned = stripHashlinePrefixes(lines);
|
|
326
|
+
if (cleaned !== lines) {
|
|
327
|
+
return { text: cleaned.join("\n"), stripped: true };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const headerIndex = lines.findIndex(line => line.trim().length > 0);
|
|
331
|
+
if (headerIndex === -1 || !LOOSE_HASHLINE_HEADER_RE.test(lines[headerIndex])) {
|
|
332
|
+
return { text: lines.join("\n"), stripped: false };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const linesWithoutHeader = lines.slice(0, headerIndex).concat(lines.slice(headerIndex + 1));
|
|
336
|
+
const cleanedWithoutHeader = stripHashlinePrefixes(linesWithoutHeader);
|
|
337
|
+
if (cleanedWithoutHeader === linesWithoutHeader) {
|
|
338
|
+
return { text: lines.join("\n"), stripped: false };
|
|
339
|
+
}
|
|
340
|
+
return { text: cleanedWithoutHeader.join("\n"), stripped: true };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Strip hashline display prefixes from write content.
|
|
345
|
+
*
|
|
346
|
+
* Only active when hashline edit mode is enabled — the model sees `[PATH#HASH]`
|
|
347
|
+
* headers plus `LINE:` prefixes in read output and sometimes copies them into write content.
|
|
348
|
+
*/
|
|
349
|
+
function stripWriteContent(session: ToolSession, content: string): { text: string; stripped: boolean } {
|
|
350
|
+
if (!resolveFileDisplayMode(session).hashLines) {
|
|
351
|
+
return { text: content, stripped: false };
|
|
352
|
+
}
|
|
353
|
+
return stripWriteContentWithPotentialLooseHeader(content.split("\n"));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Record a snapshot of the freshly-written `content` for `absolutePath`
|
|
358
|
+
* so subsequent hashline edits address the new file with a current tag,
|
|
359
|
+
* and return the matching `[displayPath#TAG]` header. Returns `undefined`
|
|
360
|
+
* when the session is not in hashline mode so callers can no-op cheaply.
|
|
361
|
+
*
|
|
362
|
+
* Mirrors the post-commit snapshot recording the hashline patcher performs
|
|
363
|
+
* after a successful edit: the model gets a tag without an extra `read`.
|
|
364
|
+
*/
|
|
365
|
+
function maybeWriteSnapshotHeader(session: ToolSession, absolutePath: string, content: string): string | undefined {
|
|
366
|
+
if (!resolveFileDisplayMode(session).hashLines) return undefined;
|
|
367
|
+
const normalized = normalizeToLF(content);
|
|
368
|
+
const tag = getFileSnapshotStore(session).record(canonicalSnapshotKey(absolutePath), normalized);
|
|
369
|
+
return formatHashlineHeader(formatPathRelativeToCwd(absolutePath, session.cwd), tag);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Append a trailing note line to the first text block of a tool result.
|
|
374
|
+
* Mutates `result` in place (the result object is owned by this call).
|
|
375
|
+
*/
|
|
376
|
+
function appendNoteToResult(result: AgentToolResult<WriteToolDetails>, note: string): void {
|
|
377
|
+
const firstText = result.content.find(
|
|
378
|
+
(block): block is { type: "text"; text: string } => block.type === "text" && typeof block.text === "string",
|
|
379
|
+
);
|
|
380
|
+
if (firstText) {
|
|
381
|
+
firstText.text = firstText.text.length > 0 ? `${firstText.text}\n${note}` : note;
|
|
382
|
+
} else {
|
|
383
|
+
result.content.push({ type: "text", text: note });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function emitWriteProgress(
|
|
388
|
+
onUpdate: AgentToolUpdateCallback<WriteToolDetails> | undefined,
|
|
389
|
+
content: string,
|
|
390
|
+
displayPath: string,
|
|
391
|
+
resolvedPath?: string,
|
|
392
|
+
): void {
|
|
393
|
+
onUpdate?.({
|
|
394
|
+
content: [{ type: "text", text: `Writing ${content.length} bytes to ${shortenPath(displayPath)}...` }],
|
|
395
|
+
details: resolvedPath ? { resolvedPath } : {},
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* If `content` begins with a `#!` shebang, ensure the file is executable.
|
|
401
|
+
*
|
|
402
|
+
* Mirrors `chmod a+x` (adds user/group/other execute bits to existing mode).
|
|
403
|
+
* Errors are swallowed: chmod failure (e.g. Windows ACL, read-only mount)
|
|
404
|
+
* MUST NOT fail an otherwise successful write. Returns whether the mode
|
|
405
|
+
* actually changed so the caller can surface a note.
|
|
406
|
+
*/
|
|
407
|
+
async function maybeMarkExecutableForShebang(absolutePath: string, content: string): Promise<boolean> {
|
|
408
|
+
if (!content.startsWith("#!")) return false;
|
|
409
|
+
try {
|
|
410
|
+
const stat = await fs.stat(absolutePath);
|
|
411
|
+
const currentMode = stat.mode & 0o7777;
|
|
412
|
+
const newMode = currentMode | 0o111;
|
|
413
|
+
if (newMode === currentMode) return false;
|
|
414
|
+
await fs.chmod(absolutePath, newMode);
|
|
415
|
+
return true;
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
422
|
+
// Tool Class
|
|
423
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
424
|
+
|
|
425
|
+
type WriteParams = WriteToolInput;
|
|
426
|
+
|
|
427
|
+
interface ResolvedArchiveWritePath {
|
|
428
|
+
absolutePath: string;
|
|
429
|
+
archivePath: string;
|
|
430
|
+
archiveSubPath: string;
|
|
431
|
+
exists: boolean;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
interface ResolvedSqliteWritePath {
|
|
435
|
+
absolutePath: string;
|
|
436
|
+
sqlitePath: string;
|
|
437
|
+
table: string;
|
|
438
|
+
key?: string;
|
|
439
|
+
exists: boolean;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function isArchivePathNotFound(error: unknown): boolean {
|
|
443
|
+
if (isEnoent(error)) return true;
|
|
444
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTDIR";
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function normalizeArchiveWriteSubPath(rawPath: string): string {
|
|
448
|
+
const normalized = rawPath.replace(/\\/g, "/");
|
|
449
|
+
if (normalized.length === 0) {
|
|
450
|
+
throw new ToolError("Archive write path must target a file inside the archive");
|
|
451
|
+
}
|
|
452
|
+
if (normalized.endsWith("/")) {
|
|
453
|
+
throw new ToolError("Archive write path must target a file, not a directory");
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const parts = normalized.split("/");
|
|
457
|
+
const normalizedParts: string[] = [];
|
|
458
|
+
for (const part of parts) {
|
|
459
|
+
if (!part || part === ".") continue;
|
|
460
|
+
if (part === "..") {
|
|
461
|
+
throw new ToolError("Archive path cannot contain '..'");
|
|
462
|
+
}
|
|
463
|
+
normalizedParts.push(part);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (normalizedParts.length === 0) {
|
|
467
|
+
throw new ToolError("Archive write path must target a file inside the archive");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return normalizedParts.join("/");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function parseSqliteWriteTarget(subPath: string, queryString: string): { table: string; key?: string } {
|
|
474
|
+
if (queryString.trim().length > 0) {
|
|
475
|
+
throw new ToolError("SQLite write paths do not support query parameters");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const normalized = subPath.replace(/^:+/, "").trim();
|
|
479
|
+
if (!normalized) {
|
|
480
|
+
throw new ToolError("SQLite write path must target a table");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const separatorIndex = normalized.indexOf(":");
|
|
484
|
+
const table = separatorIndex === -1 ? normalized : normalized.slice(0, separatorIndex);
|
|
485
|
+
const key = separatorIndex === -1 ? undefined : normalized.slice(separatorIndex + 1);
|
|
486
|
+
if (!table) {
|
|
487
|
+
throw new ToolError("SQLite write path must target a table");
|
|
488
|
+
}
|
|
489
|
+
if (key !== undefined && key.length === 0) {
|
|
490
|
+
throw new ToolError("SQLite row writes require a non-empty row key");
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return { table, key };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Write tool implementation.
|
|
498
|
+
*
|
|
499
|
+
* Creates or overwrites files with optional LSP formatting and diagnostics.
|
|
500
|
+
*/
|
|
501
|
+
export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails> {
|
|
502
|
+
readonly name = "write";
|
|
503
|
+
readonly approval = (args: unknown): ToolTier => {
|
|
504
|
+
const rawPath = (args as Partial<WriteParams>).path;
|
|
505
|
+
if (typeof rawPath !== "string") return "write";
|
|
506
|
+
// Unwrap a hashline `[path#TAG]` wrapper first (parity with execute) so a
|
|
507
|
+
// wrapped `[ssh://h/x#ABCD]` can't dodge scheme detection and the tier checks below.
|
|
508
|
+
const path = unwrapHashlineHeaderPath(rawPath);
|
|
509
|
+
// xd:// device writes execute the mounted tool — take its approval tier.
|
|
510
|
+
// The resolution devices (xd://resolve, xd://reject, xd://propose)
|
|
511
|
+
// finalize a staged, already-previewed action, so they stay at read tier.
|
|
512
|
+
const xdevTarget = parseXdUrl(path);
|
|
513
|
+
if (xdevTarget) {
|
|
514
|
+
if (xdevTarget.name === REPORT_ISSUE_DEVICE_NAME) return "write";
|
|
515
|
+
if (xdevTarget.name && isResolutionDeviceName(xdevTarget.name)) return "read";
|
|
516
|
+
const inst =
|
|
517
|
+
xdevTarget.name && this.session.xdev ? resolveXdevTool(this.session.xdev, xdevTarget.name) : undefined;
|
|
518
|
+
if (!inst) return "exec";
|
|
519
|
+
// Decode the device JSON payload and evaluate the mounted tool's own
|
|
520
|
+
// approval (which may be argument-dependent, e.g. ast_edit is read-tier
|
|
521
|
+
// for internal-URL paths, debug is read-tier for inspection actions).
|
|
522
|
+
// Malformed JSON, non-object payloads, missing content, and approval
|
|
523
|
+
// functions that reject schema-invalid objects stay exec so the gate
|
|
524
|
+
// fails closed — the dispatch itself rejects invalid arguments too.
|
|
525
|
+
const rawContent = (args as Partial<WriteParams>).content;
|
|
526
|
+
if (typeof rawContent !== "string") return "exec";
|
|
527
|
+
let parsed: unknown;
|
|
528
|
+
try {
|
|
529
|
+
parsed = JSON.parse(rawContent);
|
|
530
|
+
} catch {
|
|
531
|
+
return "exec";
|
|
532
|
+
}
|
|
533
|
+
if (!isRecord(parsed)) return "exec";
|
|
534
|
+
try {
|
|
535
|
+
return resolveToolTier(inst, parsed);
|
|
536
|
+
} catch {
|
|
537
|
+
return "exec";
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Remote SSH writes open an outbound connection and run a remote shell —
|
|
541
|
+
// gate them like the exec-tier `ssh` tool, ahead of the handler-write
|
|
542
|
+
// logic. Substring match also covers selector-suffixed targets.
|
|
543
|
+
if (pathTargetsSsh(path)) return "exec";
|
|
544
|
+
if (!isInternalUrlPath(path)) return "write";
|
|
545
|
+
// Internal URLs are usually session-local artifacts (read tier), but a
|
|
546
|
+
// scheme whose handler exposes a `write` hook mutates handler-owned user
|
|
547
|
+
// data (e.g. vault:// notes) and must take the write tier so always-ask
|
|
548
|
+
// mode actually prompts.
|
|
549
|
+
const match = /^([a-z][a-z0-9+.-]*):\/\//i.exec(path.trim());
|
|
550
|
+
const handler = match ? InternalUrlRouter.instance().getHandler(match[1]!.toLowerCase()) : undefined;
|
|
551
|
+
return handler?.write ? "write" : "read";
|
|
552
|
+
};
|
|
553
|
+
readonly formatApprovalDetails = (args: unknown): string[] => {
|
|
554
|
+
const params = args as Partial<WriteParams>;
|
|
555
|
+
const targetPath = typeof params.path === "string" ? params.path : "(missing)";
|
|
556
|
+
const content = typeof params.content === "string" ? params.content : "";
|
|
557
|
+
return [`Path: ${truncateForPrompt(targetPath)}`, `Content:\n${truncateForPrompt(content)}`];
|
|
558
|
+
};
|
|
559
|
+
readonly label = "Write";
|
|
560
|
+
readonly description: string;
|
|
561
|
+
readonly parameters = writeSchema;
|
|
562
|
+
readonly strict = true;
|
|
563
|
+
readonly concurrency = "exclusive";
|
|
564
|
+
readonly loadMode = "essential";
|
|
565
|
+
|
|
566
|
+
/** Stream matchers should see the real file content, not its JSON-escaped argument encoding. */
|
|
567
|
+
matcherDigest(args: unknown): string | undefined {
|
|
568
|
+
const content = (args as Partial<WriteParams>).content;
|
|
569
|
+
return typeof content === "string" ? content : undefined;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
readonly #writethrough: WritethroughCallback;
|
|
573
|
+
readonly #deferredDiagnostics: DeferredDiagnostics | undefined;
|
|
574
|
+
|
|
575
|
+
constructor(private readonly session: ToolSession) {
|
|
576
|
+
const enableLsp = session.enableLsp ?? true;
|
|
577
|
+
const enableFormat = enableLsp && session.settings.get("lsp.formatOnWrite");
|
|
578
|
+
const enableDiagnostics = enableLsp && session.settings.get("lsp.diagnosticsOnWrite");
|
|
579
|
+
const dedup = enableDiagnostics && session.settings.get("lsp.diagnosticsDeduplicate");
|
|
580
|
+
this.#deferredDiagnostics =
|
|
581
|
+
enableDiagnostics && session.queueDeferredDiagnostics ? new DeferredDiagnostics(session, dedup) : undefined;
|
|
582
|
+
this.#writethrough = enableLsp
|
|
583
|
+
? createLspWritethrough(session.cwd, {
|
|
584
|
+
enableFormat,
|
|
585
|
+
enableDiagnostics,
|
|
586
|
+
transformDiagnostics: dedup
|
|
587
|
+
? (path, result) => getDiagnosticsLedger(session).reduce(path, result)
|
|
588
|
+
: undefined,
|
|
589
|
+
})
|
|
590
|
+
: writethroughNoop;
|
|
591
|
+
this.description = prompt.render(writeDescription);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async #resolveArchiveWritePath(writePath: string): Promise<ResolvedArchiveWritePath | null> {
|
|
595
|
+
const candidates = parseArchivePathCandidates(writePath).filter(candidate => candidate.archivePath !== writePath);
|
|
596
|
+
if (candidates.length === 0) {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const fallbackCandidate = candidates[candidates.length - 1]!;
|
|
601
|
+
const fallback: ResolvedArchiveWritePath = {
|
|
602
|
+
absolutePath: resolvePlanPath(this.session, fallbackCandidate.archivePath),
|
|
603
|
+
archivePath: fallbackCandidate.archivePath,
|
|
604
|
+
archiveSubPath: normalizeArchiveWriteSubPath(fallbackCandidate.subPath),
|
|
605
|
+
exists: false,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
for (const candidate of candidates) {
|
|
609
|
+
const absolutePath = resolvePlanPath(this.session, candidate.archivePath);
|
|
610
|
+
try {
|
|
611
|
+
const stat = await Bun.file(absolutePath).stat();
|
|
612
|
+
if (stat.isDirectory()) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
absolutePath,
|
|
618
|
+
archivePath: candidate.archivePath,
|
|
619
|
+
archiveSubPath: normalizeArchiveWriteSubPath(candidate.subPath),
|
|
620
|
+
exists: true,
|
|
621
|
+
};
|
|
622
|
+
} catch (error) {
|
|
623
|
+
if (!isArchivePathNotFound(error)) {
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return fallback;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
async #writeArchiveEntry(
|
|
633
|
+
content: string,
|
|
634
|
+
resolvedArchivePath: ResolvedArchiveWritePath,
|
|
635
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
636
|
+
// Resolve symlinks before the tmp+rename swap: renaming over a symlink
|
|
637
|
+
// replaces the link itself with a regular file instead of writing
|
|
638
|
+
// through to its target.
|
|
639
|
+
const finalPath = resolvedArchivePath.exists
|
|
640
|
+
? await fs.realpath(resolvedArchivePath.absolutePath).catch(() => resolvedArchivePath.absolutePath)
|
|
641
|
+
: resolvedArchivePath.absolutePath;
|
|
642
|
+
// A realpath swap can land on a name without an archive extension; a
|
|
643
|
+
// whole-archive rewrite then defaults to an uncompressed tar, matching the
|
|
644
|
+
// previous `isZip`/`isGzip`/else fallthrough.
|
|
645
|
+
const format = archiveFormatFromPath(finalPath) ?? "tar";
|
|
646
|
+
// Rewrites are whole-archive: write to a temp file and rename so a
|
|
647
|
+
// crash/disk-full mid-write can't destroy the original archive.
|
|
648
|
+
const tmpPath = `${finalPath}.tmp-${process.pid}`;
|
|
649
|
+
|
|
650
|
+
const parentDir = path.dirname(resolvedArchivePath.absolutePath);
|
|
651
|
+
if (parentDir && parentDir !== ".") {
|
|
652
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const entries = new Map<string, ArchiveMemberContent>();
|
|
656
|
+
if (resolvedArchivePath.exists) {
|
|
657
|
+
try {
|
|
658
|
+
const existing = await readArchiveEntries({ bytes: await Bun.file(finalPath).bytes(), format });
|
|
659
|
+
for (const [entryPath, data] of existing) {
|
|
660
|
+
entries.set(entryPath, data);
|
|
661
|
+
}
|
|
662
|
+
} catch (error) {
|
|
663
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const writeTarget = `${resolvedArchivePath.archivePath}:${resolvedArchivePath.archiveSubPath}`;
|
|
667
|
+
const sel = readSelectorForEmptyWrite(writeTarget, content);
|
|
668
|
+
if (sel !== undefined && !entries.has(resolvedArchivePath.archiveSubPath)) {
|
|
669
|
+
throwReadSelectorMisfire(writeTarget, sel);
|
|
670
|
+
}
|
|
671
|
+
entries.set(resolvedArchivePath.archiveSubPath, content);
|
|
672
|
+
|
|
673
|
+
try {
|
|
674
|
+
await writeArchive(tmpPath, format, entries);
|
|
675
|
+
await fs.rename(tmpPath, finalPath);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
await fs.rm(tmpPath, { force: true }).catch(() => {});
|
|
678
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
invalidateFsScanAfterWrite(resolvedArchivePath.absolutePath);
|
|
682
|
+
const outputPath = `${formatPathRelativeToCwd(resolvedArchivePath.absolutePath, this.session.cwd)}:${
|
|
683
|
+
resolvedArchivePath.archiveSubPath
|
|
684
|
+
}`;
|
|
685
|
+
return {
|
|
686
|
+
content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${outputPath}` }],
|
|
687
|
+
details: { resolvedPath: resolvedArchivePath.absolutePath },
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async #resolveSqliteWritePath(writePath: string): Promise<ResolvedSqliteWritePath | null> {
|
|
692
|
+
const candidates = parseSqlitePathCandidates(writePath).filter(candidate => candidate.sqlitePath !== writePath);
|
|
693
|
+
if (candidates.length === 0) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const fallbackCandidate = candidates[candidates.length - 1]!;
|
|
698
|
+
const fallbackTarget = parseSqliteWriteTarget(fallbackCandidate.subPath, fallbackCandidate.queryString);
|
|
699
|
+
const fallback: ResolvedSqliteWritePath = {
|
|
700
|
+
absolutePath: resolvePlanPath(this.session, fallbackCandidate.sqlitePath),
|
|
701
|
+
sqlitePath: fallbackCandidate.sqlitePath,
|
|
702
|
+
table: fallbackTarget.table,
|
|
703
|
+
key: fallbackTarget.key,
|
|
704
|
+
exists: false,
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
let sawExistingNonSqlite = false;
|
|
708
|
+
for (const candidate of candidates) {
|
|
709
|
+
const target = parseSqliteWriteTarget(candidate.subPath, candidate.queryString);
|
|
710
|
+
const absolutePath = resolvePlanPath(this.session, candidate.sqlitePath);
|
|
711
|
+
try {
|
|
712
|
+
const stat = await Bun.file(absolutePath).stat();
|
|
713
|
+
if (stat.isDirectory()) {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (!(await isSqliteFile(absolutePath))) {
|
|
717
|
+
sawExistingNonSqlite = true;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return {
|
|
722
|
+
absolutePath,
|
|
723
|
+
sqlitePath: candidate.sqlitePath,
|
|
724
|
+
table: target.table,
|
|
725
|
+
key: target.key,
|
|
726
|
+
exists: true,
|
|
727
|
+
};
|
|
728
|
+
} catch (error) {
|
|
729
|
+
if (!isArchivePathNotFound(error)) {
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (sawExistingNonSqlite) {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return fallback;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
async #writeSqliteRow(
|
|
743
|
+
displayPath: string,
|
|
744
|
+
content: string,
|
|
745
|
+
resolvedSqlitePath: ResolvedSqliteWritePath,
|
|
746
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
747
|
+
let db: Database | null = null;
|
|
748
|
+
try {
|
|
749
|
+
if (!resolvedSqlitePath.exists) {
|
|
750
|
+
throw new ToolError(`SQLite database '${displayPath}' not found`);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
db = new Database(resolvedSqlitePath.absolutePath, { create: false, strict: true });
|
|
754
|
+
db.run("PRAGMA busy_timeout = 3000");
|
|
755
|
+
|
|
756
|
+
const trimmedContent = content.trim();
|
|
757
|
+
let resultText: string;
|
|
758
|
+
if (trimmedContent.length === 0) {
|
|
759
|
+
if (!resolvedSqlitePath.key) {
|
|
760
|
+
throw new ToolError("SQLite deletes require a row key in the path");
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);
|
|
764
|
+
const deleted =
|
|
765
|
+
lookup.kind === "pk"
|
|
766
|
+
? deleteRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key)
|
|
767
|
+
: deleteRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key);
|
|
768
|
+
resultText =
|
|
769
|
+
deleted > 0
|
|
770
|
+
? `Deleted row '${resolvedSqlitePath.key}' from ${resolvedSqlitePath.table}`
|
|
771
|
+
: `No row deleted from ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
|
|
772
|
+
} else {
|
|
773
|
+
let parsedContent: unknown;
|
|
774
|
+
try {
|
|
775
|
+
parsedContent = Bun.JSON5.parse(content);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
throw new ToolError(
|
|
778
|
+
`SQLite write content must be valid JSON5: ${error instanceof Error ? error.message : String(error)}`,
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (!isRecord(parsedContent)) {
|
|
783
|
+
throw new ToolError("SQLite write content must be a JSON object");
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (resolvedSqlitePath.key) {
|
|
787
|
+
const lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);
|
|
788
|
+
const updated =
|
|
789
|
+
lookup.kind === "pk"
|
|
790
|
+
? updateRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key, parsedContent)
|
|
791
|
+
: updateRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key, parsedContent);
|
|
792
|
+
resultText =
|
|
793
|
+
updated > 0
|
|
794
|
+
? `Updated row '${resolvedSqlitePath.key}' in ${resolvedSqlitePath.table}`
|
|
795
|
+
: `No row updated in ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
|
|
796
|
+
} else {
|
|
797
|
+
insertRow(db, resolvedSqlitePath.table, parsedContent);
|
|
798
|
+
resultText = `Inserted row into ${resolvedSqlitePath.table}`;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
invalidateFsScanAfterWrite(resolvedSqlitePath.absolutePath);
|
|
803
|
+
return toolResult<WriteToolDetails>({ resolvedPath: resolvedSqlitePath.absolutePath })
|
|
804
|
+
.text(resultText)
|
|
805
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
806
|
+
.done();
|
|
807
|
+
} catch (error) {
|
|
808
|
+
if (isEnoent(error)) {
|
|
809
|
+
throw new ToolError(`SQLite database '${displayPath}' not found`);
|
|
810
|
+
}
|
|
811
|
+
if (error instanceof ToolError) {
|
|
812
|
+
throw error;
|
|
813
|
+
}
|
|
814
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
815
|
+
} finally {
|
|
816
|
+
db?.close();
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Resolve a single `conflict://<N>` write by splicing the recorded
|
|
822
|
+
* marker region in the registered file with `replacementContent`.
|
|
823
|
+
* The write deliberately bypasses the LSP writethrough: the file may
|
|
824
|
+
* still hold other unresolved marker blocks, so formatting could
|
|
825
|
+
* corrupt them and diagnostics would be marker-noise anyway.
|
|
826
|
+
*
|
|
827
|
+
* Entry ids are session-stable: they keep working even after later
|
|
828
|
+
* writes resolve other blocks in the same file. The recorded range
|
|
829
|
+
* is re-validated on disk before splicing so an out-of-band edit
|
|
830
|
+
* surfaces as a clear error instead of corrupting the file.
|
|
831
|
+
*/
|
|
832
|
+
async #resolveConflict(
|
|
833
|
+
entry: ConflictEntry,
|
|
834
|
+
replacementContent: string,
|
|
835
|
+
stripped: boolean,
|
|
836
|
+
signal: AbortSignal | undefined,
|
|
837
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
838
|
+
const absolutePath = entry.absolutePath;
|
|
839
|
+
if (!(await fs.exists(absolutePath))) {
|
|
840
|
+
throw new ToolError(`Conflict #${entry.id} target '${entry.displayPath}' no longer exists.`);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const expanded = expandContentTokens(replacementContent, entry);
|
|
844
|
+
const originalText = await Bun.file(absolutePath).text();
|
|
845
|
+
const splice = spliceConflict(originalText, entry, expanded);
|
|
846
|
+
const newContent = splice.text;
|
|
847
|
+
|
|
848
|
+
await writethroughNoop(absolutePath, newContent, signal);
|
|
849
|
+
invalidateFsScanAfterWrite(absolutePath);
|
|
850
|
+
this.session.bumpFileMutationVersion?.(absolutePath);
|
|
851
|
+
this.session.fileSnapshotStore?.invalidate(absolutePath);
|
|
852
|
+
const history = this.session.conflictHistory;
|
|
853
|
+
history?.invalidate(entry.id);
|
|
854
|
+
if (history) {
|
|
855
|
+
// Drop stale duplicate registrations of the same region: a re-read
|
|
856
|
+
// after an out-of-band shift registers a fresh id at the new
|
|
857
|
+
// startLine while the stale twin persists at the old one. A DISTINCT
|
|
858
|
+
// conflict block that is merely byte-identical still occurs in the
|
|
859
|
+
// post-splice content and must stay addressable.
|
|
860
|
+
for (const other of history.entries()) {
|
|
861
|
+
if (
|
|
862
|
+
other.absolutePath === absolutePath &&
|
|
863
|
+
conflictRegionsEqual(other, entry) &&
|
|
864
|
+
!conflictRegionPresent(newContent, other)
|
|
865
|
+
) {
|
|
866
|
+
history.invalidate(other.id);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const header = maybeWriteSnapshotHeader(this.session, absolutePath, newContent);
|
|
872
|
+
const range =
|
|
873
|
+
entry.startLine === entry.endLine
|
|
874
|
+
? `line ${entry.startLine}`
|
|
875
|
+
: `lines ${entry.startLine}\u2013${entry.endLine}`;
|
|
876
|
+
const summary = `Resolved conflict #${entry.id} at ${range} in ${entry.displayPath}.`;
|
|
877
|
+
let resultText = header ? `${header}\n${summary}` : summary;
|
|
878
|
+
if (stripped) {
|
|
879
|
+
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
880
|
+
}
|
|
881
|
+
const echoTrimmed = splice.trimmedLeading + splice.trimmedTrailing;
|
|
882
|
+
if (echoTrimmed > 0) {
|
|
883
|
+
resultText += `\nNote: dropped ${echoTrimmed} content line(s) that duplicated the code adjacent to the conflict region — writes replace only the marker block; surrounding lines stay in place.`;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
return {
|
|
887
|
+
content: [{ type: "text", text: resultText }],
|
|
888
|
+
details: { resolvedPath: absolutePath },
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Look up a single conflict entry by id and dispatch to {@link #resolveConflict}.
|
|
894
|
+
* Throws a clear `not found` error when the id has been invalidated.
|
|
895
|
+
*/
|
|
896
|
+
async #resolveSingleConflictById(
|
|
897
|
+
id: number,
|
|
898
|
+
replacementContent: string,
|
|
899
|
+
stripped: boolean,
|
|
900
|
+
signal: AbortSignal | undefined,
|
|
901
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
902
|
+
const entry = getConflictHistory(this.session).get(id);
|
|
903
|
+
if (!entry) {
|
|
904
|
+
throw new ToolError(
|
|
905
|
+
`Conflict #${id} not found. Conflict ids are registered when \`read\` surfaces a marker block; re-read the file to get a current id.`,
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
return this.#resolveConflict(entry, replacementContent, stripped, signal);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Bulk-resolve every registered conflict via `conflict://*`.
|
|
913
|
+
*
|
|
914
|
+
* Entries are grouped by file and applied bottom-up by recorded start
|
|
915
|
+
* line so each splice keeps later anchors valid. `content` tokens are
|
|
916
|
+
* expanded *per entry*, so `content: "@ours"` keeps each block's own
|
|
917
|
+
* ours side rather than collapsing every conflict to the first
|
|
918
|
+
* block's ours.
|
|
919
|
+
*
|
|
920
|
+
* All-or-nothing semantics within a file: if any splice for a file
|
|
921
|
+
* fails (stale anchors, missing base for `@base`, etc.), that file is
|
|
922
|
+
* left untouched and the error is surfaced. Files that succeed are
|
|
923
|
+
* still written. The result text reports per-file counts so the agent
|
|
924
|
+
* can re-read the failed files and retry.
|
|
925
|
+
*/
|
|
926
|
+
async #resolveAllConflicts(
|
|
927
|
+
replacementContent: string,
|
|
928
|
+
stripped: boolean,
|
|
929
|
+
signal: AbortSignal | undefined,
|
|
930
|
+
rawContent: string = replacementContent,
|
|
931
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
932
|
+
const history = getConflictHistory(this.session);
|
|
933
|
+
const allEntries = history.entries();
|
|
934
|
+
if (allEntries.length === 0) {
|
|
935
|
+
throw new ToolError(
|
|
936
|
+
"`conflict://*` has nothing to resolve — no conflicts are currently registered. Re-read the file(s) with conflicts first.",
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// Per-id directive mode: content made solely of `<id>: @side` lines
|
|
941
|
+
// resolves each listed conflict with that side in one call. Ideal for
|
|
942
|
+
// merge-hell files where dozens of pick-one blocks each need their own
|
|
943
|
+
// winner — one call instead of one write per conflict. Parsed from the
|
|
944
|
+
// PRE-strip content: hashline prefix stripping would otherwise eat the
|
|
945
|
+
// `<id>: ` heads as echoed line numbers.
|
|
946
|
+
const directives = resolveBulkDirectives(rawContent, replacementContent);
|
|
947
|
+
if (directives) {
|
|
948
|
+
const known = new Set(allEntries.map(entry => entry.id));
|
|
949
|
+
const unknown = [...directives.keys()].filter(id => !known.has(id));
|
|
950
|
+
if (unknown.length > 0) {
|
|
951
|
+
throw new ToolError(
|
|
952
|
+
`Bulk directive references unknown conflict id(s) ${unknown.map(id => `#${id}`).join(", ")}. Currently registered: ${allEntries.map(e => `#${e.id}`).join(", ")}.`,
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const selectedEntries = directives ? allEntries.filter(entry => directives.has(entry.id)) : allEntries;
|
|
957
|
+
const contentFor = (entry: ConflictEntry): string =>
|
|
958
|
+
directives ? (directives.get(entry.id) as string) : replacementContent;
|
|
959
|
+
|
|
960
|
+
const byFile = new Map<string, ConflictEntry[]>();
|
|
961
|
+
for (const entry of selectedEntries) {
|
|
962
|
+
const bucket = byFile.get(entry.absolutePath) ?? [];
|
|
963
|
+
bucket.push(entry);
|
|
964
|
+
byFile.set(entry.absolutePath, bucket);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const succeededFiles: { displayPath: string; count: number; header?: string }[] = [];
|
|
968
|
+
const failedFiles: { displayPath: string; count: number; error: string }[] = [];
|
|
969
|
+
let totalResolvedIds = 0;
|
|
970
|
+
let totalEchoTrimmed = 0;
|
|
971
|
+
|
|
972
|
+
for (const [absolutePath, fileEntries] of byFile) {
|
|
973
|
+
const sample = fileEntries[0]!;
|
|
974
|
+
if (!(await fs.exists(absolutePath))) {
|
|
975
|
+
failedFiles.push({
|
|
976
|
+
displayPath: sample.displayPath,
|
|
977
|
+
count: fileEntries.length,
|
|
978
|
+
error: "file no longer exists",
|
|
979
|
+
});
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
fileEntries.sort((a, b) => b.startLine - a.startLine);
|
|
984
|
+
|
|
985
|
+
let text: string;
|
|
986
|
+
const resolvedEntries: ConflictEntry[] = [];
|
|
987
|
+
const staleEntries: ConflictEntry[] = [];
|
|
988
|
+
let failure: string | undefined;
|
|
989
|
+
try {
|
|
990
|
+
text = await Bun.file(absolutePath).text();
|
|
991
|
+
} catch (error) {
|
|
992
|
+
failedFiles.push({
|
|
993
|
+
displayPath: sample.displayPath,
|
|
994
|
+
count: fileEntries.length,
|
|
995
|
+
error: error instanceof Error ? error.message : String(error),
|
|
996
|
+
});
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
for (const entry of fileEntries) {
|
|
1000
|
+
try {
|
|
1001
|
+
const expanded = expandContentTokens(contentFor(entry), entry);
|
|
1002
|
+
const splice = spliceConflict(text, entry, expanded);
|
|
1003
|
+
text = splice.text;
|
|
1004
|
+
totalEchoTrimmed += splice.trimmedLeading + splice.trimmedTrailing;
|
|
1005
|
+
resolvedEntries.push(entry);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
// A locate-miss for a region an earlier entry already spliced
|
|
1008
|
+
// in this pass is a stale duplicate registration (re-read after
|
|
1009
|
+
// an out-of-band shift) — treat it as already resolved.
|
|
1010
|
+
if (resolvedEntries.some(done => conflictRegionsEqual(done, entry))) {
|
|
1011
|
+
staleEntries.push(entry);
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (failure !== undefined) {
|
|
1019
|
+
failedFiles.push({
|
|
1020
|
+
displayPath: sample.displayPath,
|
|
1021
|
+
count: fileEntries.length,
|
|
1022
|
+
error: failure,
|
|
1023
|
+
});
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
await writethroughNoop(absolutePath, text, signal);
|
|
1028
|
+
invalidateFsScanAfterWrite(absolutePath);
|
|
1029
|
+
this.session.bumpFileMutationVersion?.(absolutePath);
|
|
1030
|
+
this.session.fileSnapshotStore?.invalidate(absolutePath);
|
|
1031
|
+
for (const entry of resolvedEntries) history.invalidate(entry.id);
|
|
1032
|
+
for (const entry of staleEntries) history.invalidate(entry.id);
|
|
1033
|
+
const header = maybeWriteSnapshotHeader(this.session, absolutePath, text);
|
|
1034
|
+
succeededFiles.push({ displayPath: sample.displayPath, count: resolvedEntries.length, header });
|
|
1035
|
+
totalResolvedIds += resolvedEntries.length;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const summaryLines: string[] = [];
|
|
1039
|
+
const fileWord = (n: number) => (n === 1 ? "file" : "files");
|
|
1040
|
+
const conflictWord = (n: number) => (n === 1 ? "conflict" : "conflicts");
|
|
1041
|
+
if (succeededFiles.length > 0) {
|
|
1042
|
+
summaryLines.push(
|
|
1043
|
+
`Resolved ${totalResolvedIds} ${conflictWord(totalResolvedIds)} across ${succeededFiles.length} ${fileWord(succeededFiles.length)}:`,
|
|
1044
|
+
);
|
|
1045
|
+
for (const file of succeededFiles) {
|
|
1046
|
+
summaryLines.push(` ${file.displayPath}: ${file.count} ${conflictWord(file.count)}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (directives && selectedEntries.length < allEntries.length) {
|
|
1050
|
+
const remaining = allEntries.filter(entry => !directives.has(entry.id)).map(entry => `#${entry.id}`);
|
|
1051
|
+
summaryLines.push(
|
|
1052
|
+
`Directive mode: ${remaining.length} unlisted ${conflictWord(remaining.length)} still registered (${remaining.join(", ")}).`,
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
if (totalEchoTrimmed > 0) {
|
|
1056
|
+
summaryLines.push(
|
|
1057
|
+
`Note: dropped ${totalEchoTrimmed} content line(s) that duplicated code adjacent to conflict regions — writes replace only the marker block; surrounding lines stay in place.`,
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
if (failedFiles.length > 0) {
|
|
1061
|
+
summaryLines.push(
|
|
1062
|
+
`Failed to resolve ${failedFiles.length} ${fileWord(failedFiles.length)} — registered entries left intact for retry:`,
|
|
1063
|
+
);
|
|
1064
|
+
for (const file of failedFiles) {
|
|
1065
|
+
summaryLines.push(` ${file.displayPath}: ${file.count} ${conflictWord(file.count)} (${file.error})`);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const headerLines = succeededFiles
|
|
1069
|
+
.map(file => file.header)
|
|
1070
|
+
.filter((header): header is string => header !== undefined);
|
|
1071
|
+
if (headerLines.length > 0) {
|
|
1072
|
+
summaryLines.push("Snapshots:");
|
|
1073
|
+
for (const header of headerLines) summaryLines.push(` ${header}`);
|
|
1074
|
+
}
|
|
1075
|
+
if (stripped && !directives) {
|
|
1076
|
+
summaryLines.push("Note: auto-stripped hashline display prefixes from content before writing.");
|
|
1077
|
+
}
|
|
1078
|
+
const resultText = summaryLines.join("\n");
|
|
1079
|
+
|
|
1080
|
+
if (failedFiles.length > 0 && succeededFiles.length === 0) {
|
|
1081
|
+
throw new ToolError(resultText);
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
content: [{ type: "text", text: resultText }],
|
|
1085
|
+
details: {},
|
|
1086
|
+
isError: failedFiles.length > 0 ? true : undefined,
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
async execute(
|
|
1091
|
+
_toolCallId: string,
|
|
1092
|
+
{ path: rawPath, content }: WriteParams,
|
|
1093
|
+
signal?: AbortSignal,
|
|
1094
|
+
onUpdate?: AgentToolUpdateCallback<WriteToolDetails>,
|
|
1095
|
+
context?: AgentToolContext,
|
|
1096
|
+
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
1097
|
+
// Strip a hashline `[path#TAG]` wrapper up front so every downstream
|
|
1098
|
+
// decision (scheme routing, internal-URL handler dispatch, plan-mode
|
|
1099
|
+
// guard, plan path resolution, ACP bridge routing) sees the same
|
|
1100
|
+
// filesystem target. Without this, a model that pastes a `read`
|
|
1101
|
+
// header as the `path` arg would slip past `isInternalUrlPath`
|
|
1102
|
+
// (which fails on a leading `[`) and the bridge router would send a
|
|
1103
|
+
// `[local://scratch.md#ABCD]` write to the editor instead of the
|
|
1104
|
+
// session-local sandbox.
|
|
1105
|
+
// Peel a read-tool selector (`:raw`, `:1-20`, …) so the write target matches
|
|
1106
|
+
// what `read` resolves for the same URL; line-range/malformed selectors throw.
|
|
1107
|
+
const path = peelWriteUrlSelector(unwrapHashlineHeaderPath(rawPath));
|
|
1108
|
+
return untilAborted(signal, async () => {
|
|
1109
|
+
// Strip hashline display prefixes ([PATH#HASH] + LINE:) if the model copied them from read output
|
|
1110
|
+
const { text: cleanContent, stripped } = stripWriteContent(this.session, content);
|
|
1111
|
+
const internalRouter = InternalUrlRouter.instance();
|
|
1112
|
+
assertWriteTargetAddressable(path, internalRouter);
|
|
1113
|
+
if (internalRouter.canHandle(path)) {
|
|
1114
|
+
const parsed = parseInternalUrl(path);
|
|
1115
|
+
const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
|
|
1116
|
+
const handler = internalRouter.getHandler(scheme);
|
|
1117
|
+
if (handler?.write) {
|
|
1118
|
+
// Handler-owned writes mutate user data outside the local
|
|
1119
|
+
// sandbox. xd:// dispatches retain each wrapped tool's tier.
|
|
1120
|
+
if (scheme !== "xd") {
|
|
1121
|
+
enforcePlanModeWrite(this.session, path, { op: "update" });
|
|
1122
|
+
emitWriteProgress(onUpdate, cleanContent, path);
|
|
1123
|
+
}
|
|
1124
|
+
let xdResult: AgentToolResult<WriteToolDetails> | undefined;
|
|
1125
|
+
await internalRouter.write(path, cleanContent, {
|
|
1126
|
+
cwd: this.session.cwd,
|
|
1127
|
+
signal,
|
|
1128
|
+
xd: {
|
|
1129
|
+
write: async (name, deviceContent) => {
|
|
1130
|
+
if (name === REPORT_ISSUE_DEVICE_NAME) {
|
|
1131
|
+
const { result, xdev } = await dispatchReportIssueDevice(this.session, deviceContent);
|
|
1132
|
+
xdResult = {
|
|
1133
|
+
content: result.content,
|
|
1134
|
+
details: { xdev },
|
|
1135
|
+
isError: result.isError,
|
|
1136
|
+
useless: result.useless,
|
|
1137
|
+
};
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
if (name && isResolutionDeviceName(name)) {
|
|
1141
|
+
const { result, xdev } = await dispatchResolutionDevice(this.session, name, deviceContent);
|
|
1142
|
+
xdResult = {
|
|
1143
|
+
content: result.content,
|
|
1144
|
+
details: { xdev },
|
|
1145
|
+
isError: result.isError,
|
|
1146
|
+
useless: result.useless,
|
|
1147
|
+
};
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const xdev = this.session.xdev;
|
|
1151
|
+
if (!xdev) {
|
|
1152
|
+
throw new ToolError("xd:// is not mounted in this session.");
|
|
1153
|
+
}
|
|
1154
|
+
if (!name) {
|
|
1155
|
+
throw new ToolError(`Cannot write to xd:// itself — pick a device:\n${xdevListing(xdev)}`);
|
|
1156
|
+
}
|
|
1157
|
+
const { result, xdev: dispatch } = await dispatchXdevTool(
|
|
1158
|
+
xdev,
|
|
1159
|
+
name,
|
|
1160
|
+
deviceContent,
|
|
1161
|
+
_toolCallId,
|
|
1162
|
+
signal,
|
|
1163
|
+
onUpdate as AgentToolUpdateCallback,
|
|
1164
|
+
// The write tool's own gate just resolved approval at this
|
|
1165
|
+
// device's tier (see #approval above) — mark it so a wrapped
|
|
1166
|
+
// inner tool does not prompt a second time.
|
|
1167
|
+
context ? { ...context, xdevApproved: true } : undefined,
|
|
1168
|
+
);
|
|
1169
|
+
xdResult = {
|
|
1170
|
+
content: result.content,
|
|
1171
|
+
details: { xdev: dispatch },
|
|
1172
|
+
isError: result.isError,
|
|
1173
|
+
useless: result.useless,
|
|
1174
|
+
};
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
});
|
|
1178
|
+
if (xdResult) return xdResult;
|
|
1179
|
+
let resultText = `Successfully wrote ${cleanContent.length} bytes to ${path}`;
|
|
1180
|
+
if (stripped) {
|
|
1181
|
+
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
1182
|
+
}
|
|
1183
|
+
return { content: [{ type: "text", text: resultText }], details: {} };
|
|
1184
|
+
}
|
|
1185
|
+
if (scheme !== "local") await internalRouter.write(path, cleanContent);
|
|
1186
|
+
// local:// is backed by the session-local artifact sandbox and is
|
|
1187
|
+
// resolved by resolvePlanPath below so write/read share the same root.
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const conflictUri = parseConflictUri(path);
|
|
1191
|
+
if (conflictUri) {
|
|
1192
|
+
if (conflictUri.scope) {
|
|
1193
|
+
throw new ToolError(
|
|
1194
|
+
`Conflict URI scope '/${conflictUri.scope}' is read-only — read \`conflict://${conflictUri.id}/${conflictUri.scope}\` to inspect that side. To write, drop the scope (\`conflict://${conflictUri.id}\`) and put the chosen content (or shorthand like \`@${conflictUri.scope}\`) in \`content\`.`,
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
emitWriteProgress(onUpdate, cleanContent, path);
|
|
1198
|
+
const result =
|
|
1199
|
+
conflictUri.id === "*"
|
|
1200
|
+
? await this.#resolveAllConflicts(cleanContent, stripped, signal, content)
|
|
1201
|
+
: await this.#resolveSingleConflictById(conflictUri.id, cleanContent, stripped, signal);
|
|
1202
|
+
if (conflictUri.recoveredPrefix !== undefined) {
|
|
1203
|
+
appendNoteToResult(
|
|
1204
|
+
result,
|
|
1205
|
+
`Note: stripped erroneous '${conflictUri.recoveredPrefix}:' prefix from path; conflict URIs are global (use \`conflict://${conflictUri.id}\`, not \`<file>:conflict://${conflictUri.id}\`).`,
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
return result;
|
|
1209
|
+
}
|
|
1210
|
+
const resolvedArchivePath = await this.#resolveArchiveWritePath(path);
|
|
1211
|
+
if (resolvedArchivePath) {
|
|
1212
|
+
enforcePlanModeWrite(this.session, resolvedArchivePath.archivePath, {
|
|
1213
|
+
op: resolvedArchivePath.exists ? "update" : "create",
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
emitWriteProgress(
|
|
1217
|
+
onUpdate,
|
|
1218
|
+
cleanContent,
|
|
1219
|
+
`${formatPathRelativeToCwd(resolvedArchivePath.absolutePath, this.session.cwd)}:${
|
|
1220
|
+
resolvedArchivePath.archiveSubPath
|
|
1221
|
+
}`,
|
|
1222
|
+
resolvedArchivePath.absolutePath,
|
|
1223
|
+
);
|
|
1224
|
+
const archiveResult = await this.#writeArchiveEntry(cleanContent, resolvedArchivePath);
|
|
1225
|
+
if (stripped) {
|
|
1226
|
+
const firstText = archiveResult.content.find(
|
|
1227
|
+
(block): block is { type: "text"; text: string } =>
|
|
1228
|
+
block.type === "text" && typeof block.text === "string",
|
|
1229
|
+
);
|
|
1230
|
+
if (firstText) {
|
|
1231
|
+
firstText.text += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return archiveResult;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const resolvedSqlitePath = await this.#resolveSqliteWritePath(path);
|
|
1238
|
+
if (resolvedSqlitePath) {
|
|
1239
|
+
enforcePlanModeWrite(this.session, resolvedSqlitePath.sqlitePath, { op: "update" });
|
|
1240
|
+
|
|
1241
|
+
emitWriteProgress(onUpdate, cleanContent, path, resolvedSqlitePath.absolutePath);
|
|
1242
|
+
const sqliteResult = await this.#writeSqliteRow(path, cleanContent, resolvedSqlitePath);
|
|
1243
|
+
if (stripped) {
|
|
1244
|
+
const firstText = sqliteResult.content.find(
|
|
1245
|
+
(block): block is { type: "text"; text: string } =>
|
|
1246
|
+
block.type === "text" && typeof block.text === "string",
|
|
1247
|
+
);
|
|
1248
|
+
if (firstText) {
|
|
1249
|
+
firstText.text += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return sqliteResult;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
await assertNotReadSelectorMisfire(path, cleanContent, this.session.cwd);
|
|
1256
|
+
enforcePlanModeWrite(this.session, path, { op: "create" });
|
|
1257
|
+
const absolutePath = resolvePlanPath(this.session, path);
|
|
1258
|
+
const batchRequest = getLspBatchRequest(context?.toolCall);
|
|
1259
|
+
|
|
1260
|
+
// Check if file exists and is auto-generated before overwriting
|
|
1261
|
+
if (await fs.exists(absolutePath)) {
|
|
1262
|
+
await assertEditableFile(absolutePath, path, this.session.settings);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const displayPath = formatPathRelativeToCwd(absolutePath, this.session.cwd);
|
|
1266
|
+
emitWriteProgress(onUpdate, cleanContent, displayPath, absolutePath);
|
|
1267
|
+
|
|
1268
|
+
// Try ACP bridge first for editor-visible filesystem paths. Internal
|
|
1269
|
+
// artifacts such as local:// plans are owned by OMP, not the editor.
|
|
1270
|
+
const bridgeWrite = await routeWriteThroughBridge(this.session, path, absolutePath, cleanContent, signal);
|
|
1271
|
+
if (bridgeWrite) {
|
|
1272
|
+
// `write` always replaces the whole file, so (unlike hashline's
|
|
1273
|
+
// hunk-scoped diff) there's no size cost to keying the header/
|
|
1274
|
+
// executable-bit check on the verified post-write content —
|
|
1275
|
+
// use it so a drifted write (e.g. client format-on-save) still
|
|
1276
|
+
// hands back a tag that matches what's actually on disk.
|
|
1277
|
+
const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, bridgeWrite.text);
|
|
1278
|
+
const header = maybeWriteSnapshotHeader(this.session, absolutePath, bridgeWrite.text);
|
|
1279
|
+
const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
|
|
1280
|
+
let resultText = header ? `${header}\n${writeLine}` : writeLine;
|
|
1281
|
+
if (stripped) {
|
|
1282
|
+
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
1283
|
+
}
|
|
1284
|
+
if (madeExecutable) {
|
|
1285
|
+
resultText += `\n${EXECUTABLE_NOTICE}`;
|
|
1286
|
+
}
|
|
1287
|
+
return {
|
|
1288
|
+
content: [{ type: "text", text: resultText }],
|
|
1289
|
+
details: { resolvedPath: absolutePath, madeExecutable: madeExecutable || undefined },
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
const diagnostics = await this.#writethrough(
|
|
1294
|
+
absolutePath,
|
|
1295
|
+
cleanContent,
|
|
1296
|
+
signal,
|
|
1297
|
+
undefined,
|
|
1298
|
+
batchRequest,
|
|
1299
|
+
dst => this.#deferredDiagnostics?.begin(dst),
|
|
1300
|
+
);
|
|
1301
|
+
invalidateFsScanAfterWrite(absolutePath);
|
|
1302
|
+
if (!this.#deferredDiagnostics || batchRequest?.flush === false) {
|
|
1303
|
+
this.session.bumpFileMutationVersion?.(absolutePath);
|
|
1304
|
+
}
|
|
1305
|
+
const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, cleanContent);
|
|
1306
|
+
|
|
1307
|
+
const header = maybeWriteSnapshotHeader(this.session, absolutePath, cleanContent);
|
|
1308
|
+
const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
|
|
1309
|
+
let resultText = header ? `${header}\n${writeLine}` : writeLine;
|
|
1310
|
+
if (stripped) {
|
|
1311
|
+
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
1312
|
+
}
|
|
1313
|
+
if (madeExecutable) {
|
|
1314
|
+
resultText += `\n${EXECUTABLE_NOTICE}`;
|
|
1315
|
+
}
|
|
1316
|
+
if (!diagnostics) {
|
|
1317
|
+
return {
|
|
1318
|
+
content: [{ type: "text", text: resultText }],
|
|
1319
|
+
details: { resolvedPath: absolutePath, madeExecutable: madeExecutable || undefined },
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
return {
|
|
1324
|
+
content: [{ type: "text", text: resultText }],
|
|
1325
|
+
details: {
|
|
1326
|
+
resolvedPath: absolutePath,
|
|
1327
|
+
diagnostics,
|
|
1328
|
+
madeExecutable: madeExecutable || undefined,
|
|
1329
|
+
meta: outputMeta()
|
|
1330
|
+
.diagnostics(diagnostics.summary, diagnostics.messages ?? [])
|
|
1331
|
+
.get(),
|
|
1332
|
+
},
|
|
1333
|
+
};
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// =============================================================================
|
|
1339
|
+
// TUI Renderer
|
|
1340
|
+
// =============================================================================
|
|
1341
|
+
|
|
1342
|
+
interface WriteRenderArgs {
|
|
1343
|
+
path?: unknown;
|
|
1344
|
+
file_path?: unknown;
|
|
1345
|
+
content?: unknown;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
const WRITE_PREVIEW_LINES = 6;
|
|
1349
|
+
const WRITE_STREAMING_PREVIEW_LINES = 12;
|
|
1350
|
+
|
|
1351
|
+
function countLines(text: string): number {
|
|
1352
|
+
if (!text) return 0;
|
|
1353
|
+
return text.split("\n").length;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/** Bounded newline scan: whether `text` spans more than `maxLines` lines.
|
|
1357
|
+
* Runs on every live compose (the repaint predicate below), so it must not
|
|
1358
|
+
* materialize the split the way `countLines` does. */
|
|
1359
|
+
function exceedsLineCount(text: string, maxLines: number): boolean {
|
|
1360
|
+
if (!text) return false;
|
|
1361
|
+
let lines = 1;
|
|
1362
|
+
for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1)) {
|
|
1363
|
+
if (++lines > maxLines) return true;
|
|
1364
|
+
}
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
function writeContentOf(args: unknown): string {
|
|
1369
|
+
if (args == null || typeof args !== "object" || !("content" in args)) return "";
|
|
1370
|
+
const content = args.content;
|
|
1371
|
+
return typeof content === "string" ? content : "";
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function formatLineCountSuffix(lineCount: number, uiTheme: Theme): string {
|
|
1375
|
+
if (lineCount <= 0) return "";
|
|
1376
|
+
return uiTheme.fg("dim", ` · ${lineCount} line${lineCount === 1 ? "" : "s"}`);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function normalizeDisplayText(text: unknown): string {
|
|
1380
|
+
let displayText = "";
|
|
1381
|
+
if (typeof text === "string") {
|
|
1382
|
+
displayText = text;
|
|
1383
|
+
} else if (text !== undefined && text !== null) {
|
|
1384
|
+
displayText = String(text);
|
|
1385
|
+
}
|
|
1386
|
+
return displayText.replace(/\r/g, "");
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Minimum line-number gutter width for write previews. The streaming preview's
|
|
1391
|
+
* gutter must stay byte-stable as the line count grows: a width derived purely
|
|
1392
|
+
* from `String(totalLines).length` widens at the 10/100/1000-line crossings,
|
|
1393
|
+
* rewriting every already-rendered row — which forces the transcript's commit
|
|
1394
|
+
* audit to recommit the block's committed prefix (a full duplicate in native
|
|
1395
|
+
* scrollback). Reserving 3 digits keeps the gutter constant through 999 lines
|
|
1396
|
+
* and keeps the streamed rows byte-identical to the final result render.
|
|
1397
|
+
*/
|
|
1398
|
+
const WRITE_GUTTER_MIN_WIDTH = 3;
|
|
1399
|
+
|
|
1400
|
+
function formatStreamingContent(
|
|
1401
|
+
content: string,
|
|
1402
|
+
expanded: boolean,
|
|
1403
|
+
language: string | undefined,
|
|
1404
|
+
uiTheme: Theme,
|
|
1405
|
+
spinnerFrame?: number,
|
|
1406
|
+
cache?: RenderedStringCache,
|
|
1407
|
+
): string {
|
|
1408
|
+
if (!content) return "";
|
|
1409
|
+
const bodyText = cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
|
|
1410
|
+
const lines = normalizeDisplayText(content).split("\n");
|
|
1411
|
+
const totalLines = lines.length;
|
|
1412
|
+
// Collapsed: follow the streaming edge with a bounded tail window so the box
|
|
1413
|
+
// stays short enough not to strand its scrolled-off head above the viewport
|
|
1414
|
+
// while the block is volatile. `Ctrl+O` (expanded) lifts the cap for a
|
|
1415
|
+
// deliberate full view — matching the eval streaming preview.
|
|
1416
|
+
const startIndex = expanded ? 0 : Math.max(0, totalLines - WRITE_STREAMING_PREVIEW_LINES);
|
|
1417
|
+
const visibleLines = lines.slice(startIndex);
|
|
1418
|
+
const hidden = startIndex;
|
|
1419
|
+
const highlighted = highlightCode(visibleLines.join("\n"), language);
|
|
1420
|
+
const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
|
|
1421
|
+
|
|
1422
|
+
let text = "\n\n";
|
|
1423
|
+
if (hidden > 0) {
|
|
1424
|
+
text += `${uiTheme.fg("dim", `… (${hidden} earlier line${hidden === 1 ? "" : "s"})`)}\n`;
|
|
1425
|
+
}
|
|
1426
|
+
for (let i = 0; i < highlighted.length; i++) {
|
|
1427
|
+
const lineNum = startIndex + i + 1;
|
|
1428
|
+
const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
|
|
1429
|
+
const body = replaceTabs(highlighted[i] ?? "");
|
|
1430
|
+
text += `${gutter}${body}\n`;
|
|
1431
|
+
}
|
|
1432
|
+
return text;
|
|
1433
|
+
});
|
|
1434
|
+
// The animated glyph lives on this trailing line — inside the transcript's
|
|
1435
|
+
// volatile-tail holdback — never in the header: an animating head row pins
|
|
1436
|
+
// the native-scrollback commit boundary at the top of the block, so a long
|
|
1437
|
+
// expanded preview could never scroll-append mid-stream.
|
|
1438
|
+
const spinner = spinnerFrame !== undefined ? `${formatStatusIcon("running", uiTheme, spinnerFrame)} ` : "";
|
|
1439
|
+
return `${bodyText}${spinner}${uiTheme.fg("dim", `… (streaming)`)}`;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function renderContentPreview(
|
|
1443
|
+
content: string,
|
|
1444
|
+
expanded: boolean,
|
|
1445
|
+
language: string | undefined,
|
|
1446
|
+
uiTheme: Theme,
|
|
1447
|
+
cache?: RenderedStringCache,
|
|
1448
|
+
): string {
|
|
1449
|
+
if (!content) return "";
|
|
1450
|
+
return cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
|
|
1451
|
+
const rawLines = normalizeDisplayText(content).split("\n");
|
|
1452
|
+
const totalLines = rawLines.length;
|
|
1453
|
+
const maxLines = expanded ? totalLines : Math.min(totalLines, WRITE_PREVIEW_LINES);
|
|
1454
|
+
const visibleLines = rawLines.slice(0, maxLines);
|
|
1455
|
+
const highlighted = highlightCode(visibleLines.join("\n"), language);
|
|
1456
|
+
const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
|
|
1457
|
+
const hidden = totalLines - maxLines;
|
|
1458
|
+
|
|
1459
|
+
let text = "\n\n";
|
|
1460
|
+
for (let i = 0; i < highlighted.length; i++) {
|
|
1461
|
+
const lineNum = i + 1;
|
|
1462
|
+
const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
|
|
1463
|
+
const body = replaceTabs(highlighted[i] ?? "");
|
|
1464
|
+
text += `${gutter}${body}\n`;
|
|
1465
|
+
}
|
|
1466
|
+
if (!expanded && hidden > 0) {
|
|
1467
|
+
const hint = formatExpandHint(uiTheme, expanded, hidden > 0);
|
|
1468
|
+
const moreLine = `${formatMoreItems(hidden, "line")}${hint ? ` ${hint}` : ""}`;
|
|
1469
|
+
text += uiTheme.fg("dim", moreLine);
|
|
1470
|
+
}
|
|
1471
|
+
return text.trimEnd();
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
/** Render context for the write tool: resolves an `xd://`-mounted tool so its live renderer drives device dispatch previews. */
|
|
1476
|
+
export interface WriteRenderContext {
|
|
1477
|
+
resolveXdevMounted?: (name: string) => AgentTool | undefined;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
export const writeToolRenderer = {
|
|
1481
|
+
renderCall(
|
|
1482
|
+
args: WriteRenderArgs,
|
|
1483
|
+
options: RenderResultOptions & { renderContext?: WriteRenderContext },
|
|
1484
|
+
uiTheme: Theme,
|
|
1485
|
+
): Component | undefined {
|
|
1486
|
+
const rawPath =
|
|
1487
|
+
typeof args.file_path === "string" ? args.file_path : typeof args.path === "string" ? args.path : "";
|
|
1488
|
+
// Render NOTHING until the streamed path arrives and provably is not an
|
|
1489
|
+
// xd:// device; xd:// writes then delegate to the mounted tool's renderer.
|
|
1490
|
+
// A present-but-malformed path (array/object from a bad provider parse)
|
|
1491
|
+
// is definitively not xd:// — fall through to the legacy frame.
|
|
1492
|
+
if (args.path === undefined && args.file_path === undefined) return undefined;
|
|
1493
|
+
if (rawPath && couldBecomeXdUrl(rawPath)) {
|
|
1494
|
+
const xdev = parseXdUrl(rawPath);
|
|
1495
|
+
// The path string is settled once the content field started streaming.
|
|
1496
|
+
const pathSettled = args.content !== undefined;
|
|
1497
|
+
if (!xdev?.name || !pathSettled) return undefined;
|
|
1498
|
+
if (isResolutionDeviceName(xdev.name)) return renderResolutionDeviceCall(xdev.name, args.content, uiTheme);
|
|
1499
|
+
if (xdev.name === REPORT_ISSUE_DEVICE_NAME) return renderReportIssueDeviceCall(args.content, uiTheme);
|
|
1500
|
+
return renderXdevCall(xdev.name, args.content, options, uiTheme, options.renderContext?.resolveXdevMounted);
|
|
1501
|
+
}
|
|
1502
|
+
const filePath = shortenPath(rawPath);
|
|
1503
|
+
const lang = rawPath ? (getLanguageFromPath(rawPath) ?? "text") : "text";
|
|
1504
|
+
const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
1505
|
+
const pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
|
|
1506
|
+
// No status icon on the head row: it's the head of the framed block, and
|
|
1507
|
+
// native-scrollback commits are prefix-only — an animated glyph would pin
|
|
1508
|
+
// the commit boundary at the top, and the pending hourglass just adds
|
|
1509
|
+
// noise. The liveness cue rides the trailing "(streaming)" line instead.
|
|
1510
|
+
const header = renderStatusLine(
|
|
1511
|
+
{
|
|
1512
|
+
title: "Write",
|
|
1513
|
+
description: `${langIcon} ${pathDisplay}`,
|
|
1514
|
+
},
|
|
1515
|
+
uiTheme,
|
|
1516
|
+
);
|
|
1517
|
+
const content = normalizeDisplayText(args.content);
|
|
1518
|
+
const streamingCache = createRenderedStringCache();
|
|
1519
|
+
return framedBlock(uiTheme, width => {
|
|
1520
|
+
const body = content
|
|
1521
|
+
? formatStreamingContent(
|
|
1522
|
+
content,
|
|
1523
|
+
Boolean(options?.expanded),
|
|
1524
|
+
lang,
|
|
1525
|
+
uiTheme,
|
|
1526
|
+
options?.spinnerFrame,
|
|
1527
|
+
streamingCache,
|
|
1528
|
+
)
|
|
1529
|
+
: "";
|
|
1530
|
+
const bodyLines = body ? body.split("\n") : [];
|
|
1531
|
+
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
1532
|
+
return {
|
|
1533
|
+
header,
|
|
1534
|
+
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
1535
|
+
state: "pending",
|
|
1536
|
+
borderColor: "borderMuted",
|
|
1537
|
+
width,
|
|
1538
|
+
};
|
|
1539
|
+
});
|
|
1540
|
+
},
|
|
1541
|
+
|
|
1542
|
+
renderResult(
|
|
1543
|
+
result: { content: Array<{ type: string; text?: string }>; details?: WriteToolDetails; isError?: boolean },
|
|
1544
|
+
options: RenderResultOptions & { renderContext?: WriteRenderContext },
|
|
1545
|
+
uiTheme: Theme,
|
|
1546
|
+
args?: WriteRenderArgs,
|
|
1547
|
+
): Component {
|
|
1548
|
+
// xd:// dispatch results render as the mounted tool's own result.
|
|
1549
|
+
const xdev = result.details?.xdev;
|
|
1550
|
+
if (xdev) {
|
|
1551
|
+
const delegated = renderXdevResult(xdev, result, options, uiTheme, options.renderContext?.resolveXdevMounted);
|
|
1552
|
+
if (delegated) return delegated;
|
|
1553
|
+
const text = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1554
|
+
return new Text(uiTheme.fg("toolOutput", replaceTabs(text)), 0, 0);
|
|
1555
|
+
}
|
|
1556
|
+
const rawPath =
|
|
1557
|
+
typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
|
|
1558
|
+
const filePath = shortenPath(rawPath);
|
|
1559
|
+
const fileContent = normalizeDisplayText(args?.content);
|
|
1560
|
+
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
1561
|
+
const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
1562
|
+
// The header shows the cwd-relative path but links to the absolute path the
|
|
1563
|
+
// write resolved to (args.path may be relative, which would yield a broken
|
|
1564
|
+
// `file://` URI). Falls back to plain text when the result lacks a path.
|
|
1565
|
+
const linkTarget = result.details?.resolvedPath;
|
|
1566
|
+
const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
|
|
1567
|
+
const pathDisplay = filePath && linkTarget ? fileHyperlink(linkTarget, styledPath) : styledPath;
|
|
1568
|
+
|
|
1569
|
+
if (result.isError) {
|
|
1570
|
+
const errorText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1571
|
+
const header = renderStatusLine(
|
|
1572
|
+
{ icon: "error", title: "Write", description: `${langIcon} ${pathDisplay}` },
|
|
1573
|
+
uiTheme,
|
|
1574
|
+
);
|
|
1575
|
+
return framedBlock(uiTheme, width => ({
|
|
1576
|
+
header,
|
|
1577
|
+
sections: [{ lines: formatErrorDetail(errorText, uiTheme).split("\n") }],
|
|
1578
|
+
state: "error",
|
|
1579
|
+
borderColor: "error",
|
|
1580
|
+
width,
|
|
1581
|
+
}));
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
const isPartial = options.isPartial === true;
|
|
1585
|
+
const progressText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1586
|
+
const lineCount = countLines(fileContent);
|
|
1587
|
+
const lineSuffix = formatLineCountSuffix(lineCount, uiTheme);
|
|
1588
|
+
const execSuffix =
|
|
1589
|
+
!isPartial && result.details?.madeExecutable
|
|
1590
|
+
? `${uiTheme.fg("dim", " · ")}${uiTheme.fg("success", "made executable!")}`
|
|
1591
|
+
: "";
|
|
1592
|
+
const header = renderStatusLine(
|
|
1593
|
+
{
|
|
1594
|
+
icon: isPartial ? "running" : undefined,
|
|
1595
|
+
iconOverride: isPartial ? undefined : uiTheme.styledSymbol("tool.write", "accent"),
|
|
1596
|
+
spinnerFrame: options.spinnerFrame,
|
|
1597
|
+
title: "Write",
|
|
1598
|
+
description: `${langIcon} ${pathDisplay}${lineSuffix}${execSuffix}`,
|
|
1599
|
+
},
|
|
1600
|
+
uiTheme,
|
|
1601
|
+
);
|
|
1602
|
+
const diagnostics = result.details?.diagnostics;
|
|
1603
|
+
|
|
1604
|
+
const previewCache = createRenderedStringCache();
|
|
1605
|
+
return framedBlock(uiTheme, width => {
|
|
1606
|
+
const { expanded } = options;
|
|
1607
|
+
let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
|
|
1608
|
+
if (isPartial && progressText) {
|
|
1609
|
+
const safeProgressText = truncateToWidth(
|
|
1610
|
+
replaceTabs(progressText),
|
|
1611
|
+
TRUNCATE_LENGTHS.LINE,
|
|
1612
|
+
Ellipsis.Unicode,
|
|
1613
|
+
);
|
|
1614
|
+
body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
|
|
1615
|
+
}
|
|
1616
|
+
if (!isPartial && diagnostics) {
|
|
1617
|
+
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
|
1618
|
+
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
1619
|
+
);
|
|
1620
|
+
if (diagText.trim()) {
|
|
1621
|
+
const diagLines = diagText.split("\n");
|
|
1622
|
+
const firstNonEmpty = diagLines.findIndex(line => line.trim());
|
|
1623
|
+
if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
const bodyLines = body.split("\n");
|
|
1627
|
+
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
1628
|
+
return {
|
|
1629
|
+
header,
|
|
1630
|
+
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
1631
|
+
state: isPartial ? "pending" : "success",
|
|
1632
|
+
borderColor: "borderMuted",
|
|
1633
|
+
width,
|
|
1634
|
+
};
|
|
1635
|
+
});
|
|
1636
|
+
},
|
|
1637
|
+
mergeCallAndResult: true,
|
|
1638
|
+
// The collapsed pending preview follows the streaming edge with a tail
|
|
1639
|
+
// window once the content outgrows it (`… (N earlier lines)` + last rows);
|
|
1640
|
+
// the first partial result re-anchors the frame to the top of the file, so
|
|
1641
|
+
// tail rows already committed to viewport/native scrollback would survive
|
|
1642
|
+
// as stale content above the new frame without a full replay. Expanded and
|
|
1643
|
+
// short previews stay top-anchored and skip the (scrollback-wiping) reset.
|
|
1644
|
+
forceFirstResultViewportRepaint: (args: unknown, options: RenderResultOptions) =>
|
|
1645
|
+
!options.expanded && exceedsLineCount(writeContentOf(args), WRITE_STREAMING_PREVIEW_LINES),
|
|
1646
|
+
};
|