Where does the bug appear (feature/product)?
Cursor IDE
Describe the Bug
The IDE agent on Windows ignores the configured default terminal profile and always uses PowerShell for command execution. Even with terminal.integrated.defaultProfile.windows set to a custom shell (e.g., Nushell), the agent continues spawning PowerShell. The interactive terminal respects the setting – only the agent ignores it.
Settings that were tried and confirmed to have no effect on the agent:
terminal.integrated.defaultProfile.windows: “Nushell” (with profile defined)terminal.integrated.automationProfile.windows: path to nu.exeterminal.integrated.shell.windows(deprecated): path to nu.exe- “Legacy Terminal Tool” toggle: ON
- Closing all agent chats + full Cursor restart
PowerShell causes multi-second cold starts (5-30s) on every agent command and post-execution hangs of several seconds, significantly slowing down agent workflows.
Steps to Reproduce
- Install Nushell (or any non-standard shell)
- Configure in settings.json:
{
"terminal.integrated.defaultProfile.windows": "Nushell",
"terminal.integrated.profiles.windows": {
"Nushell": {
"path": "C:\\Users\\...\\nu.exe"
}
}
}
- Restart Cursor completely (quit + reopen)
- Open a new agent chat
- Ask the agent to run any shell command
- Observe: agent writes to
ps-script-*.ps1temp files, command runs in PowerShell - Meanwhile, opening a new interactive terminal correctly uses Nushell
Expected Behavior
The agent should use the shell configured in terminal.integrated.defaultProfile.windows, matching the behavior of the interactive terminal.
Operating System
Windows 10/11
Version Information
IDE: 2.4.37
Additional Information
Root cause analysis (from examining the shared shell execution library in IDE v2.4.37, same library as CLI v2026.02.13-41ac335):
The IDE correctly resolves the shell path from VS Code settings during extension host startup:
// From workbench.desktop.main.js (extensionHost init):
let shellHint;
try {
shellHint = await this._terminalProfileResolverService.getDefaultShell({
allowAutomationShell: false, os: platform, remoteAuthority: undefined
});
} catch {
try {
const profile = this._terminalProfileService.getDefaultProfile();
shellHint = profile?.path;
} catch { /* Failed to get default terminal shell path */ }
}
// Passed to agent as: { userTerminalHint: shellHint }
So userTerminalHint is correctly populated (e.g., "C:\\Users\\...\\nu.exe"). But the shell type detection function (shared with the CLI agent – identical logic) ignores it:
function detectShellType(hint) {
const shellStr = hint || process.env.SHELL || "";
const isWindows = process.platform === "win32";
return shellStr.includes("zsh") ? ShellType.Zsh
: shellStr.includes("bash") && isGitBash ? ShellType.Bash
// On Windows, the PowerShell condition includes a system-level fallback:
: shellStr.includes("pwsh") || shellStr.includes("powershell")
|| isWindows && (commandExists("pwsh") || commandExists("powershell"))
? ShellType.PowerShell
// No check for "nu", "fish", or any other shell -- UNREACHABLE on Windows
// because the PowerShell condition above always matches when PS is installed
: commandExists("zsh") ? ShellType.Zsh
: commandExists("bash") && isGitBash ? ShellType.Bash
: commandExists("pwsh") || commandExists("powershell") ? ShellType.PowerShell
: ShellType.Naive;
}
The hint "C:\\Users\\...\\nu.exe" doesn’t match any includes() check for zsh, bash, or powershell. Critically, on Windows, the PowerShell ternary arm combines both string matching and a system-level commandExists fallback in a single || chain. Since PowerShell is always installed on Windows, this condition evaluates to true regardless of the hint string, and ShellType.PowerShell is returned. Any shell check placed after this point is dead code on Windows.
Additionally, the executor factory’s default handler (which creates a NaiveTerminalExecutor) doesn’t wire userTerminalHint into its shell resolution – it falls back to the system PowerShell on Windows even when invoked.
Proposed fix (same as CLI – shared library):
The shell library needs two changes:
- Add non-standard shell recognition in
detectShellType(), placed before the PowerShell condition (critical – placing it after is unreachable on Windows due to the combinedincludes || commandExistscondition):
: shellStr.includes("bash") && isGitBash ? ShellType.Bash
// These must come BEFORE the PowerShell check:
: shellStr.includes("nu") ? ShellType.Naive
: shellStr.includes("fish") ? ShellType.Naive
: shellStr.length > 0 ? ShellType.Naive // trust any explicit user config
// Then the existing PowerShell check:
: shellStr.includes("pwsh") || shellStr.includes("powershell")
|| isWindows && (commandExists("pwsh") || commandExists("powershell"))
? ShellType.PowerShell
: /* existing fallback chain */
- Wire
userTerminalHintinto the executor factory’s default handler’s shell resolution, and/or add an explicitShellType.Naivecase using the existingNaiveTerminalExecutorclass (which spawnsshell -c "command"generically – NOT theBashStateExecutorwhich always requires Bash):
case ShellType.Naive:
return new LazyExecutor(Promise.resolve(
new NaiveTerminalExecutor(process.cwd(), {
shell: opts.userTerminalHint || process.env.SHELL || "/bin/sh",
...opts
})
));
No new settings are needed for the IDE – terminal.integrated.defaultProfile.windows already populates userTerminalHint correctly. It just needs to be respected by the detection function and routed to the correct executor.
Note: The CLI agent has a related but separate issue (no config mechanism for shell at all) – filed separately.