@@ -6,6 +6,7 @@ import (
66 "os"
77 "path/filepath"
88 "slices"
9+ "strings"
910 "testing"
1011
1112 "github.com/charmbracelet/crush/internal/env"
@@ -43,8 +44,11 @@ func writeTempFile(t *testing.T, content string) string {
4344func TestResolvedEnv_RealShell_Success(t *testing.T) {
4445 t.Parallel()
4546
46− withNL := writeTempFile(t, "token-with-nl\n")
47− noNL := writeTempFile(t, "token-no-nl")
47+ // filepath.ToSlash so Windows temp paths (C:\Users\...) survive
48+ // being injected into a shell command string — the embedded shell
49+ // treats backslashes as escapes, forward slashes work on every OS.
50+ withNL := filepath.ToSlash(writeTempFile(t, "token-with-nl\n"))
51+ noNL := filepath.ToSlash(writeTempFile(t, "token-no-nl"))
4852
4953 m := MCPConfig{
5054 Env: map[string]string{
@@ -175,14 +179,17 @@ func TestResolvedEnv_RealShell_NounsetRegression(t *testing.T) {
175179}
176180
177181// TestResolvedEnv_RealShell_FailureDetail pins that a failing inner
178−// command surfaces enough detail (exit code + stderr) to diagnose
179−// without forcing the user to re-run the command by hand. Also
180−// verifies the template is included so they know which Env entry
181−// blew up.
182+// command surfaces enough detail (exit code + stderr on POSIX, the
183+// underlying OS error on Windows where coreutils runs in-process) to
184+// diagnose without forcing the user to re-run the command by hand.
185+// Also verifies the template is included so they know which Env
186+// entry blew up.
182187func TestResolvedEnv_RealShell_FailureDetail(t *testing.T) {
183188 t.Parallel()
184189
185− missing := filepath.Join(t.TempDir(), "definitely-not-here")
190+ // Forward slashes so the path survives shell-string injection on
191+ // Windows; see TestResolvedEnv_RealShell_Success for the same note.
192+ missing := filepath.ToSlash(filepath.Join(t.TempDir(), "definitely-not-here"))
186193 m := MCPConfig{Env: map[string]string{
187194 "FORGEJO_ACCESS_TOKEN": fmt.Sprintf("$(cat %s)", missing),
188195 }}
@@ -192,7 +199,16 @@ func TestResolvedEnv_RealShell_FailureDetail(t *testing.T) {
192199 msg := err.Error()
193200 require.Contains(t, msg, "FORGEJO_ACCESS_TOKEN", "must identify the failing env var")
194201 require.Contains(t, msg, missing, "must include the template so users see what failed")
195− require.Contains(t, msg, "exit status", "must surface the inner exit code")
202+
203+ // Inner diagnostic detail must survive. POSIX surfaces "exit
204+ // status N" + stderr; Windows' in-process coreutils surfaces the
205+ // Go OS error instead. Accept either shape so the test is
206+ // portable without weakening the intent.
207+ lower := strings.ToLower(msg)
208+ hasDetail := strings.Contains(lower, "exit status") ||
209+ strings.Contains(lower, "no such file") ||
210+ strings.Contains(lower, "cannot find")
211+ require.True(t, hasDetail, "must surface inner error detail: %s", msg)
196212}
197213
198214// TestResolvedHeaders_RealShell_FailurePreservesOriginal pins two