@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12

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.
Files changed (247) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
  3. package/dist/cli.js +10775 -10762
  4. package/dist/types/advisor/runtime.d.ts +1 -1
  5. package/dist/types/capability/mcp.d.ts +11 -0
  6. package/dist/types/capability/skill.d.ts +6 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/custom-models.d.ts +31 -0
  14. package/dist/types/config/model-config-values.d.ts +19 -0
  15. package/dist/types/config/model-patch.d.ts +93 -0
  16. package/dist/types/config/model-provider-discovery.d.ts +51 -0
  17. package/dist/types/config/model-registry.d.ts +9 -52
  18. package/dist/types/config/settings-schema.d.ts +1 -34
  19. package/dist/types/config/settings.d.ts +5 -3
  20. package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
  21. package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
  22. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  23. package/dist/types/discovery/contained-path.d.ts +33 -0
  24. package/dist/types/discovery/index.d.ts +1 -0
  25. package/dist/types/eval/executor-base.d.ts +8 -2
  26. package/dist/types/eval/kernel-session-registry.d.ts +60 -0
  27. package/dist/types/export/share.d.ts +1 -1
  28. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  29. package/dist/types/extensibility/extensions/loader.d.ts +7 -0
  30. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  31. package/dist/types/extensibility/shared-events.d.ts +3 -2
  32. package/dist/types/extensibility/skills.d.ts +5 -0
  33. package/dist/types/lsp/diagnostics.d.ts +96 -0
  34. package/dist/types/lsp/index.d.ts +7 -128
  35. package/dist/types/lsp/servers.d.ts +72 -0
  36. package/dist/types/lsp/tool.d.ts +42 -0
  37. package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
  38. package/dist/types/lsp/writethrough.d.ts +33 -0
  39. package/dist/types/mcp/transports/header-policy.d.ts +46 -0
  40. package/dist/types/mcp/types.d.ts +15 -0
  41. package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
  42. package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
  43. package/dist/types/modes/components/agent-hub.d.ts +2 -0
  44. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  45. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  46. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  47. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  48. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  49. package/dist/types/modes/theme/color.d.ts +19 -0
  50. package/dist/types/modes/theme/loader.d.ts +19 -0
  51. package/dist/types/modes/theme/schema.d.ts +175 -0
  52. package/dist/types/modes/theme/symbols.d.ts +28 -0
  53. package/dist/types/modes/theme/theme-class.d.ts +244 -0
  54. package/dist/types/modes/theme/theme.d.ts +9 -280
  55. package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
  56. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  57. package/dist/types/registry/agent-registry.d.ts +1 -3
  58. package/dist/types/secrets/index.d.ts +26 -2
  59. package/dist/types/secrets/message-transform.d.ts +40 -0
  60. package/dist/types/secrets/obfuscator.d.ts +0 -108
  61. package/dist/types/secrets/placeholder-scan.d.ts +95 -0
  62. package/dist/types/secrets/placeholder.d.ts +94 -0
  63. package/dist/types/secrets/replacement.d.ts +82 -0
  64. package/dist/types/session/agent-session-events.d.ts +2 -2
  65. package/dist/types/session/agent-session-types.d.ts +6 -0
  66. package/dist/types/session/agent-session.d.ts +2 -2
  67. package/dist/types/session/messages.d.ts +1 -0
  68. package/dist/types/session/prewalk.d.ts +3 -1
  69. package/dist/types/session/session-handoff.d.ts +3 -3
  70. package/dist/types/session/session-manager.d.ts +32 -0
  71. package/dist/types/session/session-provider-boundary.d.ts +1 -1
  72. package/dist/types/session/session-tools.d.ts +8 -0
  73. package/dist/types/session/turn-recovery.d.ts +9 -4
  74. package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
  75. package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
  76. package/dist/types/slash-commands/builtin-control.d.ts +2 -0
  77. package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
  78. package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
  79. package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
  80. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  81. package/dist/types/slash-commands/builtin-session.d.ts +2 -0
  82. package/dist/types/task/index.d.ts +2 -0
  83. package/dist/types/tools/gh-common.d.ts +69 -0
  84. package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
  85. package/dist/types/tools/gh-pr-diff.d.ts +102 -0
  86. package/dist/types/tools/gh-run-watch.d.ts +78 -0
  87. package/dist/types/tools/gh-search.d.ts +61 -0
  88. package/dist/types/tools/gh-types.d.ts +340 -0
  89. package/dist/types/tools/gh-view.d.ts +88 -0
  90. package/dist/types/tools/gh.d.ts +4 -201
  91. package/dist/types/tools/read-archive.d.ts +16 -0
  92. package/dist/types/tools/read-format.d.ts +104 -0
  93. package/dist/types/tools/read-path-resolution.d.ts +19 -0
  94. package/dist/types/tools/read-pdf-images.d.ts +12 -0
  95. package/dist/types/tools/read-renderer.d.ts +24 -0
  96. package/dist/types/tools/read-selector.d.ts +27 -0
  97. package/dist/types/tools/read-sqlite.d.ts +16 -0
  98. package/dist/types/tools/read-summary.d.ts +19 -0
  99. package/dist/types/tools/read.d.ts +1 -23
  100. package/dist/types/tools/shell-tokenize.d.ts +18 -1
  101. package/dist/types/utils/changelog.d.ts +0 -5
  102. package/package.json +13 -13
  103. package/scripts/legacy-pi-virtual-module.ts +4 -1
  104. package/src/advisor/runtime.ts +12 -7
  105. package/src/capability/mcp.ts +11 -0
  106. package/src/capability/skill.ts +6 -0
  107. package/src/cli/command-help.ts +4 -0
  108. package/src/cli/gallery-cli.ts +1 -1
  109. package/src/cli-commands.ts +5 -0
  110. package/src/commands/commit.ts +16 -3
  111. package/src/commands/share.ts +71 -0
  112. package/src/commit/agentic/index.ts +39 -21
  113. package/src/commit/execute.ts +56 -0
  114. package/src/commit/index.ts +2 -1
  115. package/src/commit/pipeline.ts +20 -7
  116. package/src/config/custom-models.ts +188 -0
  117. package/src/config/model-config-values.ts +128 -0
  118. package/src/config/model-discovery.ts +1 -1
  119. package/src/config/model-patch.ts +252 -0
  120. package/src/config/model-provider-discovery.ts +132 -0
  121. package/src/config/model-registry.ts +84 -704
  122. package/src/config/settings-schema.ts +2 -33
  123. package/src/config/settings.ts +53 -3
  124. package/src/debug/raw-sse-buffer.ts +20 -0
  125. package/src/discovery/agent-plugin-format.ts +551 -0
  126. package/src/discovery/agent-plugins.ts +341 -0
  127. package/src/discovery/claude-plugins.ts +21 -5
  128. package/src/discovery/contained-path.ts +75 -0
  129. package/src/discovery/helpers.ts +23 -9
  130. package/src/discovery/index.ts +1 -0
  131. package/src/discovery/omp-plugins.ts +20 -7
  132. package/src/edit/hashline/diff.ts +5 -1
  133. package/src/eval/executor-base.ts +39 -6
  134. package/src/eval/jl/executor.ts +82 -371
  135. package/src/eval/kernel-session-registry.ts +398 -0
  136. package/src/eval/py/executor.ts +53 -261
  137. package/src/eval/rb/executor.ts +36 -279
  138. package/src/exec/non-interactive-env.ts +1 -0
  139. package/src/export/share.ts +2 -1
  140. package/src/extensibility/custom-tools/types.ts +2 -2
  141. package/src/extensibility/extensions/loader.ts +78 -14
  142. package/src/extensibility/extensions/runner.ts +6 -0
  143. package/src/extensibility/extensions/types.ts +12 -0
  144. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  145. package/src/extensibility/shared-events.ts +3 -2
  146. package/src/extensibility/skills.ts +9 -0
  147. package/src/internal-urls/skill-protocol.ts +14 -0
  148. package/src/internal-urls/vault-protocol.ts +2 -2
  149. package/src/launch/client.ts +11 -1
  150. package/src/launch/protocol.ts +7 -2
  151. package/src/lsp/diagnostics.ts +516 -0
  152. package/src/lsp/index.ts +20 -2819
  153. package/src/lsp/servers.ts +296 -0
  154. package/src/lsp/tool.ts +1352 -0
  155. package/src/lsp/workspace-diagnostics.ts +170 -0
  156. package/src/lsp/writethrough.ts +561 -0
  157. package/src/main.ts +3 -2
  158. package/src/mcp/config.ts +3 -0
  159. package/src/mcp/manager.ts +12 -9
  160. package/src/mcp/transports/header-policy.ts +95 -0
  161. package/src/mcp/transports/http.ts +35 -52
  162. package/src/mcp/transports/sse.ts +20 -27
  163. package/src/mcp/types.ts +15 -0
  164. package/src/modes/acp/acp-agent.ts +15 -4
  165. package/src/modes/components/agent-dashboard.ts +2 -0
  166. package/src/modes/components/agent-hub-projection.ts +2 -2
  167. package/src/modes/components/agent-hub-renderer.ts +3 -7
  168. package/src/modes/components/agent-hub.ts +5 -0
  169. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  170. package/src/modes/components/chat-transcript-builder.ts +3 -0
  171. package/src/modes/components/custom-editor.ts +0 -9
  172. package/src/modes/components/status-line/component.ts +1 -0
  173. package/src/modes/components/status-line/segments.ts +6 -4
  174. package/src/modes/components/status-line/types.ts +2 -0
  175. package/src/modes/components/tool-execution.ts +13 -16
  176. package/src/modes/components/tree-selector.ts +62 -3
  177. package/src/modes/controllers/command-controller.ts +8 -2
  178. package/src/modes/controllers/event-controller.ts +15 -15
  179. package/src/modes/controllers/input-controller.ts +20 -2
  180. package/src/modes/controllers/selector-controller.ts +1 -0
  181. package/src/modes/theme/color.ts +133 -0
  182. package/src/modes/theme/loader.ts +178 -0
  183. package/src/modes/theme/schema.ts +263 -0
  184. package/src/modes/theme/symbols.ts +1000 -0
  185. package/src/modes/theme/theme-class.ts +611 -0
  186. package/src/modes/theme/theme.ts +27 -2453
  187. package/src/modes/theme/tui-adapters.ts +279 -0
  188. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  189. package/src/modes/utils/ui-helpers.ts +1 -0
  190. package/src/registry/agent-registry.ts +2 -2
  191. package/src/sdk.ts +6 -49
  192. package/src/secrets/index.ts +51 -6
  193. package/src/secrets/message-transform.ts +287 -0
  194. package/src/secrets/obfuscator.ts +39 -1303
  195. package/src/secrets/placeholder-scan.ts +506 -0
  196. package/src/secrets/placeholder.ts +309 -0
  197. package/src/secrets/replacement.ts +216 -0
  198. package/src/session/agent-session-events.ts +2 -2
  199. package/src/session/agent-session-types.ts +6 -0
  200. package/src/session/agent-session.ts +146 -21
  201. package/src/session/indexed-session-storage.ts +7 -2
  202. package/src/session/messages.ts +1 -0
  203. package/src/session/prewalk.ts +57 -17
  204. package/src/session/session-advisors.ts +32 -34
  205. package/src/session/session-context.ts +3 -5
  206. package/src/session/session-handoff.ts +39 -16
  207. package/src/session/session-manager.ts +67 -3
  208. package/src/session/session-provider-boundary.ts +3 -6
  209. package/src/session/session-tools.ts +37 -3
  210. package/src/session/todo-tracker.ts +11 -1
  211. package/src/session/turn-recovery.ts +115 -84
  212. package/src/slash-commands/builtin-collaboration.ts +504 -0
  213. package/src/slash-commands/builtin-completions.ts +291 -0
  214. package/src/slash-commands/builtin-control.ts +78 -0
  215. package/src/slash-commands/builtin-lifecycle.ts +506 -0
  216. package/src/slash-commands/builtin-marketplace.ts +567 -0
  217. package/src/slash-commands/builtin-modes.ts +518 -0
  218. package/src/slash-commands/builtin-registry.ts +21 -2996
  219. package/src/slash-commands/builtin-session.ts +592 -0
  220. package/src/task/executor.ts +17 -14
  221. package/src/task/index.ts +21 -6
  222. package/src/tools/bash-skill-urls.ts +15 -0
  223. package/src/tools/bash.ts +10 -11
  224. package/src/tools/debug.ts +1 -1
  225. package/src/tools/gh-common.ts +288 -0
  226. package/src/tools/gh-pr-checkout.ts +679 -0
  227. package/src/tools/gh-pr-diff.ts +473 -0
  228. package/src/tools/gh-run-watch.ts +1015 -0
  229. package/src/tools/gh-search.ts +500 -0
  230. package/src/tools/gh-types.ts +379 -0
  231. package/src/tools/gh-view.ts +612 -0
  232. package/src/tools/gh.ts +109 -3821
  233. package/src/tools/jtd-to-json-schema.ts +123 -21
  234. package/src/tools/read-archive.ts +209 -0
  235. package/src/tools/read-format.ts +593 -0
  236. package/src/tools/read-path-resolution.ts +36 -0
  237. package/src/tools/read-pdf-images.ts +250 -0
  238. package/src/tools/read-renderer.ts +289 -0
  239. package/src/tools/read-selector.ts +84 -0
  240. package/src/tools/read-sqlite.ts +215 -0
  241. package/src/tools/read-summary.ts +199 -0
  242. package/src/tools/read.ts +82 -1824
  243. package/src/tools/shell-tokenize.ts +99 -1
  244. package/src/utils/changelog.ts +1 -1
  245. package/src/utils/title-generator.ts +3 -1
  246. package/src/web/search/providers/exa.ts +1 -1
  247. package/src/web/search/providers/zai.ts +12 -3
