diff --git a/readme.md b/readme.md index 3cd25cc..4e3eb37 100644 --- a/readme.md +++ b/readme.md @@ -74,7 +74,6 @@ uv run takopi | `--final-notify` / `--no-final-notify` | `--final-notify` | Send final response as new message (vs. edit) | | `--debug` / `--no-debug` | `--no-debug` | Enable verbose logging | | `--profile NAME` | (codex default) | Codex profile name | -| `--setup-demo` | off | Render all onboarding guide variants and exit | ## Usage diff --git a/src/takopi/exec_bridge.py b/src/takopi/exec_bridge.py index c561972..86921f4 100644 --- a/src/takopi/exec_bridge.py +++ b/src/takopi/exec_bridge.py @@ -21,7 +21,7 @@ from .config import ConfigError, load_telegram_config from .exec_render import ExecProgressRenderer, render_event_cli from .logging import setup_logging from .rendering import render_markdown -from .onboarding import check_setup, demo_results, render_setup_guide +from .onboarding import check_setup, render_setup_guide from .telegram_client import TelegramClient logger = logging.getLogger(__name__) @@ -678,20 +678,8 @@ def run( "--profile", help="Codex profile name to pass to `codex --profile`.", ), - setup_demo: bool = typer.Option( - False, - "--setup-demo", - help="Render all onboarding guide variants and exit.", - ), ) -> None: setup_logging(debug=debug) - if setup_demo: - for idx, (label, result) in enumerate(demo_results()): - if idx: - typer.echo("", err=True) - typer.echo(f"[setup demo] {label}", err=True) - render_setup_guide(result) - raise typer.Exit(code=0) setup = check_setup() if not setup.ok: render_setup_guide(setup) diff --git a/src/takopi/onboarding.py b/src/takopi/onboarding.py index 34517bf..230933e 100644 --- a/src/takopi/onboarding.py +++ b/src/takopi/onboarding.py @@ -15,13 +15,13 @@ from .constants import HOME_CONFIG_PATH _OCTOPUS = "\N{OCTOPUS}" -@dataclass +@dataclass(slots=True) class SetupResult: """Collected setup issues.""" missing_codex: bool = False missing_or_invalid_config: bool = False - config_path: Path | None = None + config_path: Path = HOME_CONFIG_PATH @property def ok(self) -> bool: @@ -30,32 +30,28 @@ class SetupResult: def check_setup() -> SetupResult: """Check all prerequisites and return collected issues.""" - result = SetupResult() - - if not shutil.which("codex"): - result.missing_codex = True + missing_codex = shutil.which("codex") is None try: config, config_path = load_telegram_config() - result.config_path = config_path except ConfigError: - result.missing_or_invalid_config = True - result.config_path = HOME_CONFIG_PATH - return result + return SetupResult( + missing_codex=missing_codex, + missing_or_invalid_config=True, + config_path=HOME_CONFIG_PATH, + ) token = config.get("bot_token") - if not isinstance(token, str) or not token.strip(): - result.missing_or_invalid_config = True + chat_id = config.get("chat_id") - chat_id_value = config.get("chat_id") - if ( - chat_id_value is None - or isinstance(chat_id_value, bool) - or not isinstance(chat_id_value, int) - ): - result.missing_or_invalid_config = True + missing_or_invalid_config = not (isinstance(token, str) and token.strip()) + missing_or_invalid_config |= type(chat_id) is not int - return result + return SetupResult( + missing_codex=missing_codex, + missing_or_invalid_config=missing_or_invalid_config, + config_path=config_path, + ) def _config_path_display(path: Path) -> str: @@ -67,65 +63,48 @@ def _config_path_display(path: Path) -> str: return str(path) -def _step_marker(step: int) -> str: - return f"{step}." - - def render_setup_guide(result: SetupResult) -> None: """Render a friendly setup guide panel to stderr.""" + if result.ok: + return + console = Console(stderr=True) parts: list[str] = [] step = 0 - needs_credentials_help = False + + def add_step(title: str, *lines: str) -> None: + nonlocal step + step += 1 + parts.append(f"[bold yellow]{step}.[/] [bold]{title}[/]") + parts.append("") + parts.extend(lines) + parts.append("") if result.missing_codex: - step += 1 - parts.append( - f"[bold yellow]{_step_marker(step)}[/] [bold]Install the Codex CLI[/]" + add_step( + "Install the Codex CLI", + " [dim]$[/] npm install -g @openai/codex", ) - parts.append("") - parts.append(" [dim]$[/] npm install -g @openai/codex") - parts.append("") - - config_display = ( - _config_path_display(result.config_path) - if result.config_path - else _config_path_display(HOME_CONFIG_PATH) - ) if result.missing_or_invalid_config: - step += 1 - parts.append(f"[bold yellow]{_step_marker(step)}[/] [bold]Create a config[/]") - parts.append("") - parts.append(f" [dim]{config_display}[/]") - parts.append("") - parts.append(' [cyan]bot_token[/] = [green]"123456789:ABCdef..."[/]') - parts.append(" [cyan]chat_id[/] = [green]123456789[/]") - parts.append("") - needs_credentials_help = True - - if needs_credentials_help: - needs_token_help = True - needs_chat_id_help = True - - parts.append("[dim]" + ("-" * 56) + "[/]") - parts.append("") - parts.append("[bold]Getting your Telegram credentials:[/]") - parts.append("") - if needs_token_help: - parts.append( - " [cyan]bot_token[/] create a bot with [link=https://t.me/BotFather]@BotFather[/]" - ) - if needs_chat_id_help: - parts.append( - " [cyan]chat_id[/] get from [link=https://t.me/myidbot]@myidbot[/]" - ) - - while parts and not parts[-1].strip(): - parts.pop() + config_display = _config_path_display(result.config_path) + add_step( + "Create a config", + f" [dim]{config_display}[/]", + "", + ' [cyan]bot_token[/] = [green]"123456789:ABCdef..."[/]', + " [cyan]chat_id[/] = [green]123456789[/]", + "", + "[dim]" + ("-" * 56) + "[/]", + "", + "[bold]Getting your Telegram credentials:[/]", + "", + " [cyan]bot_token[/] create a bot with [link=https://t.me/BotFather]@BotFather[/]", + " [cyan]chat_id[/] get from [link=https://t.me/myidbot]@myidbot[/]", + ) panel = Panel( - "\n".join(parts), + "\n".join(parts).rstrip(), title="[bold]Welcome to Takopi![/]", subtitle=f"{_OCTOPUS} setup required", border_style="yellow", @@ -133,32 +112,3 @@ def render_setup_guide(result: SetupResult) -> None: expand=False, ) console.print(panel) - - -def demo_results() -> list[tuple[str, SetupResult]]: - """Return sample setup results for previewing all onboarding modes.""" - config_path = HOME_CONFIG_PATH - return [ - ( - "fresh-install", - SetupResult( - missing_codex=True, - missing_or_invalid_config=True, - config_path=config_path, - ), - ), - ( - "missing-codex", - SetupResult( - missing_codex=True, - config_path=config_path, - ), - ), - ( - "missing-or-invalid-config", - SetupResult( - missing_or_invalid_config=True, - config_path=config_path, - ), - ), - ]