Skip to content

Commit 5f6ec1f

Browse files
committed
fix: avoid login flag for csh shells
1 parent cb27309 commit 5f6ec1f

4 files changed

Lines changed: 61 additions & 6 deletions

File tree

core/tools/implementations/runTerminalCommand.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ function getDecodedOutput(data: Buffer): string {
2222
return data.toString();
2323
}
2424
} // Simple helper function to use login shell on Unix/macOS and PowerShell on Windows
25-
function getShellCommand(command: string): { shell: string; args: string[] } {
25+
export function getShellCommand(command: string): {
26+
shell: string;
27+
args: string[];
28+
} {
2629
if (process.platform === "win32") {
2730
// Windows: Use PowerShell
2831
return {
@@ -32,10 +35,20 @@ function getShellCommand(command: string): { shell: string; args: string[] } {
3235
} else {
3336
// Unix/macOS: Use login shell to source .bashrc/.zshrc etc.
3437
const userShell = process.env.SHELL || "/bin/bash";
35-
return { shell: userShell, args: ["-l", "-c", command] };
38+
return {
39+
shell: userShell,
40+
args: shellSupportsLoginFlag(userShell)
41+
? ["-l", "-c", command]
42+
: ["-c", command],
43+
};
3644
}
3745
}
3846

47+
function shellSupportsLoginFlag(shell: string): boolean {
48+
const shellName = shell.split(/[\\/]/).pop()?.toLowerCase();
49+
return shellName !== "csh" && shellName !== "tcsh";
50+
}
51+
3952
import { fileURLToPath } from "node:url";
4053
import { ToolImpl } from ".";
4154
import {

core/tools/implementations/runTerminalCommand.vitest.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { IDE, ToolExtras } from "../..";
1616
import * as processTerminalStates from "../../util/processTerminalStates";
1717
import { runTerminalCommandTool } from "../definitions/runTerminalCommand";
18-
import { runTerminalCommandImpl } from "./runTerminalCommand";
18+
import { getShellCommand, runTerminalCommandImpl } from "./runTerminalCommand";
1919

2020
// We're using real child processes, so ensure these aren't mocked
2121
vi.unmock("node:child_process");
@@ -857,4 +857,16 @@ describe("runTerminalCommandTool.evaluateToolCallPolicy", () => {
857857

858858
expect(result).toBe("allowedWithPermission");
859859
});
860+
861+
it("does not pass login-shell flag to csh-family shells", () => {
862+
const previousShell = process.env.SHELL;
863+
process.env.SHELL = "/bin/tcsh";
864+
try {
865+
const { shell, args } = getShellCommand("pwd");
866+
expect(shell).toBe("/bin/tcsh");
867+
expect(args).toEqual(["-c", "pwd"]);
868+
} finally {
869+
process.env.SHELL = previousShell;
870+
}
871+
});
860872
});

extensions/cli/src/tools/runTerminalCommand.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
getShellCommand,
23
isRunningInWsl,
34
runTerminalCommandTool,
45
} from "./runTerminalCommand.js";
@@ -121,4 +122,18 @@ describe("runTerminalCommandTool", () => {
121122
});
122123
}
123124
});
125+
126+
describe("shell selection", () => {
127+
it("does not pass login-shell flag to csh-family shells", () => {
128+
const previousShell = process.env.SHELL;
129+
process.env.SHELL = "/bin/tcsh";
130+
try {
131+
const { shell, args } = getShellCommand("pwd");
132+
expect(shell).toBe("/bin/tcsh");
133+
expect(args).toEqual(["-c", "pwd"]);
134+
} finally {
135+
process.env.SHELL = previousShell;
136+
}
137+
});
138+
});
124139
});

extensions/cli/src/tools/runTerminalCommand.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ function getBashMaxLines(): number {
6363
}
6464

6565
// Helper function to use login shell on Unix/macOS and PowerShell on Windows and available shell in WSL
66-
function getShellCommand(command: string): { shell: string; args: string[] } {
66+
export function getShellCommand(command: string): {
67+
shell: string;
68+
args: string[];
69+
} {
6770
if (process.platform === "win32") {
6871
// Windows: Use PowerShell
6972
return {
@@ -77,13 +80,25 @@ function getShellCommand(command: string): { shell: string; args: string[] } {
7780
const wslShell = process.env.SHELL || "/bin/bash";
7881
return {
7982
shell: wslShell,
80-
args: ["-l", "-c", command],
83+
args: shellSupportsLoginFlag(wslShell)
84+
? ["-l", "-c", command]
85+
: ["-c", command],
8186
};
8287
}
8388

8489
// Unix/macOS: Use login shell to source .bashrc/.zshrc etc.
8590
const userShell = process.env.SHELL || "/bin/bash";
86-
return { shell: userShell, args: ["-l", "-c", command] };
91+
return {
92+
shell: userShell,
93+
args: shellSupportsLoginFlag(userShell)
94+
? ["-l", "-c", command]
95+
: ["-c", command],
96+
};
97+
}
98+
99+
function shellSupportsLoginFlag(shell: string): boolean {
100+
const shellName = shell.split(/[\\/]/).pop()?.toLowerCase();
101+
return shellName !== "csh" && shellName !== "tcsh";
87102
}
88103

89104
export function runCommandInBackground(command: string): {

0 commit comments

Comments
 (0)