Skip to content

[BUG] System prompt on windows says shell is cmd.exe but if powershell is used via VS Code terminal provider when inline terminal is off #82

@rossdonald

Description

@rossdonald

Problem

On Windows, the system prompt reports the shell as cmd.exe (and generates cmd.exe-appropriate command chaining hints), but when using the VS Code terminal provider, commands actually execute in the user's configured default shell (typically PowerShell). This causes the Zoo to generate commands incompatible with the actual runtime shell.

Context (who is affected and when)

Affects all Windows users who have inline terminal off. When Inline terminal is unticked it uses VS Code default terminal profile, typically PowerShell or pwsh.

Reproduction steps

  1. On Windows, ensure VS Code default terminal profile is PowerShell (the default): terminal.integrated.defaultProfile.windows is set to a PowerShell profile.
  2. Go to Zoo Settings, Terminal Settings and Untick "Use Inline Terminal" .
  3. Start a Zoo Code task and ask it to run a command.
  4. Observe:
    • The system prompt will contain Default Shell: C:\Windows\System32\cmd.exe in the SYSTEM INFORMATION section.
    • The rules section will include cmd.exe-specific notes like "Using && for cmd.exe command chaining".
    • The actual command will execute in the VS Code integrated terminal, which runs PowerShell.

Expected result

The system prompt should report the same shell that will actually execute commands. When the VS Code terminal provider is active, the shell reported should match the user's VS Code terminal profile. When the Execa provider is active, it should report the Execa shell (which may differ).

Actual result

The system prompt always reports the shell determined by getShell() in src/utils/shell.ts, which independently reads VS Code terminal config but falls back to cmd.exe. Meanwhile, the VS Code terminal provider executes commands in whatever shell VS Code actually uses (typically PowerShell). The two code paths do not coordinate.

Analysis

The prompt-side shell detection

The system prompt is built in src/core/prompts/sections/system-info.ts and calls getShell() from src/utils/shell.ts:

Default Shell: ${getShell()}

The getShell() function in src/utils/shell.ts reads VS Code's terminal.integrated.defaultProfile.windows config to determine the shell:

Zoo-Code/src/utils/shell.ts

Lines 196 to 213 in d191fe0

// If the profile name indicates PowerShell, do version-based detection.
// In testing it was found these typically do not have a path, and this
// implementation manages to deductively get the correct version of PowerShell
if (defaultProfileName.toLowerCase().includes("powershell")) {
const normalizedPath = normalizeShellPath(profile?.path)
if (normalizedPath) {
// If there's an explicit PowerShell path, return that
return normalizedPath
} else if (profile?.source === "PowerShell") {
// If the profile is sourced from PowerShell, assume the newest
return SHELL_PATHS.POWERSHELL_7
}
// Otherwise, assume legacy Windows PowerShell
return SHELL_PATHS.POWERSHELL_LEGACY
}
// If there's a specific path, return that immediately
const normalizedPath = normalizeShellPath(profile?.path)

When defaultProfileName is not set (null), the fallback chain eventually reaches COMSPEC or cmd.exe:

Zoo-Code/src/utils/shell.ts

Lines 355 to 359 in d191fe0

if (!shell) {
shell = getShellFromEnv()
}
// 4. Finally, fall back to a default

This same getShell() function is also used to determine command chaining operators and shell-specific notes in the rules section:

export function getCommandChainOperator(): string {
const shell = getShell().toLowerCase()
// Check for PowerShell (both Windows PowerShell and PowerShell Core)
if (shell.includes("powershell") || shell.includes("pwsh")) {
return ";"
}
// Check for cmd.exe
if (shell.includes("cmd.exe")) {
return "&&"
}
// Default to Unix-style && for bash, zsh, sh, and other shells
// This also covers Git Bash, WSL, and other Unix-like environments on Windows
return "&&"
}

The execution-side shell selection

The actual terminal provider is selected in src/core/tools/ExecuteCommandTool.ts based on terminalShellIntegrationDisabled:

const terminalProvider = terminalShellIntegrationDisabled ? "execa" : "vscode"

When using the VS Code terminal provider (src/integrations/terminal/Terminal.ts), the shell is whatever VS Code's integrated terminal uses. The extension does not control this; VS Code uses the user's configured default profile.

When using the Execa provider (src/integrations/terminal/ExecaTerminalProcess.ts), the shell defaults to true (which means cmd.exe on Windows) unless an explicit execaShellPath is set:

shell: BaseTerminal.getExecaShellPath() || true,

Cause

The root cause is that getShell() does not account for which terminal provider will actually be used at execution time, and does not handle the case where VS Code's defaultProfile.windows is not explicitly set but the effective default is still PowerShell. VS Code defaults to PowerShell on Windows without writing it to settings, so the config lookup returns null, causing the fallback to cmd.exe.

Possible fix

  1. Use unified logic for getShell() so the system prompt is aware of the terminal provider. getShell() should check terminalShellIntegrationDisabled setting and use different detection logic:

    • For the VS Code terminal provider: detect the shell VS Code will actually use. Consider using terminal profile detection logic that accounts for VS Code's built-in defaults (PowerShell on Windows).
    • For the Execa provider: use getExecaShellPath() if set, otherwise report cmd.exe on Windows.
  2. Fix getWindowsShellFromVSCode() to handle the case where defaultProfileName is null. VS Code's effective default on Windows is PowerShell, so the fallback should not immediately jump to cmd.exe. Consider returning PowerShell as the default when no profile name is configured on Windows.

Environment

  • OS: Windows 11
  • App version: latest

Related issues

RooCodeInc/Roo-Code#11958
RooCodeInc/Roo-Code#8530

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions