Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/integrations/terminal/ExecaTerminalProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<RooTerminal>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -111,7 +112,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
timeoutId = setTimeout(() => {
try {
this.subprocess?.kill("SIGKILL")
} catch (e) {}
} catch (e) { }

resolve()
}, 5_000)
Expand Down
3 changes: 2 additions & 1 deletion src/integrations/terminal/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BaseTerminal } from "./BaseTerminal"
import { TerminalProcess } from "./TerminalProcess"
import { ShellIntegrationManager } from "./ShellIntegrationManager"
import { mergePromise } from "./mergePromise"
import { getShell } from "../../utils/shell"

export class Terminal extends BaseTerminal {
public terminal: vscode.Terminal
Expand All @@ -17,7 +18,7 @@ export class Terminal extends BaseTerminal {

const env = Terminal.getEnv()
const iconPath = new vscode.ThemeIcon("rocket")
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env })
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env, shellPath: getShell() })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user has WSL configured but no explicit shell path, getShell() returns /bin/bash (a Unix path) — does passing that as shellPath actually work on Windows, or would wsl.exe be needed here?


if (Terminal.getTerminalZdotdir()) {
ShellIntegrationManager.terminalTmpDirs.set(id, env.ZDOTDIR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { execa } from "execa"
import { ExecaTerminalProcess } from "../ExecaTerminalProcess"
import { BaseTerminal } from "../BaseTerminal"
import type { RooTerminal } from "../types"
import * as shellUtils from "../../../utils/shell"

describe("ExecaTerminalProcess", () => {
let mockTerminal: RooTerminal
Expand All @@ -34,6 +35,7 @@ describe("ExecaTerminalProcess", () => {
beforeEach(() => {
originalEnv = { ...process.env }
BaseTerminal.setExecaShellPath(undefined)
vitest.spyOn(shellUtils, "getShell").mockReturnValue("/mock/fallback-shell")
mockTerminal = {
provider: "execa",
id: 1,
Expand All @@ -54,7 +56,7 @@ describe("ExecaTerminalProcess", () => {

afterEach(() => {
process.env = originalEnv
vitest.clearAllMocks()
vitest.restoreAllMocks()
})

describe("UTF-8 encoding fix", () => {
Expand All @@ -63,7 +65,7 @@ describe("ExecaTerminalProcess", () => {
const execaMock = vitest.mocked(execa)
expect(execaMock).toHaveBeenCalledWith(
expect.objectContaining({
shell: true,
shell: "/mock/fallback-shell",
cwd: "/test/cwd",
all: true,
env: expect.objectContaining({
Expand Down Expand Up @@ -105,13 +107,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: "/mock/fallback-shell",
}),
)
})
Expand Down
19 changes: 15 additions & 4 deletions src/integrations/terminal/__tests__/TerminalRegistry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as vscode from "vscode"
import { Terminal } from "../Terminal"
import { TerminalRegistry } from "../TerminalRegistry"
import * as shellUtils from "../../../utils/shell"

const PAGER = process.platform === "win32" ? "" : "cat"

Expand Down Expand Up @@ -34,6 +35,12 @@ describe("TerminalRegistry", () => {
},
}) as any,
)

vi.spyOn(shellUtils, "getShell").mockReturnValue("/mock/fallback-shell")
})

afterEach(() => {
vi.restoreAllMocks()
})

describe("createTerminal", () => {
Expand All @@ -43,13 +50,14 @@ describe("TerminalRegistry", () => {
expect(mockCreateTerminal).toHaveBeenCalledWith({
cwd: "/test/path",
name: "Roo Code",
iconPath: expect.any(Object),
iconPath: expect.objectContaining({ id: expect.any(String) }),
env: {
PAGER,
ROO_ACTIVE: "true",
VTE_VERSION: "0",
PROMPT_EOL_MARK: "",
},
shellPath: "/mock/fallback-shell",
})
})

Expand All @@ -64,14 +72,15 @@ describe("TerminalRegistry", () => {
expect(mockCreateTerminal).toHaveBeenCalledWith({
cwd: "/test/path",
name: "Roo Code",
iconPath: expect.any(Object),
iconPath: expect.objectContaining({ id: expect.any(String) }),
env: {
PAGER,
ROO_ACTIVE: "true",
PROMPT_COMMAND: "sleep 0.05",
VTE_VERSION: "0",
PROMPT_EOL_MARK: "",
},
shellPath: "/mock/fallback-shell",
})
} finally {
// Restore original delay
Expand All @@ -87,14 +96,15 @@ describe("TerminalRegistry", () => {
expect(mockCreateTerminal).toHaveBeenCalledWith({
cwd: "/test/path",
name: "Roo Code",
iconPath: expect.any(Object),
iconPath: expect.objectContaining({ id: expect.any(String) }),
env: {
PAGER,
ROO_ACTIVE: "true",
VTE_VERSION: "0",
PROMPT_EOL_MARK: "",
ITERM_SHELL_INTEGRATION_INSTALLED: "Yes",
},
shellPath: "/mock/fallback-shell",
})
} finally {
Terminal.setTerminalZshOhMy(false)
Expand All @@ -109,14 +119,15 @@ describe("TerminalRegistry", () => {
expect(mockCreateTerminal).toHaveBeenCalledWith({
cwd: "/test/path",
name: "Roo Code",
iconPath: expect.any(Object),
iconPath: expect.objectContaining({ id: expect.any(String) }),
env: {
PAGER,
ROO_ACTIVE: "true",
VTE_VERSION: "0",
PROMPT_EOL_MARK: "",
POWERLEVEL9K_TERM_SHELL_INTEGRATION: "true",
},
shellPath: "/mock/fallback-shell",
})
} finally {
Terminal.setTerminalZshP10k(false)
Expand Down
Loading