pi-agent-zh 17.1.8
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 +14459 -0
- package/README.md +35 -0
- package/dist/CHANGELOG-vt8ene9g.md +14459 -0
- package/dist/cli.js +22079 -0
- package/dist/template-dys3vk5b.js +1671 -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 +79 -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 +606 -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 +133 -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 +195 -0
- package/scripts/measure-prompt-tokens.ts +63 -0
- package/scripts/omp +42 -0
- package/scripts/omp.ts +19 -0
- package/src/advisor/advise-tool.ts +216 -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 +1232 -0
- package/src/advisor/transcript-recorder.ts +159 -0
- package/src/advisor/watchdog.ts +135 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +821 -0
- package/src/auto-thinking/classifier.ts +182 -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 +269 -0
- package/src/autoresearch/tools/log-experiment.ts +521 -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 +97 -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 +63 -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 +420 -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/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -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 +352 -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 +220 -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 +946 -0
- package/src/cli/grep-cli.ts +161 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/models-cli.ts +388 -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 +93 -0
- package/src/cli/setup-cli.ts +320 -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/tiny-models-cli.ts +153 -0
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli/update-cli.ts +1219 -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 +166 -0
- package/src/cli.ts +407 -0
- package/src/collab/crypto.ts +63 -0
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +731 -0
- package/src/collab/host.ts +691 -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 +33 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/auth-broker.ts +99 -0
- package/src/commands/auth-gateway.ts +69 -0
- package/src/commands/bench.ts +65 -0
- package/src/commands/cleanse.ts +45 -0
- package/src/commands/commit.ts +46 -0
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +60 -0
- package/src/commands/gc.ts +46 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +51 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/join.ts +39 -0
- package/src/commands/launch.ts +212 -0
- package/src/commands/models.ts +60 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +38 -0
- package/src/commands/say.ts +145 -0
- package/src/commands/setup.ts +67 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -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 +33 -0
- package/src/commands/usage.ts +54 -0
- package/src/commands/web-search.ts +42 -0
- package/src/commands/worktree.ts +62 -0
- package/src/commit/agentic/agent.ts +322 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +382 -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 +243 -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/file-lock.ts +164 -0
- package/src/config/inline-tool-descriptors-mode.ts +26 -0
- package/src/config/keybindings.ts +688 -0
- package/src/config/mcp-schema.json +247 -0
- package/src/config/model-discovery.ts +999 -0
- package/src/config/model-registry.ts +2725 -0
- package/src/config/model-resolver.ts +2068 -0
- package/src/config/model-roles.ts +113 -0
- package/src/config/models-config-schema-bundle.ts +313 -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 +94 -0
- package/src/config/service-tier.ts +141 -0
- package/src/config/settings-schema.ts +5751 -0
- package/src/config/settings.ts +2369 -0
- package/src/config.ts +242 -0
- package/src/cursor.ts +513 -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/agents-md.ts +67 -0
- package/src/discovery/agents.ts +230 -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 +55 -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 +935 -0
- package/src/discovery/claude-plugins.ts +624 -0
- package/src/discovery/claude.ts +588 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +540 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +383 -0
- package/src/discovery/github.ts +337 -0
- package/src/discovery/helpers.ts +1159 -0
- package/src/discovery/index.ts +81 -0
- package/src/discovery/mcp-json.ts +172 -0
- package/src/discovery/omp-extension-roots.ts +213 -0
- package/src/discovery/omp-plugins.ts +387 -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 +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +999 -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 +356 -0
- package/src/edit/hashline/execute.ts +280 -0
- package/src/edit/hashline/filesystem.ts +223 -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 +676 -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 +1137 -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 +698 -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 +62 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +211 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/executor-base.ts +504 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +6 -0
- package/src/eval/jl/executor.ts +540 -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 +701 -0
- package/src/eval/js/executor.ts +175 -0
- package/src/eval/js/index.ts +40 -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 +603 -0
- package/src/eval/py/index.ts +57 -0
- package/src/eval/py/kernel.ts +234 -0
- package/src/eval/py/prelude.py +672 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1388 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +139 -0
- package/src/eval/py/tool-bridge.ts +181 -0
- package/src/eval/rb/executor.ts +504 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +231 -0
- package/src/eval/rb/prelude.rb +552 -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 +313 -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 +1671 -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 +682 -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 +286 -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 +624 -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 +1209 -0
- package/src/extensibility/extensions/types.ts +1526 -0
- package/src/extensibility/extensions/wrapper.ts +377 -0
- package/src/extensibility/hooks/index.ts +6 -0
- package/src/extensibility/hooks/loader.ts +244 -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 +140 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1384 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +43 -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 +2532 -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 +926 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -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 +511 -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/typebox.ts +958 -0
- package/src/extensibility/utils.ts +184 -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 +550 -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 +27 -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 +470 -0
- package/src/internal-urls/mcp-protocol.ts +165 -0
- package/src/internal-urls/memory-protocol.ts +347 -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 +150 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/skill-protocol.ts +117 -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 +1134 -0
- package/src/launch/client.ts +350 -0
- package/src/launch/paths.ts +17 -0
- package/src/launch/presence.ts +82 -0
- package/src/launch/protocol.ts +396 -0
- package/src/launch/spawn-options.ts +17 -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 +214 -0
- package/src/live/voices.ts +18 -0
- package/src/lsp/client.ts +1429 -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 +2766 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +668 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +453 -0
- package/src/lsp/utils.ts +718 -0
- package/src/main.ts +1647 -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/mammoth.d.ts +24 -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 +381 -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 +1419 -0
- package/src/mcp/oauth-credentials.ts +104 -0
- package/src/mcp/oauth-discovery.ts +587 -0
- package/src/mcp/oauth-flow.ts +820 -0
- package/src/mcp/render.ts +214 -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/http.ts +523 -0
- package/src/mcp/transports/index.ts +7 -0
- package/src/mcp/transports/sse.ts +377 -0
- package/src/mcp/transports/stdio.ts +903 -0
- package/src/mcp/types.ts +437 -0
- package/src/memories/index.ts +1434 -0
- package/src/memories/storage.ts +578 -0
- package/src/memory-backend/index.ts +18 -0
- package/src/memory-backend/local-backend.ts +47 -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 +246 -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 +906 -0
- package/src/modes/acp/acp-agent.ts +2577 -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.ts +648 -0
- package/src/modes/components/agent-transcript-viewer.ts +649 -0
- package/src/modes/components/ask-dialog.ts +975 -0
- package/src/modes/components/assistant-message.ts +972 -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 +124 -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 +495 -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 +1027 -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 +321 -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 +276 -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 +65 -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 +872 -0
- package/src/modes/components/model-hub.ts +2011 -0
- package/src/modes/components/model-picker.ts +233 -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 +678 -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 +1019 -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 +1516 -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/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 +1356 -0
- package/src/modes/components/transcript-container.ts +527 -0
- package/src/modes/components/tree-selector.ts +1008 -0
- package/src/modes/components/ttsr-notification.ts +123 -0
- package/src/modes/components/usage-row.ts +47 -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 +198 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +1993 -0
- package/src/modes/controllers/event-controller.ts +1781 -0
- package/src/modes/controllers/extension-ui-controller.ts +1242 -0
- package/src/modes/controllers/input-controller.ts +2004 -0
- package/src/modes/controllers/live-command-controller.ts +260 -0
- package/src/modes/controllers/mcp-command-controller.ts +2403 -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 +1968 -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 +229 -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 +4957 -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 +265 -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 +235 -0
- package/src/modes/rpc/rpc-client.ts +1195 -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 +1504 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +533 -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 +218 -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 +3019 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +479 -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 +63 -0
- package/src/modes/utils/interactive-context-helpers.ts +30 -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 +963 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/modes/workflow.ts +49 -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/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/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 +12 -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 +283 -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 +25 -0
- package/src/prompts/tools/browser.md +28 -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 +15 -0
- package/src/prompts/tools/goal.md +11 -0
- package/src/prompts/tools/grep.md +12 -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 +29 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +14 -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 +455 -0
- package/src/registry/agent-registry.ts +217 -0
- package/src/registry/persisted-agents.ts +98 -0
- package/src/sdk.ts +3553 -0
- package/src/secrets/index.ts +246 -0
- package/src/secrets/obfuscator.ts +2574 -0
- package/src/secrets/regex.ts +21 -0
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +346 -0
- package/src/session/agent-session.ts +8406 -0
- package/src/session/agent-storage.ts +805 -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/client-bridge.ts +85 -0
- package/src/session/codex-auto-reset.ts +202 -0
- package/src/session/compact-modes.ts +105 -0
- package/src/session/eval-runner.ts +217 -0
- package/src/session/exit-diagnostics.ts +310 -0
- package/src/session/history-storage.ts +327 -0
- package/src/session/indexed-session-storage.ts +544 -0
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +1280 -0
- package/src/session/model-controls.ts +750 -0
- package/src/session/prewalk.ts +257 -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 +1754 -0
- package/src/session/session-context.ts +554 -0
- package/src/session/session-dump-format.ts +216 -0
- package/src/session/session-entries.ts +260 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +459 -0
- package/src/session/session-listing.ts +715 -0
- package/src/session/session-loader.ts +280 -0
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +2557 -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 +222 -0
- package/src/session/session-persistence.ts +293 -0
- package/src/session/session-provider-boundary.ts +306 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-storage.ts +734 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/session-tools.ts +1226 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/settings-stream-fn.ts +74 -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 +1728 -0
- package/src/session/unexpected-stop-classifier.ts +132 -0
- package/src/session/yield-queue.ts +183 -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 +2982 -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 +66 -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 +85 -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 +894 -0
- package/src/task/agents.ts +168 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +145 -0
- package/src/task/executor.ts +3129 -0
- package/src/task/index.ts +1519 -0
- package/src/task/isolation-ownership.ts +106 -0
- package/src/task/isolation-runner.ts +413 -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 +141 -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/render.ts +1814 -0
- package/src/task/renderer.ts +14 -0
- package/src/task/repair-args.ts +118 -0
- package/src/task/spawn-policy.ts +58 -0
- package/src/task/structured-subagent.ts +649 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +551 -0
- package/src/task/worktree.ts +967 -0
- package/src/task/yield-assembly.ts +198 -0
- package/src/telemetry-export.ts +500 -0
- package/src/thinking.ts +364 -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 +278 -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 +81 -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 +520 -0
- package/src/tools/auto-generated-guard.ts +335 -0
- package/src/tools/bash-interactive.ts +435 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +335 -0
- package/src/tools/bash.ts +1762 -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 +194 -0
- package/src/tools/browser/cmux/cmux-tab.ts +1400 -0
- package/src/tools/browser/cmux/rpc.ts +206 -0
- package/src/tools/browser/cmux/socket-client.ts +445 -0
- package/src/tools/browser/launch.ts +788 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +287 -0
- package/src/tools/browser/render.ts +226 -0
- package/src/tools/browser/run-cancellation.ts +131 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/tab-protocol.ts +115 -0
- package/src/tools/browser/tab-supervisor.ts +1040 -0
- package/src/tools/browser/tab-worker-entry.ts +29 -0
- package/src/tools/browser/tab-worker.ts +1842 -0
- package/src/tools/browser.ts +457 -0
- package/src/tools/builtin-names.ts +66 -0
- package/src/tools/checkpoint.ts +150 -0
- package/src/tools/computer/exposure.ts +38 -0
- package/src/tools/computer/protocol.ts +28 -0
- package/src/tools/computer/supervisor.ts +306 -0
- package/src/tools/computer/worker-entry.ts +34 -0
- package/src/tools/computer/worker.ts +131 -0
- package/src/tools/computer-renderer.ts +108 -0
- package/src/tools/computer.ts +516 -0
- package/src/tools/conflict-detect.ts +815 -0
- package/src/tools/context.ts +46 -0
- package/src/tools/debug.ts +1121 -0
- package/src/tools/default-renderer.ts +139 -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 +3959 -0
- package/src/tools/github-cache.ts +663 -0
- package/src/tools/glob.ts +682 -0
- package/src/tools/grep.ts +1858 -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 +579 -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 +691 -0
- package/src/tools/inspect-image-renderer.ts +133 -0
- package/src/tools/inspect-image.ts +290 -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 +845 -0
- package/src/tools/output-schema-validator.ts +307 -0
- package/src/tools/path-utils.ts +1381 -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 +3742 -0
- package/src/tools/render-utils.ts +924 -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/shell-tokenize.ts +83 -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 +37 -0
- package/src/tools/tts.ts +266 -0
- package/src/tools/vibe.ts +607 -0
- package/src/tools/write.ts +1640 -0
- package/src/tools/xdev.ts +508 -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 +232 -0
- package/src/utils/clipboard.ts +327 -0
- package/src/utils/command-args.ts +76 -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 +78 -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 +2425 -0
- package/src/utils/image-loading.ts +231 -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 +416 -0
- package/src/utils/lang-from-path.ts +251 -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 +316 -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 +63 -0
- package/src/utils/tools-manager.ts +411 -0
- package/src/utils/turndown.ts +83 -0
- package/src/utils/zip.ts +1106 -0
- package/src/vibe/runtime.ts +1540 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/kagi.ts +304 -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 +140 -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 +120 -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 +376 -0
- package/src/web/search/provider.ts +272 -0
- package/src/web/search/providers/anthropic.ts +401 -0
- package/src/web/search/providers/base.ts +110 -0
- package/src/web/search/providers/brave.ts +179 -0
- package/src/web/search/providers/browser-headers.ts +109 -0
- package/src/web/search/providers/browser-page.ts +123 -0
- package/src/web/search/providers/codex.ts +749 -0
- package/src/web/search/providers/duckduckgo.ts +213 -0
- package/src/web/search/providers/ecosia.ts +182 -0
- package/src/web/search/providers/exa.ts +511 -0
- package/src/web/search/providers/firecrawl.ts +211 -0
- package/src/web/search/providers/gemini.ts +628 -0
- package/src/web/search/providers/google.ts +194 -0
- package/src/web/search/providers/jina.ts +134 -0
- package/src/web/search/providers/kagi.ts +95 -0
- package/src/web/search/providers/kimi.ts +217 -0
- package/src/web/search/providers/mojeek.ts +219 -0
- package/src/web/search/providers/parallel.ts +182 -0
- package/src/web/search/providers/perplexity-auth.ts +142 -0
- package/src/web/search/providers/perplexity.ts +994 -0
- package/src/web/search/providers/public.ts +199 -0
- package/src/web/search/providers/searxng.ts +461 -0
- package/src/web/search/providers/startpage.ts +219 -0
- package/src/web/search/providers/synthetic.ts +120 -0
- package/src/web/search/providers/tavily.ts +242 -0
- package/src/web/search/providers/tinyfish.ts +164 -0
- package/src/web/search/providers/utils.ts +128 -0
- package/src/web/search/providers/xai.ts +372 -0
- package/src/web/search/providers/zai.ts +437 -0
- package/src/web/search/query.ts +850 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +507 -0
- package/src/web/search/utils.ts +17 -0
- package/src/workspace-tree.ts +326 -0
|
@@ -0,0 +1,2982 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
5
|
+
import { type AutocompleteItem, Spacer } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import { APP_NAME, getProjectDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { reset as resetCapabilities } from "../capability";
|
|
8
|
+
import { COLLAB_GUEST_ALLOWED_COMMANDS, CollabGuestLink } from "../collab/guest";
|
|
9
|
+
import { CollabHost } from "../collab/host";
|
|
10
|
+
import {
|
|
11
|
+
expandRoleAlias,
|
|
12
|
+
formatModelString,
|
|
13
|
+
getModelMatchPreferences,
|
|
14
|
+
resolveCliModel,
|
|
15
|
+
} from "../config/model-resolver";
|
|
16
|
+
import { applyProviderGlobalsFromSettings } from "../config/provider-globals";
|
|
17
|
+
import type { SettingPath, SettingValue } from "../config/settings";
|
|
18
|
+
import { settings } from "../config/settings";
|
|
19
|
+
import {
|
|
20
|
+
clearPluginRootsAndCaches,
|
|
21
|
+
resolveActiveProjectRegistryPath,
|
|
22
|
+
resolveOrDefaultProjectRegistryPath,
|
|
23
|
+
} from "../discovery/helpers.js";
|
|
24
|
+
import { parseExportArgs } from "../export/html/args";
|
|
25
|
+
import { shareSession } from "../export/share";
|
|
26
|
+
import { PluginManager } from "../extensibility/plugins";
|
|
27
|
+
import {
|
|
28
|
+
getInstalledPluginsRegistryPath,
|
|
29
|
+
getMarketplacesCacheDir,
|
|
30
|
+
getMarketplacesRegistryPath,
|
|
31
|
+
getPluginsCacheDir,
|
|
32
|
+
MarketplaceManager,
|
|
33
|
+
} from "../extensibility/plugins/marketplace";
|
|
34
|
+
import { resolveMemoryBackend } from "../memory-backend";
|
|
35
|
+
import { runPauseScreen } from "../modes/components/pause-screen";
|
|
36
|
+
import { describeLoopLimitRuntime } from "../modes/loop-limit";
|
|
37
|
+
import { theme } from "../modes/theme/theme";
|
|
38
|
+
import type { InteractiveModeContext } from "../modes/types";
|
|
39
|
+
import { extractLastCodeBlock, extractLastCommand } from "../modes/utils/copy-targets";
|
|
40
|
+
import type { AgentSession, FreshSessionResult } from "../session/agent-session";
|
|
41
|
+
import type { SessionOAuthAccountList } from "../session/agent-session-types";
|
|
42
|
+
import { COMPACT_MODES, parseCompactArgs } from "../session/compact-modes";
|
|
43
|
+
import { resolveResumableSession } from "../session/session-listing";
|
|
44
|
+
import { formatShakeSummary, type ShakeMode } from "../session/shake-types";
|
|
45
|
+
import type { ComputerTool } from "../tools/computer";
|
|
46
|
+
import { computerExposureMode } from "../tools/computer/exposure";
|
|
47
|
+
import { expandTilde, resolveToCwd } from "../tools/path-utils";
|
|
48
|
+
import { urlHyperlinkAlways } from "../tui";
|
|
49
|
+
import {
|
|
50
|
+
getChangelogPath,
|
|
51
|
+
parseChangelog,
|
|
52
|
+
RECENT_CHANGELOG_ENTRY_LIMIT,
|
|
53
|
+
renderChangelogEntries,
|
|
54
|
+
} from "../utils/changelog";
|
|
55
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
56
|
+
import type { InspectImageMode } from "../utils/inspect-image-mode";
|
|
57
|
+
import { CollabQrCodeComponent } from "./helpers/collab-qrcode";
|
|
58
|
+
import { buildContextReportText } from "./helpers/context-report";
|
|
59
|
+
import { formatDuration } from "./helpers/format";
|
|
60
|
+
import { createMarketplaceManager } from "./helpers/marketplace-manager";
|
|
61
|
+
import { handleMcpAcp } from "./helpers/mcp";
|
|
62
|
+
import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
|
|
63
|
+
import { describeRedeemOutcome, type ResetUsageAccount, toResetUsageAccounts } from "./helpers/reset-usage";
|
|
64
|
+
import { matchSessionPinAccounts, toSessionPinAccounts } from "./helpers/session-pin";
|
|
65
|
+
import { handleSshAcp } from "./helpers/ssh";
|
|
66
|
+
import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
|
|
67
|
+
import { handleTodoAcp } from "./helpers/todo";
|
|
68
|
+
import { buildUsageReportText } from "./helpers/usage-report";
|
|
69
|
+
import { parseMarketplaceInstallArgs, parsePluginScopeArgs } from "./marketplace-install-parser";
|
|
70
|
+
import type {
|
|
71
|
+
BuiltinSlashCommand,
|
|
72
|
+
ParsedSlashCommand,
|
|
73
|
+
SlashCommandResult,
|
|
74
|
+
SlashCommandRuntime,
|
|
75
|
+
SlashCommandSpec,
|
|
76
|
+
SubcommandDef,
|
|
77
|
+
TuiSlashCommandRuntime,
|
|
78
|
+
} from "./types";
|
|
79
|
+
|
|
80
|
+
export type { BuiltinSlashCommand, SubcommandDef } from "./types";
|
|
81
|
+
|
|
82
|
+
/** TUI-specific runtime accepted by `executeBuiltinSlashCommand`. */
|
|
83
|
+
export type BuiltinSlashCommandRuntime = TuiSlashCommandRuntime;
|
|
84
|
+
|
|
85
|
+
export interface TuiBuiltinSlashCommand extends BuiltinSlashCommand {
|
|
86
|
+
getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;
|
|
87
|
+
getInlineHint?: (argumentText: string) => string | null;
|
|
88
|
+
getAutocompleteDescription?: () => string | undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function refreshStatusLine(ctx: InteractiveModeContext): void {
|
|
92
|
+
ctx.statusLine.invalidate();
|
|
93
|
+
ctx.ui.requestRender();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** `/fast status` label for the active model: "on" when its family is priority, else "off". */
|
|
97
|
+
function formatFastModeStatus(session: AgentSession): string {
|
|
98
|
+
return session.isFastModeEnabled() ? "on" : "off";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Detailed, session-effective `/computer status` diagnostics. */
|
|
102
|
+
function formatComputerUseStatus(session: AgentSession): string {
|
|
103
|
+
const enabled = session.settings.get("computer.enabled");
|
|
104
|
+
const active = session.getEnabledToolNames().includes("computer");
|
|
105
|
+
const model = session.model;
|
|
106
|
+
const modelName = model ? formatModelString(model) : "none";
|
|
107
|
+
const exposure = !enabled || !active ? "not exposed" : computerExposureMode(model);
|
|
108
|
+
const toolState = active ? "active" : enabled ? "unavailable" : "inactive";
|
|
109
|
+
const configured = {
|
|
110
|
+
backend: session.settings.get("computer.backend"),
|
|
111
|
+
display: session.settings.get("computer.display"),
|
|
112
|
+
maxWidth: session.settings.get("computer.maxWidth"),
|
|
113
|
+
maxHeight: session.settings.get("computer.maxHeight"),
|
|
114
|
+
};
|
|
115
|
+
const computerTool = session.getToolByName("computer") as Pick<ComputerTool, "effectiveConfiguration"> | undefined;
|
|
116
|
+
const effective = computerTool?.effectiveConfiguration ?? configured;
|
|
117
|
+
const configurationChanged =
|
|
118
|
+
effective.backend !== configured.backend ||
|
|
119
|
+
effective.display !== configured.display ||
|
|
120
|
+
effective.maxWidth !== configured.maxWidth ||
|
|
121
|
+
effective.maxHeight !== configured.maxHeight;
|
|
122
|
+
return [
|
|
123
|
+
`Computer use: ${enabled ? "enabled" : "disabled"}`,
|
|
124
|
+
`tool: ${toolState}`,
|
|
125
|
+
`backend: ${effective.backend}`,
|
|
126
|
+
`display: ${effective.display}`,
|
|
127
|
+
`capture: ${effective.maxWidth}×${effective.maxHeight}`,
|
|
128
|
+
...(configurationChanged
|
|
129
|
+
? [
|
|
130
|
+
`next-session settings: backend=${configured.backend}, display=${configured.display}, capture=${configured.maxWidth}×${configured.maxHeight}`,
|
|
131
|
+
]
|
|
132
|
+
: []),
|
|
133
|
+
`model: ${modelName}`,
|
|
134
|
+
`exposure: ${exposure}`,
|
|
135
|
+
].join(" · ");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Apply a session-scoped computer-use toggle: flip the active tool slate first
|
|
140
|
+
* (so a failed enable never leaves a stale settings override), then record the
|
|
141
|
+
* runtime override — never `settings.set`, which would persist to settings.json.
|
|
142
|
+
* Returns the operator feedback line.
|
|
143
|
+
*/
|
|
144
|
+
async function applyComputerUseToggle(session: AgentSession, enable: boolean): Promise<string> {
|
|
145
|
+
const applied = await session.setComputerToolEnabled(enable);
|
|
146
|
+
if (enable && !applied) {
|
|
147
|
+
return "Computer use is unavailable in this session.";
|
|
148
|
+
}
|
|
149
|
+
session.settings.override("computer.enabled", enable);
|
|
150
|
+
return enable
|
|
151
|
+
? `Computer use enabled for this session. ${formatComputerUseStatus(session)}`
|
|
152
|
+
: "Computer use disabled for this session.";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Session-effective `/vision status` line. */
|
|
156
|
+
function formatVisionStatus(session: AgentSession): string {
|
|
157
|
+
const { mode, active, model } = session.inspectImageState();
|
|
158
|
+
const override = session.getInspectImageModeOverride();
|
|
159
|
+
const modelObj = session.model;
|
|
160
|
+
const capability = modelObj
|
|
161
|
+
? modelObj.input.includes("image")
|
|
162
|
+
? "native image input"
|
|
163
|
+
: "no native image input"
|
|
164
|
+
: "no active model";
|
|
165
|
+
return [
|
|
166
|
+
`inspect_image: ${active ? "active" : "inactive"}`,
|
|
167
|
+
`mode: ${mode}${override ? " (session override)" : ""}`,
|
|
168
|
+
...(override ? [`configured: ${session.settings.get("inspect_image.mode")}`] : []),
|
|
169
|
+
`model: ${model ?? "none"} (${capability})`,
|
|
170
|
+
].join(" · ");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Applies a `/vision` mode for this session and returns the operator feedback line. */
|
|
174
|
+
async function applyVisionMode(session: AgentSession, mode: InspectImageMode): Promise<string> {
|
|
175
|
+
const applied = await session.setInspectImageMode(mode);
|
|
176
|
+
if (!applied) {
|
|
177
|
+
return "inspect_image is unavailable in this session.";
|
|
178
|
+
}
|
|
179
|
+
return `Vision mode: ${mode}. ${formatVisionStatus(session)}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const AUTOCOMPLETE_DETAIL_LIMIT = 48;
|
|
183
|
+
|
|
184
|
+
function shortDetail(value: string, limit = AUTOCOMPLETE_DETAIL_LIMIT): string {
|
|
185
|
+
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
186
|
+
return singleLine.length <= limit ? singleLine : `${singleLine.slice(0, limit - 1)}…`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function formatTokenCount(value: number): string {
|
|
190
|
+
return value.toLocaleString();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Scheme-less display form of a browser deep link: accent + underline, OSC-8 linked to the full URL. */
|
|
194
|
+
function collabWebLinkClickable(webLink: string): string {
|
|
195
|
+
const display = theme.fg("accent", `\x1b[4m${webLink.replace(/^https?:\/\//, "")}\x1b[24m`);
|
|
196
|
+
return urlHyperlinkAlways(webLink, display);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Join hint printed by /collab: compact terminal link + clickable browser deep link. */
|
|
200
|
+
function collabLinkHint(host: CollabHost, heading: string, view = false): string {
|
|
201
|
+
const bullet = theme.fg("accent", theme.format.bullet);
|
|
202
|
+
const link = view ? host.viewLink : host.link;
|
|
203
|
+
const webLink = view ? host.webViewLink : host.webLink;
|
|
204
|
+
return [
|
|
205
|
+
theme.fg("success", heading),
|
|
206
|
+
` ${bullet} ${theme.fg("muted", view ? "从另一终端观看:" : "从另一终端加入:")} ${APP_NAME} join "${link}"`,
|
|
207
|
+
` ${bullet} ${theme.fg("muted", "或在任意浏览器中:")} ${collabWebLinkClickable(webLink)}`,
|
|
208
|
+
theme.fg(
|
|
209
|
+
"dim",
|
|
210
|
+
view
|
|
211
|
+
? "Anyone with this link can watch the session but cannot prompt the agent."
|
|
212
|
+
: "Anyone with the link can read the session and prompt the agent. Read-only link: /collab view",
|
|
213
|
+
),
|
|
214
|
+
].join("\n");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function showCollabQrCode(ctx: InteractiveModeContext, webLink: string): void {
|
|
218
|
+
try {
|
|
219
|
+
ctx.present([new Spacer(1), new CollabQrCodeComponent(webLink)]);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
ctx.showError(`Failed to render collab QR code: ${errorMessage(err)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function showCollabLink(ctx: InteractiveModeContext, host: CollabHost, heading: string, view = false): void {
|
|
226
|
+
ctx.showStatus(collabLinkHint(host, heading, view), { dim: false });
|
|
227
|
+
showCollabQrCode(ctx, view ? host.webViewLink : host.webLink);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function formatFreshSessionResult(result: FreshSessionResult): string {
|
|
231
|
+
const stateLabel = result.closedProviderSessions === 1 ? "provider state" : "provider states";
|
|
232
|
+
return `Fresh provider session started (${result.closedProviderSessions} ${stateLabel} pruned).`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime): SlashCommandResult => {
|
|
236
|
+
runtime.ctx.editor.setText("");
|
|
237
|
+
void runtime.ctx.shutdown();
|
|
238
|
+
return commandConsumed();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
async function handleUsageResetCommand(
|
|
242
|
+
arg: string,
|
|
243
|
+
session: AgentSession,
|
|
244
|
+
output: SlashCommandRuntime["output"],
|
|
245
|
+
): Promise<void> {
|
|
246
|
+
let accounts: ResetUsageAccount[];
|
|
247
|
+
try {
|
|
248
|
+
accounts = toResetUsageAccounts(await session.listResetCredits());
|
|
249
|
+
} catch (error) {
|
|
250
|
+
await output(`Could not load saved resets: ${errorMessage(error)}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (accounts.length === 0) {
|
|
254
|
+
await output("未找到 Codex 账号。使用 /login 添加。");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const targetArg = arg.trim();
|
|
258
|
+
if (!targetArg) {
|
|
259
|
+
const lines = ["已保存的 Codex 速率限制重置:"];
|
|
260
|
+
for (const account of accounts) {
|
|
261
|
+
const detail = account.error ? `unavailable (${account.error})` : `${account.availableCount} available`;
|
|
262
|
+
lines.push(`- ${account.label}: ${detail}${account.active ? " (active)" : ""}`);
|
|
263
|
+
}
|
|
264
|
+
lines.push("", "Spend one with `/usage reset <account email>` or `/usage reset active`.");
|
|
265
|
+
await output(lines.join("\n"));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const wanted = targetArg.toLowerCase();
|
|
269
|
+
const target =
|
|
270
|
+
wanted === "active"
|
|
271
|
+
? accounts.find(account => account.active)
|
|
272
|
+
: accounts.find(
|
|
273
|
+
account =>
|
|
274
|
+
account.label.toLowerCase() === wanted ||
|
|
275
|
+
account.target.email?.toLowerCase() === wanted ||
|
|
276
|
+
account.target.accountId?.toLowerCase() === wanted,
|
|
277
|
+
);
|
|
278
|
+
if (!target) {
|
|
279
|
+
await output(`No Codex account matches "${targetArg}".`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (target.availableCount <= 0) {
|
|
283
|
+
await output(`${target.label}: no saved resets to spend.`);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const outcome = await session.redeemResetCredit(target.target);
|
|
287
|
+
await output(describeRedeemOutcome(outcome, target.label));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function handleSessionPinCommand(
|
|
291
|
+
arg: string,
|
|
292
|
+
session: AgentSession,
|
|
293
|
+
output: SlashCommandRuntime["output"],
|
|
294
|
+
): Promise<void> {
|
|
295
|
+
if (session.isStreaming) {
|
|
296
|
+
await output("Cannot pin an account while the session is streaming.");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
let accountList: SessionOAuthAccountList | undefined;
|
|
300
|
+
try {
|
|
301
|
+
accountList = await session.listCurrentProviderOAuthAccounts();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
await output(`Could not load provider accounts: ${errorMessage(error)}`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!accountList) {
|
|
307
|
+
await output("Select a model before pinning a provider account.");
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const provider = getOAuthProviders().find(candidate => candidate.id === accountList.provider);
|
|
311
|
+
const providerName = provider?.name ?? accountList.provider;
|
|
312
|
+
const accounts = toSessionPinAccounts(accountList.accounts);
|
|
313
|
+
if (accounts.length === 0) {
|
|
314
|
+
const source = session.modelRegistry.authStorage.describeCredentialSource(
|
|
315
|
+
accountList.provider,
|
|
316
|
+
session.sessionId,
|
|
317
|
+
);
|
|
318
|
+
await output(
|
|
319
|
+
source
|
|
320
|
+
? `No stored OAuth accounts for ${providerName}. Current auth comes from ${source}.`
|
|
321
|
+
: `No stored OAuth accounts for ${providerName}. Use /login to add one.`,
|
|
322
|
+
);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const selector = arg.trim();
|
|
327
|
+
if (!selector) {
|
|
328
|
+
const lines = [`OAuth accounts for ${providerName}:`];
|
|
329
|
+
for (const account of accounts) {
|
|
330
|
+
lines.push(`${account.position + 1}. ${account.label}${account.active ? " (active)" : ""}`);
|
|
331
|
+
}
|
|
332
|
+
lines.push("", "Pin one with `/session pin <number|email|account id>`.");
|
|
333
|
+
await output(lines.join("\n"));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const matches = matchSessionPinAccounts(accounts, selector);
|
|
338
|
+
if (matches.length === 0) {
|
|
339
|
+
await output(`No ${providerName} account matches "${selector}".`);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (matches.length > 1) {
|
|
343
|
+
await output(
|
|
344
|
+
`"${selector}" matches multiple ${providerName} accounts: ${matches
|
|
345
|
+
.map(account => `${account.position + 1}. ${account.label}`)
|
|
346
|
+
.join(", ")}. Use the account number.`,
|
|
347
|
+
);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const account = matches[0];
|
|
351
|
+
if (!account || !session.pinCurrentProviderOAuthAccount(account.credentialId)) {
|
|
352
|
+
await output(`${account?.label ?? selector} is no longer available to pin.`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
await output(`Pinned ${account.label} to this session for ${providerName}.`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Parse the `/shake` subcommand into a {@link ShakeMode}; empty defaults to elide. */
|
|
359
|
+
function parseShakeMode(args: string): ShakeMode | { error: string } {
|
|
360
|
+
const verb = args.trim().toLowerCase();
|
|
361
|
+
if (verb === "" || verb === "elide") return "elide";
|
|
362
|
+
if (verb === "images") return "images";
|
|
363
|
+
return { error: `Unknown /shake mode "${verb}". Use elide or images.` };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Format the session's workspace directories (cwd + additional) for display. */
|
|
367
|
+
function formatWorkspaceDirectories(runtime: SlashCommandRuntime, note?: string): string {
|
|
368
|
+
const cwd = runtime.sessionManager.getCwd();
|
|
369
|
+
const additional = runtime.sessionManager.getAdditionalDirectories();
|
|
370
|
+
const lines = ["Workspace directories:", ` ${cwd} (working directory)`, ...additional.map(d => ` ${d}`)];
|
|
371
|
+
return note ? `${note}\n${lines.join("\n")}` : lines.join("\n");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
375
|
+
{
|
|
376
|
+
name: "settings",
|
|
377
|
+
description: "打开设置菜单",
|
|
378
|
+
handleTui: (_command, runtime) => {
|
|
379
|
+
runtime.ctx.showSettingsSelector();
|
|
380
|
+
runtime.ctx.editor.setText("");
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: "setup",
|
|
385
|
+
aliases: ["providers"],
|
|
386
|
+
description: "Open provider setup / 打开提供商设置",
|
|
387
|
+
allowArgs: true,
|
|
388
|
+
subcommands: [{ name: "providers", description: "配置登录和网络搜索提供商" }],
|
|
389
|
+
handleTui: async (command, runtime) => {
|
|
390
|
+
const args = command.args.trim().toLowerCase();
|
|
391
|
+
const opensProviders = args === "" || args === "providers";
|
|
392
|
+
if (opensProviders) {
|
|
393
|
+
await runtime.ctx.showProviderSetup();
|
|
394
|
+
} else {
|
|
395
|
+
runtime.ctx.showWarning(`Usage: /${command.name} [providers]`);
|
|
396
|
+
}
|
|
397
|
+
runtime.ctx.editor.setText("");
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "plan",
|
|
402
|
+
description: "切换计划模式(agent 先规划再执行)",
|
|
403
|
+
inlineHint: "[prompt]",
|
|
404
|
+
allowArgs: true,
|
|
405
|
+
getTuiAutocompleteDescription: runtime => {
|
|
406
|
+
if (!runtime.ctx.settings.get("plan.enabled" as SettingPath)) return "Plan: disabled in settings";
|
|
407
|
+
if (runtime.ctx.planModeEnabled) {
|
|
408
|
+
const planFile = runtime.ctx.planModePlanFilePath;
|
|
409
|
+
return `Plan: on${planFile ? ` (${path.basename(planFile)})` : ""}`;
|
|
410
|
+
}
|
|
411
|
+
if (runtime.ctx.goalModeEnabled) return "Plan: blocked by goal mode";
|
|
412
|
+
return "Plan: off";
|
|
413
|
+
},
|
|
414
|
+
handleTui: async (command, runtime) => {
|
|
415
|
+
await runtime.ctx.handlePlanModeCommand(command.args || undefined);
|
|
416
|
+
runtime.ctx.editor.setText("");
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "plan-review",
|
|
421
|
+
description: "重新打开最近计划的审查(仅计划模式)",
|
|
422
|
+
getTuiAutocompleteDescription: runtime =>
|
|
423
|
+
runtime.ctx.planModeEnabled ? "Plan review: available" : "Plan review: plan mode inactive",
|
|
424
|
+
handleTui: async (_command, runtime) => {
|
|
425
|
+
await runtime.ctx.openPlanReview();
|
|
426
|
+
runtime.ctx.editor.setText("");
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: "vibe",
|
|
431
|
+
description: "切换 Vibe 模式(持久快速工作会话;只读工具集)",
|
|
432
|
+
inlineHint: "[prompt]",
|
|
433
|
+
allowArgs: true,
|
|
434
|
+
getTuiAutocompleteDescription: runtime => {
|
|
435
|
+
if (runtime.ctx.vibeModeEnabled) return "Vibe: on";
|
|
436
|
+
if (runtime.ctx.planModeEnabled) return "Vibe: blocked by plan mode";
|
|
437
|
+
if (runtime.ctx.goalModeEnabled) return "Vibe: blocked by goal mode";
|
|
438
|
+
return "Vibe: off";
|
|
439
|
+
},
|
|
440
|
+
handleTui: async (command, runtime) => {
|
|
441
|
+
await runtime.ctx.handleVibeModeCommand(command.args || undefined);
|
|
442
|
+
runtime.ctx.editor.setText("");
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: "goal",
|
|
447
|
+
description: "切换目标模式(持久自主目标)",
|
|
448
|
+
subcommands: [
|
|
449
|
+
{ name: "set", description: "设置或替换目标", usage: "<objective>" },
|
|
450
|
+
{ name: "show", description: "显示当前目标详情" },
|
|
451
|
+
{ name: "pause", description: "暂停当前目标" },
|
|
452
|
+
{ name: "resume", description: "恢复已暂停的目标" },
|
|
453
|
+
{ name: "drop", description: "放弃当前目标" },
|
|
454
|
+
{ name: "budget", description: "调整 Token 预算", usage: "<N|off>" },
|
|
455
|
+
],
|
|
456
|
+
inlineHint: "[objective]",
|
|
457
|
+
allowArgs: true,
|
|
458
|
+
getTuiAutocompleteDescription: runtime => {
|
|
459
|
+
if (!runtime.ctx.settings.get("goal.enabled" as SettingPath)) return "Goal: disabled in settings";
|
|
460
|
+
if (runtime.ctx.planModeEnabled) return "Goal: blocked by plan mode";
|
|
461
|
+
const state = runtime.ctx.session.getGoalModeState();
|
|
462
|
+
return state ? `Goal: ${state.goal.status} (${shortDetail(state.goal.objective)})` : "Goal: off";
|
|
463
|
+
},
|
|
464
|
+
handleTui: async (command, runtime) => {
|
|
465
|
+
await runtime.ctx.handleGoalModeCommand(command.args || undefined);
|
|
466
|
+
runtime.ctx.editor.setText("");
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: "guided-goal",
|
|
471
|
+
description: "让代理在聊天中采访你,然后设置目标模式",
|
|
472
|
+
inlineHint: "[rough objective]",
|
|
473
|
+
allowArgs: true,
|
|
474
|
+
handleTui: async (command, runtime) => {
|
|
475
|
+
// Clear the slash draft BEFORE the await: the handler blocks for the
|
|
476
|
+
// whole kickoff turn, and a post-await clear would wipe an answer the
|
|
477
|
+
// user starts typing while the first interview question streams.
|
|
478
|
+
runtime.ctx.editor.setText("");
|
|
479
|
+
await runtime.ctx.handleGuidedGoalCommand(command.args || undefined);
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: "loop",
|
|
484
|
+
description:
|
|
485
|
+
"切换循环模式 / Toggle loop mode",
|
|
486
|
+
inlineHint: "[count|duration] [prompt]",
|
|
487
|
+
allowArgs: true,
|
|
488
|
+
getTuiAutocompleteDescription: runtime => {
|
|
489
|
+
if (!runtime.ctx.loopModeEnabled) return "Loop: off";
|
|
490
|
+
if (runtime.ctx.loopModePaused) return "Loop: paused";
|
|
491
|
+
if (runtime.ctx.loopLimit) return `Loop: on (${describeLoopLimitRuntime(runtime.ctx.loopLimit)})`;
|
|
492
|
+
if (runtime.ctx.loopPrompt) return "Loop: on (repeating prompt)";
|
|
493
|
+
return "Loop: on (waiting for next prompt)";
|
|
494
|
+
},
|
|
495
|
+
handleTui: async (command, runtime) => {
|
|
496
|
+
const prompt = await runtime.ctx.handleLoopCommand(command.args);
|
|
497
|
+
runtime.ctx.editor.setText("");
|
|
498
|
+
// Surface any inline prompt so the dispatcher returns it and the normal
|
|
499
|
+
// submit flow runs the first loop iteration (recording it as the loop prompt).
|
|
500
|
+
if (prompt) return { prompt };
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: "queue",
|
|
505
|
+
description: "排队一条消息,在 agent 让出回合后发送",
|
|
506
|
+
inlineHint: "<message>",
|
|
507
|
+
allowArgs: true,
|
|
508
|
+
handleTui: async (command, runtime) => {
|
|
509
|
+
await runtime.ctx.handleQueueCommand(command.args);
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
name: "model",
|
|
514
|
+
aliases: ["models"],
|
|
515
|
+
description: "为当前会话切换模型",
|
|
516
|
+
acpDescription: "Show current model selection",
|
|
517
|
+
getTuiAutocompleteDescription: runtime => {
|
|
518
|
+
const model = runtime.ctx.session.model;
|
|
519
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
520
|
+
},
|
|
521
|
+
handle: async (command, runtime) => {
|
|
522
|
+
if (command.args) {
|
|
523
|
+
const modelId = command.args.trim();
|
|
524
|
+
const availableModels = runtime.session.getAvailableModels?.() ?? [];
|
|
525
|
+
const match = availableModels.find(
|
|
526
|
+
model => model.id === modelId || `${model.provider}/${model.id}` === modelId,
|
|
527
|
+
);
|
|
528
|
+
if (!match) {
|
|
529
|
+
return usage(
|
|
530
|
+
`Unknown model: ${modelId}. Use ACP \`session/setModel\` for picker-driven selection or list available models with /model.`,
|
|
531
|
+
runtime,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
await runtime.session.setModel(match);
|
|
536
|
+
await runtime.output(`Model set to ${match.provider}/${match.id}.`);
|
|
537
|
+
await runtime.notifyTitleChanged?.();
|
|
538
|
+
await runtime.notifyConfigChanged?.();
|
|
539
|
+
return commandConsumed();
|
|
540
|
+
} catch (err) {
|
|
541
|
+
return usage(`Failed to set model: ${errorMessage(err)}`, runtime);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const model = runtime.session.model;
|
|
546
|
+
await runtime.output(
|
|
547
|
+
model ? `Current model: ${model.provider}/${model.id}` : "No model is currently selected.",
|
|
548
|
+
);
|
|
549
|
+
return commandConsumed();
|
|
550
|
+
},
|
|
551
|
+
handleTui: (_command, runtime) => {
|
|
552
|
+
runtime.ctx.showModelSelector();
|
|
553
|
+
runtime.ctx.editor.setText("");
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "switch",
|
|
558
|
+
description: "切换模型(同 alt+p)",
|
|
559
|
+
getTuiAutocompleteDescription: runtime => {
|
|
560
|
+
const model = runtime.ctx.session.model;
|
|
561
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
562
|
+
},
|
|
563
|
+
handleTui: (_command, runtime) => {
|
|
564
|
+
runtime.ctx.showModelSelector({ temporaryOnly: true });
|
|
565
|
+
runtime.ctx.editor.setText("");
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "fast",
|
|
570
|
+
description: "切换优先服务等级 / Toggle priority tier",
|
|
571
|
+
acpDescription: "Toggle fast mode",
|
|
572
|
+
acpInputHint: "[on|off|status]",
|
|
573
|
+
subcommands: [
|
|
574
|
+
{ name: "on", description: "启用快速模式" },
|
|
575
|
+
{ name: "off", description: "禁用快速模式" },
|
|
576
|
+
{ name: "status", description: "显示快速模式状态" },
|
|
577
|
+
],
|
|
578
|
+
allowArgs: true,
|
|
579
|
+
getTuiAutocompleteDescription: runtime => `Fast: ${formatFastModeStatus(runtime.ctx.session)}`,
|
|
580
|
+
handle: async (command, runtime) => {
|
|
581
|
+
const arg = command.args.toLowerCase();
|
|
582
|
+
if (!arg || arg === "toggle") {
|
|
583
|
+
const enabled = runtime.session.toggleFastMode();
|
|
584
|
+
await runtime.output(`Fast mode ${enabled ? "enabled" : "disabled"}.`);
|
|
585
|
+
return commandConsumed();
|
|
586
|
+
}
|
|
587
|
+
if (arg === "on") {
|
|
588
|
+
const supported = runtime.session.setFastMode(true);
|
|
589
|
+
await runtime.output(supported ? "Fast mode enabled." : "Fast mode is unavailable for the current model.");
|
|
590
|
+
return commandConsumed();
|
|
591
|
+
}
|
|
592
|
+
if (arg === "off") {
|
|
593
|
+
runtime.session.setFastMode(false);
|
|
594
|
+
await runtime.output("Fast mode disabled.");
|
|
595
|
+
return commandConsumed();
|
|
596
|
+
}
|
|
597
|
+
if (arg === "status") {
|
|
598
|
+
await runtime.output(`Fast mode is ${formatFastModeStatus(runtime.session)}.`);
|
|
599
|
+
return commandConsumed();
|
|
600
|
+
}
|
|
601
|
+
return usage("Usage: /fast [on|off|status]", runtime);
|
|
602
|
+
},
|
|
603
|
+
handleTui: (command, runtime) => {
|
|
604
|
+
const arg = command.args.trim().toLowerCase();
|
|
605
|
+
if (!arg || arg === "toggle") {
|
|
606
|
+
const enabled = runtime.ctx.session.toggleFastMode();
|
|
607
|
+
refreshStatusLine(runtime.ctx);
|
|
608
|
+
runtime.ctx.showStatus(`Fast mode ${enabled ? "enabled" : "disabled"}.`);
|
|
609
|
+
runtime.ctx.editor.setText("");
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (arg === "on") {
|
|
613
|
+
const supported = runtime.ctx.session.setFastMode(true);
|
|
614
|
+
refreshStatusLine(runtime.ctx);
|
|
615
|
+
runtime.ctx.showStatus(
|
|
616
|
+
supported ? "Fast mode enabled." : "Fast mode is unavailable for the current model.",
|
|
617
|
+
);
|
|
618
|
+
runtime.ctx.editor.setText("");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (arg === "off") {
|
|
622
|
+
runtime.ctx.session.setFastMode(false);
|
|
623
|
+
refreshStatusLine(runtime.ctx);
|
|
624
|
+
runtime.ctx.showStatus("Fast mode disabled.");
|
|
625
|
+
runtime.ctx.editor.setText("");
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (arg === "status") {
|
|
629
|
+
runtime.ctx.showStatus(`Fast mode is ${formatFastModeStatus(runtime.ctx.session)}.`);
|
|
630
|
+
runtime.ctx.editor.setText("");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
runtime.ctx.showStatus("Usage: /fast [on|off|status]");
|
|
634
|
+
runtime.ctx.editor.setText("");
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: "computer",
|
|
639
|
+
description: "切换本会话的原生计算机使用工具",
|
|
640
|
+
acpDescription: "Toggle computer use",
|
|
641
|
+
acpInputHint: "[on|off|status]",
|
|
642
|
+
subcommands: [
|
|
643
|
+
{ name: "on", description: "为本会话启用计算机使用" },
|
|
644
|
+
{ name: "off", description: "为本会话禁用计算机使用" },
|
|
645
|
+
{ name: "status", description: "显示计算机使用状态" },
|
|
646
|
+
],
|
|
647
|
+
allowArgs: true,
|
|
648
|
+
getTuiAutocompleteDescription: runtime =>
|
|
649
|
+
`Computer: ${runtime.ctx.session.settings.get("computer.enabled") ? "on" : "off"}`,
|
|
650
|
+
handle: async (command, runtime) => {
|
|
651
|
+
const arg = command.args.trim().toLowerCase();
|
|
652
|
+
if (arg === "status") {
|
|
653
|
+
await runtime.output(formatComputerUseStatus(runtime.session));
|
|
654
|
+
return commandConsumed();
|
|
655
|
+
}
|
|
656
|
+
if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
|
|
657
|
+
const enable = arg === "off" ? false : arg === "on" || !runtime.session.settings.get("computer.enabled");
|
|
658
|
+
await runtime.output(await applyComputerUseToggle(runtime.session, enable));
|
|
659
|
+
return commandConsumed();
|
|
660
|
+
}
|
|
661
|
+
return usage("Usage: /computer [on|off|status]", runtime);
|
|
662
|
+
},
|
|
663
|
+
handleTui: async (command, runtime) => {
|
|
664
|
+
const arg = command.args.trim().toLowerCase();
|
|
665
|
+
if (arg === "status") {
|
|
666
|
+
runtime.ctx.showStatus(formatComputerUseStatus(runtime.ctx.session));
|
|
667
|
+
runtime.ctx.editor.setText("");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
|
|
671
|
+
const enable =
|
|
672
|
+
arg === "off" ? false : arg === "on" || !runtime.ctx.session.settings.get("computer.enabled");
|
|
673
|
+
runtime.ctx.showStatus(await applyComputerUseToggle(runtime.ctx.session, enable));
|
|
674
|
+
runtime.ctx.editor.setText("");
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
runtime.ctx.showStatus("Usage: /computer [on|off|status]");
|
|
678
|
+
runtime.ctx.editor.setText("");
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "vision",
|
|
683
|
+
description: "控制本会话的 inspect_image 视觉委派工具",
|
|
684
|
+
acpDescription: "Toggle vision delegation",
|
|
685
|
+
acpInputHint: "[on|off|auto|status]",
|
|
686
|
+
subcommands: [
|
|
687
|
+
{ name: "on", description: "本会话始终暴露 inspect_image" },
|
|
688
|
+
{ name: "off", description: "本会话从不暴露 inspect_image" },
|
|
689
|
+
{ name: "auto", description: "遵循 inspect_image.mode(对支持视觉的模型自动隐藏)" },
|
|
690
|
+
{ name: "status", description: "显示 inspect_image 状态" },
|
|
691
|
+
],
|
|
692
|
+
allowArgs: true,
|
|
693
|
+
getTuiAutocompleteDescription: runtime => `Vision: ${runtime.ctx.session.inspectImageState().mode}`,
|
|
694
|
+
handle: async (command, runtime) => {
|
|
695
|
+
const arg = command.args.trim().toLowerCase();
|
|
696
|
+
if (arg === "status") {
|
|
697
|
+
await runtime.output(formatVisionStatus(runtime.session));
|
|
698
|
+
return commandConsumed();
|
|
699
|
+
}
|
|
700
|
+
if (arg === "on" || arg === "off" || arg === "auto") {
|
|
701
|
+
await runtime.output(await applyVisionMode(runtime.session, arg));
|
|
702
|
+
return commandConsumed();
|
|
703
|
+
}
|
|
704
|
+
return usage("Usage: /vision [on|off|auto|status]", runtime);
|
|
705
|
+
},
|
|
706
|
+
handleTui: async (command, runtime) => {
|
|
707
|
+
const arg = command.args.trim().toLowerCase();
|
|
708
|
+
if (arg === "status") {
|
|
709
|
+
runtime.ctx.showStatus(formatVisionStatus(runtime.ctx.session));
|
|
710
|
+
runtime.ctx.editor.setText("");
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (arg === "on" || arg === "off" || arg === "auto") {
|
|
714
|
+
runtime.ctx.showStatus(await applyVisionMode(runtime.ctx.session, arg));
|
|
715
|
+
runtime.ctx.editor.setText("");
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
runtime.ctx.showStatus("Usage: /vision [on|off|auto|status]");
|
|
719
|
+
runtime.ctx.editor.setText("");
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: "prewalk",
|
|
724
|
+
description: "下个操作切换到快速/廉价模型(即使没有 --prewalk 也有效)",
|
|
725
|
+
acpDescription: "Prewalk at the next action",
|
|
726
|
+
handle: async (_command, runtime) => {
|
|
727
|
+
const rolePattern = expandRoleAlias("@smol", runtime.settings);
|
|
728
|
+
const resolved = resolveCliModel({
|
|
729
|
+
cliModel: rolePattern,
|
|
730
|
+
modelRegistry: runtime.session.modelRegistry,
|
|
731
|
+
preferences: getModelMatchPreferences(runtime.settings),
|
|
732
|
+
});
|
|
733
|
+
if (resolved.error || !resolved.model) {
|
|
734
|
+
return usage(resolved.error ?? `Model "${rolePattern}" not found`, runtime);
|
|
735
|
+
}
|
|
736
|
+
if (!runtime.session.modelRegistry.hasConfiguredAuth(resolved.model)) {
|
|
737
|
+
return usage(`No API key for ${resolved.model.provider}/${resolved.model.id}`, runtime);
|
|
738
|
+
}
|
|
739
|
+
runtime.session.armPrewalk(resolved.model, resolved.thinkingLevel);
|
|
740
|
+
await runtime.output(
|
|
741
|
+
`Prewalk on: switching to ${resolved.model.provider}/${resolved.model.id} at the next edit/write (todo-gated).`,
|
|
742
|
+
);
|
|
743
|
+
return commandConsumed();
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: "advisor",
|
|
748
|
+
description: "切换顾问(第二模型审查每轮对话)/ Toggle advisor",
|
|
749
|
+
acpDescription: "Toggle advisor",
|
|
750
|
+
acpInputHint: "[on|off|status|dump [raw]|configure]",
|
|
751
|
+
subcommands: [
|
|
752
|
+
{ name: "on", description: "启用顾问" },
|
|
753
|
+
{ name: "off", description: "禁用顾问" },
|
|
754
|
+
{ name: "status", description: "显示顾问状态" },
|
|
755
|
+
{ name: "dump", description: "Copy advisor transcript / 复制顾问记录", usage: "[raw]" },
|
|
756
|
+
{ name: "configure", description: "打开顾问配置编辑器(TUI)" },
|
|
757
|
+
],
|
|
758
|
+
allowArgs: true,
|
|
759
|
+
getTuiAutocompleteDescription: runtime => {
|
|
760
|
+
const stats = runtime.ctx.session.getAdvisorStats();
|
|
761
|
+
if (stats.active && stats.advisors.length > 1) return `Advisor: on (${stats.advisors.length} advisors)`;
|
|
762
|
+
if (stats.active && stats.model) return `Advisor: on (${stats.model.provider}/${stats.model.id})`;
|
|
763
|
+
if (stats.configured) return "Advisor: configured, no model";
|
|
764
|
+
return "Advisor: off";
|
|
765
|
+
},
|
|
766
|
+
handle: async (command, runtime) => {
|
|
767
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
768
|
+
if (!verb || verb === "toggle") {
|
|
769
|
+
const active = runtime.session.toggleAdvisorEnabled();
|
|
770
|
+
const configured = runtime.session.isAdvisorEnabled();
|
|
771
|
+
if (active) {
|
|
772
|
+
await runtime.output("Advisor enabled.");
|
|
773
|
+
} else if (configured) {
|
|
774
|
+
await runtime.output("Advisor setting enabled, but no model is assigned to the 'advisor' role.");
|
|
775
|
+
} else {
|
|
776
|
+
await runtime.output("Advisor disabled.");
|
|
777
|
+
}
|
|
778
|
+
return commandConsumed();
|
|
779
|
+
}
|
|
780
|
+
if (verb === "on") {
|
|
781
|
+
const active = runtime.session.setAdvisorEnabled(true);
|
|
782
|
+
await runtime.output(
|
|
783
|
+
active ? "Advisor enabled." : "Advisor setting enabled, but no model is assigned to the 'advisor' role.",
|
|
784
|
+
);
|
|
785
|
+
return commandConsumed();
|
|
786
|
+
}
|
|
787
|
+
if (verb === "off") {
|
|
788
|
+
runtime.session.setAdvisorEnabled(false);
|
|
789
|
+
await runtime.output("Advisor disabled.");
|
|
790
|
+
return commandConsumed();
|
|
791
|
+
}
|
|
792
|
+
if (verb === "status") {
|
|
793
|
+
await runtime.output(runtime.session.formatAdvisorStatus());
|
|
794
|
+
return commandConsumed();
|
|
795
|
+
}
|
|
796
|
+
if (verb === "dump") {
|
|
797
|
+
const isRaw = rest.toLowerCase() === "raw";
|
|
798
|
+
const text = runtime.session.formatAdvisorHistoryAsText({ compact: !isRaw });
|
|
799
|
+
await runtime.output(text ?? "Advisor is not active for this session.");
|
|
800
|
+
return commandConsumed();
|
|
801
|
+
}
|
|
802
|
+
if (verb === "configure") {
|
|
803
|
+
await runtime.output(
|
|
804
|
+
"/advisor configure opens an interactive editor and is only available in the interactive TUI.",
|
|
805
|
+
);
|
|
806
|
+
return commandConsumed();
|
|
807
|
+
}
|
|
808
|
+
return usage("Usage: /advisor [on|off|status|dump [raw]|configure]", runtime);
|
|
809
|
+
},
|
|
810
|
+
handleTui: async (command, runtime) => {
|
|
811
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
812
|
+
if (!verb || verb === "toggle") {
|
|
813
|
+
const active = runtime.ctx.session.toggleAdvisorEnabled();
|
|
814
|
+
const configured = runtime.ctx.session.isAdvisorEnabled();
|
|
815
|
+
if (active) {
|
|
816
|
+
runtime.ctx.showStatus("Advisor enabled.");
|
|
817
|
+
} else if (configured) {
|
|
818
|
+
runtime.ctx.showStatus("Advisor setting enabled, but no model is assigned to the 'advisor' role.");
|
|
819
|
+
} else {
|
|
820
|
+
runtime.ctx.showStatus("Advisor disabled.");
|
|
821
|
+
}
|
|
822
|
+
refreshStatusLine(runtime.ctx);
|
|
823
|
+
runtime.ctx.editor.setText("");
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (verb === "on") {
|
|
827
|
+
const active = runtime.ctx.session.setAdvisorEnabled(true);
|
|
828
|
+
runtime.ctx.showStatus(
|
|
829
|
+
active ? "Advisor enabled." : "Advisor setting enabled, but no model is assigned to the 'advisor' role.",
|
|
830
|
+
);
|
|
831
|
+
refreshStatusLine(runtime.ctx);
|
|
832
|
+
runtime.ctx.editor.setText("");
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (verb === "off") {
|
|
836
|
+
runtime.ctx.session.setAdvisorEnabled(false);
|
|
837
|
+
runtime.ctx.showStatus("Advisor disabled.");
|
|
838
|
+
refreshStatusLine(runtime.ctx);
|
|
839
|
+
runtime.ctx.editor.setText("");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (verb === "status") {
|
|
843
|
+
await runtime.ctx.handleAdvisorStatusCommand();
|
|
844
|
+
runtime.ctx.editor.setText("");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (verb === "dump") {
|
|
848
|
+
const isRaw = rest.toLowerCase() === "raw";
|
|
849
|
+
runtime.ctx.handleAdvisorDumpCommand(isRaw);
|
|
850
|
+
runtime.ctx.editor.setText("");
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (verb === "configure") {
|
|
854
|
+
runtime.ctx.showAdvisorConfigure();
|
|
855
|
+
runtime.ctx.editor.setText("");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
runtime.ctx.showStatus("Usage: /advisor [on|off|status|dump [raw]|configure]");
|
|
859
|
+
runtime.ctx.editor.setText("");
|
|
860
|
+
},
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
name: "export",
|
|
864
|
+
description: "Export to HTML / 导出为 HTML",
|
|
865
|
+
inlineHint: "[--themes] [path]",
|
|
866
|
+
allowArgs: true,
|
|
867
|
+
handle: async (command, runtime) => {
|
|
868
|
+
try {
|
|
869
|
+
const { outputPath, useUserThemes } = parseExportArgs(command.args);
|
|
870
|
+
if (outputPath === "--copy" || outputPath === "clipboard" || outputPath === "copy") {
|
|
871
|
+
return usage("Use /dump to copy the session to clipboard.", runtime);
|
|
872
|
+
}
|
|
873
|
+
const filePath = await runtime.session.exportToHtml(outputPath, useUserThemes);
|
|
874
|
+
await runtime.output(`Session exported to: ${filePath}`);
|
|
875
|
+
return commandConsumed();
|
|
876
|
+
} catch (err) {
|
|
877
|
+
return usage(`Failed to export session: ${errorMessage(err)}`, runtime);
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
handleTui: async (command, runtime) => {
|
|
881
|
+
await runtime.ctx.handleExportCommand(command.text);
|
|
882
|
+
runtime.ctx.editor.setText("");
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
name: "dump",
|
|
887
|
+
description: "Copy transcript / 复制会话记录",
|
|
888
|
+
acpDescription: "Return full transcript as plain text, with LLM request JSON path",
|
|
889
|
+
allowArgs: true,
|
|
890
|
+
handle: async (_command, runtime) => {
|
|
891
|
+
const text = runtime.session.formatSessionAsText();
|
|
892
|
+
if (!text) {
|
|
893
|
+
await runtime.output("No messages to dump yet.");
|
|
894
|
+
return commandConsumed();
|
|
895
|
+
}
|
|
896
|
+
let sidecarPath: string | undefined;
|
|
897
|
+
try {
|
|
898
|
+
sidecarPath = await runtime.session.dumpLlmRequestToTmpDir();
|
|
899
|
+
} catch {
|
|
900
|
+
// Sidecar is best-effort; the transcript is still output below.
|
|
901
|
+
}
|
|
902
|
+
const lines = [text];
|
|
903
|
+
if (sidecarPath)
|
|
904
|
+
lines.push(
|
|
905
|
+
"",
|
|
906
|
+
`LLM request JSON: ${sidecarPath}`,
|
|
907
|
+
"This file persists on disk and may contain raw context/secrets — treat accordingly.",
|
|
908
|
+
);
|
|
909
|
+
await runtime.output(lines.join("\n"));
|
|
910
|
+
return commandConsumed();
|
|
911
|
+
},
|
|
912
|
+
handleTui: async (_command, runtime) => {
|
|
913
|
+
await runtime.ctx.handleDumpCommand();
|
|
914
|
+
runtime.ctx.editor.setText("");
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
name: "share",
|
|
919
|
+
description: "Share via link / 通过加密链接分享",
|
|
920
|
+
handle: async (_command, runtime) => {
|
|
921
|
+
try {
|
|
922
|
+
const result = await shareSession(runtime.sessionManager, {
|
|
923
|
+
serverUrl: runtime.settings.get("share.serverUrl"),
|
|
924
|
+
store: runtime.settings.get("share.store"),
|
|
925
|
+
state: runtime.session.state,
|
|
926
|
+
obfuscator: runtime.settings.get("share.redactSecrets") ? runtime.session.obfuscator : undefined,
|
|
927
|
+
});
|
|
928
|
+
const lines = [`Share URL: ${result.url}`];
|
|
929
|
+
if (result.gistUrl) lines.push(`Gist: ${result.gistUrl}`);
|
|
930
|
+
if (result.truncated) lines.push("Note: large content was trimmed to fit the share size limit.");
|
|
931
|
+
await runtime.output(lines.join("\n"));
|
|
932
|
+
return commandConsumed();
|
|
933
|
+
} catch (err) {
|
|
934
|
+
return usage(`Failed to share session: ${errorMessage(err)}`, runtime);
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
handleTui: async (_command, runtime) => {
|
|
938
|
+
await runtime.ctx.handleShareCommand();
|
|
939
|
+
runtime.ctx.editor.setText("");
|
|
940
|
+
},
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
name: "collab",
|
|
944
|
+
description: "Collab share / 实时协作分享",
|
|
945
|
+
inlineHint: "[start|view|stop|status] [relayUrl]",
|
|
946
|
+
subcommands: [
|
|
947
|
+
{ name: "view", description: "分享只读链接(客人可观看,不可提示)" },
|
|
948
|
+
{ name: "status", description: "显示链接和参与者" },
|
|
949
|
+
{ name: "stop", description: "停止分享" },
|
|
950
|
+
],
|
|
951
|
+
allowArgs: true,
|
|
952
|
+
getTuiAutocompleteDescription: runtime => {
|
|
953
|
+
if (runtime.ctx.collabHost) {
|
|
954
|
+
return `Collab: hosting (${Math.max(0, runtime.ctx.collabHost.participants.length - 1)} guests)`;
|
|
955
|
+
}
|
|
956
|
+
if (runtime.ctx.collabGuest?.readOnly) return "Collab: read-only guest";
|
|
957
|
+
if (runtime.ctx.collabGuest) return "Collab: guest";
|
|
958
|
+
return "Collab: off";
|
|
959
|
+
},
|
|
960
|
+
handleTui: async (command, runtime) => {
|
|
961
|
+
const ctx = runtime.ctx;
|
|
962
|
+
ctx.editor.setText("");
|
|
963
|
+
const args = command.args.trim();
|
|
964
|
+
const { verb, rest } = parseSubcommand(args);
|
|
965
|
+
if (verb === "stop") {
|
|
966
|
+
if (!ctx.collabHost) {
|
|
967
|
+
ctx.showStatus("Not hosting a collab session");
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
await ctx.collabHost.stop("host stopped");
|
|
971
|
+
ctx.showStatus("Collab stopped");
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (verb === "status") {
|
|
975
|
+
if (ctx.collabHost) {
|
|
976
|
+
const names = ctx.collabHost.participants.map(p =>
|
|
977
|
+
p.role === "host" ? `${p.name} (host)` : p.readOnly ? `${p.name} (view-only)` : p.name,
|
|
978
|
+
);
|
|
979
|
+
ctx.showStatus(`Collab: ${names.join(", ")} — ${collabWebLinkClickable(ctx.collabHost.webLink)}`);
|
|
980
|
+
} else if (ctx.collabGuest) {
|
|
981
|
+
ctx.showStatus(
|
|
982
|
+
ctx.collabGuest.readOnly
|
|
983
|
+
? "In a collab session as a read-only guest (/leave to exit)"
|
|
984
|
+
: "In a collab session as a guest (/leave to exit)",
|
|
985
|
+
);
|
|
986
|
+
} else {
|
|
987
|
+
ctx.showStatus("Not in a collab session");
|
|
988
|
+
}
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
if (ctx.collabGuest) {
|
|
992
|
+
ctx.showError("Already in a collab session as a guest (/leave first)");
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const knownStartVerb = verb === "start" || verb === "view";
|
|
996
|
+
const view = verb === "view";
|
|
997
|
+
if (ctx.collabHost) {
|
|
998
|
+
showCollabLink(
|
|
999
|
+
ctx,
|
|
1000
|
+
ctx.collabHost,
|
|
1001
|
+
view ? "Read-only collab session active" : "Collab session active",
|
|
1002
|
+
view,
|
|
1003
|
+
);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const explicitUrl = knownStartVerb ? rest : args;
|
|
1007
|
+
const relayInput = explicitUrl || ctx.settings.get("collab.relayUrl") || "";
|
|
1008
|
+
if (!relayInput) {
|
|
1009
|
+
ctx.showError(
|
|
1010
|
+
"No relay configured. Set collab.relayUrl in /settings or pass one: /collab relay.example.com",
|
|
1011
|
+
);
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
// Scheme-less relay args default to wss (ws:// must be spelled out for localhost).
|
|
1015
|
+
const relayUrl = relayInput.includes("://") ? relayInput : `wss://${relayInput}`;
|
|
1016
|
+
const webUrl = ctx.settings.get("collab.webUrl") || "";
|
|
1017
|
+
const host = new CollabHost(ctx);
|
|
1018
|
+
try {
|
|
1019
|
+
await host.start(relayUrl, webUrl);
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
ctx.showError(`Failed to start collab session: ${errorMessage(err)}`);
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
ctx.collabHost = host;
|
|
1025
|
+
showCollabLink(ctx, host, "Collab session started!", view);
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
name: "join",
|
|
1030
|
+
description: "加入一个协作会话",
|
|
1031
|
+
inlineHint: "<link>",
|
|
1032
|
+
allowArgs: true,
|
|
1033
|
+
handleTui: async (command, runtime) => {
|
|
1034
|
+
const ctx = runtime.ctx;
|
|
1035
|
+
ctx.editor.setText("");
|
|
1036
|
+
const link = command.args.trim();
|
|
1037
|
+
if (!link) {
|
|
1038
|
+
ctx.showError("Usage: /join <link>");
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (ctx.collabHost) {
|
|
1042
|
+
ctx.showError("Stop hosting first (/collab stop)");
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
if (ctx.collabGuest) {
|
|
1046
|
+
ctx.showError("Already in a collab session (/leave first)");
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
try {
|
|
1050
|
+
await new CollabGuestLink(ctx).join(link);
|
|
1051
|
+
} catch (err) {
|
|
1052
|
+
ctx.showError(`Failed to join collab session: ${errorMessage(err)}`);
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
name: "leave",
|
|
1058
|
+
description: "离开协作会话",
|
|
1059
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1060
|
+
if (runtime.ctx.collabHost) return "Leave collab: hosting";
|
|
1061
|
+
if (runtime.ctx.collabGuest) return "Leave collab: guest";
|
|
1062
|
+
return "Leave collab: not in collab";
|
|
1063
|
+
},
|
|
1064
|
+
handleTui: async (_command, runtime) => {
|
|
1065
|
+
const ctx = runtime.ctx;
|
|
1066
|
+
ctx.editor.setText("");
|
|
1067
|
+
if (ctx.collabGuest) {
|
|
1068
|
+
await ctx.collabGuest.leave("left");
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (ctx.collabHost) {
|
|
1072
|
+
await ctx.collabHost.stop("host stopped");
|
|
1073
|
+
ctx.showStatus("Collab stopped");
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
ctx.showStatus("Not in a collab session");
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
name: "browser",
|
|
1081
|
+
description: "切换浏览器无头/可见模式",
|
|
1082
|
+
acpInputHint: "[headless|visible]",
|
|
1083
|
+
subcommands: [
|
|
1084
|
+
{ name: "headless", description: "切换到无头模式" },
|
|
1085
|
+
{ name: "visible", description: "切换到可见模式" },
|
|
1086
|
+
],
|
|
1087
|
+
allowArgs: true,
|
|
1088
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1089
|
+
if (!runtime.ctx.settings.get("browser.enabled" as SettingPath)) return "Browser: disabled";
|
|
1090
|
+
return runtime.ctx.settings.get("browser.headless" as SettingPath) ? "Browser: headless" : "Browser: visible";
|
|
1091
|
+
},
|
|
1092
|
+
handle: async (command, runtime) => {
|
|
1093
|
+
const arg = command.args.toLowerCase();
|
|
1094
|
+
const enabled = runtime.settings.get("browser.enabled" as SettingPath) as boolean;
|
|
1095
|
+
if (!enabled) return usage("Browser tool is disabled (enable in settings).", runtime);
|
|
1096
|
+
const current = runtime.settings.get("browser.headless" as SettingPath) as boolean;
|
|
1097
|
+
let next = current;
|
|
1098
|
+
if (!arg) next = !current;
|
|
1099
|
+
else if (arg === "headless" || arg === "hidden") next = true;
|
|
1100
|
+
else if (arg === "visible" || arg === "show" || arg === "headful") next = false;
|
|
1101
|
+
else return usage("Usage: /browser [headless|visible]", runtime);
|
|
1102
|
+
runtime.settings.set("browser.headless" as SettingPath, next as SettingValue<SettingPath>);
|
|
1103
|
+
const tool = runtime.session.getToolByName("browser");
|
|
1104
|
+
if (tool && "restartForModeChange" in tool) {
|
|
1105
|
+
try {
|
|
1106
|
+
await (tool as { restartForModeChange: () => Promise<void> }).restartForModeChange();
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
// Setting was already mutated; surface the restart failure so the
|
|
1109
|
+
// user knows the browser is in an inconsistent state.
|
|
1110
|
+
await runtime.output(
|
|
1111
|
+
`Browser mode set to ${next ? "headless" : "visible"}, but restart failed: ${errorMessage(err)}`,
|
|
1112
|
+
);
|
|
1113
|
+
return commandConsumed();
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
await runtime.output(`Browser mode: ${next ? "headless" : "visible"}`);
|
|
1117
|
+
return commandConsumed();
|
|
1118
|
+
},
|
|
1119
|
+
handleTui: async (command, runtime) => {
|
|
1120
|
+
const arg = command.args.toLowerCase();
|
|
1121
|
+
const current = settings.get("browser.headless" as SettingPath) as boolean;
|
|
1122
|
+
let next = current;
|
|
1123
|
+
if (!(settings.get("browser.enabled" as SettingPath) as boolean)) {
|
|
1124
|
+
runtime.ctx.showWarning("Browser tool is disabled (enable in settings)");
|
|
1125
|
+
runtime.ctx.editor.setText("");
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (!arg) {
|
|
1129
|
+
next = !current;
|
|
1130
|
+
} else if (arg === "headless" || arg === "hidden") {
|
|
1131
|
+
next = true;
|
|
1132
|
+
} else if (arg === "visible" || arg === "show" || arg === "headful") {
|
|
1133
|
+
next = false;
|
|
1134
|
+
} else {
|
|
1135
|
+
runtime.ctx.showStatus("Usage: /browser [headless|visible]");
|
|
1136
|
+
runtime.ctx.editor.setText("");
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
settings.set("browser.headless" as SettingPath, next as SettingValue<SettingPath>);
|
|
1140
|
+
const tool = runtime.ctx.session.getToolByName("browser");
|
|
1141
|
+
if (tool && "restartForModeChange" in tool) {
|
|
1142
|
+
try {
|
|
1143
|
+
await (tool as { restartForModeChange: () => Promise<void> }).restartForModeChange();
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
runtime.ctx.showWarning(`Failed to restart browser: ${errorMessage(error)}`);
|
|
1146
|
+
runtime.ctx.editor.setText("");
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
runtime.ctx.showStatus(`Browser mode: ${next ? "headless" : "visible"}`);
|
|
1151
|
+
runtime.ctx.editor.setText("");
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
name: "copy",
|
|
1156
|
+
description: "选择要复制的对话文本或代码",
|
|
1157
|
+
allowArgs: true,
|
|
1158
|
+
handleTui: async (command, runtime) => {
|
|
1159
|
+
const arg = command.args.trim().toLowerCase();
|
|
1160
|
+
if (!arg) {
|
|
1161
|
+
runtime.ctx.showCopySelector();
|
|
1162
|
+
runtime.ctx.editor.setText("");
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
if (arg === "code") {
|
|
1166
|
+
const block = extractLastCodeBlock(runtime.ctx.session.messages);
|
|
1167
|
+
if (!block) {
|
|
1168
|
+
runtime.ctx.showStatus("No code block to copy.");
|
|
1169
|
+
runtime.ctx.editor.setText("");
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
await copyToClipboard(block.code);
|
|
1173
|
+
runtime.ctx.showStatus("Copied code block to clipboard");
|
|
1174
|
+
runtime.ctx.editor.setText("");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (arg === "cmd" || arg === "command") {
|
|
1178
|
+
const lastCommand = extractLastCommand(runtime.ctx.session.messages);
|
|
1179
|
+
if (!lastCommand) {
|
|
1180
|
+
runtime.ctx.showStatus("No command to copy.");
|
|
1181
|
+
runtime.ctx.editor.setText("");
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
await copyToClipboard(lastCommand.code);
|
|
1185
|
+
runtime.ctx.showStatus(`Copied ${lastCommand.kind === "bash" ? "bash command" : "eval code"} to clipboard`);
|
|
1186
|
+
runtime.ctx.editor.setText("");
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
runtime.ctx.showStatus("Usage: /copy [code|cmd]");
|
|
1190
|
+
runtime.ctx.editor.setText("");
|
|
1191
|
+
},
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
name: "todo",
|
|
1195
|
+
description: "查看或修改待办列表",
|
|
1196
|
+
acpDescription: "Manage todos",
|
|
1197
|
+
acpInputHint: "<subcommand>",
|
|
1198
|
+
subcommands: [
|
|
1199
|
+
{ name: "edit", description: "在编辑器中打开待办(Markdown 往返)" },
|
|
1200
|
+
{ name: "copy", description: "将待办作为 Markdown 复制到剪贴板" },
|
|
1201
|
+
{ name: "export", description: "将待办写入 Markdown 文件(默认:TODO.md)", usage: "[<path>]" },
|
|
1202
|
+
{ name: "import", description: "从 Markdown 文件替换待办(默认:TODO.md)", usage: "[<path>]" },
|
|
1203
|
+
{
|
|
1204
|
+
name: "append",
|
|
1205
|
+
description: "追加任务;阶段模糊匹配或自动创建",
|
|
1206
|
+
usage: "[<phase>] <task...>",
|
|
1207
|
+
},
|
|
1208
|
+
{ name: "start", description: "标记任务进行中(模糊匹配)", usage: "<task>" },
|
|
1209
|
+
{ name: "done", description: "标记任务/阶段/全部完成(模糊匹配)", usage: "[<task|phase>]" },
|
|
1210
|
+
{ name: "drop", description: "标记任务/阶段/全部放弃(模糊匹配)", usage: "[<task|phase>]" },
|
|
1211
|
+
{ name: "rm", description: "移除任务/阶段/全部(模糊匹配)", usage: "[<task|phase>]" },
|
|
1212
|
+
],
|
|
1213
|
+
allowArgs: true,
|
|
1214
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1215
|
+
const tasks = runtime.ctx.todoPhases.flatMap(phase => phase.tasks);
|
|
1216
|
+
if (tasks.length === 0) return "Todos: none";
|
|
1217
|
+
const pending = tasks.filter(task => task.status === "pending").length;
|
|
1218
|
+
const inProgress = tasks.filter(task => task.status === "in_progress").length;
|
|
1219
|
+
const completed = tasks.filter(task => task.status === "completed").length;
|
|
1220
|
+
return `Todos: ${pending + inProgress} open (${inProgress} in progress, ${completed} done)`;
|
|
1221
|
+
},
|
|
1222
|
+
handle: handleTodoAcp,
|
|
1223
|
+
handleTui: async (command, runtime) => {
|
|
1224
|
+
await runtime.ctx.handleTodoCommand(command.args);
|
|
1225
|
+
runtime.ctx.editor.setText("");
|
|
1226
|
+
},
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
name: "session",
|
|
1230
|
+
description: "会话管理命令",
|
|
1231
|
+
acpDescription: "Show or configure the current session",
|
|
1232
|
+
acpInputHint: "[info|delete|pin [account]]",
|
|
1233
|
+
subcommands: [
|
|
1234
|
+
{ name: "info", description: "显示会话信息和统计" },
|
|
1235
|
+
{ name: "delete", description: "删除当前会话并返回选择器" },
|
|
1236
|
+
{
|
|
1237
|
+
name: "pin",
|
|
1238
|
+
description: "将当前提供商固定到已存储的 OAuth 账户",
|
|
1239
|
+
usage: "[account]",
|
|
1240
|
+
},
|
|
1241
|
+
],
|
|
1242
|
+
allowArgs: true,
|
|
1243
|
+
handle: async (command, runtime) => {
|
|
1244
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
1245
|
+
if (!verb || (verb === "info" && !rest)) {
|
|
1246
|
+
await runtime.output(
|
|
1247
|
+
[
|
|
1248
|
+
`Session: ${runtime.session.sessionId}`,
|
|
1249
|
+
`Title: ${runtime.session.sessionName}`,
|
|
1250
|
+
`CWD: ${runtime.cwd}`,
|
|
1251
|
+
].join("\n"),
|
|
1252
|
+
);
|
|
1253
|
+
return commandConsumed();
|
|
1254
|
+
}
|
|
1255
|
+
if (verb === "delete" && !rest) {
|
|
1256
|
+
if (runtime.session.isStreaming) return usage("Cannot delete the session while streaming.", runtime);
|
|
1257
|
+
const sessionFile = runtime.sessionManager.getSessionFile();
|
|
1258
|
+
if (!sessionFile) return usage("No session file to delete (in-memory session).", runtime);
|
|
1259
|
+
// Route through the active SessionManager so the persist writer is
|
|
1260
|
+
// closed before the file is deleted. Constructing a fresh
|
|
1261
|
+
// FileSessionStorage and calling deleteSessionWithArtifacts leaves
|
|
1262
|
+
// the active writer attached to the now-deleted path, so the next
|
|
1263
|
+
// prompt would silently resurrect or corrupt the "deleted" file.
|
|
1264
|
+
try {
|
|
1265
|
+
await runtime.sessionManager.dropSession(sessionFile);
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
return usage(`Failed to delete session: ${errorMessage(err)}`, runtime);
|
|
1268
|
+
}
|
|
1269
|
+
await runtime.output(
|
|
1270
|
+
`Session deleted: ${sessionFile}. Use ACP \`session/load\` to switch to another session.`,
|
|
1271
|
+
);
|
|
1272
|
+
return commandConsumed();
|
|
1273
|
+
}
|
|
1274
|
+
if (verb === "pin") {
|
|
1275
|
+
await handleSessionPinCommand(rest, runtime.session, runtime.output);
|
|
1276
|
+
return commandConsumed();
|
|
1277
|
+
}
|
|
1278
|
+
return usage("Usage: /session [info|delete|pin [account]]", runtime);
|
|
1279
|
+
},
|
|
1280
|
+
handleTui: async (command, runtime) => {
|
|
1281
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
1282
|
+
if (verb === "delete" && !rest) {
|
|
1283
|
+
runtime.ctx.editor.setText("");
|
|
1284
|
+
await runtime.ctx.handleSessionDeleteCommand();
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
if (verb === "pin") {
|
|
1288
|
+
if (rest) {
|
|
1289
|
+
await handleSessionPinCommand(rest, runtime.ctx.session, text => runtime.ctx.showStatus(text));
|
|
1290
|
+
refreshStatusLine(runtime.ctx);
|
|
1291
|
+
} else {
|
|
1292
|
+
await runtime.ctx.showSessionPinSelector();
|
|
1293
|
+
}
|
|
1294
|
+
runtime.ctx.editor.setText("");
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
if (!verb || (verb === "info" && !rest)) {
|
|
1298
|
+
await runtime.ctx.handleSessionCommand();
|
|
1299
|
+
} else {
|
|
1300
|
+
runtime.ctx.showStatus("Usage: /session [info|delete|pin [account]]");
|
|
1301
|
+
}
|
|
1302
|
+
runtime.ctx.editor.setText("");
|
|
1303
|
+
},
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
name: "jobs",
|
|
1307
|
+
description: "显示异步后台任务状态",
|
|
1308
|
+
acpDescription: "Show background jobs",
|
|
1309
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1310
|
+
const snapshot = runtime.ctx.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
1311
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) return "Jobs: none";
|
|
1312
|
+
return `Jobs: ${snapshot.running.length} running, ${snapshot.recent.length} recent`;
|
|
1313
|
+
},
|
|
1314
|
+
handle: async (_command, runtime) => {
|
|
1315
|
+
const snapshot = runtime.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
1316
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) {
|
|
1317
|
+
await runtime.output(
|
|
1318
|
+
"No background jobs running. (Background jobs run async tools — e.g. long-running bash, debug, or task subagents that would otherwise tie up a turn. They appear here while alive and for ~5 minutes after.)",
|
|
1319
|
+
);
|
|
1320
|
+
return commandConsumed();
|
|
1321
|
+
}
|
|
1322
|
+
const now = Date.now();
|
|
1323
|
+
const lines: string[] = ["Background Jobs", `Running: ${snapshot.running.length}`];
|
|
1324
|
+
if (snapshot.running.length > 0) {
|
|
1325
|
+
lines.push("", "Running Jobs");
|
|
1326
|
+
for (const job of snapshot.running) {
|
|
1327
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now - job.startTime)}`);
|
|
1328
|
+
lines.push(` ${job.label}`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (snapshot.recent.length > 0) {
|
|
1332
|
+
lines.push("", "Recent Jobs");
|
|
1333
|
+
for (const job of snapshot.recent) {
|
|
1334
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now - job.startTime)}`);
|
|
1335
|
+
lines.push(` ${job.label}`);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
await runtime.output(lines.join("\n"));
|
|
1339
|
+
return commandConsumed();
|
|
1340
|
+
},
|
|
1341
|
+
handleTui: async (_command, runtime) => {
|
|
1342
|
+
await runtime.ctx.handleJobsCommand();
|
|
1343
|
+
runtime.ctx.editor.setText("");
|
|
1344
|
+
},
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
name: "usage",
|
|
1348
|
+
description: "显示提供商用量与限额",
|
|
1349
|
+
acpDescription: "Show token usage",
|
|
1350
|
+
acpInputHint: "[show|reset [account|active]]",
|
|
1351
|
+
subcommands: [
|
|
1352
|
+
{ name: "show", description: "显示提供商用量与限额" },
|
|
1353
|
+
{ name: "reset", description: "消耗一个已保存的 Codex 速率限制重置", usage: "[account|active]" },
|
|
1354
|
+
],
|
|
1355
|
+
allowArgs: true,
|
|
1356
|
+
handle: async (command, runtime) => {
|
|
1357
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
1358
|
+
if (!verb || (verb === "show" && !rest)) {
|
|
1359
|
+
await runtime.output(await buildUsageReportText(runtime));
|
|
1360
|
+
return commandConsumed();
|
|
1361
|
+
}
|
|
1362
|
+
if (verb === "reset") {
|
|
1363
|
+
await handleUsageResetCommand(rest, runtime.session, runtime.output);
|
|
1364
|
+
return commandConsumed();
|
|
1365
|
+
}
|
|
1366
|
+
return usage("Usage: /usage [show|reset [account|active]]", runtime);
|
|
1367
|
+
},
|
|
1368
|
+
handleTui: async (command, runtime) => {
|
|
1369
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
1370
|
+
if (!verb || (verb === "show" && !rest)) {
|
|
1371
|
+
await runtime.ctx.handleUsageCommand();
|
|
1372
|
+
runtime.ctx.editor.setText("");
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (verb === "reset") {
|
|
1376
|
+
if (rest) {
|
|
1377
|
+
await handleUsageResetCommand(rest, runtime.ctx.session, text => runtime.ctx.showStatus(text));
|
|
1378
|
+
} else {
|
|
1379
|
+
await runtime.ctx.showResetUsageSelector();
|
|
1380
|
+
}
|
|
1381
|
+
runtime.ctx.editor.setText("");
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
runtime.ctx.showStatus("Usage: /usage [show|reset [account|active]]");
|
|
1385
|
+
runtime.ctx.editor.setText("");
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
name: "stats",
|
|
1390
|
+
description: "启动本地统计面板",
|
|
1391
|
+
inlineHint: "[--port <port>]",
|
|
1392
|
+
allowArgs: true,
|
|
1393
|
+
handle: async (command, runtime) => {
|
|
1394
|
+
const parsed = parseStatsDashboardArgs(command.args);
|
|
1395
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
1396
|
+
|
|
1397
|
+
await runtime.output("Syncing session files...");
|
|
1398
|
+
try {
|
|
1399
|
+
const result = await launchStatsDashboard(parsed);
|
|
1400
|
+
await runtime.output(result.message);
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
await runtime.output(`Stats dashboard failed: ${errorMessage(error)}`);
|
|
1403
|
+
}
|
|
1404
|
+
return commandConsumed();
|
|
1405
|
+
},
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
name: "changelog",
|
|
1409
|
+
description: "显示更新日志",
|
|
1410
|
+
acpDescription: "Show changelog",
|
|
1411
|
+
acpInputHint: "[full]",
|
|
1412
|
+
subcommands: [{ name: "full", description: "显示完整更新日志" }],
|
|
1413
|
+
allowArgs: true,
|
|
1414
|
+
handle: async (command, runtime) => {
|
|
1415
|
+
const changelogPath = getChangelogPath();
|
|
1416
|
+
const allEntries = await parseChangelog(changelogPath);
|
|
1417
|
+
const showFull = command.args.trim().toLowerCase() === "full";
|
|
1418
|
+
const entriesToShow = showFull ? allEntries : allEntries.slice(0, RECENT_CHANGELOG_ENTRY_LIMIT);
|
|
1419
|
+
if (entriesToShow.length === 0) {
|
|
1420
|
+
await runtime.output("No changelog entries found.");
|
|
1421
|
+
return commandConsumed();
|
|
1422
|
+
}
|
|
1423
|
+
await runtime.output(renderChangelogEntries(entriesToShow).markdown);
|
|
1424
|
+
return commandConsumed();
|
|
1425
|
+
},
|
|
1426
|
+
handleTui: async (command, runtime) => {
|
|
1427
|
+
const showFull = command.args.split(/\s+/).filter(Boolean).includes("full");
|
|
1428
|
+
await runtime.ctx.handleChangelogCommand(showFull);
|
|
1429
|
+
runtime.ctx.editor.setText("");
|
|
1430
|
+
},
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
name: "hotkeys",
|
|
1434
|
+
description: "显示键盘快捷键",
|
|
1435
|
+
handleTui: (_command, runtime) => {
|
|
1436
|
+
runtime.ctx.handleHotkeysCommand();
|
|
1437
|
+
runtime.ctx.editor.setText("");
|
|
1438
|
+
},
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
name: "tools",
|
|
1442
|
+
description: "显示 agent 当前可见工具",
|
|
1443
|
+
acpDescription: "Show available tools",
|
|
1444
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1445
|
+
const active = runtime.ctx.session.getActiveToolNames().length;
|
|
1446
|
+
const all = runtime.ctx.session.getAllToolNames().length;
|
|
1447
|
+
return all === 0 ? "Tools: none available" : `Tools: ${active} active / ${all} available`;
|
|
1448
|
+
},
|
|
1449
|
+
handle: async (_command, runtime) => {
|
|
1450
|
+
const active = runtime.session.getActiveToolNames();
|
|
1451
|
+
const all = runtime.session.getAllToolNames();
|
|
1452
|
+
if (all.length === 0) {
|
|
1453
|
+
await runtime.output("No tools are available.");
|
|
1454
|
+
return commandConsumed();
|
|
1455
|
+
}
|
|
1456
|
+
const lines = all.map(name => `${active.includes(name) ? "*" : "-"} ${name}`);
|
|
1457
|
+
for (const mounted of runtime.session.getXdevToolEntries()) {
|
|
1458
|
+
lines.push(`~ xd://${mounted.name}`);
|
|
1459
|
+
}
|
|
1460
|
+
await runtime.output(lines.join("\n"));
|
|
1461
|
+
return commandConsumed();
|
|
1462
|
+
},
|
|
1463
|
+
handleTui: (_command, runtime) => {
|
|
1464
|
+
runtime.ctx.handleToolsCommand();
|
|
1465
|
+
runtime.ctx.editor.setText("");
|
|
1466
|
+
},
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
name: "context",
|
|
1470
|
+
description: "显示估计的上下文用量明细",
|
|
1471
|
+
acpDescription: "Show context usage",
|
|
1472
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1473
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1474
|
+
if (!usage) return "Context: unavailable";
|
|
1475
|
+
return `Context: ${Math.round(usage.percent)}% (${formatTokenCount(usage.tokens)}/${formatTokenCount(usage.contextWindow)})`;
|
|
1476
|
+
},
|
|
1477
|
+
handle: async (_command, runtime) => {
|
|
1478
|
+
await runtime.output(buildContextReportText(runtime));
|
|
1479
|
+
return commandConsumed();
|
|
1480
|
+
},
|
|
1481
|
+
handleTui: (_command, runtime) => {
|
|
1482
|
+
runtime.ctx.handleContextCommand();
|
|
1483
|
+
runtime.ctx.editor.setText("");
|
|
1484
|
+
},
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "extensions",
|
|
1488
|
+
aliases: ["status"],
|
|
1489
|
+
description: "打开扩展控制中心",
|
|
1490
|
+
handleTui: (_command, runtime) => {
|
|
1491
|
+
runtime.ctx.showExtensionsDashboard();
|
|
1492
|
+
runtime.ctx.editor.setText("");
|
|
1493
|
+
},
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
name: "agents",
|
|
1497
|
+
description: "打开代理控制中心",
|
|
1498
|
+
handleTui: (_command, runtime) => {
|
|
1499
|
+
runtime.ctx.showAgentsDashboard();
|
|
1500
|
+
runtime.ctx.editor.setText("");
|
|
1501
|
+
},
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
name: "branch",
|
|
1505
|
+
description: "从历史消息创建新分支",
|
|
1506
|
+
handleTui: (_command, runtime) => {
|
|
1507
|
+
if (settings.get("doubleEscapeAction") === "tree") {
|
|
1508
|
+
runtime.ctx.showTreeSelector();
|
|
1509
|
+
} else {
|
|
1510
|
+
runtime.ctx.showUserMessageSelector();
|
|
1511
|
+
}
|
|
1512
|
+
runtime.ctx.editor.setText("");
|
|
1513
|
+
},
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
name: "fork",
|
|
1517
|
+
description: "从历史消息创建新分支(fork)",
|
|
1518
|
+
handleTui: async (_command, runtime) => {
|
|
1519
|
+
runtime.ctx.editor.setText("");
|
|
1520
|
+
await runtime.ctx.handleForkCommand();
|
|
1521
|
+
},
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
name: "tree",
|
|
1525
|
+
description: "导航会话树(切换分支)",
|
|
1526
|
+
handleTui: (_command, runtime) => {
|
|
1527
|
+
runtime.ctx.showTreeSelector();
|
|
1528
|
+
runtime.ctx.editor.setText("");
|
|
1529
|
+
},
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
name: "login",
|
|
1533
|
+
description: "使用 OAuth 提供商登录",
|
|
1534
|
+
inlineHint: "[provider|redirect URL]",
|
|
1535
|
+
allowArgs: true,
|
|
1536
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1537
|
+
runtime.ctx.oauthManualInput.hasPending()
|
|
1538
|
+
? `Login: waiting for ${runtime.ctx.oauthManualInput.pendingProviderId ?? "OAuth"} callback`
|
|
1539
|
+
: "Login: choose provider",
|
|
1540
|
+
handleTui: (command, runtime) => {
|
|
1541
|
+
const manualInput = runtime.ctx.oauthManualInput;
|
|
1542
|
+
const args = command.args.trim();
|
|
1543
|
+
if (args.length > 0) {
|
|
1544
|
+
const matchedProvider = getOAuthProviders().find(provider => provider.id === args);
|
|
1545
|
+
if (matchedProvider) {
|
|
1546
|
+
if (manualInput.hasPending()) {
|
|
1547
|
+
const pendingProvider = manualInput.pendingProviderId;
|
|
1548
|
+
const message = pendingProvider
|
|
1549
|
+
? `OAuth login already in progress for ${pendingProvider}. Paste the redirect URL with /login <url>.`
|
|
1550
|
+
: "OAuth login already in progress. Paste the redirect URL with /login <url>.";
|
|
1551
|
+
runtime.ctx.showWarning(message);
|
|
1552
|
+
runtime.ctx.editor.setText("");
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
void runtime.ctx.showOAuthSelector("login", matchedProvider.id);
|
|
1556
|
+
runtime.ctx.editor.setText("");
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const submitted = manualInput.submit(args);
|
|
1560
|
+
if (submitted) {
|
|
1561
|
+
runtime.ctx.showStatus("OAuth callback received; completing login…");
|
|
1562
|
+
} else {
|
|
1563
|
+
runtime.ctx.showWarning("No OAuth login is waiting for a manual callback.");
|
|
1564
|
+
}
|
|
1565
|
+
runtime.ctx.editor.setText("");
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
if (manualInput.hasPending()) {
|
|
1570
|
+
const provider = manualInput.pendingProviderId;
|
|
1571
|
+
const message = provider
|
|
1572
|
+
? `OAuth login already in progress for ${provider}. Paste the redirect URL with /login <url>.`
|
|
1573
|
+
: "OAuth login already in progress. Paste the redirect URL with /login <url>.";
|
|
1574
|
+
runtime.ctx.showWarning(message);
|
|
1575
|
+
runtime.ctx.editor.setText("");
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
void runtime.ctx.showOAuthSelector("login");
|
|
1580
|
+
runtime.ctx.editor.setText("");
|
|
1581
|
+
},
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
name: "logout",
|
|
1585
|
+
description: "登出 OAuth 提供商",
|
|
1586
|
+
inlineHint: "[provider]",
|
|
1587
|
+
allowArgs: true,
|
|
1588
|
+
handleTui: (command, runtime) => {
|
|
1589
|
+
const providerId = command.args.trim();
|
|
1590
|
+
if (providerId) {
|
|
1591
|
+
const matchedProvider = getOAuthProviders().find(provider => provider.id === providerId);
|
|
1592
|
+
if (!matchedProvider) {
|
|
1593
|
+
runtime.ctx.showWarning(`Unknown OAuth provider: ${providerId}`);
|
|
1594
|
+
runtime.ctx.editor.setText("");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
void runtime.ctx.showOAuthSelector("logout", matchedProvider.id);
|
|
1598
|
+
runtime.ctx.editor.setText("");
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
void runtime.ctx.showOAuthSelector("logout");
|
|
1602
|
+
runtime.ctx.editor.setText("");
|
|
1603
|
+
},
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
name: "mcp",
|
|
1607
|
+
description: "管理 MCP 服务器(添加、列出、移除、测试)",
|
|
1608
|
+
acpDescription: "Manage MCP servers",
|
|
1609
|
+
inlineHint: "<subcommand>",
|
|
1610
|
+
subcommands: [
|
|
1611
|
+
{
|
|
1612
|
+
name: "add",
|
|
1613
|
+
description: "添加新 MCP 服务器",
|
|
1614
|
+
usage: "<name> [--scope project|user] [--url <url>] [-- <command...>]",
|
|
1615
|
+
},
|
|
1616
|
+
{ name: "list", description: "列出所有配置的 MCP 服务器" },
|
|
1617
|
+
{ name: "remove", description: "移除 MCP 服务器", usage: "<name> [--scope project|user]" },
|
|
1618
|
+
{ name: "test", description: "测试与服务器的连接", usage: "<name>" },
|
|
1619
|
+
{ name: "reauth", description: "重新授权服务器的 OAuth", usage: "<name>" },
|
|
1620
|
+
{ name: "unauth", description: "移除服务器的 OAuth 授权", usage: "<name>" },
|
|
1621
|
+
{ name: "enable", description: "启用 MCP 服务器", usage: "<name>" },
|
|
1622
|
+
{ name: "disable", description: "禁用 MCP 服务器", usage: "<name>" },
|
|
1623
|
+
{
|
|
1624
|
+
name: "smithery-search",
|
|
1625
|
+
description: "搜索 Smithery 注册表并部署 MCP 服务器",
|
|
1626
|
+
usage: "<keyword> [--scope project|user] [--limit <1-100>] [--semantic]",
|
|
1627
|
+
},
|
|
1628
|
+
{ name: "smithery-login", description: "登录 Smithery 并缓存 API 密钥" },
|
|
1629
|
+
{ name: "smithery-logout", description: "移除缓存的 Smithery API 密钥" },
|
|
1630
|
+
{ name: "reconnect", description: "重新连接到指定 MCP 服务器", usage: "<name>" },
|
|
1631
|
+
{ name: "reload", description: "强制重载 MCP 运行时工具" },
|
|
1632
|
+
{ name: "resources", description: "列出已连接服务器的可用资源" },
|
|
1633
|
+
{ name: "prompts", description: "列出已连接服务器的可用提示" },
|
|
1634
|
+
{ name: "notifications", description: "显示通知能力和订阅" },
|
|
1635
|
+
{ name: "help", description: "显示帮助信息" },
|
|
1636
|
+
],
|
|
1637
|
+
allowArgs: true,
|
|
1638
|
+
handle: handleMcpAcp,
|
|
1639
|
+
handleTui: async (command, runtime) => {
|
|
1640
|
+
runtime.ctx.editor.setText("");
|
|
1641
|
+
await runtime.ctx.handleMCPCommand(command.text);
|
|
1642
|
+
},
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
name: "ssh",
|
|
1646
|
+
description: "管理 SSH 主机(添加、列出、移除)",
|
|
1647
|
+
acpDescription: "Manage SSH connections",
|
|
1648
|
+
inlineHint: "<subcommand>",
|
|
1649
|
+
subcommands: [
|
|
1650
|
+
{
|
|
1651
|
+
name: "add",
|
|
1652
|
+
description: "添加 SSH 主机",
|
|
1653
|
+
usage: "<name> --host <host> [--user <user>] [--port <port>] [--key <keyPath>]",
|
|
1654
|
+
},
|
|
1655
|
+
{ name: "list", description: "列出所有配置的 SSH 主机" },
|
|
1656
|
+
{ name: "remove", description: "移除 SSH 主机", usage: "<name> [--scope project|user]" },
|
|
1657
|
+
{ name: "help", description: "显示帮助信息" },
|
|
1658
|
+
],
|
|
1659
|
+
allowArgs: true,
|
|
1660
|
+
handle: handleSshAcp,
|
|
1661
|
+
handleTui: async (command, runtime) => {
|
|
1662
|
+
runtime.ctx.editor.setText("");
|
|
1663
|
+
await runtime.ctx.handleSSHCommand(command.text);
|
|
1664
|
+
},
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
name: "new",
|
|
1668
|
+
aliases: ["clear"],
|
|
1669
|
+
description: "开始新会话",
|
|
1670
|
+
handleTui: async (_command, runtime) => {
|
|
1671
|
+
runtime.ctx.editor.setText("");
|
|
1672
|
+
await runtime.ctx.handleClearCommand();
|
|
1673
|
+
},
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
name: "fresh",
|
|
1677
|
+
description: "重置提供商流状态(不改变本地对话记录)",
|
|
1678
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1679
|
+
runtime.ctx.session.isStreaming ? "Fresh: unavailable while streaming" : "Fresh: ready",
|
|
1680
|
+
handle: async (_command, runtime) => {
|
|
1681
|
+
const result = runtime.session.freshSession();
|
|
1682
|
+
if (!result) {
|
|
1683
|
+
await runtime.output(
|
|
1684
|
+
"Wait for the current response to finish or abort it before refreshing provider state.",
|
|
1685
|
+
);
|
|
1686
|
+
return commandConsumed();
|
|
1687
|
+
}
|
|
1688
|
+
await runtime.output(formatFreshSessionResult(result));
|
|
1689
|
+
return commandConsumed();
|
|
1690
|
+
},
|
|
1691
|
+
handleTui: async (_command, runtime) => {
|
|
1692
|
+
runtime.ctx.editor.setText("");
|
|
1693
|
+
await runtime.ctx.handleFreshCommand();
|
|
1694
|
+
},
|
|
1695
|
+
},
|
|
1696
|
+
{
|
|
1697
|
+
name: "drop",
|
|
1698
|
+
description: "删除当前会话并开始新会话",
|
|
1699
|
+
handleTui: async (_command, runtime) => {
|
|
1700
|
+
runtime.ctx.editor.setText("");
|
|
1701
|
+
await runtime.ctx.handleDropCommand();
|
|
1702
|
+
},
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
name: "compact",
|
|
1706
|
+
description: "手动压缩会话上下文",
|
|
1707
|
+
acpDescription: "Compact the conversation",
|
|
1708
|
+
subcommands: COMPACT_MODES.map(mode => ({
|
|
1709
|
+
name: mode.name,
|
|
1710
|
+
description: mode.description,
|
|
1711
|
+
usage: mode.rejectsFocus ? undefined : "[focus]",
|
|
1712
|
+
})),
|
|
1713
|
+
acpInputHint: `[${COMPACT_MODES.map(mode => mode.name).join("|")}] [focus]`,
|
|
1714
|
+
allowArgs: true,
|
|
1715
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1716
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1717
|
+
return usage ? `Compact: context ${Math.round(usage.percent)}% used` : "Compact: context unavailable";
|
|
1718
|
+
},
|
|
1719
|
+
handle: async (command, runtime) => {
|
|
1720
|
+
const parsed = parseCompactArgs(command.args);
|
|
1721
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
1722
|
+
const before = runtime.session.getContextUsage?.();
|
|
1723
|
+
const beforeTokens = before?.tokens;
|
|
1724
|
+
try {
|
|
1725
|
+
await runtime.session.compact(parsed.instructions, parsed.mode ? { mode: parsed.mode } : undefined);
|
|
1726
|
+
} catch (err) {
|
|
1727
|
+
// Compaction precondition failures (no model, already compacted, too
|
|
1728
|
+
// small) and provider errors propagate as plain Errors; surface them
|
|
1729
|
+
// via runtime.output so they don't fail the ACP prompt turn.
|
|
1730
|
+
return usage(`Compaction failed: ${errorMessage(err)}`, runtime);
|
|
1731
|
+
}
|
|
1732
|
+
const after = runtime.session.getContextUsage?.();
|
|
1733
|
+
const afterTokens = after?.tokens;
|
|
1734
|
+
if (beforeTokens != null && afterTokens != null) {
|
|
1735
|
+
const saved = beforeTokens - afterTokens;
|
|
1736
|
+
await runtime.output(`Compaction complete. Tokens: ${beforeTokens} -> ${afterTokens} (saved ${saved}).`);
|
|
1737
|
+
} else {
|
|
1738
|
+
await runtime.output("Compaction complete.");
|
|
1739
|
+
}
|
|
1740
|
+
return commandConsumed();
|
|
1741
|
+
},
|
|
1742
|
+
handleTui: async (command, runtime) => {
|
|
1743
|
+
const parsed = parseCompactArgs(command.args);
|
|
1744
|
+
runtime.ctx.editor.setText("");
|
|
1745
|
+
if ("error" in parsed) {
|
|
1746
|
+
runtime.ctx.showWarning(parsed.error);
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
await runtime.ctx.handleCompactCommand(parsed.instructions, parsed.mode);
|
|
1750
|
+
},
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
name: "shake",
|
|
1754
|
+
description: "从上下文丢弃重内容(工具结果、大块)",
|
|
1755
|
+
acpDescription: "Shake heavy content out of the conversation context",
|
|
1756
|
+
subcommands: [
|
|
1757
|
+
{ name: "elide", description: "剥离工具结果和大块内容(默认)" },
|
|
1758
|
+
{ name: "images", description: "剥离图片块" },
|
|
1759
|
+
],
|
|
1760
|
+
acpInputHint: "[elide|images]",
|
|
1761
|
+
allowArgs: true,
|
|
1762
|
+
handle: async (command, runtime) => {
|
|
1763
|
+
const mode = parseShakeMode(command.args);
|
|
1764
|
+
if (typeof mode !== "string") return usage(mode.error, runtime);
|
|
1765
|
+
const result = await runtime.session.shake(mode);
|
|
1766
|
+
await runtime.output(formatShakeSummary(result));
|
|
1767
|
+
return commandConsumed();
|
|
1768
|
+
},
|
|
1769
|
+
handleTui: async (command, runtime) => {
|
|
1770
|
+
runtime.ctx.editor.setText("");
|
|
1771
|
+
const mode = parseShakeMode(command.args);
|
|
1772
|
+
if (typeof mode !== "string") {
|
|
1773
|
+
runtime.ctx.showWarning(mode.error);
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
await runtime.ctx.handleShakeCommand(mode);
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
name: "handoff",
|
|
1781
|
+
description: "交接会话上下文到新会话",
|
|
1782
|
+
inlineHint: "[focus instructions]",
|
|
1783
|
+
allowArgs: true,
|
|
1784
|
+
handleTui: async (command, runtime) => {
|
|
1785
|
+
const customInstructions = command.args || undefined;
|
|
1786
|
+
runtime.ctx.editor.setText("");
|
|
1787
|
+
await runtime.ctx.handleHandoffCommand(customInstructions);
|
|
1788
|
+
},
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
name: "resume",
|
|
1792
|
+
description: "恢复另一个会话",
|
|
1793
|
+
inlineHint: "[session id]",
|
|
1794
|
+
allowArgs: true,
|
|
1795
|
+
handleTui: async (command, runtime) => {
|
|
1796
|
+
const sessionArg = command.args.trim();
|
|
1797
|
+
runtime.ctx.editor.setText("");
|
|
1798
|
+
if (!sessionArg) {
|
|
1799
|
+
runtime.ctx.showSessionSelector();
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
const match = await resolveResumableSession(
|
|
1803
|
+
sessionArg,
|
|
1804
|
+
runtime.ctx.sessionManager.getCwd(),
|
|
1805
|
+
runtime.ctx.sessionManager.getSessionDir(),
|
|
1806
|
+
{ allowGlobalFallback: true },
|
|
1807
|
+
);
|
|
1808
|
+
if (!match) {
|
|
1809
|
+
runtime.ctx.showError(`Session "${sessionArg}" not found`);
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
await runtime.ctx.handleResumeSession(match.session.path);
|
|
1813
|
+
},
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
name: "btw",
|
|
1817
|
+
description: "用当前上下文问临时问题",
|
|
1818
|
+
inlineHint: "<question>",
|
|
1819
|
+
allowArgs: true,
|
|
1820
|
+
handleTui: async (command, runtime) => {
|
|
1821
|
+
const question = command.text.slice(`/${command.name}`.length).trim();
|
|
1822
|
+
runtime.ctx.editor.setText("");
|
|
1823
|
+
await runtime.ctx.handleBtwCommand(question);
|
|
1824
|
+
},
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
name: "tan",
|
|
1828
|
+
description: "后台运行 agent 处理旁支任务",
|
|
1829
|
+
inlineHint: "<work>",
|
|
1830
|
+
allowArgs: true,
|
|
1831
|
+
handleTui: async (command, runtime) => {
|
|
1832
|
+
const work = command.text.slice(`/${command.name}`.length).trim();
|
|
1833
|
+
runtime.ctx.editor.setText("");
|
|
1834
|
+
await runtime.ctx.handleTanCommand(work);
|
|
1835
|
+
},
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
name: "omfg",
|
|
1839
|
+
description: "根据投诉创建 TTSR 规则",
|
|
1840
|
+
inlineHint: "<complaint>",
|
|
1841
|
+
allowArgs: true,
|
|
1842
|
+
handleTui: async (command, runtime) => {
|
|
1843
|
+
const complaint = command.text.slice(`/${command.name}`.length).trim();
|
|
1844
|
+
runtime.ctx.editor.setText("");
|
|
1845
|
+
await runtime.ctx.handleOmfgCommand(complaint);
|
|
1846
|
+
},
|
|
1847
|
+
},
|
|
1848
|
+
{
|
|
1849
|
+
name: "retry",
|
|
1850
|
+
description: "重试上次失败的 agent 回合",
|
|
1851
|
+
handleTui: async (_command, runtime) => {
|
|
1852
|
+
const didRetry = await runtime.ctx.session.retry();
|
|
1853
|
+
if (!didRetry) {
|
|
1854
|
+
runtime.ctx.showStatus("Nothing to retry");
|
|
1855
|
+
}
|
|
1856
|
+
runtime.ctx.editor.setText("");
|
|
1857
|
+
},
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
name: "debug",
|
|
1861
|
+
description: "打开调试工具选择器",
|
|
1862
|
+
handleTui: async (_command, runtime) => {
|
|
1863
|
+
await runtime.ctx.showDebugSelector();
|
|
1864
|
+
runtime.ctx.editor.setText("");
|
|
1865
|
+
},
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
name: "memory",
|
|
1869
|
+
description: "检查和操作记忆维护",
|
|
1870
|
+
acpDescription: "Manage memory",
|
|
1871
|
+
acpInputHint: "<subcommand>",
|
|
1872
|
+
subcommands: [
|
|
1873
|
+
{ name: "view", description: "显示当前记忆注入载荷" },
|
|
1874
|
+
{ name: "stats", description: "显示记忆后端统计" },
|
|
1875
|
+
{ name: "diagnose", description: "运行记忆后端诊断" },
|
|
1876
|
+
{ name: "clear", description: "清除持久化记忆数据和产物" },
|
|
1877
|
+
{ name: "reset", description: "清除的别名" },
|
|
1878
|
+
{ name: "enqueue", description: "排队记忆整合维护" },
|
|
1879
|
+
{ name: "rebuild", description: "入队(enqueue)的别名" },
|
|
1880
|
+
{ name: "mm list", description: "列出当前记忆库中的心智模型" },
|
|
1881
|
+
{ name: "mm show", description: "显示单个心智模型(需要 id)" },
|
|
1882
|
+
{
|
|
1883
|
+
name: "mm refresh",
|
|
1884
|
+
description: "刷新整个记忆库的自动刷新模型,或按 id 刷新单个模型",
|
|
1885
|
+
},
|
|
1886
|
+
{ name: "mm history", description: "对比心智模型的变更历史" },
|
|
1887
|
+
{ name: "mm seed", description: "创建缺失的内置心智模型" },
|
|
1888
|
+
{ name: "mm delete", description: "从记忆库删除心智模型(需要 id)" },
|
|
1889
|
+
{ name: "mm reload", description: "重新拉取缓存的 <mental_models> 块" },
|
|
1890
|
+
],
|
|
1891
|
+
allowArgs: true,
|
|
1892
|
+
handle: async (command, runtime) => {
|
|
1893
|
+
const verb = (command.args.trim().split(/\s+/)[0] ?? "").toLowerCase() || "view";
|
|
1894
|
+
const backend = await resolveMemoryBackend(runtime.settings);
|
|
1895
|
+
switch (verb) {
|
|
1896
|
+
case "view": {
|
|
1897
|
+
const payload = await backend.buildDeveloperInstructions(
|
|
1898
|
+
runtime.settings.getAgentDir(),
|
|
1899
|
+
runtime.settings,
|
|
1900
|
+
runtime.session,
|
|
1901
|
+
);
|
|
1902
|
+
await runtime.output(payload || "Memory payload is empty.");
|
|
1903
|
+
return commandConsumed();
|
|
1904
|
+
}
|
|
1905
|
+
case "clear":
|
|
1906
|
+
case "reset": {
|
|
1907
|
+
await backend.clear(runtime.settings.getAgentDir(), runtime.cwd, runtime.session);
|
|
1908
|
+
await runtime.session.refreshBaseSystemPrompt();
|
|
1909
|
+
await runtime.output("Memory cleared.");
|
|
1910
|
+
return commandConsumed();
|
|
1911
|
+
}
|
|
1912
|
+
case "enqueue":
|
|
1913
|
+
case "rebuild": {
|
|
1914
|
+
await backend.enqueue(runtime.settings.getAgentDir(), runtime.cwd, runtime.session);
|
|
1915
|
+
await runtime.output("Memory consolidation enqueued.");
|
|
1916
|
+
return commandConsumed();
|
|
1917
|
+
}
|
|
1918
|
+
case "stats":
|
|
1919
|
+
case "diagnose": {
|
|
1920
|
+
const hook = verb === "stats" ? backend.stats : backend.diagnose;
|
|
1921
|
+
const payload = await hook?.(runtime.settings.getAgentDir(), runtime.cwd, runtime.session);
|
|
1922
|
+
await runtime.output(payload ?? `Memory ${verb} is not available for the ${backend.id} backend.`);
|
|
1923
|
+
return commandConsumed();
|
|
1924
|
+
}
|
|
1925
|
+
case "mm":
|
|
1926
|
+
return usage(
|
|
1927
|
+
"Mental-model maintenance via /memory mm is unsupported in ACP mode; use the hindsight HTTP API directly.",
|
|
1928
|
+
runtime,
|
|
1929
|
+
);
|
|
1930
|
+
default:
|
|
1931
|
+
return usage("Usage: /memory <view|stats|diagnose|clear|reset|enqueue|rebuild>", runtime);
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
handleTui: async (command, runtime) => {
|
|
1935
|
+
runtime.ctx.editor.setText("");
|
|
1936
|
+
await runtime.ctx.handleMemoryCommand(command.text);
|
|
1937
|
+
},
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
name: "rename",
|
|
1941
|
+
description: "重命名当前会话",
|
|
1942
|
+
inlineHint: "<title>",
|
|
1943
|
+
allowArgs: true,
|
|
1944
|
+
handle: async (command, runtime) => {
|
|
1945
|
+
if (!command.args) return usage("Usage: /rename <title>", runtime);
|
|
1946
|
+
const ok = await runtime.sessionManager.setSessionName(command.args, "user");
|
|
1947
|
+
if (!ok) {
|
|
1948
|
+
await runtime.output("Session name not changed (a user-set name takes precedence).");
|
|
1949
|
+
return commandConsumed();
|
|
1950
|
+
}
|
|
1951
|
+
await runtime.notifyTitleChanged?.();
|
|
1952
|
+
await runtime.output(`Session renamed to ${command.args}.`);
|
|
1953
|
+
return commandConsumed();
|
|
1954
|
+
},
|
|
1955
|
+
handleTui: async (command, runtime) => {
|
|
1956
|
+
const title = command.args.trim();
|
|
1957
|
+
if (!title) {
|
|
1958
|
+
runtime.ctx.showError("Usage: /rename <title>");
|
|
1959
|
+
runtime.ctx.editor.setText("");
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
runtime.ctx.editor.setText("");
|
|
1963
|
+
await runtime.ctx.handleRenameCommand(title);
|
|
1964
|
+
},
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
name: "move",
|
|
1968
|
+
description: "移动当前会话到其他目录",
|
|
1969
|
+
acpDescription: "移动当前会话到其他目录",
|
|
1970
|
+
inlineHint: "[<path>]",
|
|
1971
|
+
allowArgs: true,
|
|
1972
|
+
handle: async (command, runtime) => {
|
|
1973
|
+
if (runtime.session.isStreaming) return usage("Cannot move while streaming.", runtime);
|
|
1974
|
+
if (!command.args) return usage("Usage: /move <path>", runtime);
|
|
1975
|
+
const resolvedPath = resolveToCwd(command.args, runtime.cwd);
|
|
1976
|
+
try {
|
|
1977
|
+
const stat = await fs.stat(resolvedPath);
|
|
1978
|
+
if (!stat.isDirectory()) {
|
|
1979
|
+
return usage(`Not a directory: ${resolvedPath}`, runtime);
|
|
1980
|
+
}
|
|
1981
|
+
} catch {
|
|
1982
|
+
return usage(`Directory does not exist: ${resolvedPath}`, runtime);
|
|
1983
|
+
}
|
|
1984
|
+
try {
|
|
1985
|
+
await runtime.settings.flush();
|
|
1986
|
+
} catch (err) {
|
|
1987
|
+
return usage(`Failed to save pending settings: ${errorMessage(err)}`, runtime);
|
|
1988
|
+
}
|
|
1989
|
+
try {
|
|
1990
|
+
await runtime.session.moveSession(resolvedPath);
|
|
1991
|
+
} catch (err) {
|
|
1992
|
+
return usage(`Move failed: ${errorMessage(err)}`, runtime);
|
|
1993
|
+
}
|
|
1994
|
+
setProjectDir(resolvedPath);
|
|
1995
|
+
await runtime.settings.reloadForCwd(resolvedPath);
|
|
1996
|
+
applyProviderGlobalsFromSettings(runtime.settings);
|
|
1997
|
+
// Reload plugin/capability caches so the next prompt sees commands and
|
|
1998
|
+
// capabilities scoped to the new cwd.
|
|
1999
|
+
await runtime.reloadPlugins();
|
|
2000
|
+
await runtime.notifyConfigChanged?.();
|
|
2001
|
+
await runtime.notifyTitleChanged?.();
|
|
2002
|
+
await runtime.output(`Moved to ${runtime.sessionManager.getCwd()}.`);
|
|
2003
|
+
return commandConsumed();
|
|
2004
|
+
},
|
|
2005
|
+
handleTui: async (command, runtime) => {
|
|
2006
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
2007
|
+
runtime.ctx.editor.setText("");
|
|
2008
|
+
await runtime.ctx.handleMoveCommand(command.args || undefined);
|
|
2009
|
+
},
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
name: "add-dir",
|
|
2013
|
+
description: "向本会话添加工作区目录(多根)",
|
|
2014
|
+
acpDescription: "Add a workspace directory to this session",
|
|
2015
|
+
inlineHint: "<path>",
|
|
2016
|
+
allowArgs: true,
|
|
2017
|
+
handle: async (command, runtime) => {
|
|
2018
|
+
if (runtime.session.isStreaming) return usage("Cannot add a directory while streaming.", runtime);
|
|
2019
|
+
if (!command.args) return usage(formatWorkspaceDirectories(runtime, "Usage: /add-dir <path>"), runtime);
|
|
2020
|
+
const resolved = resolveToCwd(command.args, runtime.cwd);
|
|
2021
|
+
try {
|
|
2022
|
+
const stat = await fs.stat(resolved);
|
|
2023
|
+
if (!stat.isDirectory()) return usage(`Not a directory: ${resolved}`, runtime);
|
|
2024
|
+
} catch {
|
|
2025
|
+
return usage(`Directory does not exist: ${resolved}`, runtime);
|
|
2026
|
+
}
|
|
2027
|
+
let added: string | null;
|
|
2028
|
+
try {
|
|
2029
|
+
added = await runtime.sessionManager.addWorkspaceDirectory(resolved);
|
|
2030
|
+
} catch (err) {
|
|
2031
|
+
return usage(errorMessage(err), runtime);
|
|
2032
|
+
}
|
|
2033
|
+
if (added === null) {
|
|
2034
|
+
await runtime.output(`Already in the workspace: ${resolved}`);
|
|
2035
|
+
return commandConsumed();
|
|
2036
|
+
}
|
|
2037
|
+
await runtime.session.refreshBaseSystemPrompt();
|
|
2038
|
+
await runtime.output(formatWorkspaceDirectories(runtime, `Added ${added}.`));
|
|
2039
|
+
return commandConsumed();
|
|
2040
|
+
},
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
name: "remove-dir",
|
|
2044
|
+
description: "从本会话移除工作区目录",
|
|
2045
|
+
acpDescription: "Remove a workspace directory from this session",
|
|
2046
|
+
inlineHint: "<path>",
|
|
2047
|
+
allowArgs: true,
|
|
2048
|
+
handle: async (command, runtime) => {
|
|
2049
|
+
if (runtime.session.isStreaming) return usage("Cannot remove a directory while streaming.", runtime);
|
|
2050
|
+
if (!command.args) return usage("Usage: /remove-dir <path>", runtime);
|
|
2051
|
+
const resolved = resolveToCwd(command.args, runtime.cwd);
|
|
2052
|
+
if (resolved === path.resolve(runtime.cwd)) {
|
|
2053
|
+
return usage("Cannot remove the working directory; use /move to change it.", runtime);
|
|
2054
|
+
}
|
|
2055
|
+
let removed: string | null;
|
|
2056
|
+
try {
|
|
2057
|
+
removed = await runtime.sessionManager.removeWorkspaceDirectory(resolved);
|
|
2058
|
+
} catch (err) {
|
|
2059
|
+
return usage(errorMessage(err), runtime);
|
|
2060
|
+
}
|
|
2061
|
+
if (removed === null) {
|
|
2062
|
+
await runtime.output(`Not a workspace directory: ${resolved}`);
|
|
2063
|
+
return commandConsumed();
|
|
2064
|
+
}
|
|
2065
|
+
await runtime.session.refreshBaseSystemPrompt();
|
|
2066
|
+
await runtime.output(formatWorkspaceDirectories(runtime, `Removed ${removed}.`));
|
|
2067
|
+
return commandConsumed();
|
|
2068
|
+
},
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "dirs",
|
|
2072
|
+
description: "列出本会话的工作区目录",
|
|
2073
|
+
acpDescription: "List this session's workspace directories",
|
|
2074
|
+
handle: async (_command, runtime) => {
|
|
2075
|
+
await runtime.output(formatWorkspaceDirectories(runtime));
|
|
2076
|
+
return commandConsumed();
|
|
2077
|
+
},
|
|
2078
|
+
},
|
|
2079
|
+
{
|
|
2080
|
+
name: "exit",
|
|
2081
|
+
description: "退出应用",
|
|
2082
|
+
handleTui: shutdownHandlerTui,
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
name: "marketplace",
|
|
2086
|
+
description: "管理市场插件源和已安装插件",
|
|
2087
|
+
acpDescription: "Manage plugins from marketplaces",
|
|
2088
|
+
acpInputHint: "<subcommand>",
|
|
2089
|
+
subcommands: [
|
|
2090
|
+
{ name: "add", description: "添加市场源", usage: "<source>" },
|
|
2091
|
+
{ name: "remove", description: "移除市场源", usage: "<name>" },
|
|
2092
|
+
{ name: "update", description: "更新市场目录", usage: "[name]" },
|
|
2093
|
+
{ name: "list", description: "列出已配置的市场" },
|
|
2094
|
+
{ name: "discover", description: "浏览可用插件", usage: "[marketplace]" },
|
|
2095
|
+
{
|
|
2096
|
+
name: "install",
|
|
2097
|
+
description: "安装插件(无参数时交互式浏览)",
|
|
2098
|
+
usage: "[--force] [name@marketplace]",
|
|
2099
|
+
},
|
|
2100
|
+
{ name: "uninstall", description: "卸载插件(无参数时选择器)", usage: "[name@marketplace]" },
|
|
2101
|
+
{ name: "installed", description: "列出已安装的市场插件" },
|
|
2102
|
+
{ name: "upgrade", description: "升级过期插件", usage: "[name@marketplace]" },
|
|
2103
|
+
{ name: "help", description: "显示使用指南" },
|
|
2104
|
+
],
|
|
2105
|
+
allowArgs: true,
|
|
2106
|
+
handle: async (command, runtime) => {
|
|
2107
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
2108
|
+
if (!verb) {
|
|
2109
|
+
try {
|
|
2110
|
+
const manager = await createMarketplaceManager(runtime);
|
|
2111
|
+
const marketplaces = await manager.listMarketplaces();
|
|
2112
|
+
if (marketplaces.length === 0) {
|
|
2113
|
+
await runtime.output(
|
|
2114
|
+
"No marketplaces configured.\n\nGet started:\n /marketplace add anthropics/claude-plugins-official\n\nThen browse with /marketplace discover",
|
|
2115
|
+
);
|
|
2116
|
+
} else {
|
|
2117
|
+
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
2118
|
+
await runtime.output(
|
|
2119
|
+
`Marketplaces:\n${lines.join("\n")}\n\nUse /marketplace discover to browse plugins, or /marketplace help for all commands`,
|
|
2120
|
+
);
|
|
2121
|
+
}
|
|
2122
|
+
return commandConsumed();
|
|
2123
|
+
} catch (err) {
|
|
2124
|
+
return usage(`Marketplace error: ${errorMessage(err)}`, runtime);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
if (verb === "help") {
|
|
2128
|
+
await runtime.output(
|
|
2129
|
+
[
|
|
2130
|
+
"Marketplace commands:",
|
|
2131
|
+
" /marketplace List configured marketplaces",
|
|
2132
|
+
" /marketplace add <source> Add a marketplace (e.g. owner/repo)",
|
|
2133
|
+
" /marketplace remove <name> Remove a marketplace",
|
|
2134
|
+
" /marketplace update [name] Re-fetch catalog(s)",
|
|
2135
|
+
" /marketplace list List configured marketplaces",
|
|
2136
|
+
" /marketplace discover [marketplace] Browse available plugins",
|
|
2137
|
+
" /marketplace install <name@marketplace> Install a plugin",
|
|
2138
|
+
" /marketplace uninstall <name@marketplace> Uninstall a plugin",
|
|
2139
|
+
" /marketplace installed List installed plugins",
|
|
2140
|
+
" /marketplace upgrade [name@marketplace] Upgrade plugin(s)",
|
|
2141
|
+
"",
|
|
2142
|
+
"Quick start:",
|
|
2143
|
+
" /marketplace add anthropics/claude-plugins-official",
|
|
2144
|
+
].join("\n"),
|
|
2145
|
+
);
|
|
2146
|
+
return commandConsumed();
|
|
2147
|
+
}
|
|
2148
|
+
if ((verb === "install" || verb === "uninstall") && !rest) {
|
|
2149
|
+
return usage(
|
|
2150
|
+
"Interactive plugin pickers are TUI-only. Pass an explicit name@marketplace argument.",
|
|
2151
|
+
runtime,
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
try {
|
|
2155
|
+
const manager = await createMarketplaceManager(runtime);
|
|
2156
|
+
switch (verb) {
|
|
2157
|
+
case "add": {
|
|
2158
|
+
if (!rest) return usage("Usage: /marketplace add <source>", runtime);
|
|
2159
|
+
const entry = await manager.addMarketplace(rest);
|
|
2160
|
+
await runtime.output(`Added marketplace: ${entry.name}`);
|
|
2161
|
+
return commandConsumed();
|
|
2162
|
+
}
|
|
2163
|
+
case "remove":
|
|
2164
|
+
case "rm": {
|
|
2165
|
+
if (!rest) return usage("Usage: /marketplace remove <name>", runtime);
|
|
2166
|
+
await manager.removeMarketplace(rest);
|
|
2167
|
+
await runtime.output(`Removed marketplace: ${rest}`);
|
|
2168
|
+
return commandConsumed();
|
|
2169
|
+
}
|
|
2170
|
+
case "update": {
|
|
2171
|
+
if (rest) {
|
|
2172
|
+
await manager.updateMarketplace(rest);
|
|
2173
|
+
await runtime.output(`Updated marketplace: ${rest}`);
|
|
2174
|
+
} else {
|
|
2175
|
+
const results = await manager.updateAllMarketplaces();
|
|
2176
|
+
await runtime.output(`Updated ${results.length} marketplace(s)`);
|
|
2177
|
+
}
|
|
2178
|
+
return commandConsumed();
|
|
2179
|
+
}
|
|
2180
|
+
case "list": {
|
|
2181
|
+
const marketplaces = await manager.listMarketplaces();
|
|
2182
|
+
if (marketplaces.length === 0) {
|
|
2183
|
+
await runtime.output("No marketplaces configured.");
|
|
2184
|
+
} else {
|
|
2185
|
+
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
2186
|
+
await runtime.output(`Marketplaces:\n${lines.join("\n")}`);
|
|
2187
|
+
}
|
|
2188
|
+
return commandConsumed();
|
|
2189
|
+
}
|
|
2190
|
+
case "discover": {
|
|
2191
|
+
const plugins = await manager.listAvailablePlugins(rest || undefined);
|
|
2192
|
+
if (plugins.length === 0) {
|
|
2193
|
+
const marketplaces = await manager.listMarketplaces();
|
|
2194
|
+
await runtime.output(
|
|
2195
|
+
marketplaces.length === 0
|
|
2196
|
+
? "No marketplaces configured. Try:\n /marketplace add anthropics/claude-plugins-official"
|
|
2197
|
+
: "No plugins available in configured marketplaces",
|
|
2198
|
+
);
|
|
2199
|
+
return commandConsumed();
|
|
2200
|
+
}
|
|
2201
|
+
const lines = ["Available plugins:"];
|
|
2202
|
+
for (const plugin of plugins) {
|
|
2203
|
+
lines.push(` - ${plugin.name}${plugin.version ? `@${plugin.version}` : ""}`);
|
|
2204
|
+
if (plugin.description) lines.push(` ${plugin.description}`);
|
|
2205
|
+
}
|
|
2206
|
+
await runtime.output(lines.join("\n"));
|
|
2207
|
+
return commandConsumed();
|
|
2208
|
+
}
|
|
2209
|
+
case "install": {
|
|
2210
|
+
const parsed = parseMarketplaceInstallArgs(rest);
|
|
2211
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
2212
|
+
const atIndex = parsed.installSpec.lastIndexOf("@");
|
|
2213
|
+
const pluginName = parsed.installSpec.slice(0, atIndex);
|
|
2214
|
+
const marketplace = parsed.installSpec.slice(atIndex + 1);
|
|
2215
|
+
await manager.installPlugin(pluginName, marketplace, { force: parsed.force, scope: parsed.scope });
|
|
2216
|
+
await runtime.reloadPlugins();
|
|
2217
|
+
await runtime.output(`Installed ${pluginName} from ${marketplace}`);
|
|
2218
|
+
return commandConsumed();
|
|
2219
|
+
}
|
|
2220
|
+
case "uninstall": {
|
|
2221
|
+
const parsed = parsePluginScopeArgs(
|
|
2222
|
+
rest,
|
|
2223
|
+
"Usage: /marketplace uninstall [--scope user|project] <name@marketplace>",
|
|
2224
|
+
);
|
|
2225
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
2226
|
+
await manager.uninstallPlugin(parsed.pluginId, parsed.scope);
|
|
2227
|
+
await runtime.reloadPlugins();
|
|
2228
|
+
await runtime.output(`Uninstalled ${parsed.pluginId}`);
|
|
2229
|
+
return commandConsumed();
|
|
2230
|
+
}
|
|
2231
|
+
case "installed": {
|
|
2232
|
+
const installed = await manager.listInstalledPlugins();
|
|
2233
|
+
if (installed.length === 0) {
|
|
2234
|
+
await runtime.output("No marketplace plugins installed");
|
|
2235
|
+
} else {
|
|
2236
|
+
const lines = installed.map(
|
|
2237
|
+
p => ` ${p.id} [${p.scope}]${p.shadowedBy ? " [shadowed]" : ""} (${p.entries.length} entry)`,
|
|
2238
|
+
);
|
|
2239
|
+
await runtime.output(`Installed plugins:\n${lines.join("\n")}`);
|
|
2240
|
+
}
|
|
2241
|
+
return commandConsumed();
|
|
2242
|
+
}
|
|
2243
|
+
case "upgrade": {
|
|
2244
|
+
if (rest) {
|
|
2245
|
+
const parsed = parsePluginScopeArgs(
|
|
2246
|
+
rest,
|
|
2247
|
+
"Usage: /marketplace upgrade [--scope user|project] <name@marketplace>",
|
|
2248
|
+
);
|
|
2249
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
2250
|
+
const result = await manager.upgradePlugin(parsed.pluginId, parsed.scope);
|
|
2251
|
+
await runtime.reloadPlugins();
|
|
2252
|
+
await runtime.output(`Upgraded ${parsed.pluginId} to ${result.version}`);
|
|
2253
|
+
return commandConsumed();
|
|
2254
|
+
}
|
|
2255
|
+
const results = await manager.upgradeAllPlugins();
|
|
2256
|
+
if (results.length === 0) {
|
|
2257
|
+
await runtime.output("All marketplace plugins are up to date");
|
|
2258
|
+
} else {
|
|
2259
|
+
await runtime.reloadPlugins();
|
|
2260
|
+
const lines = results.map(r => ` ${r.pluginId}: ${r.from} -> ${r.to}`);
|
|
2261
|
+
await runtime.output(`Upgraded ${results.length} plugin(s):\n${lines.join("\n")}`);
|
|
2262
|
+
}
|
|
2263
|
+
return commandConsumed();
|
|
2264
|
+
}
|
|
2265
|
+
default:
|
|
2266
|
+
return usage(
|
|
2267
|
+
`Unknown /marketplace subcommand: ${verb}. Use /marketplace help for available commands.`,
|
|
2268
|
+
runtime,
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
} catch (err) {
|
|
2272
|
+
return usage(`Marketplace error: ${errorMessage(err)}`, runtime);
|
|
2273
|
+
}
|
|
2274
|
+
},
|
|
2275
|
+
handleTui: async (command, runtime) => {
|
|
2276
|
+
runtime.ctx.editor.setText("");
|
|
2277
|
+
const args = command.args.trim().split(/\s+/);
|
|
2278
|
+
const sub = args[0] || "install";
|
|
2279
|
+
const rest = args.slice(1).join(" ").trim();
|
|
2280
|
+
|
|
2281
|
+
// /marketplace (no args) or /marketplace install (no args) → interactive browser
|
|
2282
|
+
if ((sub === "install" && !rest) || (!args[0] && !command.args.trim())) {
|
|
2283
|
+
try {
|
|
2284
|
+
runtime.ctx.showPluginSelector("install");
|
|
2285
|
+
} catch (err) {
|
|
2286
|
+
runtime.ctx.showStatus(`Marketplace error: ${err}`);
|
|
2287
|
+
}
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
const mgr = new MarketplaceManager({
|
|
2292
|
+
marketplacesRegistryPath: getMarketplacesRegistryPath(),
|
|
2293
|
+
installedRegistryPath: getInstalledPluginsRegistryPath(),
|
|
2294
|
+
projectInstalledRegistryPath: await resolveOrDefaultProjectRegistryPath(
|
|
2295
|
+
runtime.ctx.sessionManager.getCwd(),
|
|
2296
|
+
),
|
|
2297
|
+
marketplacesCacheDir: getMarketplacesCacheDir(),
|
|
2298
|
+
pluginsCacheDir: getPluginsCacheDir(),
|
|
2299
|
+
clearPluginRootsCache: clearPluginRootsAndCaches,
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
try {
|
|
2303
|
+
switch (sub) {
|
|
2304
|
+
case "add": {
|
|
2305
|
+
if (!rest) {
|
|
2306
|
+
runtime.ctx.showStatus("Usage: /marketplace add <source>");
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
const entry = await mgr.addMarketplace(rest);
|
|
2310
|
+
runtime.ctx.showStatus(`Added marketplace: ${entry.name}`);
|
|
2311
|
+
break;
|
|
2312
|
+
}
|
|
2313
|
+
case "remove":
|
|
2314
|
+
case "rm": {
|
|
2315
|
+
if (!rest) {
|
|
2316
|
+
runtime.ctx.showStatus("Usage: /marketplace remove <name>");
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
await mgr.removeMarketplace(rest);
|
|
2320
|
+
runtime.ctx.showStatus(`Removed marketplace: ${rest}`);
|
|
2321
|
+
break;
|
|
2322
|
+
}
|
|
2323
|
+
case "update": {
|
|
2324
|
+
if (rest) {
|
|
2325
|
+
await mgr.updateMarketplace(rest);
|
|
2326
|
+
runtime.ctx.showStatus(`Updated marketplace: ${rest}`);
|
|
2327
|
+
} else {
|
|
2328
|
+
const results = await mgr.updateAllMarketplaces();
|
|
2329
|
+
runtime.ctx.showStatus(`Updated ${results.length} marketplace(s)`);
|
|
2330
|
+
}
|
|
2331
|
+
break;
|
|
2332
|
+
}
|
|
2333
|
+
case "discover": {
|
|
2334
|
+
const plugins = await mgr.listAvailablePlugins(rest || undefined);
|
|
2335
|
+
if (plugins.length === 0) {
|
|
2336
|
+
const marketplaces = await mgr.listMarketplaces();
|
|
2337
|
+
if (marketplaces.length === 0) {
|
|
2338
|
+
runtime.ctx.showStatus(
|
|
2339
|
+
"No marketplaces configured. Try:\n /marketplace add anthropics/claude-plugins-official",
|
|
2340
|
+
);
|
|
2341
|
+
} else {
|
|
2342
|
+
runtime.ctx.showStatus("No plugins available in configured marketplaces");
|
|
2343
|
+
}
|
|
2344
|
+
} else {
|
|
2345
|
+
const lines = plugins.map(
|
|
2346
|
+
p =>
|
|
2347
|
+
` ${p.name}${p.version ? `@${p.version}` : ""}${p.description ? ` - ${p.description}` : ""}`,
|
|
2348
|
+
);
|
|
2349
|
+
runtime.ctx.showStatus(`Available plugins:\n${lines.join("\n")}`);
|
|
2350
|
+
}
|
|
2351
|
+
break;
|
|
2352
|
+
}
|
|
2353
|
+
case "install": {
|
|
2354
|
+
// Parse: /marketplace install [--force] [--scope user|project] name@marketplace
|
|
2355
|
+
const parsed = parseMarketplaceInstallArgs(rest);
|
|
2356
|
+
if ("error" in parsed) {
|
|
2357
|
+
runtime.ctx.showStatus(parsed.error);
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
const atIdx = parsed.installSpec.lastIndexOf("@");
|
|
2361
|
+
const name = parsed.installSpec.slice(0, atIdx);
|
|
2362
|
+
const marketplace = parsed.installSpec.slice(atIdx + 1);
|
|
2363
|
+
await mgr.installPlugin(name, marketplace, { force: parsed.force, scope: parsed.scope });
|
|
2364
|
+
runtime.ctx.showStatus(`Installed ${name} from ${marketplace}`);
|
|
2365
|
+
break;
|
|
2366
|
+
}
|
|
2367
|
+
case "uninstall": {
|
|
2368
|
+
if (!rest) {
|
|
2369
|
+
// No args → open interactive uninstall selector
|
|
2370
|
+
runtime.ctx.showPluginSelector("uninstall");
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
const uninstArgs = parsePluginScopeArgs(
|
|
2374
|
+
rest,
|
|
2375
|
+
"Usage: /marketplace uninstall [--scope user|project] <name@marketplace>",
|
|
2376
|
+
);
|
|
2377
|
+
if ("error" in uninstArgs) {
|
|
2378
|
+
runtime.ctx.showStatus(uninstArgs.error);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
await mgr.uninstallPlugin(uninstArgs.pluginId, uninstArgs.scope);
|
|
2382
|
+
runtime.ctx.showStatus(`Uninstalled ${uninstArgs.pluginId}`);
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
case "installed": {
|
|
2386
|
+
const installed = await mgr.listInstalledPlugins();
|
|
2387
|
+
if (installed.length === 0) {
|
|
2388
|
+
runtime.ctx.showStatus("No marketplace plugins installed");
|
|
2389
|
+
} else {
|
|
2390
|
+
const lines = installed.map(
|
|
2391
|
+
p => ` ${p.id} [${p.scope}]${p.shadowedBy ? " [shadowed]" : ""} (${p.entries.length} entry)`,
|
|
2392
|
+
);
|
|
2393
|
+
runtime.ctx.showStatus(`Installed plugins:\n${lines.join("\n")}`);
|
|
2394
|
+
}
|
|
2395
|
+
break;
|
|
2396
|
+
}
|
|
2397
|
+
case "upgrade": {
|
|
2398
|
+
if (rest) {
|
|
2399
|
+
const upArgs = parsePluginScopeArgs(
|
|
2400
|
+
rest,
|
|
2401
|
+
"Usage: /marketplace upgrade [--scope user|project] <name@marketplace>",
|
|
2402
|
+
);
|
|
2403
|
+
if ("error" in upArgs) {
|
|
2404
|
+
runtime.ctx.showStatus(upArgs.error);
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
const result = await mgr.upgradePlugin(upArgs.pluginId, upArgs.scope);
|
|
2408
|
+
runtime.ctx.showStatus(`Upgraded ${upArgs.pluginId} to ${result.version}`);
|
|
2409
|
+
} else {
|
|
2410
|
+
const results = await mgr.upgradeAllPlugins();
|
|
2411
|
+
if (results.length === 0) {
|
|
2412
|
+
runtime.ctx.showStatus("All marketplace plugins are up to date");
|
|
2413
|
+
} else {
|
|
2414
|
+
const lines = results.map(r => ` ${r.pluginId}: ${r.from} -> ${r.to}`);
|
|
2415
|
+
runtime.ctx.showStatus(`Upgraded ${results.length} plugin(s):\n${lines.join("\n")}`);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
break;
|
|
2419
|
+
}
|
|
2420
|
+
case "help": {
|
|
2421
|
+
runtime.ctx.showStatus(
|
|
2422
|
+
[
|
|
2423
|
+
"Marketplace commands:",
|
|
2424
|
+
" /marketplace Browse and install plugins",
|
|
2425
|
+
" /marketplace add <source> Add a marketplace (e.g. owner/repo)",
|
|
2426
|
+
" /marketplace remove <name> Remove a marketplace",
|
|
2427
|
+
" /marketplace update [name] Re-fetch catalog(s)",
|
|
2428
|
+
" /marketplace list List configured marketplaces",
|
|
2429
|
+
" /marketplace discover [marketplace] Browse available plugins",
|
|
2430
|
+
" /marketplace install <name@marketplace> Install a plugin",
|
|
2431
|
+
" /marketplace uninstall <name@marketplace> Uninstall a plugin",
|
|
2432
|
+
" /marketplace installed List installed plugins",
|
|
2433
|
+
" /marketplace upgrade [name@marketplace] Upgrade plugin(s)",
|
|
2434
|
+
"",
|
|
2435
|
+
"Quick start:",
|
|
2436
|
+
" /marketplace add anthropics/claude-plugins-official",
|
|
2437
|
+
" /marketplace (opens interactive browser)",
|
|
2438
|
+
].join("\n"),
|
|
2439
|
+
);
|
|
2440
|
+
break;
|
|
2441
|
+
}
|
|
2442
|
+
default: {
|
|
2443
|
+
const marketplaces = await mgr.listMarketplaces();
|
|
2444
|
+
if (marketplaces.length === 0) {
|
|
2445
|
+
runtime.ctx.showStatus(
|
|
2446
|
+
"No marketplaces configured.\n\nGet started:\n /marketplace add anthropics/claude-plugins-official\n\nThen browse plugins with /marketplace or /marketplace discover",
|
|
2447
|
+
);
|
|
2448
|
+
} else {
|
|
2449
|
+
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
2450
|
+
runtime.ctx.showStatus(
|
|
2451
|
+
`Marketplaces:\n${lines.join("\n")}\n\nUse /marketplace discover to browse plugins, or /marketplace help for all commands`,
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
break;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
} catch (err) {
|
|
2458
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2459
|
+
runtime.ctx.showStatus(`Marketplace error: ${msg}`);
|
|
2460
|
+
}
|
|
2461
|
+
},
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
name: "plugins",
|
|
2465
|
+
description: "查看和管理已安装插件",
|
|
2466
|
+
acpDescription: "Manage plugins",
|
|
2467
|
+
acpInputHint: "[list|enable|disable]",
|
|
2468
|
+
subcommands: [
|
|
2469
|
+
{ name: "list", description: "列出所有已安装插件(npm + 市场)" },
|
|
2470
|
+
{ name: "enable", description: "启用市场插件", usage: "<name@marketplace>" },
|
|
2471
|
+
{ name: "disable", description: "禁用市场插件", usage: "<name@marketplace>" },
|
|
2472
|
+
],
|
|
2473
|
+
allowArgs: true,
|
|
2474
|
+
handle: async (command, runtime) => {
|
|
2475
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
2476
|
+
try {
|
|
2477
|
+
if (verb === "enable" || verb === "disable") {
|
|
2478
|
+
const parsed = parsePluginScopeArgs(
|
|
2479
|
+
rest,
|
|
2480
|
+
`Usage: /plugins ${verb} [--scope user|project] <name@marketplace>`,
|
|
2481
|
+
);
|
|
2482
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
2483
|
+
const manager = await createMarketplaceManager(runtime);
|
|
2484
|
+
const isEnable = verb === "enable";
|
|
2485
|
+
await manager.setPluginEnabled(parsed.pluginId, isEnable, parsed.scope);
|
|
2486
|
+
await runtime.reloadPlugins();
|
|
2487
|
+
await runtime.output(`${isEnable ? "Enabled" : "Disabled"} ${parsed.pluginId}`);
|
|
2488
|
+
return commandConsumed();
|
|
2489
|
+
}
|
|
2490
|
+
// Default: list
|
|
2491
|
+
const lines: string[] = [];
|
|
2492
|
+
const npmManager = new PluginManager();
|
|
2493
|
+
const npmPlugins = await npmManager.list();
|
|
2494
|
+
if (npmPlugins.length > 0) {
|
|
2495
|
+
lines.push("npm plugins:");
|
|
2496
|
+
for (const plugin of npmPlugins) {
|
|
2497
|
+
const status = plugin.enabled === false ? " (disabled)" : "";
|
|
2498
|
+
lines.push(` ${plugin.name}@${plugin.version}${status}`);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
const marketplaceManager = await createMarketplaceManager(runtime);
|
|
2503
|
+
const marketplacePlugins = await marketplaceManager.listInstalledPlugins();
|
|
2504
|
+
if (marketplacePlugins.length > 0) {
|
|
2505
|
+
if (lines.length > 0) lines.push("");
|
|
2506
|
+
lines.push("marketplace plugins:");
|
|
2507
|
+
for (const plugin of marketplacePlugins) {
|
|
2508
|
+
const entry = plugin.entries[0];
|
|
2509
|
+
const status = entry?.enabled === false ? " (disabled)" : "";
|
|
2510
|
+
const shadowed = plugin.shadowedBy ? " [shadowed]" : "";
|
|
2511
|
+
lines.push(` ${plugin.id} v${entry?.version ?? "?"}${status} [${plugin.scope}]${shadowed}`);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
await runtime.output(lines.length === 0 ? "No plugins installed" : lines.join("\n"));
|
|
2516
|
+
return commandConsumed();
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
return usage(`Plugin error: ${errorMessage(err)}`, runtime);
|
|
2519
|
+
}
|
|
2520
|
+
},
|
|
2521
|
+
handleTui: async (command, runtime) => {
|
|
2522
|
+
runtime.ctx.editor.setText("");
|
|
2523
|
+
const args = command.args.trim().split(/\s+/);
|
|
2524
|
+
const sub = args[0] || "list";
|
|
2525
|
+
const rest = args.slice(1).join(" ").trim();
|
|
2526
|
+
|
|
2527
|
+
try {
|
|
2528
|
+
const mgr = new MarketplaceManager({
|
|
2529
|
+
marketplacesRegistryPath: getMarketplacesRegistryPath(),
|
|
2530
|
+
installedRegistryPath: getInstalledPluginsRegistryPath(),
|
|
2531
|
+
projectInstalledRegistryPath: await resolveOrDefaultProjectRegistryPath(
|
|
2532
|
+
runtime.ctx.sessionManager.getCwd(),
|
|
2533
|
+
),
|
|
2534
|
+
marketplacesCacheDir: getMarketplacesCacheDir(),
|
|
2535
|
+
pluginsCacheDir: getPluginsCacheDir(),
|
|
2536
|
+
clearPluginRootsCache: clearPluginRootsAndCaches,
|
|
2537
|
+
});
|
|
2538
|
+
|
|
2539
|
+
switch (sub) {
|
|
2540
|
+
case "enable":
|
|
2541
|
+
case "disable": {
|
|
2542
|
+
const parsed = parsePluginScopeArgs(
|
|
2543
|
+
rest ?? "",
|
|
2544
|
+
`Usage: /plugins ${sub} [--scope user|project] <name@marketplace>`,
|
|
2545
|
+
);
|
|
2546
|
+
if ("error" in parsed) {
|
|
2547
|
+
runtime.ctx.showStatus(parsed.error);
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
const isEnable = sub === "enable";
|
|
2551
|
+
await mgr.setPluginEnabled(parsed.pluginId, isEnable, parsed.scope);
|
|
2552
|
+
runtime.ctx.showStatus(`${isEnable ? "Enabled" : "Disabled"} ${parsed.pluginId}`);
|
|
2553
|
+
break;
|
|
2554
|
+
}
|
|
2555
|
+
default: {
|
|
2556
|
+
const lines: string[] = [];
|
|
2557
|
+
|
|
2558
|
+
const npm = new PluginManager();
|
|
2559
|
+
const npmPlugins = await npm.list();
|
|
2560
|
+
if (npmPlugins.length > 0) {
|
|
2561
|
+
lines.push("npm plugins:");
|
|
2562
|
+
for (const p of npmPlugins) {
|
|
2563
|
+
const status = p.enabled === false ? " (disabled)" : "";
|
|
2564
|
+
lines.push(` ${p.name}@${p.version}${status}`);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
const mktPlugins = await mgr.listInstalledPlugins();
|
|
2569
|
+
if (mktPlugins.length > 0) {
|
|
2570
|
+
if (lines.length > 0) lines.push("");
|
|
2571
|
+
lines.push("marketplace plugins:");
|
|
2572
|
+
for (const p of mktPlugins) {
|
|
2573
|
+
const entry = p.entries[0];
|
|
2574
|
+
const status = entry?.enabled === false ? " (disabled)" : "";
|
|
2575
|
+
const shadowed = p.shadowedBy ? " [shadowed]" : "";
|
|
2576
|
+
lines.push(` ${p.id} v${entry?.version ?? "?"}${status} [${p.scope}]${shadowed}`);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
if (lines.length === 0) {
|
|
2581
|
+
runtime.ctx.showStatus("No plugins installed");
|
|
2582
|
+
} else {
|
|
2583
|
+
runtime.ctx.showStatus(lines.join("\n"));
|
|
2584
|
+
}
|
|
2585
|
+
break;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
} catch (err) {
|
|
2589
|
+
runtime.ctx.showStatus(`Plugin error: ${err}`);
|
|
2590
|
+
}
|
|
2591
|
+
},
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
name: "reload-plugins",
|
|
2595
|
+
description: "重新加载全部插件",
|
|
2596
|
+
acpDescription: "Reload all plugins",
|
|
2597
|
+
handle: async (_command, runtime) => {
|
|
2598
|
+
await runtime.reloadPlugins();
|
|
2599
|
+
await runtime.output("Plugins reloaded.");
|
|
2600
|
+
return commandConsumed();
|
|
2601
|
+
},
|
|
2602
|
+
handleTui: async (_command, runtime) => {
|
|
2603
|
+
// Invalidate registry fs caches and the plugin roots cache so
|
|
2604
|
+
// listClaudePluginRoots re-reads from disk on next access.
|
|
2605
|
+
const projectPath = await resolveActiveProjectRegistryPath(runtime.ctx.sessionManager.getCwd());
|
|
2606
|
+
clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
|
|
2607
|
+
await runtime.ctx.refreshSkillState();
|
|
2608
|
+
await runtime.ctx.refreshSlashCommandState();
|
|
2609
|
+
resetCapabilities();
|
|
2610
|
+
runtime.ctx.showStatus("Plugins reloaded.");
|
|
2611
|
+
runtime.ctx.editor.setText("");
|
|
2612
|
+
},
|
|
2613
|
+
},
|
|
2614
|
+
{
|
|
2615
|
+
name: "force",
|
|
2616
|
+
description: "强制下回合使用指定工具",
|
|
2617
|
+
aliases: ["force:"],
|
|
2618
|
+
inlineHint: "<tool-name> [prompt]",
|
|
2619
|
+
allowArgs: true,
|
|
2620
|
+
getTuiAutocompleteDescription: runtime => {
|
|
2621
|
+
const count = runtime.ctx.session.getActiveToolNames().length;
|
|
2622
|
+
return count === 0 ? "Force: no active tools" : `Force: ${count} active tools`;
|
|
2623
|
+
},
|
|
2624
|
+
handle: async (command, runtime) => {
|
|
2625
|
+
const spaceIdx = command.args.indexOf(" ");
|
|
2626
|
+
const toolName = spaceIdx === -1 ? command.args : command.args.slice(0, spaceIdx);
|
|
2627
|
+
const prompt = spaceIdx === -1 ? "" : command.args.slice(spaceIdx + 1).trim();
|
|
2628
|
+
if (!toolName) return usage("Usage: /force:<tool-name> [prompt]", runtime);
|
|
2629
|
+
try {
|
|
2630
|
+
runtime.session.setForcedToolChoice(toolName);
|
|
2631
|
+
} catch (err) {
|
|
2632
|
+
return usage(errorMessage(err), runtime);
|
|
2633
|
+
}
|
|
2634
|
+
await runtime.output(`Next turn forced to use ${toolName}.`);
|
|
2635
|
+
return prompt ? { prompt } : commandConsumed();
|
|
2636
|
+
},
|
|
2637
|
+
handleTui: (command, runtime) => {
|
|
2638
|
+
const spaceIdx = command.args.indexOf(" ");
|
|
2639
|
+
const toolName = spaceIdx === -1 ? command.args : command.args.slice(0, spaceIdx);
|
|
2640
|
+
const prompt = spaceIdx === -1 ? "" : command.args.slice(spaceIdx + 1).trim();
|
|
2641
|
+
|
|
2642
|
+
if (!toolName) {
|
|
2643
|
+
runtime.ctx.showError("Usage: /force:<tool-name> [prompt]");
|
|
2644
|
+
runtime.ctx.editor.setText("");
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
try {
|
|
2649
|
+
runtime.ctx.session.setForcedToolChoice(toolName);
|
|
2650
|
+
runtime.ctx.showStatus(`Next turn forced to use ${toolName}.`);
|
|
2651
|
+
} catch (error) {
|
|
2652
|
+
runtime.ctx.showError(errorMessage(error));
|
|
2653
|
+
runtime.ctx.editor.setText("");
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
runtime.ctx.editor.setText("");
|
|
2658
|
+
|
|
2659
|
+
// If a prompt was provided, pass it through as input
|
|
2660
|
+
if (prompt) return { prompt };
|
|
2661
|
+
},
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
name: "live",
|
|
2665
|
+
description: "启动 Codex 驱动的实时语音模式",
|
|
2666
|
+
handleTui: async (_command, runtime) => {
|
|
2667
|
+
runtime.ctx.editor.setText("");
|
|
2668
|
+
await runtime.ctx.handleLiveCommand();
|
|
2669
|
+
},
|
|
2670
|
+
},
|
|
2671
|
+
{
|
|
2672
|
+
name: "pause",
|
|
2673
|
+
description: "冻结所有代理(主代理、子代理、顾问)直到恢复",
|
|
2674
|
+
handleTui: async (_command, runtime) => {
|
|
2675
|
+
runtime.ctx.editor.setText("");
|
|
2676
|
+
await runPauseScreen(runtime.ctx);
|
|
2677
|
+
},
|
|
2678
|
+
},
|
|
2679
|
+
{
|
|
2680
|
+
name: "quit",
|
|
2681
|
+
aliases: ["q"],
|
|
2682
|
+
description: "退出应用",
|
|
2683
|
+
handleTui: shutdownHandlerTui,
|
|
2684
|
+
},
|
|
2685
|
+
];
|
|
2686
|
+
|
|
2687
|
+
const BUILTIN_SLASH_COMMAND_LOOKUP = new Map<string, SlashCommandSpec>();
|
|
2688
|
+
for (const command of BUILTIN_SLASH_COMMAND_REGISTRY) {
|
|
2689
|
+
BUILTIN_SLASH_COMMAND_LOOKUP.set(command.name, command);
|
|
2690
|
+
for (const alias of command.aliases ?? []) {
|
|
2691
|
+
BUILTIN_SLASH_COMMAND_LOOKUP.set(alias, command);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
export const BUILTIN_SLASH_COMMAND_RESERVED_NAMES: ReadonlySet<string> = new Set(BUILTIN_SLASH_COMMAND_LOOKUP.keys());
|
|
2696
|
+
|
|
2697
|
+
/**
|
|
2698
|
+
* Build getArgumentCompletions from declarative subcommand definitions.
|
|
2699
|
+
* Returns subcommand names filtered by prefix in the dropdown.
|
|
2700
|
+
*/
|
|
2701
|
+
function buildArgumentCompletions(subcommands: SubcommandDef[]): (prefix: string) => AutocompleteItem[] | null {
|
|
2702
|
+
return (argumentPrefix: string) => {
|
|
2703
|
+
if (argumentPrefix.includes(" ")) return null; // past the subcommand
|
|
2704
|
+
const lower = argumentPrefix.toLowerCase();
|
|
2705
|
+
const matches = subcommands
|
|
2706
|
+
.filter(s => s.name.startsWith(lower))
|
|
2707
|
+
.map(s => ({
|
|
2708
|
+
value: `${s.name} `,
|
|
2709
|
+
label: s.name,
|
|
2710
|
+
description: s.description,
|
|
2711
|
+
hint: s.usage,
|
|
2712
|
+
}));
|
|
2713
|
+
return matches.length > 0 ? matches : null;
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
/**
|
|
2718
|
+
* Build getInlineHint from declarative subcommand definitions.
|
|
2719
|
+
* Shows remaining completion + usage as dim ghost text after cursor.
|
|
2720
|
+
*/
|
|
2721
|
+
function buildSubcommandInlineHint(subcommands: SubcommandDef[]): (argumentText: string) => string | null {
|
|
2722
|
+
return (argumentText: string) => {
|
|
2723
|
+
const trimmed = argumentText.trimStart();
|
|
2724
|
+
const spaceIndex = trimmed.indexOf(" ");
|
|
2725
|
+
|
|
2726
|
+
if (spaceIndex === -1) {
|
|
2727
|
+
// Still typing subcommand name — show remaining chars + usage
|
|
2728
|
+
const prefix = trimmed.toLowerCase();
|
|
2729
|
+
if (prefix.length === 0) return null;
|
|
2730
|
+
const match = subcommands.find(s => s.name.startsWith(prefix));
|
|
2731
|
+
if (!match) return null;
|
|
2732
|
+
const remaining = match.name.slice(prefix.length);
|
|
2733
|
+
return remaining + (match.usage ? ` ${match.usage}` : "");
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
// Subcommand typed — show remaining usage params
|
|
2737
|
+
const subName = trimmed.slice(0, spaceIndex).toLowerCase();
|
|
2738
|
+
const afterSub = trimmed.slice(spaceIndex + 1);
|
|
2739
|
+
const sub = subcommands.find(s => s.name === subName);
|
|
2740
|
+
if (!sub?.usage) return null;
|
|
2741
|
+
|
|
2742
|
+
if (afterSub.length > 0) {
|
|
2743
|
+
const usageParts = sub.usage.split(" ");
|
|
2744
|
+
const inputParts = afterSub.trim().split(/\s+/);
|
|
2745
|
+
const remaining = usageParts.slice(inputParts.length);
|
|
2746
|
+
return remaining.length > 0 ? remaining.join(" ") : null;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
return sub.usage;
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
/**
|
|
2754
|
+
* Build getInlineHint for commands with a simple static hint string.
|
|
2755
|
+
* Shows the hint only when no arguments have been typed yet.
|
|
2756
|
+
*/
|
|
2757
|
+
function buildStaticInlineHint(hint: string): (argumentText: string) => string | null {
|
|
2758
|
+
return (argumentText: string) => (argumentText.trim().length === 0 ? hint : null);
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
/**
|
|
2762
|
+
* Build getArgumentCompletions that suggests directories relative to the
|
|
2763
|
+
* current project directory. Used by /move so users can Tab-complete the
|
|
2764
|
+
* destination directory.
|
|
2765
|
+
*/
|
|
2766
|
+
function buildDirectoryArgumentCompletions(): (prefix: string) => Promise<AutocompleteItem[] | null> {
|
|
2767
|
+
return async (argumentPrefix: string) => {
|
|
2768
|
+
const prefix = argumentPrefix.trim();
|
|
2769
|
+
|
|
2770
|
+
const cwd = getProjectDir();
|
|
2771
|
+
const expandedPrefix = expandTilde(prefix);
|
|
2772
|
+
const isAbsolute = path.isAbsolute(expandedPrefix);
|
|
2773
|
+
|
|
2774
|
+
let searchDir: string;
|
|
2775
|
+
let searchPrefix: string;
|
|
2776
|
+
if (
|
|
2777
|
+
prefix === "" ||
|
|
2778
|
+
prefix === "." ||
|
|
2779
|
+
prefix === "./" ||
|
|
2780
|
+
prefix === ".." ||
|
|
2781
|
+
prefix === "../" ||
|
|
2782
|
+
prefix === "~" ||
|
|
2783
|
+
prefix === "~/" ||
|
|
2784
|
+
prefix === "/"
|
|
2785
|
+
) {
|
|
2786
|
+
searchDir = isAbsolute ? expandedPrefix : path.join(cwd, expandedPrefix);
|
|
2787
|
+
searchPrefix = "";
|
|
2788
|
+
} else if (expandedPrefix.endsWith("/")) {
|
|
2789
|
+
searchDir = isAbsolute ? expandedPrefix : path.join(cwd, expandedPrefix);
|
|
2790
|
+
searchPrefix = "";
|
|
2791
|
+
} else {
|
|
2792
|
+
const dir = path.dirname(expandedPrefix);
|
|
2793
|
+
searchDir = isAbsolute ? dir : path.join(cwd, dir);
|
|
2794
|
+
searchPrefix = path.basename(expandedPrefix);
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
try {
|
|
2798
|
+
const entries = await fs.readdir(searchDir, { withFileTypes: true });
|
|
2799
|
+
const suggestions: AutocompleteItem[] = [];
|
|
2800
|
+
for (const entry of entries) {
|
|
2801
|
+
if (!entry.name.toLowerCase().startsWith(searchPrefix.toLowerCase())) continue;
|
|
2802
|
+
if (entry.name === ".git") continue;
|
|
2803
|
+
|
|
2804
|
+
let isDirectory = entry.isDirectory();
|
|
2805
|
+
if (!isDirectory && entry.isSymbolicLink()) {
|
|
2806
|
+
try {
|
|
2807
|
+
isDirectory = (await fs.stat(path.join(searchDir, entry.name))).isDirectory();
|
|
2808
|
+
} catch {
|
|
2809
|
+
continue;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (!isDirectory) continue;
|
|
2813
|
+
|
|
2814
|
+
const absoluteValue = path.join(searchDir, entry.name);
|
|
2815
|
+
const displayValue = buildDirectoryCompletionDisplayValue(prefix, absoluteValue, cwd);
|
|
2816
|
+
suggestions.push({ value: displayValue, label: `${entry.name}/` });
|
|
2817
|
+
}
|
|
2818
|
+
suggestions.sort((a, b) => a.label.localeCompare(b.label));
|
|
2819
|
+
return suggestions.length > 0 ? suggestions : null;
|
|
2820
|
+
} catch {
|
|
2821
|
+
return null;
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
function buildDirectoryCompletionDisplayValue(prefix: string, absoluteValue: string, cwd: string): string {
|
|
2826
|
+
// Preserve the user's prefix style where possible, but always return a
|
|
2827
|
+
// value that /move can resolve (absolute or relative) without escaping.
|
|
2828
|
+
const normalized = path.normalize(absoluteValue);
|
|
2829
|
+
|
|
2830
|
+
if (prefix.startsWith("~/")) {
|
|
2831
|
+
const home = os.homedir();
|
|
2832
|
+
const homeRelative = path.relative(home, normalized);
|
|
2833
|
+
return `~/${homeRelative.replaceAll("\\", "/")}/`;
|
|
2834
|
+
}
|
|
2835
|
+
if (prefix === "~") {
|
|
2836
|
+
const home = os.homedir();
|
|
2837
|
+
const homeRelative = path.relative(home, normalized);
|
|
2838
|
+
return `~/${homeRelative.replaceAll("\\", "/")}/`;
|
|
2839
|
+
}
|
|
2840
|
+
if (prefix.startsWith("/")) {
|
|
2841
|
+
return `${normalized.replaceAll("\\", "/")}/`;
|
|
2842
|
+
}
|
|
2843
|
+
if (prefix.startsWith("./")) {
|
|
2844
|
+
const relative = path.relative(cwd, normalized);
|
|
2845
|
+
return `./${relative.replaceAll("\\", "/")}/`;
|
|
2846
|
+
}
|
|
2847
|
+
if (prefix.startsWith("../")) {
|
|
2848
|
+
const relative = path.relative(cwd, normalized);
|
|
2849
|
+
return `${relative.replaceAll("\\", "/")}/`;
|
|
2850
|
+
}
|
|
2851
|
+
if (prefix === "..") {
|
|
2852
|
+
const relative = path.relative(cwd, normalized);
|
|
2853
|
+
return `${relative.replaceAll("\\", "/")}/`;
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// Default: relative to cwd.
|
|
2857
|
+
const relative = path.relative(cwd, normalized);
|
|
2858
|
+
return `${relative.replaceAll("\\", "/")}/`;
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
/** Builtin command metadata used for slash-command autocomplete and help text. */
|
|
2862
|
+
export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = BUILTIN_SLASH_COMMAND_REGISTRY.map(
|
|
2863
|
+
command => ({
|
|
2864
|
+
name: command.name,
|
|
2865
|
+
aliases: command.aliases,
|
|
2866
|
+
allowArgs: command.allowArgs === true,
|
|
2867
|
+
description: command.description,
|
|
2868
|
+
subcommands: command.subcommands,
|
|
2869
|
+
inlineHint: command.inlineHint,
|
|
2870
|
+
getTuiAutocompleteDescription: command.getTuiAutocompleteDescription,
|
|
2871
|
+
}),
|
|
2872
|
+
);
|
|
2873
|
+
|
|
2874
|
+
function materializeTuiBuiltinSlashCommand(
|
|
2875
|
+
cmd: BuiltinSlashCommand,
|
|
2876
|
+
runtime?: TuiSlashCommandRuntime,
|
|
2877
|
+
): TuiBuiltinSlashCommand {
|
|
2878
|
+
const materialized: TuiBuiltinSlashCommand = { ...cmd };
|
|
2879
|
+
if (cmd.subcommands) {
|
|
2880
|
+
materialized.getArgumentCompletions = buildArgumentCompletions(cmd.subcommands);
|
|
2881
|
+
materialized.getInlineHint = buildSubcommandInlineHint(cmd.subcommands);
|
|
2882
|
+
} else if (cmd.name === "move") {
|
|
2883
|
+
materialized.getArgumentCompletions = buildDirectoryArgumentCompletions();
|
|
2884
|
+
if (cmd.inlineHint) materialized.getInlineHint = buildStaticInlineHint(cmd.inlineHint);
|
|
2885
|
+
} else if (cmd.inlineHint) {
|
|
2886
|
+
materialized.getInlineHint = buildStaticInlineHint(cmd.inlineHint);
|
|
2887
|
+
}
|
|
2888
|
+
if (runtime && cmd.getTuiAutocompleteDescription) {
|
|
2889
|
+
materialized.getAutocompleteDescription = () => cmd.getTuiAutocompleteDescription?.(runtime);
|
|
2890
|
+
}
|
|
2891
|
+
return materialized;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
/**
|
|
2895
|
+
* Materialized builtin slash commands with completion functions derived from
|
|
2896
|
+
* declarative subcommand/hint definitions.
|
|
2897
|
+
*/
|
|
2898
|
+
export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<TuiBuiltinSlashCommand> = BUILTIN_SLASH_COMMAND_DEFS.map(cmd =>
|
|
2899
|
+
materializeTuiBuiltinSlashCommand(cmd),
|
|
2900
|
+
);
|
|
2901
|
+
|
|
2902
|
+
export function buildTuiBuiltinSlashCommands(runtime: TuiSlashCommandRuntime): ReadonlyArray<TuiBuiltinSlashCommand> {
|
|
2903
|
+
return BUILTIN_SLASH_COMMAND_DEFS.map(cmd => materializeTuiBuiltinSlashCommand(cmd, runtime));
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
/**
|
|
2907
|
+
* Unified registry exposed for cross-mode tooling. Each spec carries at least
|
|
2908
|
+
* one of `handle` / `handleTui`. The TUI dispatcher prefers `handleTui`; the
|
|
2909
|
+
* ACP dispatcher requires `handle` and skips TUI-only entries.
|
|
2910
|
+
*/
|
|
2911
|
+
export const BUILTIN_SLASH_COMMANDS_INTERNAL: ReadonlyArray<SlashCommandSpec> = BUILTIN_SLASH_COMMAND_REGISTRY;
|
|
2912
|
+
|
|
2913
|
+
/**
|
|
2914
|
+
* Execute a builtin slash command in the interactive TUI.
|
|
2915
|
+
*
|
|
2916
|
+
* Returns `false` when no builtin matched. Returns `true` when a command
|
|
2917
|
+
* consumed the input entirely. Returns a `string` when the command was handled
|
|
2918
|
+
* but remaining text should be sent as a prompt.
|
|
2919
|
+
*/
|
|
2920
|
+
export async function executeBuiltinSlashCommand(
|
|
2921
|
+
text: string,
|
|
2922
|
+
runtime: BuiltinSlashCommandRuntime,
|
|
2923
|
+
): Promise<string | boolean> {
|
|
2924
|
+
const parsed = parseSlashCommand(text);
|
|
2925
|
+
if (!parsed) return false;
|
|
2926
|
+
|
|
2927
|
+
const command = BUILTIN_SLASH_COMMAND_LOOKUP.get(parsed.name);
|
|
2928
|
+
if (!command) return false;
|
|
2929
|
+
if (parsed.args.length > 0 && !command.allowArgs) {
|
|
2930
|
+
return false;
|
|
2931
|
+
}
|
|
2932
|
+
// Collab guests run a read-mostly replica: session-mutating builtins are
|
|
2933
|
+
// host-only; the allowlist covers purely local/read-only commands.
|
|
2934
|
+
if (runtime.ctx.collabGuest && !COLLAB_GUEST_ALLOWED_COMMANDS[command.name]) {
|
|
2935
|
+
runtime.ctx.showStatus(`/${command.name} is host-only during a collab session`);
|
|
2936
|
+
runtime.ctx.editor.setText("");
|
|
2937
|
+
return true;
|
|
2938
|
+
}
|
|
2939
|
+
if (command.handleTui) {
|
|
2940
|
+
const result = await command.handleTui(parsed, runtime);
|
|
2941
|
+
if (result && typeof result === "object" && "prompt" in result) return result.prompt;
|
|
2942
|
+
return true;
|
|
2943
|
+
}
|
|
2944
|
+
if (command.handle) {
|
|
2945
|
+
// No TUI-specific override → adapt the ACP/text-mode `handle` to the
|
|
2946
|
+
// TUI by routing `runtime.output` through `ctx.showStatus`, clearing
|
|
2947
|
+
// the editor after the call, and reusing the active session's plugin
|
|
2948
|
+
// reload pipeline. Spec authors get a single body usable from either
|
|
2949
|
+
// dispatcher without forcing every TUI test to construct the full
|
|
2950
|
+
// `SlashCommandRuntime` shape.
|
|
2951
|
+
const ctx = runtime.ctx;
|
|
2952
|
+
const adapted: SlashCommandRuntime = {
|
|
2953
|
+
session: ctx.session,
|
|
2954
|
+
sessionManager: ctx.sessionManager,
|
|
2955
|
+
settings: ctx.settings,
|
|
2956
|
+
cwd: ctx.sessionManager.getCwd(),
|
|
2957
|
+
output: (text: string) => {
|
|
2958
|
+
ctx.showStatus(text);
|
|
2959
|
+
},
|
|
2960
|
+
refreshCommands: () => ctx.refreshSlashCommandState(),
|
|
2961
|
+
reloadPlugins: async () => {
|
|
2962
|
+
const projectPath = await resolveActiveProjectRegistryPath(ctx.sessionManager.getCwd());
|
|
2963
|
+
clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
|
|
2964
|
+
await ctx.refreshSkillState();
|
|
2965
|
+
await ctx.refreshSlashCommandState();
|
|
2966
|
+
resetCapabilities();
|
|
2967
|
+
},
|
|
2968
|
+
};
|
|
2969
|
+
const result = await command.handle(parsed, adapted);
|
|
2970
|
+
ctx.editor.setText("");
|
|
2971
|
+
if (result && typeof result === "object" && "prompt" in result) return result.prompt;
|
|
2972
|
+
return true;
|
|
2973
|
+
}
|
|
2974
|
+
return false;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
/** Look up a unified spec by name or alias. Used by the ACP dispatcher. */
|
|
2978
|
+
export function lookupBuiltinSlashCommand(name: string): SlashCommandSpec | undefined {
|
|
2979
|
+
return BUILTIN_SLASH_COMMAND_LOOKUP.get(name);
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
export type { ParsedSlashCommand, SlashCommandResult, SlashCommandRuntime, SlashCommandSpec, TuiSlashCommandRuntime };
|