From 6b2825468882f4ab61409298904c868dc7e7a68c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:12:07 +0000 Subject: [PATCH 01/13] Initial plan From eb009f3658bda36edee0009f8238a3d84c04bf6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:54:24 +0000 Subject: [PATCH 02/13] fix: remove compat import to prevent ConPTY startup crash on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The charm.land/lipgloss/v2/compat package has a package-level var: var HasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stdout) This executes before any init() function and calls into Windows console APIs (SetConsoleMode + blocking ReadFile via CancelReader). Under ConPTY the call fails with STATUS_DLL_INIT_FAILED (exit code 0xC0000142), crashing the binary before it can print any output. Fix: - Replace compat.AdaptiveColor with a local adaptiveColor type that reads a package-level hasDarkBackground bool (default true = dark). - Drop the charm.land/lipgloss/v2/compat import entirely, so the problematic package-level var never executes. - Simplify shouldConfigureLipglossCompat to always return false on Windows, skipping the startup probe. The lipgloss HasDarkBackground call can crash (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other pseudo-terminal environments; defaulting to dark background is the safe and correct choice for Windows. Update tests to reflect the new adaptiveColor type and the new shouldConfigureLipglossCompat behaviour (windows always → false). Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/spec_test.go | 2 +- pkg/styles/theme.go | 67 ++++++++++++++++++++++++++-------------- pkg/styles/theme_test.go | 27 ++++++---------- 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/pkg/styles/spec_test.go b/pkg/styles/spec_test.go index 18dfd115a92..06a7f45860a 100644 --- a/pkg/styles/spec_test.go +++ b/pkg/styles/spec_test.go @@ -64,7 +64,7 @@ func TestSpec_Constants_DocumentedSemanticColorHexValues(t *testing.T) { // adaptive color variables are defined with both Light and Dark hex values as // listed in the package README.md adaptive color table. // -// Specification: "These variables provide compat.AdaptiveColor values that +// Specification: "These variables provide adaptiveColor values that // auto-select the correct shade at render time." func TestSpec_Constants_AllElevenAdaptiveColors(t *testing.T) { type colorDef struct { diff --git a/pkg/styles/theme.go b/pkg/styles/theme.go index 92845694b8e..bcb6939dd10 100644 --- a/pkg/styles/theme.go +++ b/pkg/styles/theme.go @@ -4,8 +4,9 @@ // // # Adaptive Color System // -// This package uses compat.AdaptiveColor to automatically adapt colors based on the -// terminal background, ensuring good readability in both light and dark terminal themes. +// This package defines an adaptiveColor type that automatically selects between +// light and dark color variants based on the terminal background, ensuring good +// readability in both light and dark terminal themes. // Each color constant includes both Light and Dark variants that are automatically // selected based on the user's terminal configuration. // @@ -47,28 +48,46 @@ package styles import ( + "image/color" "os" "runtime" lipgloss "charm.land/lipgloss/v2" - "charm.land/lipgloss/v2/compat" - "github.com/charmbracelet/colorprofile" ) +// hasDarkBackground tracks whether the terminal has a dark background. +// Default is true (dark), which suits the vast majority of modern terminals. +// On non-Windows platforms it is updated at startup by configureLipglossCompat. +// On Windows the probe is skipped entirely: the lipgloss background-color query +// can crash (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other +// pseudo-terminal environments, so the safe default is always used instead. +var hasDarkBackground = true + +// adaptiveColor selects between a light and a dark color variant based on the +// terminal background detected at startup. +type adaptiveColor struct { + Light color.Color + Dark color.Color +} + +// RGBA satisfies the color.Color interface. +func (c adaptiveColor) RGBA() (uint32, uint32, uint32, uint32) { + if hasDarkBackground { + return c.Dark.RGBA() + } + return c.Light.RGBA() +} + func configureLipglossCompat() { - compat.HasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stderr) - compat.Profile = colorprofile.Detect(os.Stderr, os.Environ()) + hasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stderr) } func shouldConfigureLipglossCompat(goos string, stderrMode os.FileMode, statErr error) bool { - // On Windows, querying terminal capabilities against redirected/pipe handles - // can hang under some wrapper environments. Skip startup probing unless stderr - // is attached to a character device. + // On Windows, the lipgloss background-color query can crash + // (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other pseudo-terminal + // environments. Skip the probe entirely and use the default (dark background). if goos == "windows" { - if statErr != nil { - return false - } - return stderrMode&(os.ModeDevice|os.ModeCharDevice) == (os.ModeDevice | os.ModeCharDevice) + return false } return true } @@ -125,67 +144,67 @@ const ( // Dark variants use brighter colors (Dracula theme inspired) for dark backgrounds. var ( // ColorError is used for error messages and critical issues. - ColorError = compat.AdaptiveColor{ + ColorError = adaptiveColor{ Light: lipgloss.Color(hexColorErrorLight), // Darker red for light backgrounds Dark: lipgloss.Color(hexColorErrorDark), // Bright red for dark backgrounds (Dracula) } // ColorWarning is used for warning messages and cautionary information. - ColorWarning = compat.AdaptiveColor{ + ColorWarning = adaptiveColor{ Light: lipgloss.Color(hexColorWarningLight), // Darker orange for light backgrounds Dark: lipgloss.Color(hexColorWarningDark), // Bright orange for dark backgrounds (Dracula) } // ColorSuccess is used for success messages and confirmations. - ColorSuccess = compat.AdaptiveColor{ + ColorSuccess = adaptiveColor{ Light: lipgloss.Color(hexColorSuccessLight), // Darker green for light backgrounds Dark: lipgloss.Color(hexColorSuccessDark), // Bright green for dark backgrounds (Dracula) } // ColorInfo is used for informational messages - ColorInfo = compat.AdaptiveColor{ + ColorInfo = adaptiveColor{ Light: lipgloss.Color(hexColorInfoLight), // Darker cyan/blue for light backgrounds Dark: lipgloss.Color(hexColorInfoDark), // Bright cyan for dark backgrounds (Dracula) } // ColorPurple is used for file paths, commands, and highlights - ColorPurple = compat.AdaptiveColor{ + ColorPurple = adaptiveColor{ Light: lipgloss.Color(hexColorPurpleLight), // Darker purple for light backgrounds Dark: lipgloss.Color(hexColorPurpleDark), // Bright purple for dark backgrounds (Dracula) } // ColorYellow is used for progress messages and attention-grabbing content - ColorYellow = compat.AdaptiveColor{ + ColorYellow = adaptiveColor{ Light: lipgloss.Color(hexColorYellowLight), // Darker yellow/gold for light backgrounds Dark: lipgloss.Color(hexColorYellowDark), // Bright yellow for dark backgrounds (Dracula) } // ColorComment is used for secondary/muted information like line numbers - ColorComment = compat.AdaptiveColor{ + ColorComment = adaptiveColor{ Light: lipgloss.Color(hexColorCommentLight), // Muted gray-blue for light backgrounds Dark: lipgloss.Color(hexColorCommentDark), // Muted purple-gray for dark backgrounds (Dracula) } // ColorForeground is used for primary text content - ColorForeground = compat.AdaptiveColor{ + ColorForeground = adaptiveColor{ Light: lipgloss.Color(hexColorForegroundLight), // Dark gray for light backgrounds Dark: lipgloss.Color(hexColorForegroundDark), // Light gray/white for dark backgrounds (Dracula) } // ColorBackground is used for highlighted backgrounds - ColorBackground = compat.AdaptiveColor{ + ColorBackground = adaptiveColor{ Light: lipgloss.Color(hexColorBackgroundLight), // Light gray for light backgrounds Dark: lipgloss.Color(hexColorBackgroundDark), // Dark purple/gray for dark backgrounds (Dracula) } // ColorBorder is used for table borders and dividers - ColorBorder = compat.AdaptiveColor{ + ColorBorder = adaptiveColor{ Light: lipgloss.Color(hexColorBorderLight), // Light gray border for light backgrounds Dark: lipgloss.Color(hexColorBorderDark), // Dark purple border for dark backgrounds (Dracula) } // ColorTableAltRow is used for alternating row backgrounds in tables (zebra striping) - ColorTableAltRow = compat.AdaptiveColor{ + ColorTableAltRow = adaptiveColor{ Light: lipgloss.Color(hexColorTableAltRowLight), // Subtle light gray for light backgrounds Dark: lipgloss.Color(hexColorTableAltRowDark), // Subtle darker background for dark backgrounds } diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 583d0d09a36..1c861e7a253 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -8,8 +8,6 @@ import ( "testing" lipgloss "charm.land/lipgloss/v2" - "charm.land/lipgloss/v2/compat" - "github.com/charmbracelet/colorprofile" ) // TestAdaptiveColorsHaveBothVariants verifies that all adaptive colors @@ -229,11 +227,11 @@ func TestDarkColorsAreOriginalDracula(t *testing.T) { } } -// TestAdaptiveColorVarsUseHexConstants verifies that the exported AdaptiveColor vars +// TestAdaptiveColorVarsUseHexConstants verifies that the exported adaptive color vars // are backed by the expected hex constants (spot-check a few key colors). func TestAdaptiveColorVarsUseHexConstants(t *testing.T) { - // Verify that the compat.AdaptiveColor vars hold non-nil color values. - colors := map[string]compat.AdaptiveColor{ + // Verify that the adaptiveColor vars hold non-nil color values. + colors := map[string]adaptiveColor{ "ColorError": ColorError, "ColorWarning": ColorWarning, "ColorSuccess": ColorSuccess, @@ -260,23 +258,16 @@ func TestAdaptiveColorVarsUseHexConstants(t *testing.T) { } func TestConfigureLipglossCompatUsesStderr(t *testing.T) { - originalProfile := compat.Profile - originalHasDarkBackground := compat.HasDarkBackground + original := hasDarkBackground t.Cleanup(func() { - compat.Profile = originalProfile - compat.HasDarkBackground = originalHasDarkBackground + hasDarkBackground = original }) configureLipglossCompat() - expectedHasDarkBackground := lipgloss.HasDarkBackground(os.Stdin, os.Stderr) - expectedProfile := colorprofile.Detect(os.Stderr, os.Environ()) - - if compat.HasDarkBackground != expectedHasDarkBackground { - t.Fatalf("compat.HasDarkBackground = %v, want %v", compat.HasDarkBackground, expectedHasDarkBackground) - } - if compat.Profile != expectedProfile { - t.Fatalf("compat.Profile = %v, want %v", compat.Profile, expectedProfile) + expected := lipgloss.HasDarkBackground(os.Stdin, os.Stderr) + if hasDarkBackground != expected { + t.Fatalf("hasDarkBackground = %v, want %v", hasDarkBackground, expected) } } @@ -292,7 +283,7 @@ func TestShouldConfigureLipglossCompat(t *testing.T) { name: "windows character device", goos: "windows", stderrMode: os.ModeDevice | os.ModeCharDevice, - want: true, + want: false, }, { name: "windows redirected pipe", From 94d1df32590879816ae2139f2899dfcda0ee738a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:23:20 +0000 Subject: [PATCH 03/13] chore: start PR finisher pass Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 35d5eda1740..5a2f44f493a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( charm.land/bubbletea/v2 v2.0.7 charm.land/huh/v2 v2.0.3 charm.land/lipgloss/v2 v2.0.4 - github.com/charmbracelet/colorprofile v0.4.3 github.com/charmbracelet/x/exp/golden v0.0.0-20260602025833-85a30b5e440a github.com/cli/go-gh/v2 v2.13.0 github.com/creack/pty v1.1.24 @@ -42,6 +41,7 @@ require ( github.com/catppuccin/go v0.3.0 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/colorprofile v0.4.3 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 // indirect github.com/charmbracelet/x/ansi v0.11.7 // indirect From 9f810063324b4946c8cabdca497000e79a3bd829 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:35:55 +0000 Subject: [PATCH 04/13] fix: address style review follow-ups Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme.go | 29 ++++-------- pkg/styles/theme_test.go | 99 +++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 65 deletions(-) diff --git a/pkg/styles/theme.go b/pkg/styles/theme.go index bcb6939dd10..19edc3fbc79 100644 --- a/pkg/styles/theme.go +++ b/pkg/styles/theme.go @@ -53,11 +53,12 @@ import ( "runtime" lipgloss "charm.land/lipgloss/v2" + "github.com/charmbracelet/x/term" ) // hasDarkBackground tracks whether the terminal has a dark background. // Default is true (dark), which suits the vast majority of modern terminals. -// On non-Windows platforms it is updated at startup by configureLipglossCompat. +// On non-Windows platforms it is updated at startup by configureHasDarkBackground. // On Windows the probe is skipped entirely: the lipgloss background-color query // can crash (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other // pseudo-terminal environments, so the safe default is always used instead. @@ -78,14 +79,17 @@ func (c adaptiveColor) RGBA() (uint32, uint32, uint32, uint32) { return c.Light.RGBA() } -func configureLipglossCompat() { - hasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stderr) +type backgroundDetector func(term.File, term.File) bool + +func configureHasDarkBackground(detector backgroundDetector) { + hasDarkBackground = detector(os.Stdin, os.Stderr) } -func shouldConfigureLipglossCompat(goos string, stderrMode os.FileMode, statErr error) bool { +func shouldProbeTerminalBackground(goos string) bool { // On Windows, the lipgloss background-color query can crash // (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other pseudo-terminal // environments. Skip the probe entirely and use the default (dark background). + // TODO: replace this with a Windows-safe background detector. if goos == "windows" { return false } @@ -93,21 +97,8 @@ func shouldConfigureLipglossCompat(goos string, stderrMode os.FileMode, statErr } func init() { - stderrInfo, statErr := os.Stderr.Stat() - // Defensive fallback: Stat should not normally return (nil, nil). Treat that - // impossible state as an invalid stderr handle so Windows startup probing is - // safely skipped. - if statErr == nil && stderrInfo == nil { - statErr = os.ErrInvalid - } - // Zero mode means "unknown/unset"; on Windows this keeps the startup probe - // disabled unless stderr is explicitly confirmed as a character device. - stderrMode := os.FileMode(0) - if stderrInfo != nil { - stderrMode = stderrInfo.Mode() - } - if shouldConfigureLipglossCompat(runtime.GOOS, stderrMode, statErr) { - configureLipglossCompat() + if shouldProbeTerminalBackground(runtime.GOOS) { + configureHasDarkBackground(lipgloss.HasDarkBackground) } } diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 1c861e7a253..3bcdf315923 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -8,6 +8,7 @@ import ( "testing" lipgloss "charm.land/lipgloss/v2" + "github.com/charmbracelet/x/term" ) // TestAdaptiveColorsHaveBothVariants verifies that all adaptive colors @@ -257,72 +258,78 @@ func TestAdaptiveColorVarsUseHexConstants(t *testing.T) { } } -func TestConfigureLipglossCompatUsesStderr(t *testing.T) { +func TestAdaptiveColorRGBASwitching(t *testing.T) { original := hasDarkBackground t.Cleanup(func() { hasDarkBackground = original }) - configureLipglossCompat() + c := adaptiveColor{ + Light: lipgloss.Color("#ffffff"), + Dark: lipgloss.Color("#000000"), + } + + hasDarkBackground = true + r, g, b, _ := c.RGBA() + if r != 0 || g != 0 || b != 0 { + t.Fatalf("dark background RGBA = (%d,%d,%d), want (0,0,0)", r, g, b) + } - expected := lipgloss.HasDarkBackground(os.Stdin, os.Stderr) - if hasDarkBackground != expected { - t.Fatalf("hasDarkBackground = %v, want %v", hasDarkBackground, expected) + hasDarkBackground = false + r, g, b, _ = c.RGBA() + if r != 0xffff || g != 0xffff || b != 0xffff { + t.Fatalf("light background RGBA = (%d,%d,%d), want (%d,%d,%d)", r, g, b, uint32(0xffff), uint32(0xffff), uint32(0xffff)) } } -func TestShouldConfigureLipglossCompat(t *testing.T) { +func TestConfigureHasDarkBackgroundUsesStderr(t *testing.T) { + original := hasDarkBackground + t.Cleanup(func() { + hasDarkBackground = original + }) + + var gotReader term.File + var gotWriter term.File + configureHasDarkBackground(func(r term.File, w term.File) bool { + gotReader = r + gotWriter = w + return false + }) + + if gotReader != os.Stdin { + t.Fatalf("reader = %v, want os.Stdin", gotReader) + } + if gotWriter != os.Stderr { + t.Fatalf("writer = %v, want os.Stderr", gotWriter) + } + if hasDarkBackground { + t.Fatalf("hasDarkBackground = %v, want false", hasDarkBackground) + } +} + +func TestShouldProbeTerminalBackground(t *testing.T) { tests := []struct { - name string - goos string - stderrMode os.FileMode - statErr error - want bool + name string + goos string + want bool }{ { - name: "windows character device", - goos: "windows", - stderrMode: os.ModeDevice | os.ModeCharDevice, - want: false, - }, - { - name: "windows redirected pipe", - goos: "windows", - stderrMode: os.ModeNamedPipe, - want: false, - }, - { - name: "windows zero mode stat success", - goos: "windows", - stderrMode: os.FileMode(0), - want: false, - }, - { - name: "windows stat error", - goos: "windows", - statErr: os.ErrPermission, - want: false, - }, - { - name: "windows ErrInvalid fallback", - goos: "windows", - statErr: os.ErrInvalid, - want: false, + name: "windows always skips startup probe", + goos: "windows", + want: false, }, { - name: "non-windows still configures", - goos: "linux", - stderrMode: os.ModeNamedPipe, - statErr: os.ErrPermission, - want: true, + name: "non-windows still probes", + goos: "linux", + want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := shouldConfigureLipglossCompat(tt.goos, tt.stderrMode, tt.statErr) + got := shouldProbeTerminalBackground(tt.goos) if got != tt.want { - t.Fatalf("shouldConfigureLipglossCompat(%q, %v, %v) = %v, want %v", tt.goos, tt.stderrMode, tt.statErr, got, tt.want) + t.Fatalf("shouldProbeTerminalBackground(%q) = %v, want %v", tt.goos, got, tt.want) } }) } From fd4bb9adcfb9e7de2a7400973b2d98b303459ee9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:38:35 +0000 Subject: [PATCH 05/13] test: polish review follow-up assertions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 3bcdf315923..41687042aac 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -290,9 +290,9 @@ func TestConfigureHasDarkBackgroundUsesStderr(t *testing.T) { var gotReader term.File var gotWriter term.File - configureHasDarkBackground(func(r term.File, w term.File) bool { - gotReader = r - gotWriter = w + configureHasDarkBackground(func(reader term.File, writer term.File) bool { + gotReader = reader + gotWriter = writer return false }) From 6e32be839c301463857dd2fbcfbb35bc4bb0295c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:40:28 +0000 Subject: [PATCH 06/13] test: cover adaptive color alpha channel Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 41687042aac..73fc9b50d25 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -270,15 +270,15 @@ func TestAdaptiveColorRGBASwitching(t *testing.T) { } hasDarkBackground = true - r, g, b, _ := c.RGBA() - if r != 0 || g != 0 || b != 0 { - t.Fatalf("dark background RGBA = (%d,%d,%d), want (0,0,0)", r, g, b) + r, g, b, a := c.RGBA() + if r != 0 || g != 0 || b != 0 || a != 0xffff { + t.Fatalf("dark background RGBA = (%d,%d,%d,%d), want (0,0,0,%d)", r, g, b, a, uint32(0xffff)) } hasDarkBackground = false - r, g, b, _ = c.RGBA() - if r != 0xffff || g != 0xffff || b != 0xffff { - t.Fatalf("light background RGBA = (%d,%d,%d), want (%d,%d,%d)", r, g, b, uint32(0xffff), uint32(0xffff), uint32(0xffff)) + r, g, b, a = c.RGBA() + if r != 0xffff || g != 0xffff || b != 0xffff || a != 0xffff { + t.Fatalf("light background RGBA = (%d,%d,%d,%d), want (%d,%d,%d,%d)", r, g, b, a, uint32(0xffff), uint32(0xffff), uint32(0xffff), uint32(0xffff)) } } From 505450d6e54b379a4b44287aca09c059391bd112 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:43:49 +0000 Subject: [PATCH 07/13] docs: clarify Windows background probe note Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/styles/theme.go b/pkg/styles/theme.go index 19edc3fbc79..6e21b17e4fd 100644 --- a/pkg/styles/theme.go +++ b/pkg/styles/theme.go @@ -89,7 +89,8 @@ func shouldProbeTerminalBackground(goos string) bool { // On Windows, the lipgloss background-color query can crash // (STATUS_DLL_INIT_FAILED) or hang under ConPTY and other pseudo-terminal // environments. Skip the probe entirely and use the default (dark background). - // TODO: replace this with a Windows-safe background detector. + // A Windows-safe background detector can be added later without changing the + // startup-safety contract in this package. if goos == "windows" { return false } From 5d366aca8425520c28a98ea2188c8f86d88ee7eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:45:33 +0000 Subject: [PATCH 08/13] test: simplify adaptive color failure messages Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 73fc9b50d25..09323f8c675 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -272,13 +272,13 @@ func TestAdaptiveColorRGBASwitching(t *testing.T) { hasDarkBackground = true r, g, b, a := c.RGBA() if r != 0 || g != 0 || b != 0 || a != 0xffff { - t.Fatalf("dark background RGBA = (%d,%d,%d,%d), want (0,0,0,%d)", r, g, b, a, uint32(0xffff)) + t.Fatalf("dark background RGBA = (%d,%d,%d,%d), want (0,0,0,65535)", r, g, b, a) } hasDarkBackground = false r, g, b, a = c.RGBA() if r != 0xffff || g != 0xffff || b != 0xffff || a != 0xffff { - t.Fatalf("light background RGBA = (%d,%d,%d,%d), want (%d,%d,%d,%d)", r, g, b, a, uint32(0xffff), uint32(0xffff), uint32(0xffff), uint32(0xffff)) + t.Fatalf("light background RGBA = (%d,%d,%d,%d), want (65535,65535,65535,65535)", r, g, b, a) } } From 14163fac7ee7fa7e598179100a7d78cca65c657e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:47:22 +0000 Subject: [PATCH 09/13] test: strengthen adaptive color switching coverage Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/styles/theme_test.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/styles/theme_test.go b/pkg/styles/theme_test.go index 09323f8c675..ea8c42bcc2f 100644 --- a/pkg/styles/theme_test.go +++ b/pkg/styles/theme_test.go @@ -265,20 +265,22 @@ func TestAdaptiveColorRGBASwitching(t *testing.T) { }) c := adaptiveColor{ - Light: lipgloss.Color("#ffffff"), - Dark: lipgloss.Color("#000000"), + Light: lipgloss.Color("#123456"), + Dark: lipgloss.Color("#abcdef"), } hasDarkBackground = true r, g, b, a := c.RGBA() - if r != 0 || g != 0 || b != 0 || a != 0xffff { - t.Fatalf("dark background RGBA = (%d,%d,%d,%d), want (0,0,0,65535)", r, g, b, a) + wantR, wantG, wantB, wantA := c.Dark.RGBA() + if r != wantR || g != wantG || b != wantB || a != wantA { + t.Fatalf("dark background RGBA = (%d,%d,%d,%d), want (%d,%d,%d,%d)", r, g, b, a, wantR, wantG, wantB, wantA) } hasDarkBackground = false r, g, b, a = c.RGBA() - if r != 0xffff || g != 0xffff || b != 0xffff || a != 0xffff { - t.Fatalf("light background RGBA = (%d,%d,%d,%d), want (65535,65535,65535,65535)", r, g, b, a) + wantR, wantG, wantB, wantA = c.Light.RGBA() + if r != wantR || g != wantG || b != wantB || a != wantA { + t.Fatalf("light background RGBA = (%d,%d,%d,%d), want (%d,%d,%d,%d)", r, g, b, a, wantR, wantG, wantB, wantA) } } @@ -290,9 +292,9 @@ func TestConfigureHasDarkBackgroundUsesStderr(t *testing.T) { var gotReader term.File var gotWriter term.File - configureHasDarkBackground(func(reader term.File, writer term.File) bool { - gotReader = reader - gotWriter = writer + configureHasDarkBackground(func(terminalInput term.File, terminalOutput term.File) bool { + gotReader = terminalInput + gotWriter = terminalOutput return false }) From 2ff2a8bbeae0c2c83fe9388bf356b4c3a090e6eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:02:10 +0000 Subject: [PATCH 10/13] chore: start PR finisher pass Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5a2f44f493a..71787711abd 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( charm.land/huh/v2 v2.0.3 charm.land/lipgloss/v2 v2.0.4 github.com/charmbracelet/x/exp/golden v0.0.0-20260602025833-85a30b5e440a + github.com/charmbracelet/x/term v0.2.2 github.com/cli/go-gh/v2 v2.13.0 github.com/creack/pty v1.1.24 github.com/fsnotify/fsnotify v1.10.1 @@ -48,7 +49,6 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20251106172358-54469c29c2bc // indirect - github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect github.com/cli/safeexec v1.0.1 // indirect From 6864fcc88dfa707e2ff244b7d14b9892678ba3ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:06:02 +0000 Subject: [PATCH 11/13] fix: honor explicit Copilot toolcache root Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/sh/install_copilot_cli.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/actions/setup/sh/install_copilot_cli.sh b/actions/setup/sh/install_copilot_cli.sh index 6b29e47b68b..0f78803797f 100755 --- a/actions/setup/sh/install_copilot_cli.sh +++ b/actions/setup/sh/install_copilot_cli.sh @@ -326,11 +326,18 @@ find_cached_copilot_bin() { requested_version_normalized="$(normalize_version "$requested_version")" fi - for tool_cache_root in \ - "${RUNNER_TOOL_CACHE:-}" \ - /opt/hostedtoolcache \ - /home/runner/work/_tool - do + local tool_cache_roots=() + + if [ -n "${RUNNER_TOOL_CACHE:-}" ]; then + tool_cache_roots=("${RUNNER_TOOL_CACHE}") + else + tool_cache_roots=( + /opt/hostedtoolcache + /home/runner/work/_tool + ) + fi + + for tool_cache_root in "${tool_cache_roots[@]}"; do if [ -z "$tool_cache_root" ]; then continue fi From c6ae3dc27a21cef22a2726f4412db4f9a04c54c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:09:02 +0000 Subject: [PATCH 12/13] fix: isolate explicit Copilot toolcache lookup Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/sh/install_copilot_cli.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/actions/setup/sh/install_copilot_cli.sh b/actions/setup/sh/install_copilot_cli.sh index 0f78803797f..2091e7d0dff 100755 --- a/actions/setup/sh/install_copilot_cli.sh +++ b/actions/setup/sh/install_copilot_cli.sh @@ -338,9 +338,6 @@ find_cached_copilot_bin() { fi for tool_cache_root in "${tool_cache_roots[@]}"; do - if [ -z "$tool_cache_root" ]; then - continue - fi if [ ! -d "${tool_cache_root}/copilot-cli" ]; then echo " Toolcache root ${tool_cache_root}/copilot-cli not found, skipping" >&2 continue From c3389ea740f1462e2fca16bd09b47359dbe2a566 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:11:57 +0000 Subject: [PATCH 13/13] refactor: tighten Copilot toolcache root selection Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/sh/install_copilot_cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/setup/sh/install_copilot_cli.sh b/actions/setup/sh/install_copilot_cli.sh index 2091e7d0dff..8a15fd08c74 100755 --- a/actions/setup/sh/install_copilot_cli.sh +++ b/actions/setup/sh/install_copilot_cli.sh @@ -326,7 +326,7 @@ find_cached_copilot_bin() { requested_version_normalized="$(normalize_version "$requested_version")" fi - local tool_cache_roots=() + local -a tool_cache_roots if [ -n "${RUNNER_TOOL_CACHE:-}" ]; then tool_cache_roots=("${RUNNER_TOOL_CACHE}")