Migrate 203 test files (356 call sites) from fs.rm/fs.rmSync to
removeWithRetries/removeSyncWithRetries to reduce EBUSY test failures
on Windows. removeWithRetries is now exported from @oh-my-pi/pi-utils.
The migration uses a regex-based approach that:
- Replaces fs.rm(path, { recursive, force }) → removeWithRetries(path)
- Replaces fs.rmSync(path, { recursive, force }) → removeSyncWithRetries(path)
- Replaces fs.rm(path) → removeWithRetries(path) (no options)
- Skips fs.rm/fs.rmSync inside template literals (bun --eval scripts)
- Adds imports to existing @oh-my-pi/pi-utils import or creates new one
- Removes unused fs imports where fs.rm was the only fs usage (4 files)
Derived HTML default text from the resolved export card/page surface, falling back to the derived user-message export base when themes omit export overrides.
Covered light-status custom themes whose dark userMessageBg drives derived export backgrounds.
Fixes#2516
The HTML session export rendered empty text tokens (text, userMessageText,
customMessageText, toolTitle) as the dark-theme grey #e5e5e7 on every theme
whose name was not literally 'light'. getResolvedThemeColors picked
defaultText off name === 'light', and the standalone isLightTheme helper
classified on userMessageBg — both diverged from Theme.isLight, which has
classified on statusLineBg since porcelain proved userMessageBg unreliable.
Factor the status-line luminance into isLightThemeJson(themeJson) and route
both helpers through it. defaultText now falls back to #000000 on every
light theme (sandstone, limestone, porcelain, light, …) and to #e5e5e7
elsewhere, matching Theme.getColorHex.
Regression test extends test/theme-islight.test.ts with the standalone
helper and getResolvedThemeColors HTML-default coverage for sandstone +
dark.
Fixes#2516
Per review on #1715: the light-theme cap was a fixed absolute luminance (0.2) tuned for near-white backgrounds. On a mid-light shipped theme like light-poimandres (statusLineBg #7390aa, luminance ~0.55) that still left accents at only ~1.8-2.4:1. Theme now exposes statusLineLuminance + accentSurfaceLuminance; getSessionAccentHex takes the surface luminance and caps the accent to guarantee >=3:1 (WCAG AA large) against the actual surface, so near-white and mid-light light themes are both legible. Tests assert contrast across light + mid-light surfaces.
Per review on #1715: Theme.isLight drove accent darkening from userMessageBg, but the accents render on the status line / editor chrome. Themes that style the chat bubble darker than the status line (e.g. built-in porcelain: userMessageBg #5070a0, statusLineBg #f8f9fc) misclassified, leaving the bright dark-theme accent on a light status line. Derive isLight from statusLineBg; add a regression test over porcelain / light-catppuccin / dark-catppuccin.