diff --git a/EvoScientist/cli/commands.py b/EvoScientist/cli/commands.py index a012f2b..e45517d 100644 --- a/EvoScientist/cli/commands.py +++ b/EvoScientist/cli/commands.py @@ -418,7 +418,7 @@ def _main_callback( ui: Optional[str] = typer.Option( None, "--ui", - help="UI backend: rich (default) or textual (beta).", + help="UI backend: tui (default) or cli.", ), ): """EvoScientist Agent - AI-powered research & code execution CLI""" @@ -461,8 +461,8 @@ def _main_callback( if mode and mode not in ("run", "daemon"): raise typer.BadParameter("--mode must be 'run' or 'daemon'") - if ui and ui.lower() not in ("rich", "textual"): - raise typer.BadParameter("--ui must be 'rich' or 'textual'") + if ui and ui.lower() not in ("cli", "tui"): + raise typer.BadParameter("--ui must be 'tui' or 'cli'") # --name only makes sense in run mode if name and not (mode == "run" or (not mode and not workdir and not use_cwd and config.default_mode == "run")): diff --git a/EvoScientist/cli/interactive.py b/EvoScientist/cli/interactive.py index 500c5fb..3ca832f 100644 --- a/EvoScientist/cli/interactive.py +++ b/EvoScientist/cli/interactive.py @@ -161,7 +161,7 @@ def cmd_interactive( provider: str | None = None, run_name: str | None = None, thread_id: str | None = None, - ui_backend: str = "rich", + ui_backend: str = "cli", config=None, ) -> None: """Interactive conversation mode with streaming output. @@ -179,13 +179,13 @@ def cmd_interactive( provider: LLM provider name to display in banner run_name: Optional run name for /new session deduplication thread_id: Optional thread ID to resume a previous session - ui_backend: UI backend ('rich' or 'textual') + ui_backend: UI backend ('cli' or 'tui') """ import nest_asyncio nest_asyncio.apply() resolved_ui_backend = resolve_ui_backend(ui_backend, warn_fallback=True) - if resolved_ui_backend == "textual": + if resolved_ui_backend == "tui": from functools import partial load_agent = partial(_load_agent, config=config) run_textual_interactive( @@ -735,7 +735,7 @@ def cmd_run( show_thinking: bool = True, workspace_dir: str | None = None, model: str | None = None, - ui_backend: str = "rich", + ui_backend: str = "cli", ) -> None: """Single-shot execution with streaming display. @@ -746,7 +746,7 @@ def cmd_run( show_thinking: Whether to display thinking panels workspace_dir: Per-session workspace directory path model: Model name for checkpoint metadata - ui_backend: UI backend ('rich' or 'textual') + ui_backend: UI backend ('cli' or 'tui') """ thread_id = thread_id or generate_thread_id() diff --git a/EvoScientist/cli/tui_interactive.py b/EvoScientist/cli/tui_interactive.py index 69c2ec3..9b8a3cb 100644 --- a/EvoScientist/cli/tui_interactive.py +++ b/EvoScientist/cli/tui_interactive.py @@ -1924,7 +1924,7 @@ def run_textual_interactive( mode=mode, model=model, provider=provider, - ui_backend="textual", + ui_backend="tui", channels=channels_info, ) ) diff --git a/EvoScientist/cli/tui_runtime.py b/EvoScientist/cli/tui_runtime.py index c71612d..d31dc92 100644 --- a/EvoScientist/cli/tui_runtime.py +++ b/EvoScientist/cli/tui_runtime.py @@ -7,8 +7,8 @@ from typing import Any, Callable from ..stream.display import console from .tui_backends import RichStreamingBackend, StreamingTUIBackend -DEFAULT_UI_BACKEND = "rich" -SUPPORTED_UI_BACKENDS = ("rich", "textual") +DEFAULT_UI_BACKEND = "cli" +SUPPORTED_UI_BACKENDS = ("cli", "tui") def normalize_ui_backend(value: str | None) -> str: @@ -32,11 +32,11 @@ def _has_textual_support() -> bool: def resolve_ui_backend(value: str | None, *, warn_fallback: bool = False) -> str: """Resolve requested backend and fallback safely when unavailable.""" requested = normalize_ui_backend(value) - if requested == "textual" and not _has_textual_support(): + if requested == "tui" and not _has_textual_support(): if warn_fallback: console.print( - '[yellow]Textual TUI is unavailable (missing textual package). ' - 'Falling back to Rich.[/yellow]' + '[yellow]TUI is unavailable (missing textual package). ' + 'Falling back to CLI.[/yellow]' ) return DEFAULT_UI_BACKEND return requested @@ -83,9 +83,9 @@ def run_streaming( ) except RuntimeError: requested = normalize_ui_backend(ui_backend) - if requested == "textual": + if requested == "tui": console.print( - "[yellow]Textual TUI failed at runtime. Falling back to Rich for this request.[/yellow]" + "[yellow]TUI failed at runtime. Falling back to CLI for this request.[/yellow]" ) return RichStreamingBackend().run_streaming( agent=agent, diff --git a/EvoScientist/config/onboard.py b/EvoScientist/config/onboard.py index 6dd3a17..2aa9631 100644 --- a/EvoScientist/config/onboard.py +++ b/EvoScientist/config/onboard.py @@ -478,8 +478,8 @@ def _step_ui_backend(config: EvoScientistConfig) -> str: Selected backend name ("rich" or "textual"). """ choices = [ - Choice(title="Rich CLI (classic terminal)", value="rich"), - Choice(title="Textual TUI (full-screen interface)", value="textual"), + Choice(title="TUI (full-screen interface, recommended)", value="tui"), + Choice(title="CLI (classic terminal, lightweight)", value="cli"), ] backend = questionary.select( diff --git a/EvoScientist/config/settings.py b/EvoScientist/config/settings.py index f9272b7..07d1854 100644 --- a/EvoScientist/config/settings.py +++ b/EvoScientist/config/settings.py @@ -81,7 +81,7 @@ class EvoScientistConfig: # UI Settings show_thinking: bool = True - ui_backend: Literal["rich", "textual"] = "rich" + ui_backend: Literal["cli", "tui"] = "tui" # Channel Settings channel_enabled: str = "" # "imessage" | "telegram" | "discord" | "slack" | "wechat" | "dingtalk" | "feishu" | "email" | "qq" | "signal" | "" (comma-separated for multiple) diff --git a/tests/test_cli_tui_dispatch.py b/tests/test_cli_tui_dispatch.py index d0edb08..2f08290 100644 --- a/tests/test_cli_tui_dispatch.py +++ b/tests/test_cli_tui_dispatch.py @@ -9,7 +9,7 @@ def test_cmd_interactive_dispatches_to_textual(monkeypatch): def _fake_resolve_ui_backend(value, *, warn_fallback=False): # noqa: ANN001 captured["resolved_input"] = value captured["warn_fallback"] = warn_fallback - return "textual" + return "tui" def _fake_run_textual_interactive(**kwargs): # noqa: ANN003 captured["kwargs"] = kwargs @@ -33,10 +33,10 @@ def test_cmd_interactive_dispatches_to_textual(monkeypatch): provider="demo-provider", run_name="demo-run", thread_id="thread-1", - ui_backend="textual", + ui_backend="tui", ) - assert captured["resolved_input"] == "textual" + assert captured["resolved_input"] == "tui" assert captured["warn_fallback"] is True kwargs = captured["kwargs"] diff --git a/tests/test_config.py b/tests/test_config.py index bc17ac5..6a0e26e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -76,7 +76,7 @@ class TestEvoScientistConfig: assert config.default_mode == "daemon" assert config.default_workdir == "" assert config.show_thinking is True - assert config.ui_backend == "rich" + assert config.ui_backend == "tui" assert config.ollama_base_url == "" assert config.imessage_enabled is False assert config.imessage_allowed_senders == "" @@ -315,10 +315,10 @@ class TestPriorityChain: def test_env_ui_backend_override(self, temp_config_dir, monkeypatch): """UI backend can be selected via environment variable.""" - save_config(EvoScientistConfig(ui_backend="rich")) - monkeypatch.setenv("EVOSCIENTIST_UI_BACKEND", "textual") + save_config(EvoScientistConfig(ui_backend="cli")) + monkeypatch.setenv("EVOSCIENTIST_UI_BACKEND", "tui") config = get_effective_config() - assert config.ui_backend == "textual" + assert config.ui_backend == "tui" def test_env_api_key_override(self, temp_config_dir, monkeypatch): """Test API keys from env override file.""" diff --git a/tests/test_ui_runtime.py b/tests/test_ui_runtime.py index dd6b7e1..c399c7e 100644 --- a/tests/test_ui_runtime.py +++ b/tests/test_ui_runtime.py @@ -5,40 +5,40 @@ from dataclasses import dataclass from EvoScientist.cli.tui_runtime import normalize_ui_backend, resolve_ui_backend, run_streaming -def test_normalize_ui_backend_defaults_to_rich(): - assert normalize_ui_backend(None) == "rich" - assert normalize_ui_backend("") == "rich" +def test_normalize_ui_backend_defaults_to_cli(): + assert normalize_ui_backend(None) == "cli" + assert normalize_ui_backend("") == "cli" def test_normalize_ui_backend_accepts_known_values(): - assert normalize_ui_backend("rich") == "rich" - assert normalize_ui_backend("textual") == "textual" - assert normalize_ui_backend("TeXtUaL") == "textual" + assert normalize_ui_backend("cli") == "cli" + assert normalize_ui_backend("tui") == "tui" + assert normalize_ui_backend("TUI") == "tui" -def test_normalize_ui_backend_unknown_falls_back_to_rich(): - assert normalize_ui_backend("unknown-ui") == "rich" +def test_normalize_ui_backend_unknown_falls_back_to_cli(): + assert normalize_ui_backend("unknown-ui") == "cli" def test_resolve_ui_backend_falls_back_when_textual_unavailable(monkeypatch): monkeypatch.setattr("EvoScientist.cli.tui_runtime._has_textual_support", lambda: False) - assert resolve_ui_backend("textual") == "rich" + assert resolve_ui_backend("tui") == "cli" -def test_resolve_ui_backend_keeps_textual_when_available(monkeypatch): +def test_resolve_ui_backend_keeps_tui_when_available(monkeypatch): monkeypatch.setattr("EvoScientist.cli.tui_runtime._has_textual_support", lambda: True) - assert resolve_ui_backend("textual") == "textual" + assert resolve_ui_backend("tui") == "tui" @dataclass class _BrokenBackend: - name: str = "textual" + name: str = "tui" def run_streaming(self, **kwargs): # noqa: ANN003, ANN201 raise RuntimeError("boom") -def test_run_streaming_falls_back_to_rich_on_runtime_error(monkeypatch): +def test_run_streaming_falls_back_to_cli_on_runtime_error(monkeypatch): monkeypatch.setattr("EvoScientist.cli.tui_runtime.get_backend", lambda *a, **k: _BrokenBackend()) class _RichStub: @@ -48,7 +48,7 @@ def test_run_streaming_falls_back_to_rich_on_runtime_error(monkeypatch): monkeypatch.setattr("EvoScientist.cli.tui_runtime.RichStreamingBackend", lambda: _RichStub()) result = run_streaming( - ui_backend="textual", + ui_backend="tui", agent=object(), message="hello", thread_id="t1",