fix(git-master): emit csh-compatible setenv syntax for csh/tcsh shells

buildShellAwareGitPrefix incorrectly returned raw bash VAR=value prefix for csh, which does not support inline env assignment. Now routes csh through buildEnvPrefix() to emit setenv syntax. Also sets code block lang to csh and skips bash block regex prefixing for csh.

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-18 14:04:43 +09:00
parent b2961409b7
commit 82b0672c05
2 changed files with 75 additions and 4 deletions
@@ -231,6 +231,18 @@ describe("#given buildShellAwareGitPrefix", () => {
})
})
describe("#when shell type is csh", () => {
it("#then returns csh setenv syntax", () => {
const result = buildShellAwareGitPrefix("GIT_MASTER=1", "csh")
expect(result).toBe("setenv GIT_MASTER 1;")
})
it("#then handles multiple env vars", () => {
const result = buildShellAwareGitPrefix("CI=true GIT_MASTER=1", "csh")
expect(result).toBe("setenv CI true; setenv GIT_MASTER 1;")
})
})
describe("#when prefix is empty", () => {
it("#then returns empty string", () => {
const result = buildShellAwareGitPrefix("", "powershell")
@@ -319,4 +331,61 @@ describe("#given PowerShell shell detection in injectGitMasterConfig", () => {
expect(result).not.toContain("$env:")
})
})
describe("#when shell is csh (SHELL set to /bin/csh)", () => {
it("#then emits setenv prefix syntax in csh code block", () => {
process.env.SHELL = "/bin/csh"
delete process.env.PSModulePath
delete process.env.MSYSTEM
Object.defineProperty(process, "platform", { value: "linux" })
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("setenv GIT_MASTER 1;")
expect(result).toContain("```csh")
expect(result).not.toContain("```bash\nsetenv")
})
it("#then does NOT prefix bash code blocks with setenv syntax", () => {
process.env.SHELL = "/bin/csh"
delete process.env.PSModulePath
delete process.env.MSYSTEM
Object.defineProperty(process, "platform", { value: "linux" })
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
const bashBlockMatch = result.match(/```bash\r?\n([\s\S]*?)```/g)
if (bashBlockMatch) {
for (const block of bashBlockMatch) {
expect(block).not.toContain("setenv")
}
}
})
})
describe("#when shell is tcsh (SHELL set to /usr/local/bin/tcsh)", () => {
it("#then emits setenv prefix syntax in csh code block", () => {
process.env.SHELL = "/usr/local/bin/tcsh"
delete process.env.PSModulePath
delete process.env.MSYSTEM
Object.defineProperty(process, "platform", { value: "darwin" })
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("setenv GIT_MASTER 1;")
expect(result).toContain("```csh")
})
})
})
@@ -26,15 +26,17 @@ export function parseBashEnvPrefix(prefix: string): Record<string, string> {
/**
* Build the shell-aware command prefix for git commands.
* Uses the shared shell detection and env prefix builder to emit correct syntax
* for PowerShell ($env:VAR='value';), cmd (set VAR="value" &&), or unix (VAR=value).
* for PowerShell ($env:VAR='value';), cmd (set VAR="value" &&),
* csh (setenv VAR value;), or unix (VAR=value).
*
* For unix shells, we use the inline VAR=value prefix style (not export) to match
* the original behavior where the env var applies only to the immediately following command.
* For csh/tcsh, we use setenv syntax since csh does not support inline VAR=value.
*/
export function buildShellAwareGitPrefix(bashPrefix: string, shellType?: ShellType): string {
if (!bashPrefix) return ""
const resolvedShellType = shellType ?? detectShellType()
if (resolvedShellType === "unix" || resolvedShellType === "csh") {
if (resolvedShellType === "unix") {
return bashPrefix
}
const envRecord = parseBashEnvPrefix(bashPrefix)
@@ -48,8 +50,8 @@ export function injectGitMasterConfig(template: string, config?: GitMasterConfig
const shellType = detectShellType()
const shellPrefix = gitEnvPrefix ? buildShellAwareGitPrefix(gitEnvPrefix, shellType) : ""
const codeBlockLang = shellType === "powershell" ? "pwsh" : "bash"
const skipBashBlockPrefixing = shellType === "powershell" || shellType === "cmd"
const codeBlockLang = shellType === "powershell" ? "pwsh" : shellType === "csh" ? "csh" : "bash"
const skipBashBlockPrefixing = shellType === "powershell" || shellType === "cmd" || shellType === "csh"
let result = gitEnvPrefix ? injectGitEnvPrefix(template, shellPrefix, codeBlockLang) : template