feature/custom terminal configs (#717)

* feat(terminal): Add core infrastructure for custom terminal configurations

- Add TerminalConfig types to settings schema (global & project-specific)
- Create RC generator with hex-to-xterm-256 color mapping
- Create RC file manager for .automaker/terminal/ directory
- Add terminal theme color data (40 themes) to platform package
- Integrate terminal config injection into TerminalService
- Support bash, zsh, and sh with proper env var injection (BASH_ENV, ZDOTDIR, ENV)
- Add onThemeChange hook for theme synchronization

Part of custom terminal configurations feature implementation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(terminal): Wire terminal service with settings service

- Pass SettingsService to TerminalService constructor
- Initialize terminal service with settings service dependency
- Enable terminal config injection to work with actual settings

This completes Steps 1-4 of the terminal configuration plan:
- RC Generator (color mapping, prompt formats)
- RC File Manager (file I/O, atomic writes)
- Settings Schema (GlobalSettings + ProjectSettings)
- Terminal Service Integration (env var injection)

Next steps: Settings UI and theme change hooks.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(terminal): Add Settings UI and theme change synchronization

Complete Steps 5 & 6 of terminal configuration implementation:

Settings UI Components:
- Add PromptPreview component with live theme-aware rendering
- Add TerminalConfigSection with comprehensive controls:
  * Enable/disable toggle with confirmation dialog
  * Custom prompt toggle
  * Prompt format selector (4 formats)
  * Git branch/status toggles
  * Custom aliases textarea
  * Custom env vars key-value editor with validation
  * Info box explaining behavior
- Integrate into existing TerminalSection

Theme Change Hook:
- Add theme detection in update-global settings route
- Regenerate RC files for all projects when theme changes
- Skip projects with terminal config disabled
- Error handling with per-project logging
- Inject terminal service with settings service dependency

This completes the full terminal configuration feature:
✓ RC Generator (color mapping, prompts)
✓ RC File Manager (file I/O, versioning)
✓ Settings Schema (types, defaults)
✓ Terminal Service Integration (env vars, PTY spawn)
✓ Settings UI (comprehensive controls, preview)
✓ Theme Synchronization (automatic RC regeneration)

New terminals will use custom prompts matching app theme.
Existing terminals unaffected. User RC files preserved.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(terminal): Add error handling and explicit field mapping for terminal config

- Add try-catch block to handleToggleEnabled
- Explicitly set all required terminalConfig fields
- Add console logging for debugging
- Show error toast if update fails
- Include rcFileVersion: 1 in config object

This should fix the issue where the toggle doesn't enable after
clicking OK in the confirmation dialog.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(terminal): Use React Query mutation hook for settings updates

The issue was that `updateGlobalSettings` doesn't exist in the app store.
The correct pattern is to use the `useUpdateGlobalSettings` hook from
use-settings-mutations.ts, which is a React Query mutation.

Changes:
- Import useUpdateGlobalSettings from mutations hook
- Use mutation.mutate() instead of direct function call
- Add proper onSuccess/onError callbacks
- Remove async/await pattern (React Query handles this)

This fixes the toggle not enabling after clicking OK in the confirmation dialog.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(terminal): Use React Query hook for globalSettings instead of store

The root cause: Component was reading globalSettings from the app store,
which doesn't update reactively when the mutation completes.

Solution: Use useGlobalSettings() React Query hook which:
- Automatically refetches when the mutation invalidates the cache
- Triggers re-render with updated data
- Makes the toggle reflect the new state

Now the flow is:
1. User clicks toggle → confirmation dialog
2. Click OK → mutation.mutate() called
3. Mutation succeeds → invalidates queryKeys.settings.global()
4. Query refetches → component re-renders with new globalSettings
5. Toggle shows enabled state ✓

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* debug(terminal): Add detailed logging for terminal config application

Add logging to track:
- When terminal config check happens
- CWD being used
- Global and project enabled states
- Effective enabled state

This will help diagnose why RC files aren't being generated
when opening terminals in Automaker.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix terminal rc updates and bash rcfile loading

* feat(terminal): add banner on shell start

* feat(terminal): colorize banner per theme

* chore(terminal): bump rc version for banner colors

* feat(terminal): match banner colors to launcher

* feat(terminal): add prompt customization controls

* feat: integrate oh-my-posh prompt themes

* fix: resolve oh-my-posh theme path

* fix: correct oh-my-posh config invocation

* docs: add terminal theme screenshot

* fix: address review feedback and stabilize e2e test

* ui: split terminal config into separate card

* fix: enable cross-platform Warp terminal detection

- Remove macOS-only platform restriction for Warp
- Add Linux CLI alias 'warp-terminal' (primary on Linux)
- Add CLI launch handler using --cwd flag
- Fixes issue where Warp was not detected on Linux systems

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Dhanush Santosh
2026-02-03 20:34:33 +05:30
committed by GitHub
parent ebc7987988
commit 88864ad6bc
20 changed files with 4571 additions and 277 deletions

View File

@@ -27,14 +27,16 @@ export type { ModelAlias };
*
* Includes system theme and multiple color schemes organized by dark/light:
* - System: Respects OS dark/light mode preference
* - Dark themes (16): dark, retro, dracula, nord, monokai, tokyonight, solarized,
* gruvbox, catppuccin, onedark, synthwave, red, sunset, gray, forest, ocean
* - Light themes (16): light, cream, solarizedlight, github, paper, rose, mint,
* lavender, sand, sky, peach, snow, sepia, gruvboxlight, nordlight, blossom
* - Dark themes (20): dark, retro, dracula, nord, monokai, tokyonight, solarized,
* gruvbox, catppuccin, onedark, synthwave, red, sunset, gray, forest, ocean,
* ember, ayu-dark, ayu-mirage, matcha
* - Light themes (20): light, cream, solarizedlight, github, paper, rose, mint,
* lavender, sand, sky, peach, snow, sepia, gruvboxlight, nordlight, blossom,
* ayu-light, onelight, bluloco, feather
*/
export type ThemeMode =
| 'system'
// Dark themes (16)
// Dark themes (20)
| 'dark'
| 'retro'
| 'dracula'
@@ -51,7 +53,11 @@ export type ThemeMode =
| 'gray'
| 'forest'
| 'ocean'
// Light themes (16)
| 'ember'
| 'ayu-dark'
| 'ayu-mirage'
| 'matcha'
// Light themes (20)
| 'light'
| 'cream'
| 'solarizedlight'
@@ -67,7 +73,138 @@ export type ThemeMode =
| 'sepia'
| 'gruvboxlight'
| 'nordlight'
| 'blossom';
| 'blossom'
| 'ayu-light'
| 'onelight'
| 'bluloco'
| 'feather';
export type TerminalPromptTheme =
| 'custom'
| 'omp-1_shell'
| 'omp-agnoster'
| 'omp-agnoster.minimal'
| 'omp-agnosterplus'
| 'omp-aliens'
| 'omp-amro'
| 'omp-atomic'
| 'omp-atomicBit'
| 'omp-avit'
| 'omp-blue-owl'
| 'omp-blueish'
| 'omp-bubbles'
| 'omp-bubblesextra'
| 'omp-bubblesline'
| 'omp-capr4n'
| 'omp-catppuccin'
| 'omp-catppuccin_frappe'
| 'omp-catppuccin_latte'
| 'omp-catppuccin_macchiato'
| 'omp-catppuccin_mocha'
| 'omp-cert'
| 'omp-chips'
| 'omp-cinnamon'
| 'omp-clean-detailed'
| 'omp-cloud-context'
| 'omp-cloud-native-azure'
| 'omp-cobalt2'
| 'omp-craver'
| 'omp-darkblood'
| 'omp-devious-diamonds'
| 'omp-di4am0nd'
| 'omp-dracula'
| 'omp-easy-term'
| 'omp-emodipt'
| 'omp-emodipt-extend'
| 'omp-fish'
| 'omp-free-ukraine'
| 'omp-froczh'
| 'omp-gmay'
| 'omp-glowsticks'
| 'omp-grandpa-style'
| 'omp-gruvbox'
| 'omp-half-life'
| 'omp-honukai'
| 'omp-hotstick.minimal'
| 'omp-hul10'
| 'omp-hunk'
| 'omp-huvix'
| 'omp-if_tea'
| 'omp-illusi0n'
| 'omp-iterm2'
| 'omp-jandedobbeleer'
| 'omp-jblab_2021'
| 'omp-jonnychipz'
| 'omp-json'
| 'omp-jtracey93'
| 'omp-jv_sitecorian'
| 'omp-kali'
| 'omp-kushal'
| 'omp-lambda'
| 'omp-lambdageneration'
| 'omp-larserikfinholt'
| 'omp-lightgreen'
| 'omp-M365Princess'
| 'omp-marcduiker'
| 'omp-markbull'
| 'omp-material'
| 'omp-microverse-power'
| 'omp-mojada'
| 'omp-montys'
| 'omp-mt'
| 'omp-multiverse-neon'
| 'omp-negligible'
| 'omp-neko'
| 'omp-night-owl'
| 'omp-nordtron'
| 'omp-nu4a'
| 'omp-onehalf.minimal'
| 'omp-paradox'
| 'omp-pararussel'
| 'omp-patriksvensson'
| 'omp-peru'
| 'omp-pixelrobots'
| 'omp-plague'
| 'omp-poshmon'
| 'omp-powerlevel10k_classic'
| 'omp-powerlevel10k_lean'
| 'omp-powerlevel10k_modern'
| 'omp-powerlevel10k_rainbow'
| 'omp-powerline'
| 'omp-probua.minimal'
| 'omp-pure'
| 'omp-quick-term'
| 'omp-remk'
| 'omp-robbyrussell'
| 'omp-rudolfs-dark'
| 'omp-rudolfs-light'
| 'omp-sim-web'
| 'omp-slim'
| 'omp-slimfat'
| 'omp-smoothie'
| 'omp-sonicboom_dark'
| 'omp-sonicboom_light'
| 'omp-sorin'
| 'omp-space'
| 'omp-spaceship'
| 'omp-star'
| 'omp-stelbent-compact.minimal'
| 'omp-stelbent.minimal'
| 'omp-takuya'
| 'omp-the-unnamed'
| 'omp-thecyberden'
| 'omp-tiwahu'
| 'omp-tokyo'
| 'omp-tokyonight_storm'
| 'omp-tonybaloney'
| 'omp-uew'
| 'omp-unicorn'
| 'omp-velvet'
| 'omp-wholespace'
| 'omp-wopian'
| 'omp-xtoys'
| 'omp-ys'
| 'omp-zash';
/** PlanningMode - Planning levels for feature generation workflows */
export type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
@@ -840,6 +977,39 @@ export interface GlobalSettings {
// Terminal Configuration
/** How to open terminals from "Open in Terminal" worktree action */
openTerminalMode?: 'newTab' | 'split';
/** Custom terminal configuration settings (prompt theming, aliases, env vars) */
terminalConfig?: {
/** Enable custom terminal configurations (default: false) */
enabled: boolean;
/** Enable custom prompt (default: true when enabled) */
customPrompt: boolean;
/** Prompt format template */
promptFormat: 'standard' | 'minimal' | 'powerline' | 'starship';
/** Prompt theme preset */
promptTheme?: TerminalPromptTheme;
/** Show git branch in prompt (default: true) */
showGitBranch: boolean;
/** Show git status dirty indicator (default: true) */
showGitStatus: boolean;
/** Show user and host in prompt (default: true) */
showUserHost: boolean;
/** Show path in prompt (default: true) */
showPath: boolean;
/** Path display style */
pathStyle: 'full' | 'short' | 'basename';
/** Limit path depth (0 = full path) */
pathDepth: number;
/** Show current time in prompt (default: false) */
showTime: boolean;
/** Show last command exit status when non-zero (default: false) */
showExitStatus: boolean;
/** User-provided custom aliases (multiline string) */
customAliases: string;
/** User-provided custom env vars */
customEnvVars: Record<string, string>;
/** RC file format version (for migration) */
rcFileVersion?: number;
};
// UI State Preferences
/** Whether sidebar is currently open */
@@ -1245,6 +1415,33 @@ export interface ProjectSettings {
*/
defaultFeatureModel?: PhaseModelEntry;
// Terminal Configuration Override (per-project)
/** Project-specific terminal config overrides */
terminalConfig?: {
/** Override global enabled setting */
enabled?: boolean;
/** Override prompt theme preset */
promptTheme?: TerminalPromptTheme;
/** Override showing user/host */
showUserHost?: boolean;
/** Override showing path */
showPath?: boolean;
/** Override path style */
pathStyle?: 'full' | 'short' | 'basename';
/** Override path depth (0 = full path) */
pathDepth?: number;
/** Override showing time */
showTime?: boolean;
/** Override showing exit status */
showExitStatus?: boolean;
/** Project-specific custom aliases */
customAliases?: string;
/** Project-specific env vars */
customEnvVars?: Record<string, string>;
/** Custom welcome message for this project */
welcomeMessage?: string;
};
// Deprecated Claude API Profile Override
/**
* @deprecated Use phaseModelOverrides instead.