Skip to content

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

Merged
muddlebee merged 11 commits into
Tracer-Cloud:mainfrom
Devesh36:themee
Jun 24, 2026
Merged

feat(cli): add theme option for interactive shell and enhance configuration#2519
muddlebee merged 11 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 theming system for the interactive shell, allowing users to select and persist one of nine color palettes via a --theme CLI flag, OPENSRE_THEME env var, interactive.theme config key, or a new /theme slash command. It also fixes session.active_theme_name initialization and renames _drain_stale_cpr_bytes to a public symbol.

  • Theme registry and lazy tokens (ui/theme.py): A THEME_REGISTRY of nine CliTheme palettes drives _LazyRichStyle sentinel objects that resolve colors at render time, so callers never need to re-import after a theme switch.
  • Three-tier config resolution (repl_config.py, __main__.py): Theme selection follows the established CLI flag → env var → config file → default priority chain, with validation warnings for invalid values at the env-var and config-file tiers.
  • /theme slash command (command_registry/theme.py): Supports both direct argument (/theme blue) and a TTY picker with tab-completion; persists the selection to ~/.opensre/config.yml and live-refreshes the running prompt-toolkit style.

Confidence Score: 5/5

Safe to merge; the theming system is well-isolated, the three-tier config resolution follows established patterns, and previous review concerns (session initialization, public drain function) have been addressed.

The core theme selection, persistence, and live-refresh flows are correct and well-tested. The only finding is a style concern: theme.py imports three underscore-prefixed helpers from config.py, the same anti-pattern that was correctly fixed for drain_stale_cpr_bytes elsewhere in this PR. No logic errors, data-corruption paths, or security issues were identified.

app/cli/interactive_shell/command_registry/theme.py — imports private helpers from config.py that should be promoted to a public surface.

Important Files Changed

Filename Overview
app/cli/interactive_shell/command_registry/theme.py New /theme slash command for interactive theme selection and persistence; imports private helpers from config.py across module boundary.
app/cli/interactive_shell/ui/theme.py New theme registry with 9 palettes and lazy-resolved color tokens; set_active_theme correctly updates all derived ANSI and Rich constants.
app/cli/interactive_shell/config/repl_config.py Adds three-tier theme resolution (CLI flag → env var → config file → default) with proper warnings for invalid values.
app/cli/main.py Adds --theme click.Choice option and passes it through both the interactive-shell and subcommand ReplConfig.load paths.
app/cli/interactive_shell/runtime/entrypoint.py Adds session.active_theme_name = get_active_theme_name() to initialize the session with the resolved palette from ReplConfig.load.
app/cli/commands/config.py Adds interactive.theme to supported config keys with validation via _supported_themes(); the private helpers remain underscore-prefixed.
app/cli/interactive_shell/prompting/prompt_surface.py Adds refresh_prompt_theme to dynamically re-apply the active palette to the running prompt-toolkit application on theme change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant CLI as opensre CLI (--theme)
    participant ReplConfig
    participant ThemeModule as ui/theme.py
    participant Session as ReplSession
    participant PromptTK as prompt_toolkit

    User->>CLI: opensre --theme blue
    CLI->>ReplConfig: "load(cli_theme="blue")"
    ReplConfig->>ThemeModule: set_active_theme("blue")
    ThemeModule-->>ReplConfig: "CliTheme(name="blue")"
    ReplConfig-->>CLI: "ReplConfig(theme="blue")"
    CLI->>Session: ReplSession()
    CLI->>ThemeModule: get_active_theme_name()
    ThemeModule-->>CLI: "blue"
    CLI->>Session: "session.active_theme_name = "blue""
    CLI->>PromptTK: run_interactive(session)

    User->>PromptTK: /theme green
    PromptTK->>ThemeModule: set_active_theme("green")
    PromptTK->>Session: "session.active_theme_name = "green""
    PromptTK->>PromptTK: call_soon_threadsafe(refresh_prompt_theme)
    PromptTK->>CLI: "_save_config({interactive: {theme: green}})"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant CLI as opensre CLI (--theme)
    participant ReplConfig
    participant ThemeModule as ui/theme.py
    participant Session as ReplSession
    participant PromptTK as prompt_toolkit

    User->>CLI: opensre --theme blue
    CLI->>ReplConfig: "load(cli_theme="blue")"
    ReplConfig->>ThemeModule: set_active_theme("blue")
    ThemeModule-->>ReplConfig: "CliTheme(name="blue")"
    ReplConfig-->>CLI: "ReplConfig(theme="blue")"
    CLI->>Session: ReplSession()
    CLI->>ThemeModule: get_active_theme_name()
    ThemeModule-->>CLI: "blue"
    CLI->>Session: "session.active_theme_name = "blue""
    CLI->>PromptTK: run_interactive(session)

    User->>PromptTK: /theme green
    PromptTK->>ThemeModule: set_active_theme("green")
    PromptTK->>Session: "session.active_theme_name = "green""
    PromptTK->>PromptTK: call_soon_threadsafe(refresh_prompt_theme)
    PromptTK->>CLI: "_save_config({interactive: {theme: green}})"
Loading

Reviews (7): Last reviewed commit: "merge: resolve main into themee for them..." | 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

@muddlebee

Copy link
Copy Markdown
Collaborator

@Devesh36 can you fix conflicts? also a demo for an e2e flow like till investigate? I want to see how the theme goes when the interaction is happening? pls add a video demo

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	app/cli/interactive_shell/command_registry/__init__.py
#	app/cli/interactive_shell/command_registry/help.py
#	app/cli/interactive_shell/prompting/prompt_surface.py
#	app/cli/interactive_shell/runtime/entrypoint.py
#	app/cli/interactive_shell/ui/__init__.py
#	app/cli/interactive_shell/ui/banner.py
#	app/cli/interactive_shell/ui/theme.py
#	tests/cli/interactive_shell/ui/test_banner.py
@davincios

Copy link
Copy Markdown
Contributor

@Devesh36 @muddlebee this looks pretty cool, am merging this, cc @0xpaulx for visibility too.

@Devesh36

Copy link
Copy Markdown
Collaborator Author
Screenshot 2026-06-19 at 10 23 43 AM
newtheme.mov

@Devesh36

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

Comment thread app/cli/interactive_shell/ui/provider_models.py
@Devesh36

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

Integrate upstream refactors (banner_state, provider, tables, settings/diagnostics commands) while preserving dynamic theming: refresh_welcome_poster, ui_theme module references for live ANSI updates, and theme save/restore in banner_state.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Devesh36

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

@muddlebee
muddlebee merged commit 979f2be into Tracer-Cloud:main Jun 24, 2026
17 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🌊 Merged. @Devesh36 is now permanently woven into git history. No take-backs. 😄


👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants