fix(non-interactive-env): honor Windows ComSpec shell

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-13 22:47:37 +09:00
parent fef1d4530b
commit 47d60a74d3
2 changed files with 49 additions and 12 deletions
@@ -13,6 +13,7 @@ describe("non-interactive-env hook", () => {
SHELL: process.env.SHELL,
PSModulePath: process.env.PSModulePath,
MSYSTEM: process.env.MSYSTEM,
ComSpec: process.env.ComSpec,
CI: process.env.CI,
OPENCODE_NON_INTERACTIVE: process.env.OPENCODE_NON_INTERACTIVE,
}
@@ -298,6 +299,30 @@ describe("non-interactive-env hook", () => {
expect(cmd).not.toContain("export ")
})
test("#given Windows ComSpec=pwsh.exe without SHELL #when bash tool git command executes #then uses powershell syntax", async () => {
delete process.env.SHELL
delete process.env.MSYSTEM
process.env.ComSpec = "C:\\Program Files\\PowerShell\\7\\pwsh.exe"
process.env.PSModulePath = "C:\\Program Files\\PowerShell\\Modules"
Object.defineProperty(process, "platform", { value: "win32" })
const hook = createNonInteractiveEnvHook(mockCtx)
const output: { args: Record<string, unknown>; message?: string } = {
args: { command: "git status" },
}
await hook["tool.execute.before"](
{ tool: "bash", sessionID: "test", callID: "1" },
output
)
const cmd = output.args.command as string
expect(cmd).toStartWith("$env:")
expect(cmd).toContain("; git status")
expect(cmd).not.toContain("set ")
expect(cmd).not.toContain("export ")
})
test("#given Windows SHELL=pwsh.exe #when bash tool git command executes #then uses powershell syntax", async () => {
process.env.SHELL = "C:\\Program Files\\PowerShell\\7\\pwsh.exe"
delete process.env.MSYSTEM
@@ -20,24 +20,36 @@ function detectBannedCommand(command: string): string | undefined {
return undefined
}
function detectWindowsShellType(shellPath: string | undefined): ShellType | undefined {
if (!shellPath) {
return undefined
}
const shellName = shellPath.replace(/\\/g, "/").split("/").pop()?.toLowerCase()
if (shellName === "cmd" || shellName === "cmd.exe") {
return "cmd"
}
if (
shellName === "powershell" ||
shellName === "powershell.exe" ||
shellName === "pwsh" ||
shellName === "pwsh.exe"
) {
return "powershell"
}
return undefined
}
function detectCommandShellType(): ShellType {
if (process.platform === "win32" && process.env.SHELL) {
const shellName = process.env.SHELL.replace(/\\/g, "/").split("/").pop()?.toLowerCase()
if (shellName === "cmd" || shellName === "cmd.exe") {
return "cmd"
}
if (
shellName === "powershell" ||
shellName === "powershell.exe" ||
shellName === "pwsh" ||
shellName === "pwsh.exe"
) {
return "powershell"
const shellType = detectWindowsShellType(process.env.SHELL)
if (shellType) {
return shellType
}
}
if (process.platform === "win32" && !process.env.SHELL && !process.env.MSYSTEM) {
return "cmd"
return detectWindowsShellType(process.env.ComSpec) ?? "cmd"
}
return detectShellType()