Skip to content

feat(cli): add theme option for interactive shell and enhance configuration#2519

Open
Devesh36 wants to merge 8 commits into
Tracer-Cloud:mainfrom
Devesh36:themee
Open

feat(cli): add theme option for interactive shell and enhance configuration#2519
Devesh36 wants to merge 8 commits into
Tracer-Cloud:mainfrom
Devesh36:themee

Conversation

@Devesh36

Copy link
Copy Markdown
Collaborator

This pull request introduces a comprehensive theming system for the interactive shell, allowing users to select and persist a color palette via CLI options, environment variables, configuration files, or a new /theme slash command. It also improves configuration validation and enhances the user experience for theme selection and application. Additionally, there are minor improvements to code clarity and command output formatting.

Interactive shell theming system:

  • Added support for a --theme CLI option, OPENSRE_THEME environment variable, and interactive.theme config key, allowing users to select the shell color palette. Theme selection is validated and persisted, with a default of "green" if unset or invalid (app/cli/__main__.py, app/cli/interactive_shell/config/repl_config.py, app/cli/commands/config.py). [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Introduced a /theme slash command for interactive theme selection and persistence, including a TTY picker, tab-completion, and help integration (app/cli/interactive_shell/command_registry/theme.py, app/cli/interactive_shell/command_registry/__init__.py, app/cli/interactive_shell/command_registry/help.py, app/cli/interactive_shell/command_registry/slash_catalog.py). [1] [2] [3] [4] [5] [6]
  • Updated prompt rendering and UI code to use the active theme dynamically, ensuring prompt-toolkit and color codes reflect the selected palette (app/cli/interactive_shell/prompting/prompt_surface.py). [1] [2] [3] [4]

Configuration and validation improvements:

  • Enhanced config validation for interactive.theme, providing user-friendly error messages and fallback to defaults if invalid values are detected (app/cli/commands/config.py, app/cli/interactive_shell/config/repl_config.py). [1] [2]

Minor improvements and refactoring:

  • Improved code clarity and output formatting in session commands and welcome poster refresh logic (app/cli/interactive_shell/command_registry/session_cmds.py). [1] [2] [3] [4] [5] [6]

These changes provide a more customizable and user-friendly interactive shell experience.
Screenshot 2026-05-25 at 7 56 58 PM

Screenshot 2026-05-25 at 7 58 06 PM

@github-actions

Copy link
Copy Markdown
Contributor

Greptile code review

This repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md.

Run a review — add a PR comment with:

@greptile review

Give it ~5-10 minutes (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5.

Optional: automate with the greploop skill.

Comment thread app/cli/interactive_shell/ui/banner.py Fixed
Comment thread app/cli/interactive_shell/ui/choice_menu.py Fixed
Comment thread app/cli/interactive_shell/ui/rendering.py Fixed
@greptile-apps

greptile-apps Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a comprehensive interactive-shell theming system: a THEME_REGISTRY of nine named palettes, a _LazyRichStyle proxy that resolves colour tokens against the active palette at render time, three-tier resolution (--theme / OPENSRE_THEME / interactive.theme), and a /theme slash command with a TTY picker and config persistence. Previous thread issues (double ReplConfig.load() on the interactive path, the private _drain_stale_cpr_bytes import, the TTY-guard blocking named-theme args, and the hardcoded \"green\" default in session.active_theme_name) are all addressed.

  • Theme engine (ui/theme.py): _LazyRichStyle proxies all module-level colour constants so importers bound at startup automatically reflect the active palette; set_active_theme() updates both the proxy targets and the ANSI/Markdown derived constants in one call.
  • /theme command (command_registry/theme.py): named-arg path skips the TTY guard and persists immediately; picker path requires TTY and pre-selects the current theme via the new initial_value parameter on repl_choose_one.
  • Config wiring (repl_config.py, __main__.py, commands/config.py): theme resolution follows the same three-tier cascade as layout; invalid values from env/file fall back to \"green\" with a logged warning; the config set interactive.theme command validates against the full palette registry.

Confidence Score: 5/5

Safe to merge; all changes are additive theming infrastructure with no effect on the agent/investigation execution path.

The theming system is well-isolated and the previous blocking issues (double config load, private import, TTY-guard placement, stale active_theme_name) are all resolved. The two remaining findings are cosmetic: the placeholder colour won't update live when the theme changes mid-session, and the _LazyRichStyle empty-string base is a latent trap for future authors. Neither affects current functionality.

app/cli/interactive_shell/prompting/prompt_surface.py — refresh_prompt_theme has the no-op placeholder assignment; app/cli/interactive_shell/ui/theme.py — _LazyRichStyle empty-string base warrants a code comment warning future authors.

Important Files Changed

Filename Overview
app/cli/interactive_shell/ui/theme.py Core theming system: added THEME_REGISTRY, CliTheme dataclass, _LazyRichStyle proxy, and set_active_theme/get_active_theme helpers; all derived ANSI/Markdown constants now resolve dynamically against the active palette.
app/cli/interactive_shell/command_registry/theme.py New /theme slash command: args path bypasses TTY guard (correct), picker path requires TTY; persists choice to config file and schedules prompt-toolkit style refresh via call_soon_threadsafe.
app/cli/interactive_shell/prompting/prompt_surface.py Migrated from direct theme-constant imports to ui_theme module references; added refresh_prompt_theme() — app.style update is correct but app.placeholder assignment is a no-op in prompt_toolkit's Application API.
app/cli/main.py Adds --theme CLI option; interactive path calls ReplConfig.load with full args once (no double-load); subcommand path calls ReplConfig.load(cli_theme=theme) as a side-effect-only call to apply the active theme.
app/cli/interactive_shell/config/repl_config.py Three-tier theme resolution (CLI → env → file) with warnings on invalid values, calls set_active_theme() as a side effect during load; cli_theme path relies on Click validation rather than in-method validation.
app/cli/interactive_shell/runtime/session.py Added pt_style_app, main_loop, and active_theme_name fields; active_theme_name is now initialized from get_active_theme_name() in entrypoint.py, resolving the hardcoded 'green' default issue.
app/cli/interactive_shell/ui/rendering.py Added refresh_welcome_poster, repl_clear_screen, and repl_render_launch_poster; render_table signature generalized (title_style defaults to None, resolved internally).
app/cli/commands/config.py Added interactive.theme to _SUPPORTED_KEYS with validation in _coerce_value; sorted alphabetical order in error message differs from display order used elsewhere (minor UX inconsistency).
app/cli/interactive_shell/runtime/loop.py Renamed _drain_stale_cpr_bytes to drain_stale_cpr_bytes (public); stores pt_app and main_loop on session for cross-thread style refresh.
app/cli/interactive_shell/runtime/entrypoint.py session.active_theme_name now initialized from get_active_theme_name() before run_interactive, so the session reflects the configured theme immediately.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["CLI --theme / OPENSRE_THEME / config.yml"] --> B["ReplConfig.load(cli_theme=...)"]
    B --> C["set_active_theme(name)"]
    C --> D["_ACTIVE_THEME global updated\n_apply_theme() → ANSI/Markdown constants"]
    D --> E["_LazyRichStyle proxies resolve at render time"]

    F["User runs /theme"] --> G{args provided?}
    G -- "yes (/theme blue)" --> H["Validate against THEME_REGISTRY"]
    G -- "no" --> I{repl_tty_interactive?}
    I -- "no" --> J["Print error, return"]
    I -- "yes" --> K["repl_choose_one TTY picker\n(pre-selects current theme)"]
    K --> L["User picks theme"]
    H --> M["_persist_and_report_theme"]
    L --> M
    M --> N["set_active_theme(selected)"]
    M --> O["session.active_theme_name = name"]
    M --> P["_save_config (interactive.theme)"]
    M --> Q["call_soon_threadsafe(refresh_prompt_theme)"]
    M --> R["refresh_welcome_poster (clear + redraw)"]
    Q --> S["app.style = _build_prompt_style()\napp.invalidate()"]
Loading

Reviews (4): Last reviewed commit: "refactor(theme): streamline theme choice..." | Re-trigger Greptile

Comment thread app/cli/__main__.py
Comment thread app/cli/interactive_shell/command_registry/theme.py Outdated
Comment thread app/cli/interactive_shell/command_registry/theme.py
Comment on lines +104 to +105
active_theme_name: str = "green"
"""Interactive shell palette name for this REPL session (``/theme``, prompts)."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 active_theme_name not initialized from the configured theme

The field defaults to the hardcoded string "green" regardless of what theme was resolved during ReplConfig.load(). If the user has configured interactive.theme: blue (or OPENSRE_THEME=blue), any code reading session.active_theme_name before /theme is first invoked will see "green" instead of "blue". Since set_active_theme() is called during config load, the module-level global in ui/theme.py is already correct — the session field just needs to be initialized with get_active_theme_name() either at session creation or in the REPL startup path.

@Devesh36

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

@Devesh36

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

@Devesh36 Devesh36 requested a review from 0xpaulx May 27, 2026 11:25
@Devesh36

Devesh36 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants