diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 288f6c2118c..ea24a83425e 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -176,6 +176,7 @@ export const globalSettingsSchema = z.object({ terminalZshOhMy: z.boolean().optional(), terminalZshP10k: z.boolean().optional(), terminalZdotdir: z.boolean().optional(), + terminalAutoShow: z.boolean().optional(), execaShellPath: z.string().optional(), diagnosticsEnabled: z.boolean().optional(), @@ -356,6 +357,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { terminalZshP10k: false, terminalZdotdir: true, terminalShellIntegrationDisabled: true, + terminalAutoShow: false, diagnosticsEnabled: true, diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index b20539afe49..a613b3b4c84 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -282,6 +282,7 @@ export type ExtensionState = Pick< | "terminalZshOhMy" | "terminalZshP10k" | "terminalZdotdir" + | "terminalAutoShow" | "execaShellPath" | "diagnosticsEnabled" | "language" diff --git a/src/core/tools/ExecuteCommandTool.ts b/src/core/tools/ExecuteCommandTool.ts index 8fcb917b134..c6261032809 100644 --- a/src/core/tools/ExecuteCommandTool.ts +++ b/src/core/tools/ExecuteCommandTool.ts @@ -75,7 +75,7 @@ export class ExecuteCommandTool extends BaseTool<"execute_command"> { const provider = await task.providerRef.deref() const providerState = await provider?.getState() - const { terminalShellIntegrationDisabled = true } = providerState ?? {} + const { terminalShellIntegrationDisabled = true, terminalAutoShow = false } = providerState ?? {} // Get command execution timeout from VSCode configuration (in seconds) const commandExecutionTimeoutSeconds = vscode.workspace @@ -157,6 +157,7 @@ export type ExecuteCommandOptions = { command: string customCwd?: string terminalShellIntegrationDisabled?: boolean + terminalAutoShow?: boolean commandExecutionTimeout?: number agentTimeout?: number } @@ -168,6 +169,7 @@ export async function executeCommandInTerminal( command, customCwd, terminalShellIntegrationDisabled = true, + terminalAutoShow = false, commandExecutionTimeout = 0, agentTimeout = 0, }: ExecuteCommandOptions, @@ -369,7 +371,9 @@ export async function executeCommandInTerminal( const terminal = await TerminalRegistry.getOrCreateTerminal(workingDir, task.taskId, terminalProvider) if (terminal instanceof Terminal) { - terminal.terminal.show(true) + if (terminalAutoShow) { + terminal.terminal.show(true) + } // Update the working directory in case the terminal we asked for has // a different working directory so that the model will know where the diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 1106d340050..861f5cd00aa 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2162,6 +2162,7 @@ export class ClineProvider terminalZshOhMy, terminalZshP10k, terminalZdotdir, + terminalAutoShow, mcpEnabled, currentApiConfigName, listApiConfigMeta, @@ -2282,6 +2283,7 @@ export class ClineProvider terminalZshOhMy: terminalZshOhMy ?? false, terminalZshP10k: terminalZshP10k ?? false, terminalZdotdir: terminalZdotdir ?? false, + terminalAutoShow: terminalAutoShow ?? false, mcpEnabled: mcpEnabled ?? true, currentApiConfigName: currentApiConfigName ?? "default", listApiConfigMeta: listApiConfigMeta ?? [], @@ -2509,6 +2511,7 @@ export class ClineProvider terminalZshOhMy: stateValues.terminalZshOhMy ?? false, terminalZshP10k: stateValues.terminalZshP10k ?? false, terminalZdotdir: stateValues.terminalZdotdir ?? false, + terminalAutoShow: stateValues.terminalAutoShow ?? false, mode: stateValues.mode ?? defaultModeSlug, language: stateValues.language ?? formatLanguage(vscode.env.language), mcpEnabled: stateValues.mcpEnabled ?? true, diff --git a/webview-ui/src/components/chat/CommandExecution.tsx b/webview-ui/src/components/chat/CommandExecution.tsx index af1d72c6a54..2a319d64b29 100644 --- a/webview-ui/src/components/chat/CommandExecution.tsx +++ b/webview-ui/src/components/chat/CommandExecution.tsx @@ -35,6 +35,7 @@ interface CommandExecutionProps { export const CommandExecution = ({ executionId, text, icon, title }: CommandExecutionProps) => { const { terminalShellIntegrationDisabled = false, + terminalAutoShow = false, allowedCommands = [], deniedCommands = [], setAllowedCommands, @@ -44,8 +45,10 @@ export const CommandExecution = ({ executionId, text, icon, title }: CommandExec const { command, output: parsedOutput } = useMemo(() => parseCommandAndOutput(text), [text]) // If we aren't opening the VSCode terminal for this command then we default - // to expanding the command execution output. - const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled) + // to expanding the command execution output. When terminalAutoShow is + // enabled, also auto-expand output regardless of terminal mode so users + // can always see what is happening. + const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled || terminalAutoShow) const [streamingOutput, setStreamingOutput] = useState("") const [status, setStatus] = useState(null) diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 47e087615e3..e45d09144eb 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -183,6 +183,7 @@ const SettingsView = forwardRef(({ onDone, t terminalZshOhMy, terminalZshP10k, terminalZdotdir, + terminalAutoShow, writeDelayMs, showRooIgnoredFiles, enableSubfolderRules, @@ -396,6 +397,7 @@ const SettingsView = forwardRef(({ onDone, t terminalZshOhMy, terminalZshP10k, terminalZdotdir, + terminalAutoShow, terminalOutputPreviewSize: terminalOutputPreviewSize ?? "medium", mcpEnabled, maxOpenTabsContext: Math.min(Math.max(0, maxOpenTabsContext ?? 20), 500), @@ -862,6 +864,7 @@ const SettingsView = forwardRef(({ onDone, t terminalZshOhMy={terminalZshOhMy} terminalZshP10k={terminalZshP10k} terminalZdotdir={terminalZdotdir} + terminalAutoShow={terminalAutoShow} setCachedStateField={setCachedStateField} /> )} diff --git a/webview-ui/src/components/settings/TerminalSettings.tsx b/webview-ui/src/components/settings/TerminalSettings.tsx index 07f062cc018..49f8bdc4208 100644 --- a/webview-ui/src/components/settings/TerminalSettings.tsx +++ b/webview-ui/src/components/settings/TerminalSettings.tsx @@ -26,6 +26,7 @@ type TerminalSettingsProps = HTMLAttributes & { terminalZshOhMy?: boolean terminalZshP10k?: boolean terminalZdotdir?: boolean + terminalAutoShow?: boolean setCachedStateField: SetCachedStateField< | "terminalOutputPreviewSize" | "terminalShellIntegrationTimeout" @@ -36,6 +37,7 @@ type TerminalSettingsProps = HTMLAttributes & { | "terminalZshOhMy" | "terminalZshP10k" | "terminalZdotdir" + | "terminalAutoShow" > } @@ -49,6 +51,7 @@ export const TerminalSettings = ({ terminalZshOhMy, terminalZshP10k, terminalZdotdir, + terminalAutoShow, setCachedStateField, className, ...props @@ -124,6 +127,21 @@ export const TerminalSettings = ({ {t("settings:terminal.outputPreviewSize.description")} + + + setCachedStateField("terminalAutoShow", e.target.checked)} + data-testid="terminal-auto-show-checkbox"> + {t("settings:terminal.autoShow.label")} + +
+ {t("settings:terminal.autoShow.description")} +
+
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index ce7a607d9a8..500d89bd53e 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -82,6 +82,8 @@ export interface ExtensionStateContextType extends ExtensionState { setTerminalShellIntegrationTimeout: (value: number) => void terminalShellIntegrationDisabled?: boolean setTerminalShellIntegrationDisabled: (value: boolean) => void + terminalAutoShow?: boolean + setTerminalAutoShow: (value: boolean) => void terminalZdotdir?: boolean setTerminalZdotdir: (value: boolean) => void setTtsEnabled: (value: boolean) => void @@ -544,6 +546,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((prevState) => ({ ...prevState, terminalShellIntegrationTimeout: value })), setTerminalShellIntegrationDisabled: (value) => setState((prevState) => ({ ...prevState, terminalShellIntegrationDisabled: value })), + setTerminalAutoShow: (value) => setState((prevState) => ({ ...prevState, terminalAutoShow: value })), setTerminalZdotdir: (value) => setState((prevState) => ({ ...prevState, terminalZdotdir: value })), setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })), setTaskSyncEnabled: (value) => setState((prevState) => ({ ...prevState, taskSyncEnabled: value }) as any), diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 8ec42367f14..2aec034a14a 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -754,6 +754,10 @@ "label": "Terminal character limit", "description": "Overrides the line limit to prevent memory issues by enforcing a hard cap on output size. If exceeded, keeps the beginning and end and shows a placeholder to Roo where content is skipped. <0>Learn more" }, + "autoShow": { + "label": "Auto-show terminal on command execution", + "description": "Automatically reveal the terminal panel when Roo runs a command. When using the VS Code terminal (shell integration enabled), the terminal panel is shown. When using the Inline Terminal, command output in chat is auto-expanded instead." + }, "outputPreviewSize": { "label": "Command output preview size", "description": "Controls how much command output Roo sees directly. Full output is always saved and accessible when needed.",