From 28043ff02b69053b203cd22d3ab28cfdccd6212c Mon Sep 17 00:00:00 2001 From: 3aKHP <2971755027@qq.com> Date: Fri, 20 Mar 2026 08:57:13 +0800 Subject: [PATCH 1/2] fix: use PowerShell 7 and correct shell detection on Windows (Fixes #11958) --- src/integrations/terminal/ExecaTerminalProcess.ts | 3 ++- src/integrations/terminal/TerminalProcess.ts | 4 ++-- .../terminal/__tests__/ExecaTerminalProcess.spec.ts | 10 +++++++--- src/utils/__tests__/shell.spec.ts | 4 ++-- src/utils/shell.ts | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/integrations/terminal/ExecaTerminalProcess.ts b/src/integrations/terminal/ExecaTerminalProcess.ts index cc2af938027..548f3396fce 100644 --- a/src/integrations/terminal/ExecaTerminalProcess.ts +++ b/src/integrations/terminal/ExecaTerminalProcess.ts @@ -5,6 +5,7 @@ import process from "process" import type { RooTerminal } from "./types" import { BaseTerminal } from "./BaseTerminal" import { BaseTerminalProcess } from "./BaseTerminalProcess" +import { getShell } from "../../utils/shell" export class ExecaTerminalProcess extends BaseTerminalProcess { private terminalRef: WeakRef @@ -40,7 +41,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess { this.isHot = true this.subprocess = execa({ - shell: BaseTerminal.getExecaShellPath() || true, + shell: BaseTerminal.getExecaShellPath() || getShell(), cwd: this.terminal.getCurrentWorkingDirectory(), all: true, // Ignore stdin to ensure non-interactive mode and prevent hanging diff --git a/src/integrations/terminal/TerminalProcess.ts b/src/integrations/terminal/TerminalProcess.ts index d202191b958..117ffc469aa 100644 --- a/src/integrations/terminal/TerminalProcess.ts +++ b/src/integrations/terminal/TerminalProcess.ts @@ -97,8 +97,8 @@ export class TerminalProcess extends BaseTerminalProcess { // Execute command const defaultWindowsShellProfile = vscode.workspace - .getConfiguration("terminal.integrated.defaultProfile") - .get("windows") + .getConfiguration("terminal.integrated") + .get("defaultProfile.windows") const isPowerShell = process.platform === "win32" && diff --git a/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts b/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts index 5f0a21869ec..7263e5730f3 100644 --- a/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts +++ b/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts @@ -21,6 +21,10 @@ vitest.mock("ps-tree", () => ({ default: vitest.fn((_: number, cb: any) => cb(null, [])), })) +vitest.mock("../../../utils/shell", () => ({ + getShell: vitest.fn(() => "/mocked/default/shell"), +})) + import { execa } from "execa" import { ExecaTerminalProcess } from "../ExecaTerminalProcess" import { BaseTerminal } from "../BaseTerminal" @@ -63,7 +67,7 @@ describe("ExecaTerminalProcess", () => { const execaMock = vitest.mocked(execa) expect(execaMock).toHaveBeenCalledWith( expect.objectContaining({ - shell: true, + shell: "/mocked/default/shell", cwd: "/test/cwd", all: true, env: expect.objectContaining({ @@ -105,13 +109,13 @@ describe("ExecaTerminalProcess", () => { ) }) - it("should fall back to shell=true when execaShellPath is undefined", async () => { + it("should fall back to getShell() when execaShellPath is undefined", async () => { BaseTerminal.setExecaShellPath(undefined) await terminalProcess.run("echo test") const execaMock = vitest.mocked(execa) expect(execaMock).toHaveBeenCalledWith( expect.objectContaining({ - shell: true, + shell: "/mocked/default/shell", }), ) }) diff --git a/src/utils/__tests__/shell.spec.ts b/src/utils/__tests__/shell.spec.ts index 8f370e4d7f1..5882fdc0a4f 100644 --- a/src/utils/__tests__/shell.spec.ts +++ b/src/utils/__tests__/shell.spec.ts @@ -139,11 +139,11 @@ describe("Shell Detection Tests", () => { expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") }) - it("falls back to legacy PowerShell if profile includes 'powershell' but no path/source", () => { + it("falls back to PowerShell 7 if profile includes 'powershell' but no path/source", () => { mockVsCodeConfig("windows", "PowerShell", { PowerShell: {}, }) - expect(getShell()).toBe("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe") + expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") }) it("uses WSL bash when profile indicates WSL source", () => { diff --git a/src/utils/shell.ts b/src/utils/shell.ts index 45253c31b08..dc75cf7f4a1 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -205,8 +205,8 @@ function getWindowsShellFromVSCode(): string | null { // 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 + // Otherwise, assume modern PowerShell 7 (pwsh.exe) as the default + return SHELL_PATHS.POWERSHELL_7 } // If there's a specific path, return that immediately From af6440297e7617c6b88315c8410fe4e6bb455bf9 Mon Sep 17 00:00:00 2001 From: 3aKHP <2971755027@qq.com> Date: Mon, 23 Mar 2026 19:59:30 +0800 Subject: [PATCH 2/2] chore: narrow windows shell fix after review --- src/integrations/terminal/ExecaTerminalProcess.ts | 3 +-- .../terminal/__tests__/ExecaTerminalProcess.spec.ts | 10 +++------- src/utils/__tests__/shell.spec.ts | 4 ++-- src/utils/shell.ts | 4 ++-- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/integrations/terminal/ExecaTerminalProcess.ts b/src/integrations/terminal/ExecaTerminalProcess.ts index 548f3396fce..cc2af938027 100644 --- a/src/integrations/terminal/ExecaTerminalProcess.ts +++ b/src/integrations/terminal/ExecaTerminalProcess.ts @@ -5,7 +5,6 @@ import process from "process" import type { RooTerminal } from "./types" import { BaseTerminal } from "./BaseTerminal" import { BaseTerminalProcess } from "./BaseTerminalProcess" -import { getShell } from "../../utils/shell" export class ExecaTerminalProcess extends BaseTerminalProcess { private terminalRef: WeakRef @@ -41,7 +40,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess { this.isHot = true this.subprocess = execa({ - shell: BaseTerminal.getExecaShellPath() || getShell(), + shell: BaseTerminal.getExecaShellPath() || true, cwd: this.terminal.getCurrentWorkingDirectory(), all: true, // Ignore stdin to ensure non-interactive mode and prevent hanging diff --git a/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts b/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts index 7263e5730f3..5f0a21869ec 100644 --- a/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts +++ b/src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts @@ -21,10 +21,6 @@ vitest.mock("ps-tree", () => ({ default: vitest.fn((_: number, cb: any) => cb(null, [])), })) -vitest.mock("../../../utils/shell", () => ({ - getShell: vitest.fn(() => "/mocked/default/shell"), -})) - import { execa } from "execa" import { ExecaTerminalProcess } from "../ExecaTerminalProcess" import { BaseTerminal } from "../BaseTerminal" @@ -67,7 +63,7 @@ describe("ExecaTerminalProcess", () => { const execaMock = vitest.mocked(execa) expect(execaMock).toHaveBeenCalledWith( expect.objectContaining({ - shell: "/mocked/default/shell", + shell: true, cwd: "/test/cwd", all: true, env: expect.objectContaining({ @@ -109,13 +105,13 @@ describe("ExecaTerminalProcess", () => { ) }) - it("should fall back to getShell() when execaShellPath is undefined", async () => { + it("should fall back to shell=true when execaShellPath is undefined", async () => { BaseTerminal.setExecaShellPath(undefined) await terminalProcess.run("echo test") const execaMock = vitest.mocked(execa) expect(execaMock).toHaveBeenCalledWith( expect.objectContaining({ - shell: "/mocked/default/shell", + shell: true, }), ) }) diff --git a/src/utils/__tests__/shell.spec.ts b/src/utils/__tests__/shell.spec.ts index 5882fdc0a4f..8f370e4d7f1 100644 --- a/src/utils/__tests__/shell.spec.ts +++ b/src/utils/__tests__/shell.spec.ts @@ -139,11 +139,11 @@ describe("Shell Detection Tests", () => { expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") }) - it("falls back to PowerShell 7 if profile includes 'powershell' but no path/source", () => { + it("falls back to legacy PowerShell if profile includes 'powershell' but no path/source", () => { mockVsCodeConfig("windows", "PowerShell", { PowerShell: {}, }) - expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") + expect(getShell()).toBe("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe") }) it("uses WSL bash when profile indicates WSL source", () => { diff --git a/src/utils/shell.ts b/src/utils/shell.ts index dc75cf7f4a1..45253c31b08 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -205,8 +205,8 @@ function getWindowsShellFromVSCode(): string | null { // If the profile is sourced from PowerShell, assume the newest return SHELL_PATHS.POWERSHELL_7 } - // Otherwise, assume modern PowerShell 7 (pwsh.exe) as the default - return SHELL_PATHS.POWERSHELL_7 + // Otherwise, assume legacy Windows PowerShell + return SHELL_PATHS.POWERSHELL_LEGACY } // If there's a specific path, return that immediately