From 92509d8cfbfa25d425ebdf06d9d3a1cfedfe24e0 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Mon, 23 Mar 2026 19:33:54 +0900 Subject: [PATCH] fix(non-interactive-env): detect shell type for csh/tcsh env var syntax (fixes #2089) --- .../non-interactive-env-hook.ts | 8 ++++---- src/shared/shell-env.ts | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/hooks/non-interactive-env/non-interactive-env-hook.ts b/src/hooks/non-interactive-env/non-interactive-env-hook.ts index 758a1eb78..3cf70811c 100644 --- a/src/hooks/non-interactive-env/non-interactive-env-hook.ts +++ b/src/hooks/non-interactive-env/non-interactive-env-hook.ts @@ -1,6 +1,6 @@ import type { PluginInput } from "@opencode-ai/plugin" import { HOOK_NAME, NON_INTERACTIVE_ENV, SHELL_COMMAND_PATTERNS } from "./constants" -import { log, buildEnvPrefix } from "../../shared" +import { log, buildEnvPrefix, detectShellType } from "../../shared" export * from "./constants" export * from "./detector" @@ -52,9 +52,9 @@ export function createNonInteractiveEnvHook(_ctx: PluginInput) { // The env vars (GIT_EDITOR=:, EDITOR=:, etc.) must ALWAYS be injected // for git commands to prevent interactive prompts. - // The bash tool always runs in a Unix-like shell (bash/sh), even on Windows - // (via Git Bash, WSL, etc.), so always use unix export syntax. - const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix") + // Detect the current shell type for proper env var syntax (export for bash/zsh, setenv for csh/tcsh, etc.) + const shellType = detectShellType() + const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, shellType) // Check if the command already starts with the prefix to avoid stacking. // This maintains the non-interactive behavior and makes the operation idempotent. diff --git a/src/shared/shell-env.ts b/src/shared/shell-env.ts index bec4b8336..2ffa59ac2 100644 --- a/src/shared/shell-env.ts +++ b/src/shared/shell-env.ts @@ -1,4 +1,4 @@ -export type ShellType = "unix" | "powershell" | "cmd" +export type ShellType = "unix" | "powershell" | "cmd" | "csh" /** * Detect the current shell type based on environment variables. @@ -14,6 +14,10 @@ export function detectShellType(): ShellType { } if (process.env.SHELL) { + const shell = process.env.SHELL + if (shell.includes("csh") || shell.includes("tcsh")) { + return "csh" + } return "unix" } @@ -34,6 +38,7 @@ export function shellEscape(value: string, shellType: ShellType): string { switch (shellType) { case "unix": + case "csh": if (/[^a-zA-Z0-9_\-.:\/]/.test(value)) { return `'${value.replace(/'/g, "'\\''")}'` } @@ -91,6 +96,13 @@ export function buildEnvPrefix( return `export ${assignments};` } + case "csh": { + const assignments = entries + .map(([key, value]) => `setenv ${key} ${shellEscape(value, shellType)}`) + .join("; ") + return `${assignments};` + } + case "powershell": { const assignments = entries .map(([key, value]) => `$env:${key}=${shellEscape(value, shellType)}`)