diff --git a/src/features/opencode-skill-loader/git-master-template-injection.test.ts b/src/features/opencode-skill-loader/git-master-template-injection.test.ts index 06be6051e..a521e13f4 100644 --- a/src/features/opencode-skill-loader/git-master-template-injection.test.ts +++ b/src/features/opencode-skill-loader/git-master-template-injection.test.ts @@ -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") + }) + }) }) diff --git a/src/features/opencode-skill-loader/git-master-template-injection.ts b/src/features/opencode-skill-loader/git-master-template-injection.ts index 0483c2fc0..e1b1b05ba 100644 --- a/src/features/opencode-skill-loader/git-master-template-injection.ts +++ b/src/features/opencode-skill-loader/git-master-template-injection.ts @@ -26,15 +26,17 @@ export function parseBashEnvPrefix(prefix: string): Record { /** * 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