@@ -0,0 +1,518 @@
1
+ import * as path from "node:path";
2
+ import {
3
+ expandRoleAlias,
4
+ formatModelString,
5
+ getModelMatchPreferences,
6
+ resolveCliModel,
7
+ } from "../config/model-resolver";
8
+ import type { SettingPath } from "../config/settings";
9
+ import { describeLoopLimitRuntime } from "../modes/loop-limit";
10
+ import type { InteractiveModeContext } from "../modes/types";
11
+ import type { AgentSession } from "../session/agent-session";
12
+ import type { ComputerTool } from "../tools/computer";
13
+ import { computerExposureMode } from "../tools/computer/exposure";
14
+ import type { InspectImageMode } from "../utils/inspect-image-mode";
15
+ import { commandConsumed, errorMessage, usage } from "./helpers/parse";
16
+ import { handleSecurityCommand } from "./helpers/security";
17
+ import type { SlashCommandSpec } from "./types";
18
+
19
+ export function refreshStatusLine(ctx: InteractiveModeContext): void {
20
+ ctx.statusLine.invalidate();
21
+ ctx.ui.requestRender();
22
+ }
23
+
24
+ /** `/fast status` label for the active model: "on" when its family is priority, else "off". */
25
+ function formatFastModeStatus(session: AgentSession): string {
26
+ return session.isFastModeEnabled() ? "on" : "off";
27
+ }
28
+
29
+ /** Detailed, session-effective `/computer status` diagnostics. */
30
+ async function formatComputerUseStatus(session: AgentSession): Promise<string> {
31
+ const enabled = session.settings.get("computer.enabled");
32
+ const active = session.getEnabledToolNames().includes("computer");
33
+ const model = session.model;
34
+ const modelName = model ? formatModelString(model) : "none";
35
+ const exposure = !enabled
36
+ ? "not exposed (disabled)"
37
+ : !active
38
+ ? "not exposed (tool inactive)"
39
+ : computerExposureMode(model);
40
+ const configured = {
41
+ display: session.settings.get("computer.display"),
42
+ maxWidth: session.settings.get("computer.maxWidth"),
43
+ maxHeight: session.settings.get("computer.maxHeight"),
44
+ };
45
+ const computerTool = active
46
+ ? (session.getToolByName("computer") as Pick<ComputerTool, "capabilities"> | undefined)
47
+ : undefined;
48
+ const capabilities = await computerTool?.capabilities();
49
+ const capabilityStatus = capabilities
50
+ ? [
51
+ `backend=${capabilities.backend}${capabilities.displayServer ? `/${capabilities.displayServer}` : ""}`,
52
+ `capture=${capabilities.capture} (${capabilities.capturePermission})`,
53
+ `input=${capabilities.input} (${capabilities.inputPermission})`,
54
+ `ax=${capabilities.ax} (${capabilities.axPermission})`,
55
+ `backgroundWindowInput=${capabilities.backgroundWindowInput}`,
56
+ `deliveryModes=${capabilities.deliveryModes.join(",") || "none"}`,
57
+ ].join(", ")
58
+ : "session not started";
59
+ return [
60
+ `Computer use: ${enabled ? "enabled" : "disabled"}`,
61
+ `tool: ${active ? "active" : "inactive"}`,
62
+ `exposure: ${exposure}`,
63
+ `model: ${modelName}`,
64
+ `configured: display=${configured.display}, maxWidth=${configured.maxWidth}, maxHeight=${configured.maxHeight}`,
65
+ `capabilities: ${capabilityStatus}`,
66
+ ].join(" · ");
67
+ }
68
+
69
+ /**
70
+ * Apply a session-scoped computer-use toggle: flip the active tool slate first
71
+ * (so a failed enable never leaves a stale settings override), then record the
72
+ * runtime override — never `settings.set`, which would persist to settings.json.
73
+ * Returns the operator feedback line.
74
+ */
75
+ async function applyComputerUseToggle(session: AgentSession, enable: boolean): Promise<string> {
76
+ const applied = await session.setComputerToolEnabled(enable);
77
+ if (enable && !applied) {
78
+ return "Computer use is unavailable in this session.";
79
+ }
80
+ session.settings.override("computer.enabled", enable);
81
+ return enable
82
+ ? `Computer use enabled for this session. ${await formatComputerUseStatus(session)}`
83
+ : "Computer use disabled for this session.";
84
+ }
85
+
86
+ /** Session-effective `/vision status` line. */
87
+ function formatVisionStatus(session: AgentSession): string {
88
+ const { mode, active, model } = session.inspectImageState();
89
+ const override = session.getInspectImageModeOverride();
90
+ const modelObj = session.model;
91
+ const capability = modelObj
92
+ ? modelObj.input.includes("image")
93
+ ? "native image input"
94
+ : "no native image input"
95
+ : "no active model";
96
+ return [
97
+ `inspect_image: ${active ? "active" : "inactive"}`,
98
+ `mode: ${mode}${override ? " (session override)" : ""}`,
99
+ ...(override ? [`configured: ${session.settings.get("inspect_image.mode")}`] : []),
100
+ `model: ${model ?? "none"} (${capability})`,
101
+ ].join(" · ");
102
+ }
103
+
104
+ /** Applies a `/vision` mode for this session and returns the operator feedback line. */
105
+ async function applyVisionMode(session: AgentSession, mode: InspectImageMode): Promise<string> {
106
+ const applied = await session.setInspectImageMode(mode);
107
+ if (!applied) {
108
+ return "inspect_image is unavailable in this session.";
109
+ }
110
+ return `Vision mode: ${mode}. ${formatVisionStatus(session)}`;
111
+ }
112
+
113
+ const AUTOCOMPLETE_DETAIL_LIMIT = 48;
114
+
115
+ function shortDetail(value: string, limit = AUTOCOMPLETE_DETAIL_LIMIT): string {
116
+ const singleLine = value.replace(/\s+/g, " ").trim();
117
+ return singleLine.length <= limit ? singleLine : `${singleLine.slice(0, limit - 1)}…`;
118
+ }
119
+
120
+ export function formatTokenCount(value: number): string {
121
+ return value.toLocaleString();
122
+ }
123
+
124
+ export const BUILTIN_MODE_SLASH_COMMANDS: ReadonlyArray<SlashCommandSpec> = [
125
+ {
126
+ name: "security",
127
+ description: "Plan, run, inspect, import, and compare OMP-native security scans",
128
+ allowArgs: true,
129
+ acpInputHint: "<plan|scan|status|cancel|scans|show|import|export|validate|compare|disposition>",
130
+ subcommands: [
131
+ { name: "plan", description: "Create an immutable security scan plan" },
132
+ { name: "scan", description: "Start a planned or newly planned native scan" },
133
+ { name: "status", description: "Show native scan operation status" },
134
+ { name: "cancel", description: "Cancel a running native scan" },
135
+ { name: "scans", description: "List stored project security scans" },
136
+ { name: "show", description: "Render a scan or security:// resource" },
137
+ { name: "import", description: "Import SARIF or a Codex Security bundle" },
138
+ { name: "export", description: "Export a canonical bundle, SARIF, or report" },
139
+ { name: "validate", description: "Validate one finding with OMP-native tools" },
140
+ { name: "compare", description: "Compare finding lineage across two scans" },
141
+ { name: "disposition", description: "Set a finding disposition with rationale" },
142
+ ],
143
+ handle: handleSecurityCommand,
144
+ },
145
+ {
146
+ name: "settings",
147
+ description: "Open settings menu",
148
+ handleTui: (_command, runtime) => {
149
+ runtime.ctx.showSettingsSelector();
150
+ runtime.ctx.editor.setText("");
151
+ },
152
+ },
153
+ {
154
+ name: "setup",
155
+ aliases: ["providers"],
156
+ description: "Open provider setup",
157
+ allowArgs: true,
158
+ subcommands: [{ name: "providers", description: "Configure sign-in and web search providers" }],
159
+ handleTui: async (command, runtime) => {
160
+ const args = command.args.trim().toLowerCase();
161
+ const opensProviders = args === "" || args === "providers";
162
+ if (opensProviders) {
163
+ await runtime.ctx.showProviderSetup();
164
+ } else {
165
+ runtime.ctx.showWarning(`Usage: /${command.name} [providers]`);
166
+ }
167
+ runtime.ctx.editor.setText("");
168
+ },
169
+ },
170
+ {
171
+ name: "plan",
172
+ description: "Toggle plan mode (agent plans before executing)",
173
+ inlineHint: "[prompt]",
174
+ allowArgs: true,
175
+ getTuiAutocompleteDescription: runtime => {
176
+ if (!runtime.ctx.settings.get("plan.enabled" as SettingPath)) return "Plan: disabled in settings";
177
+ if (runtime.ctx.planModeEnabled) {
178
+ const planFile = runtime.ctx.planModePlanFilePath;
179
+ return `Plan: on${planFile ? ` (${path.basename(planFile)})` : ""}`;
180
+ }
181
+ if (runtime.ctx.goalModeEnabled) return "Plan: blocked by goal mode";
182
+ return "Plan: off";
183
+ },
184
+ handleTui: async (command, runtime) => {
185
+ await runtime.ctx.handlePlanModeCommand(command.args || undefined);
186
+ runtime.ctx.editor.setText("");
187
+ },
188
+ },
189
+ {
190
+ name: "plan-review",
191
+ description: "Re-open the plan review for the latest plan (plan mode only)",
192
+ getTuiAutocompleteDescription: runtime =>
193
+ runtime.ctx.planModeEnabled ? "Plan review: available" : "Plan review: plan mode inactive",
194
+ handleTui: async (_command, runtime) => {
195
+ await runtime.ctx.openPlanReview();
196
+ runtime.ctx.editor.setText("");
197
+ },
198
+ },
199
+ {
200
+ name: "vibe",
201
+ description: "Toggle vibe mode (direct persistent fast/good worker sessions; read-only toolset)",
202
+ inlineHint: "[prompt]",
203
+ allowArgs: true,
204
+ getTuiAutocompleteDescription: runtime => {
205
+ if (runtime.ctx.vibeModeEnabled) return "Vibe: on";
206
+ if (runtime.ctx.planModeEnabled) return "Vibe: blocked by plan mode";
207
+ if (runtime.ctx.goalModeEnabled) return "Vibe: blocked by goal mode";
208
+ return "Vibe: off";
209
+ },
210
+ handleTui: async (command, runtime) => {
211
+ await runtime.ctx.handleVibeModeCommand(command.args || undefined);
212
+ runtime.ctx.editor.setText("");
213
+ },
214
+ },
215
+ {
216
+ name: "goal",
217
+ description: "Toggle goal mode (persistent autonomous objective for this session)",
218
+ subcommands: [
219
+ { name: "set", description: "Set or replace the goal", usage: "<objective>" },
220
+ { name: "show", description: "Show current goal details" },
221
+ { name: "pause", description: "Pause the current goal" },
222
+ { name: "resume", description: "Resume a paused goal" },
223
+ { name: "drop", description: "Drop the current goal" },
224
+ { name: "budget", description: "Adjust the token budget", usage: "<N|off>" },
225
+ ],
226
+ inlineHint: "[objective]",
227
+ allowArgs: true,
228
+ getTuiAutocompleteDescription: runtime => {
229
+ if (!runtime.ctx.settings.get("goal.enabled" as SettingPath)) return "Goal: disabled in settings";
230
+ if (runtime.ctx.planModeEnabled) return "Goal: blocked by plan mode";
231
+ const state = runtime.ctx.session.getGoalModeState();
232
+ return state ? `Goal: ${state.goal.status} (${shortDetail(state.goal.objective)})` : "Goal: off";
233
+ },
234
+ handleTui: async (command, runtime) => {
235
+ await runtime.ctx.handleGoalModeCommand(command.args || undefined);
236
+ runtime.ctx.editor.setText("");
237
+ },
238
+ },
239
+ {
240
+ name: "guided-goal",
241
+ description: "Have the agent interview you in chat, then set up goal mode",
242
+ inlineHint: "[rough objective]",
243
+ allowArgs: true,
244
+ handleTui: async (command, runtime) => {
245
+ // Clear the slash draft BEFORE the await: the handler blocks for the
246
+ // whole kickoff turn, and a post-await clear would wipe an answer the
247
+ // user starts typing while the first interview question streams.
248
+ runtime.ctx.editor.setText("");
249
+ await runtime.ctx.handleGuidedGoalCommand(command.args || undefined);
250
+ },
251
+ },
252
+ {
253
+ name: "loop",
254
+ description:
255
+ "Toggle loop mode. While enabled, the next prompt you send re-submits after every yield. Esc cancels the current iteration; /loop again to disable.",
256
+ inlineHint: "[count|duration] [prompt]",
257
+ allowArgs: true,
258
+ getTuiAutocompleteDescription: runtime => {
259
+ if (!runtime.ctx.loopModeEnabled) return "Loop: off";
260
+ if (runtime.ctx.loopModePaused) return "Loop: paused";
261
+ if (runtime.ctx.loopLimit) return `Loop: on (${describeLoopLimitRuntime(runtime.ctx.loopLimit)})`;
262
+ if (runtime.ctx.loopPrompt) return "Loop: on (repeating prompt)";
263
+ return "Loop: on (waiting for next prompt)";
264
+ },
265
+ handleTui: async (command, runtime) => {
266
+ const prompt = await runtime.ctx.handleLoopCommand(command.args);
267
+ runtime.ctx.editor.setText("");
268
+ // Surface any inline prompt so the dispatcher returns it and the normal
269
+ // submit flow runs the first loop iteration (recording it as the loop prompt).
270
+ if (prompt) return { prompt };
271
+ },
272
+ },
273
+ {
274
+ name: "queue",
275
+ description: "Queue a message for after the agent yields",
276
+ inlineHint: "<message>",
277
+ allowArgs: true,
278
+ handleTui: async (command, runtime) => {
279
+ await runtime.ctx.handleQueueCommand(command.args);
280
+ },
281
+ },
282
+ {
283
+ name: "model",
284
+ aliases: ["models"],
285
+ description: "Switch model for this session",
286
+ acpDescription: "Show current model selection",
287
+ getTuiAutocompleteDescription: runtime => {
288
+ const model = runtime.ctx.session.model;
289
+ return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
290
+ },
291
+ handle: async (command, runtime) => {
292
+ if (command.args) {
293
+ const modelId = command.args.trim();
294
+ const availableModels = runtime.session.getAvailableModels?.() ?? [];
295
+ const match = availableModels.find(
296
+ model => model.id === modelId || `${model.provider}/${model.id}` === modelId,
297
+ );
298
+ if (!match) {
299
+ return usage(
300
+ `Unknown model: ${modelId}. Use ACP \`session/setModel\` for picker-driven selection or list available models with /model.`,
301
+ runtime,
302
+ );
303
+ }
304
+ try {
305
+ await runtime.session.setModel(match);
306
+ await runtime.output(`Model set to ${match.provider}/${match.id}.`);
307
+ await runtime.notifyTitleChanged?.();
308
+ await runtime.notifyConfigChanged?.();
309
+ return commandConsumed();
310
+ } catch (err) {
311
+ return usage(`Failed to set model: ${errorMessage(err)}`, runtime);
312
+ }
313
+ }
314
+
315
+ const model = runtime.session.model;
316
+ await runtime.output(
317
+ model ? `Current model: ${model.provider}/${model.id}` : "No model is currently selected.",
318
+ );
319
+ return commandConsumed();
320
+ },
321
+ handleTui: (_command, runtime) => {
322
+ runtime.ctx.showModelSelector();
323
+ runtime.ctx.editor.setText("");
324
+ },
325
+ },
326
+ {
327
+ name: "switch",
328
+ description: "Switch model for this session (same as alt+p)",
329
+ getTuiAutocompleteDescription: runtime => {
330
+ const model = runtime.ctx.session.model;
331
+ return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
332
+ },
333
+ handleTui: (_command, runtime) => {
334
+ runtime.ctx.showModelSelector({ temporaryOnly: true });
335
+ runtime.ctx.editor.setText("");
336
+ },
337
+ },
338
+ {
339
+ name: "fast",
340
+ description: "Toggle priority service tier (OpenAI service_tier=priority, Anthropic speed=fast)",
341
+ acpDescription: "Toggle fast mode",
342
+ acpInputHint: "[on|off|status]",
343
+ subcommands: [
344
+ { name: "on", description: "Enable fast mode" },
345
+ { name: "off", description: "Disable fast mode" },
346
+ { name: "status", description: "Show fast mode status" },
347
+ ],
348
+ allowArgs: true,
349
+ getTuiAutocompleteDescription: runtime => `Fast: ${formatFastModeStatus(runtime.ctx.session)}`,
350
+ handle: async (command, runtime) => {
351
+ const arg = command.args.toLowerCase();
352
+ if (!arg || arg === "toggle") {
353
+ const enabled = runtime.session.toggleFastMode();
354
+ await runtime.output(`Fast mode ${enabled ? "enabled" : "disabled"}.`);
355
+ return commandConsumed();
356
+ }
357
+ if (arg === "on") {
358
+ const supported = runtime.session.setFastMode(true);
359
+ await runtime.output(supported ? "Fast mode enabled." : "Fast mode is unavailable for the current model.");
360
+ return commandConsumed();
361
+ }
362
+ if (arg === "off") {
363
+ runtime.session.setFastMode(false);
364
+ await runtime.output("Fast mode disabled.");
365
+ return commandConsumed();
366
+ }
367
+ if (arg === "status") {
368
+ await runtime.output(`Fast mode is ${formatFastModeStatus(runtime.session)}.`);
369
+ return commandConsumed();
370
+ }
371
+ return usage("Usage: /fast [on|off|status]", runtime);
372
+ },
373
+ handleTui: (command, runtime) => {
374
+ const arg = command.args.trim().toLowerCase();
375
+ if (!arg || arg === "toggle") {
376
+ const enabled = runtime.ctx.session.toggleFastMode();
377
+ refreshStatusLine(runtime.ctx);
378
+ runtime.ctx.showStatus(`Fast mode ${enabled ? "enabled" : "disabled"}.`);
379
+ runtime.ctx.editor.setText("");
380
+ return;
381
+ }
382
+ if (arg === "on") {
383
+ const supported = runtime.ctx.session.setFastMode(true);
384
+ refreshStatusLine(runtime.ctx);
385
+ runtime.ctx.showStatus(
386
+ supported ? "Fast mode enabled." : "Fast mode is unavailable for the current model.",
387
+ );
388
+ runtime.ctx.editor.setText("");
389
+ return;
390
+ }
391
+ if (arg === "off") {
392
+ runtime.ctx.session.setFastMode(false);
393
+ refreshStatusLine(runtime.ctx);
394
+ runtime.ctx.showStatus("Fast mode disabled.");
395
+ runtime.ctx.editor.setText("");
396
+ return;
397
+ }
398
+ if (arg === "status") {
399
+ runtime.ctx.showStatus(`Fast mode is ${formatFastModeStatus(runtime.ctx.session)}.`);
400
+ runtime.ctx.editor.setText("");
401
+ return;
402
+ }
403
+ runtime.ctx.showStatus("Usage: /fast [on|off|status]");
404
+ runtime.ctx.editor.setText("");
405
+ },
406
+ },
407
+ {
408
+ name: "computer",
409
+ description: "Toggle the native computer-use tool for this session",
410
+ acpDescription: "Toggle computer use",
411
+ acpInputHint: "[on|off|status]",
412
+ subcommands: [
413
+ { name: "on", description: "Enable computer use for this session" },
414
+ { name: "off", description: "Disable computer use for this session" },
415
+ { name: "status", description: "Show computer use status" },
416
+ ],
417
+ allowArgs: true,
418
+ getTuiAutocompleteDescription: runtime =>
419
+ `Computer: ${runtime.ctx.session.settings.get("computer.enabled") ? "on" : "off"}`,
420
+ handle: async (command, runtime) => {
421
+ const arg = command.args.trim().toLowerCase();
422
+ if (arg === "status") {
423
+ await runtime.output(await formatComputerUseStatus(runtime.session));
424
+ return commandConsumed();
425
+ }
426
+ if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
427
+ const enable = arg === "off" ? false : arg === "on" || !runtime.session.settings.get("computer.enabled");
428
+ await runtime.output(await applyComputerUseToggle(runtime.session, enable));
429
+ return commandConsumed();
430
+ }
431
+ return usage("Usage: /computer [on|off|status]", runtime);
432
+ },
433
+ handleTui: async (command, runtime) => {
434
+ const arg = command.args.trim().toLowerCase();
435
+ if (arg === "status") {
436
+ runtime.ctx.showStatus(await formatComputerUseStatus(runtime.ctx.session));
437
+ runtime.ctx.editor.setText("");
438
+ return;
439
+ }
440
+ if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
441
+ const enable =
442
+ arg === "off" ? false : arg === "on" || !runtime.ctx.session.settings.get("computer.enabled");
443
+ runtime.ctx.showStatus(await applyComputerUseToggle(runtime.ctx.session, enable));
444
+ runtime.ctx.editor.setText("");
445
+ return;
446
+ }
447
+ runtime.ctx.showStatus("Usage: /computer [on|off|status]");
448
+ runtime.ctx.editor.setText("");
449
+ },
450
+ },
451
+ {
452
+ name: "vision",
453
+ description: "Control the inspect_image vision-delegation tool for this session",
454
+ acpDescription: "Toggle vision delegation",
455
+ acpInputHint: "[on|off|auto|status]",
456
+ subcommands: [
457
+ { name: "on", description: "Always expose inspect_image this session" },
458
+ { name: "off", description: "Never expose inspect_image this session" },
459
+ { name: "auto", description: "Follow inspect_image.mode (auto hides it for vision-capable models)" },
460
+ { name: "status", description: "Show inspect_image status" },
461
+ ],
462
+ allowArgs: true,
463
+ getTuiAutocompleteDescription: runtime => `Vision: ${runtime.ctx.session.inspectImageState().mode}`,
464
+ handle: async (command, runtime) => {
465
+ const arg = command.args.trim().toLowerCase();
466
+ if (arg === "status") {
467
+ await runtime.output(formatVisionStatus(runtime.session));
468
+ return commandConsumed();
469
+ }
470
+ if (arg === "on" || arg === "off" || arg === "auto") {
471
+ await runtime.output(await applyVisionMode(runtime.session, arg));
472
+ return commandConsumed();
473
+ }
474
+ return usage("Usage: /vision [on|off|auto|status]", runtime);
475
+ },
476
+ handleTui: async (command, runtime) => {
477
+ const arg = command.args.trim().toLowerCase();
478
+ if (arg === "status") {
479
+ runtime.ctx.showStatus(formatVisionStatus(runtime.ctx.session));
480
+ runtime.ctx.editor.setText("");
481
+ return;
482
+ }
483
+ if (arg === "on" || arg === "off" || arg === "auto") {
484
+ runtime.ctx.showStatus(await applyVisionMode(runtime.ctx.session, arg));
485
+ runtime.ctx.editor.setText("");
486
+ return;
487
+ }
488
+ runtime.ctx.showStatus("Usage: /vision [on|off|auto|status]");
489
+ runtime.ctx.editor.setText("");
490
+ },
491
+ },
492
+ {
493
+ name: "prewalk",
494
+ description: "Switch to a fast/cheap model at the next action (works even without --prewalk)",
495
+ acpDescription: "Prewalk at the next action",
496
+ handle: async (_command, runtime) => {
497
+ const rolePattern = expandRoleAlias("@smol", runtime.settings);
498
+ const resolved = resolveCliModel({
499
+ cliModel: rolePattern,
500
+ modelRegistry: runtime.session.modelRegistry,
501
+ preferences: getModelMatchPreferences(runtime.settings),
502
+ });
503
+ if (resolved.error || !resolved.model) {
504
+ return usage(resolved.error ?? `Model "${rolePattern}" not found`, runtime);
505
+ }
506
+ if (!runtime.session.modelRegistry.hasConfiguredAuth(resolved.model)) {
507
+ return usage(`No API key for ${resolved.model.provider}/${resolved.model.id}`, runtime);
508
+ }
509
+ const armed = runtime.session.armPrewalk(resolved.model, resolved.thinkingLevel);
510
+ if (armed) {
511
+ await runtime.output(
512
+ `Prewalk on: switching to ${resolved.model.provider}/${resolved.model.id} at the next edit/write (todo-gated).`,
513
+ );
514
+ }
515
+ return commandConsumed();
516
+ },
517
+ },
518
+ ];