Problem
docker-agent always starts with the dark built-in default theme. Users whose terminal follows the OS appearance (e.g. Ghostty with theme = light:...,dark:..., switching by time of day) get a dark TUI on a light terminal background — illegible and incoherent. There is currently no terminal-background detection anywhere: the theme is resolved once, pre-TUI, in cmd/root/run.go applyTheme() (--theme flag > settings.theme > default).
Proposal (no change to default behavior)
Add an explicit, opt-in theme value auto (accepted in settings.theme, --theme auto, and as an "Auto (match terminal)" entry in the /theme picker). When — and only when — auto is selected:
- Startup detection (works in any terminal answering OSC 11): synchronous
lipgloss.BackgroundColor(os.Stdin, os.Stdout) query in applyTheme(), gated on TTY, short timeout, dark fallback. One implementation point covers the full TUI, --lean, docker agent board, the agent picker and docker-agent new, and avoids a wrong-theme first frame.
- Live switching (Ghostty, kitty, contour, …): in the full TUI, enable DEC mode 2031 (
tea.Raw(ansi.SetModeLightDark)) in appModel.Init and handle the resulting uv.DarkColorSchemeEvent / uv.LightColorSchemeEvent (bubbletea v2.0.8 passes them through translateInputEvent untyped; the ultraviolet decoder already parses CSI ?997;1|2 n). Apply the theme on the TUI goroutine via the existing styles.ApplyTheme + ThemeChangedMsg invalidation path. Reset mode 2031 on exit. Optionally re-issue tea.RequestBackgroundColor() on tea.FocusMsg for terminals without 2031.
- Theme pair resolution:
auto resolves dark → settings.theme_dark (default default), light → settings.theme_light (default: new bundled default-light theme, to be authored with contrast checks against pkg/tui/styles/colorutil.go helpers).
Explicitly out of scope / guarantees
- Unset
settings.theme keeps meaning the dark default — zero behavior change and zero extra startup queries for existing users.
- tmux/pipes/CI: query is skipped (non-TTY) or times out → dark fallback.
- Live 2031 handling inside the lean TUI's custom event loop is a follow-up.
Implementation plan
A detailed step-by-step plan (files, functions, tests, docs, risks) exists in the team's shared plan store under adaptive-terminal-theme; summary of steps: (1) AutoThemeRef sentinel + polarity state + resolver in pkg/tui/styles; (2) author themes/default-light.yaml + contrast test; (3) theme_dark/theme_light in pkg/userconfig; (4) startup detection in applyTheme + --theme auto validation/completion; (5) live switching + mode reset in pkg/tui/tui.go; (6) picker "Auto" entry + persistence; (7) tests; (8) docs (docs/features/tui/index.md, docs/features/cli/index.md). Can ship as one PR or split: startup coherence first, live switching second.
Acceptance criteria
Problem
docker-agent always starts with the dark built-in
defaulttheme. Users whose terminal follows the OS appearance (e.g. Ghostty withtheme = light:...,dark:..., switching by time of day) get a dark TUI on a light terminal background — illegible and incoherent. There is currently no terminal-background detection anywhere: the theme is resolved once, pre-TUI, incmd/root/run.go applyTheme()(--themeflag >settings.theme>default).Proposal (no change to default behavior)
Add an explicit, opt-in theme value
auto(accepted insettings.theme,--theme auto, and as an "Auto (match terminal)" entry in the/themepicker). When — and only when —autois selected:lipgloss.BackgroundColor(os.Stdin, os.Stdout)query inapplyTheme(), gated on TTY, short timeout, dark fallback. One implementation point covers the full TUI,--lean,docker agent board, the agent picker anddocker-agent new, and avoids a wrong-theme first frame.tea.Raw(ansi.SetModeLightDark)) inappModel.Initand handle the resultinguv.DarkColorSchemeEvent/uv.LightColorSchemeEvent(bubbletea v2.0.8 passes them throughtranslateInputEventuntyped; the ultraviolet decoder already parsesCSI ?997;1|2 n). Apply the theme on the TUI goroutine via the existingstyles.ApplyTheme+ThemeChangedMsginvalidation path. Reset mode 2031 on exit. Optionally re-issuetea.RequestBackgroundColor()ontea.FocusMsgfor terminals without 2031.autoresolves dark →settings.theme_dark(defaultdefault), light →settings.theme_light(default: new bundleddefault-lighttheme, to be authored with contrast checks againstpkg/tui/styles/colorutil.gohelpers).Explicitly out of scope / guarantees
settings.themekeeps meaning the darkdefault— zero behavior change and zero extra startup queries for existing users.Implementation plan
A detailed step-by-step plan (files, functions, tests, docs, risks) exists in the team's shared plan store under
adaptive-terminal-theme; summary of steps: (1)AutoThemeRefsentinel + polarity state + resolver inpkg/tui/styles; (2) authorthemes/default-light.yaml+ contrast test; (3)theme_dark/theme_lightinpkg/userconfig; (4) startup detection inapplyTheme+--theme autovalidation/completion; (5) live switching + mode reset inpkg/tui/tui.go; (6) picker "Auto" entry + persistence; (7) tests; (8) docs (docs/features/tui/index.md,docs/features/cli/index.md). Can ship as one PR or split: startup coherence first, live switching second.Acceptance criteria
theme: auto+ light Ghostty → light theme at launch; dark Ghostty → dark theme.--theme autoaccepted, validated, shell-completed;/themepicker offers Auto and persists it.?997reports in the shell afterwards).task build && task test && task lintclean; docs updated in the same PR.