fix(non-interactive-env): use detectShellType instead of hardcoded win32 check (#3310, #3338)

Hardcoded platform check forced PowerShell env syntax on Windows
regardless of the actual shell. Replaced with detectShellType() so
Git Bash, WSL, and similar environments use unix prefixes while
native PowerShell continues to get powershell prefixes.

🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
This commit is contained in:
YeonGyu-Kim
2026-04-12 02:29:28 +09:00
parent 2d8b4a9d53
commit 796a646c63
2 changed files with 104 additions and 10 deletions
+102 -9
View File
@@ -251,6 +251,7 @@ describe("non-interactive-env hook", () => {
})
test("#given Windows with PowerShell env #when bash tool git command executes #then uses powershell syntax", async () => {
delete process.env.SHELL
process.env.PSModulePath = "C:\\Program Files\\PowerShell\\Modules"
Object.defineProperty(process, "platform", { value: "win32" })
@@ -272,7 +273,7 @@ describe("non-interactive-env hook", () => {
expect(cmd).not.toContain("export ")
})
test("#given Windows without SHELL env #when bash tool git command executes #then uses powershell syntax", async () => {
test("#given Windows without SHELL env #when bash tool git command executes #then uses cmd syntax", async () => {
delete process.env.PSModulePath
delete process.env.SHELL
Object.defineProperty(process, "platform", { value: "win32" })
@@ -288,10 +289,10 @@ describe("non-interactive-env hook", () => {
)
const cmd = output.args.command as string
expect(cmd).toStartWith("$env:")
expect(cmd).toContain("; git log")
expect(cmd).not.toContain("set ")
expect(cmd).toContain("$env:GIT_EDITOR=':'")
expect(cmd).toStartWith("set ")
expect(cmd).toContain(" && git log")
expect(cmd).toContain('GIT_EDITOR=":"')
expect(cmd).not.toContain("$env:")
expect(cmd).not.toContain("export ")
})
@@ -317,7 +318,7 @@ describe("non-interactive-env hook", () => {
expect(cmd.length).toBeGreaterThan("git status".length)
})
test("#given Windows platform #when chained git commands via bash tool #then uses powershell syntax", async () => {
test("#given Windows platform #when chained git commands via bash tool #then uses cmd syntax", async () => {
delete process.env.PSModulePath
delete process.env.SHELL
Object.defineProperty(process, "platform", { value: "win32" })
@@ -333,10 +334,102 @@ describe("non-interactive-env hook", () => {
)
const cmd = output.args.command as string
expect(cmd).toStartWith("$env:")
expect(cmd).toContain("; git add file && git commit")
expect(cmd).toContain("$env:GIT_EDITOR=':'")
expect(cmd).toStartWith("set ")
expect(cmd).toContain(" && git add file && git commit")
expect(cmd).toContain('GIT_EDITOR=":"')
expect(cmd).not.toContain("export ")
expect(cmd).not.toContain("$env:")
})
test("#given SHELL=/bin/bash on win32 #when git command executes #then uses unix syntax", async () => {
// Git Bash or WSL sets SHELL env var - should override platform detection
delete process.env.PSModulePath
process.env.SHELL = "/bin/bash"
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("export ")
expect(cmd).toContain("; git status")
expect(cmd).not.toContain("$env:")
})
test("#given PSModulePath set on non-Windows #when git command executes #then uses powershell syntax", async () => {
// PowerShell detection via PSModulePath should work regardless of platform
delete process.env.SHELL
process.env.PSModulePath = "C:\\Program Files\\PowerShell\\Modules"
Object.defineProperty(process, "platform", { value: "linux" })
const hook = createNonInteractiveEnvHook(mockCtx)
const output: { args: Record<string, unknown>; message?: string } = {
args: { command: "git log" },
}
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 log")
expect(cmd).not.toContain("export ")
})
test("#given no SHELL and no PSModulePath on win32 #when git command executes #then uses cmd syntax", async () => {
// Platform fallback: win32 without env hints should use cmd
delete process.env.SHELL
delete process.env.PSModulePath
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("set ")
expect(cmd).toContain(" && git status")
expect(cmd).toContain('GIT_EDITOR=":"')
expect(cmd).not.toContain("export ")
expect(cmd).not.toContain("$env:")
})
test("#given no SHELL and no PSModulePath on linux #when git command executes #then uses unix syntax", async () => {
// Platform fallback: non-win32 without env hints should use unix
delete process.env.SHELL
delete process.env.PSModulePath
Object.defineProperty(process, "platform", { value: "linux" })
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("export ")
expect(cmd).toContain("; git status")
expect(cmd).not.toContain("$env:")
expect(cmd).not.toContain("set ")
})
})
})
@@ -1,6 +1,7 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { HOOK_NAME, NON_INTERACTIVE_ENV, SHELL_COMMAND_PATTERNS } from "./constants"
import { log, buildEnvPrefix } from "../../shared"
import { detectShellType } from "../../shared/shell-env"
export * from "./constants"
export * from "./detector"
@@ -52,7 +53,7 @@ export function createNonInteractiveEnvHook(_ctx: PluginInput) {
// The env vars (GIT_EDITOR=:, EDITOR=:, etc.) must ALWAYS be injected
// for git commands to prevent interactive prompts.
const shellType = process.platform === "win32" ? "powershell" : "unix"
const shellType = detectShellType()
const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, shellType)
// Check if the command already starts with the prefix to avoid stacking.