From 3ab00ea93a30054d537cad94c2faf46ff2f6520e Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:57:06 -0700 Subject: [PATCH] refactor(cli/setup.py): shared _info() line printer; pack print_info runs across setup modules --- hermes_cli/setup.py | 47 ++++++++------- hermes_cli/setup_platforms.py | 19 ++---- hermes_cli/setup_quick.py | 106 ++++++++++++++-------------------- hermes_cli/setup_summary.py | 10 ++-- hermes_cli/setup_terminal.py | 39 +++++-------- hermes_cli/setup_tts.py | 14 ++--- 6 files changed, 98 insertions(+), 137 deletions(-) diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 1229d7179f..8ee6e79924 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -48,6 +48,12 @@ from hermes_cli.cli_output import print_error, print_info, print_success, print_ from hermes_cli.secret_prompt import masked_secret_prompt # noqa: E402 +def _info(*lines: str | None) -> None: + """print_info each line in order; ``None`` emits a bare blank ``print()``.""" + for line in lines: + print() if line is None else print_info(line) + + def _current_reasoning_effort(config: dict) -> str: agent_cfg = config.get("agent") if isinstance(agent_cfg, dict): @@ -81,16 +87,13 @@ def print_noninteractive_setup_guidance(reason: str | None = None) -> None: print() if reason: print_info(reason) - print_info("The interactive wizard cannot be used here.") - print() - print_info("Configure Hermes using environment variables or config commands:") - print_info(" hermes config set model.provider custom") - print_info(" hermes config set model.base_url http://localhost:8080/v1") - print_info(" hermes config set model.default your-model-name") - print() - print_info("Or set OPENROUTER_API_KEY / OPENAI_API_KEY in your environment.") - print_info("Run 'hermes setup' in an interactive terminal to use the full wizard.") - print() + _info("The interactive wizard cannot be used here.", None, + "Configure Hermes using environment variables or config commands:", + " hermes config set model.provider custom", + " hermes config set model.base_url http://localhost:8080/v1", + " hermes config set model.default your-model-name", None, + "Or set OPENROUTER_API_KEY / OPENAI_API_KEY in your environment.", + "Run 'hermes setup' in an interactive terminal to use the full wizard.", None) _BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~") @@ -473,11 +476,9 @@ def _apply_default_agent_settings(config: dict): save_config(config) print_success("Applied recommended defaults:") - print_info(" Max iterations: 150") - print_info(" Tool progress: all") - print_info(" Compression threshold: 0.50") - print_info(" Session reset: never (use /reset or compression)") - print_info(" Run `hermes setup agent` later to customize.") + _info(" Max iterations: 150", " Tool progress: all", " Compression threshold: 0.50", + " Session reset: never (use /reset or compression)", + " Run `hermes setup agent` later to customize.") def _prompt_int_setting(section: dict, key: str, label: str, current, accept) -> None: @@ -786,10 +787,9 @@ def _run_full_setup(config: dict, hermes_home, *, is_existing: bool, migration_r print_info("You can edit these files directly or use 'hermes config edit'") if migration_ran: - print() - print_info("Settings were imported from OpenClaw.") - print_info("Each section below will show what was imported — press Enter to keep,") - print_info("or choose to reconfigure if needed.") + _info(None, "Settings were imported from OpenClaw.", + "Each section below will show what was imported — press Enter to keep,", + "or choose to reconfigure if needed.") # Agent Settings are not prompted: first installs get defaults, existing keep theirs. if not is_existing: @@ -896,11 +896,10 @@ def _run_setup_wizard_impl(args): print() print_header("Reconfigure") print_success("You already have Hermes configured.") - print_info("Running the full wizard — each prompt shows your current value.") - print_info("Press Enter to keep it, or type a new value to change it.") - print_info("") - print_info("Tip: jump straight to a section with 'hermes setup model|terminal|") - print_info(" gateway|tools|agent', or fill only missing items with --quick.") + _info("Running the full wizard — each prompt shows your current value.", + "Press Enter to keep it, or type a new value to change it.", "", + "Tip: jump straight to a section with 'hermes setup model|terminal|", + " gateway|tools|agent', or fill only missing items with --quick.") # --reconfigure is kept for backwards compatibility and is a no-op here. else: # ── First-Time Setup ── (--reconfigure / --quick are meaningless here; fall through) diff --git a/hermes_cli/setup_platforms.py b/hermes_cli/setup_platforms.py index 2a804491fa..e22922f03c 100644 --- a/hermes_cli/setup_platforms.py +++ b/hermes_cli/setup_platforms.py @@ -15,13 +15,6 @@ def _is_valid_telegram_bot_token(token: str) -> bool: return bool(_TELEGRAM_BOT_TOKEN_RE.match(token)) -def _info(*lines: str | None) -> None: - """print_info each line in order; ``None`` emits a bare blank ``print()``.""" - from hermes_cli.setup import print_info - for line in lines: - print() if line is None else print_info(line) - - def _profile_name_from_hermes_home(hermes_home) -> str | None: """Return the active profile name when HERMES_HOME is a profile dir.""" if hermes_home.parent.name == "profiles": @@ -108,7 +101,7 @@ def _telegram_allowlist_nudge() -> None: def _obtain_telegram_token(): """Return (token, setup_result); auto flow first when chosen, else manual paste.""" - from hermes_cli.setup import print_error, prompt + from hermes_cli.setup import _info, print_error, prompt _info("How would you like to create your Telegram bot?", None, " [1] Automatic (recommended)", " Scan a QR code → confirm in Telegram → done.", @@ -139,7 +132,7 @@ def _obtain_telegram_token(): def _setup_telegram(): """Configure Telegram bot credentials and allowlist.""" from hermes_cli.setup import print_header, print_info, print_success, prompt, prompt_yes_no - from hermes_cli.setup import save_env_value + from hermes_cli.setup import _info, save_env_value print_header("Telegram") if _declines_reconfigure("TELEGRAM_BOT_TOKEN", "Telegram", "Reconfigure Telegram?"): _telegram_allowlist_nudge() @@ -205,7 +198,7 @@ def _setup_telegram(): def _setup_bluebubbles(): """Configure BlueBubbles iMessage gateway.""" from hermes_cli.setup import print_header, print_info, print_success, print_warning, prompt - from hermes_cli.setup import prompt_yes_no, save_env_value + from hermes_cli.setup import _info, prompt_yes_no, save_env_value print_header("BlueBubbles (iMessage)") if _declines_reconfigure("BLUEBUBBLES_SERVER_URL", "BlueBubbles", "Reconfigure BlueBubbles?"): return @@ -252,7 +245,7 @@ def _setup_bluebubbles(): def _setup_webhooks(): """Configure webhook integration.""" - from hermes_cli.setup import print_header, print_success, print_warning, prompt, save_env_value + from hermes_cli.setup import _info, print_header, print_success, print_warning, prompt, save_env_value print_header("Webhooks") if _declines_reconfigure("WEBHOOK_ENABLED", "Webhooks", "Reconfigure webhooks?"): return @@ -308,7 +301,7 @@ def _is_progress(status: str) -> bool: def _warn_missing_home_channels() -> None: """Platforms with a token but no home channel.""" - from hermes_cli.setup import get_env_value, print_warning + from hermes_cli.setup import get_env_value, _info, print_warning missing_home = [ plat for plat, token_var, home_vars in _HOME_CHANNEL_CHECKS if get_env_value(token_var) and not any(get_env_value(v) for v in home_vars) @@ -362,7 +355,7 @@ def _restart_running_gateway(any_messaging: bool, supports_systemd: bool) -> Non def setup_gateway(config: dict): """Configure messaging platform integrations.""" - from hermes_cli.setup import print_header, print_info, print_success, prompt_checklist + from hermes_cli.setup import _info, print_header, print_info, print_success, prompt_checklist from hermes_cli.gateway import _all_platforms, _platform_status, _configure_platform print_header("Messaging Platforms") _info("Connect to messaging platforms to chat with Hermes from anywhere.", diff --git a/hermes_cli/setup_quick.py b/hermes_cli/setup_quick.py index 35ae90412b..0c3ebc9921 100644 --- a/hermes_cli/setup_quick.py +++ b/hermes_cli/setup_quick.py @@ -38,18 +38,15 @@ def _run_portal_one_shot(config: dict) -> None: — the same flow ``_run_first_time_quick_setup`` and ``hermes model`` use for Nous — so there is a single source of truth and ``hermes portal`` always offers a picker. """ - from hermes_cli.setup import Colors, color, load_config, print_error, print_info, print_success + from hermes_cli.setup import Colors, color, _info, load_config, print_error, print_info, print_success print() print(color("┌─────────────────────────────────────────────────────────┐", Colors.MAGENTA)) print(color("│ ⚕ Hermes Setup — Nous Portal (one-shot) │", Colors.MAGENTA)) print(color("└─────────────────────────────────────────────────────────┘", Colors.MAGENTA)) - print() - print_info(" One subscription, 300+ models, plus the Tool Gateway:") - print_info(" web search, image generation, TTS, browser automation") - print_info(" — all routed through your Nous Portal sub.") - print() - print_info(" Sign up: https://portal.nousresearch.com/manage-subscription") - print() + _info(None, " One subscription, 300+ models, plus the Tool Gateway:", + " web search, image generation, TTS, browser automation", + " — all routed through your Nous Portal sub.", None, + " Sign up: https://portal.nousresearch.com/manage-subscription", None) # Handles both the logged-out path (device-code OAuth, selects a model) and the # logged-in path (curated picker), then Tool Gateway opt-in; sets provider=nous. @@ -61,9 +58,7 @@ def _run_portal_one_shot(config: dict) -> None: # _login_nous raises SystemExit(130)/(1) on cancel/failure; the expired- # session re-login path inside _model_flow_nous only catches Exception, # so a SystemExit there would otherwise escape and kill the whole CLI. - print() - print_info(" Setup cancelled.") - print_info(" You can retry later with `hermes portal`.") + _info(None, " Setup cancelled.", " You can retry later with `hermes portal`.") return except Exception as exc: logger.debug("_model_flow_nous error during `hermes portal`: %s", exc) @@ -95,17 +90,16 @@ def _run_first_time_quick_setup(config: dict, hermes_home, is_existing: bool):
`` or switch providers with ``hermes model``. """ from hermes_cli.setup import ( - _apply_default_agent_settings, _print_macos_fda_tip, _print_setup_summary, print_header, - print_info, print_success, print_warning, prompt_choice, save_config, setup_gateway, + _apply_default_agent_settings, _info, print_header, print_info, _print_macos_fda_tip, + _print_setup_summary, print_success, print_warning, prompt_choice, save_config, setup_gateway, setup_terminal_backend, ) # Step 1: Nous Portal — OAuth login + model selection (provider set to "nous" by the save). print() print_header("Nous Portal") - print_info("One subscription, 300+ models, plus the Tool Gateway:") - print_info(" web search, image generation, TTS, browser automation.") - print_info("Sign up: https://portal.nousresearch.com/manage-subscription") - print() + _info("One subscription, 300+ models, plus the Tool Gateway:", + " web search, image generation, TTS, browser automation.", + "Sign up: https://portal.nousresearch.com/manage-subscription", None) try: from hermes_cli.main import _model_flow_nous _model_flow_nous(config) @@ -161,7 +155,7 @@ def _print_macos_fda_tip() -> None: (the TCC db dir is FDA-gated but probing it never triggers a dialog). Silent on non-macOS and when FDA is already granted or indeterminate. """ - from hermes_cli.setup import print_info + from hermes_cli.setup import _info if sys.platform != "darwin": return tcc_dir = Path.home() / "Library" / "Application Support" / "com.apple.TCC" @@ -172,12 +166,11 @@ def _print_macos_fda_tip() -> None: pass except OSError: return # indeterminate — don't nag - print() - print_info(" macOS tip: silence ALL folder permission prompts with one switch —") - print_info(" System Settings → Privacy & Security → Full Disk Access → enable") - print_info(" your terminal (and Hermes.app if you use Desktop), or run:") - print_info(" open \"x-apple.systempreferences:com.apple.preference" ".security?Privacy_AllFiles\"") - print_info(" The grant is permanent — it survives every Hermes update.") + _info(None, " macOS tip: silence ALL folder permission prompts with one switch —", + " System Settings → Privacy & Security → Full Disk Access → enable", + " your terminal (and Hermes.app if you use Desktop), or run:", + " open \"x-apple.systempreferences:com.apple.preference" ".security?Privacy_AllFiles\"", + " The grant is permanent — it survives every Hermes update.") def _blank_slate_minimal_toolsets(config: dict): @@ -240,22 +233,20 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool): else OFF; then either finish now or walk through opting capabilities back in. Nothing is enabled that the user did not explicitly choose.""" from hermes_cli.setup import ( - _blank_slate_minimal_toolsets, _blank_slate_minimize_config, _blank_slate_walkthrough, - _print_setup_summary, print_header, print_info, print_success, prompt_choice, save_config, + _blank_slate_minimal_toolsets, _blank_slate_minimize_config, _blank_slate_walkthrough, _info, + print_header, print_info, _print_setup_summary, print_success, prompt_choice, save_config, setup_model_provider, setup_terminal_backend, ) print() print_header("Blank Slate Setup") - print_info("Everything starts OFF. First we force-enable only what's required") - print_info("to run an agent, then you choose whether to stop there or walk") - print_info("through enabling more — opting in to exactly what you want.") - print_info("") - print_info("Forced on: Provider & Model, File Operations, Terminal, Vision, Skills.") - print_info("Everything else (web, browser, code exec, memory,") - print_info("delegation, cron, plugins, MCP, …) starts disabled. The") - print_info("essential `hermes-agent` skill is always kept so the agent") - print_info("can help you drive and configure Hermes itself.") - print() + _info("Everything starts OFF. First we force-enable only what's required", + "to run an agent, then you choose whether to stop there or walk", + "through enabling more — opting in to exactly what you want.", "", + "Forced on: Provider & Model, File Operations, Terminal, Vision, Skills.", + "Everything else (web, browser, code exec, memory,", + "delegation, cron, plugins, MCP, …) starts disabled. The", + "essential `hermes-agent` skill is always kept so the agent", + "can help you drive and configure Hermes itself.", None) # Step 1: Provider & Model (REQUIRED — the agent cannot run without it) print_header("Step 1 — Provider & Model (required)") @@ -296,13 +287,9 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool): logger.debug("blank-slate skill opt-out error: %s", exc) print() print_success("Blank Slate setup complete — minimal agent ready.") - print_info("Enable anything later, on demand:") - print_info(" Enable tools: hermes tools") - print_info(" Seed skills: hermes skills opt-in --sync") - print_info(" Add MCP servers: hermes mcp add") - print_info(" Enable plugins: hermes plugins") - print_info(" Tune agent settings: hermes setup agent") - print() + _info("Enable anything later, on demand:", " Enable tools: hermes tools", + " Seed skills: hermes skills opt-in --sync", " Add MCP servers: hermes mcp add", + " Enable plugins: hermes plugins", " Tune agent settings: hermes setup agent", None) _print_setup_summary(config, hermes_home) return @@ -312,8 +299,8 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool): def _blank_slate_walkthrough(config: dict, hermes_home): """Opt-in walkthrough for Blank Slate: skills, tools, plugins, MCP, gateway.""" from hermes_cli.setup import ( - _print_setup_summary, print_header, print_info, print_success, print_warning, - prompt_yes_no, save_config, setup_gateway, + _info, print_header, print_info, _print_setup_summary, print_success, print_warning, prompt_yes_no, + save_config, setup_gateway, ) # Bundled skills — default to NONE, offer to seed all print() @@ -334,10 +321,10 @@ def _blank_slate_walkthrough(config: dict, hermes_home): set_bundled_skills_opt_out(True) # Essential skills (`hermes-agent`) are still seeded for an opted-out profile. sync_skills(quiet=True) - print_info("No skills seeded (except the essential `hermes-agent`") - print_info("skill). A .no-bundled-skills marker keeps future") - print_info("`hermes update` runs from re-injecting them. Opt back in any") - print_info("time with `hermes skills opt-in --sync`.") + _info("No skills seeded (except the essential `hermes-agent`", + "skill). A .no-bundled-skills marker keeps future", + "`hermes update` runs from re-injecting them. Opt back in any", + "time with `hermes skills opt-in --sync`.") except Exception as exc: logger.debug("blank-slate skill handling error: %s", exc) print_warning(f"Skill setup step encountered an error: {exc}") @@ -345,9 +332,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home): # Walk through enabling additional tools print() print_header("Tools") - print_info("Pick exactly which additional toolsets to turn on.") - print_info("(file and terminal are already on; leave the rest off if you want") - print_info(" the most minimal agent.)") + _info("Pick exactly which additional toolsets to turn on.", + "(file and terminal are already on; leave the rest off if you want", " the most minimal agent.)") if prompt_yes_no("Open the tool selector to enable more tools?", default=False): try: from hermes_cli.tools_config import tools_command @@ -384,11 +370,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home): print() print_success("Blank Slate setup complete — minimal agent ready.") - print_info(" Enable more tools: hermes tools") - print_info(" Seed skills: hermes skills opt-in --sync") - print_info(" Add MCP servers: hermes mcp add") - print_info(" Tune agent settings: hermes setup agent") - print() + _info(" Enable more tools: hermes tools", " Seed skills: hermes skills opt-in --sync", + " Add MCP servers: hermes mcp add", " Tune agent settings: hermes setup agent", None) _print_setup_summary(config, hermes_home) @@ -396,8 +379,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home): def _run_quick_setup(config: dict, hermes_home): """Quick setup — only configure items that are missing.""" from hermes_cli.setup import ( - Colors, _print_setup_summary, _prompt_api_key, color, print_header, print_info, - print_success, print_warning, prompt_checklist, save_config, save_env_value, + Colors, color, _info, print_header, print_info, _print_setup_summary, print_success, print_warning, + _prompt_api_key, prompt_checklist, save_config, save_env_value, ) from hermes_cli.config import (get_missing_env_vars, get_missing_config_fields, check_config_version) print() @@ -411,9 +394,8 @@ def _run_quick_setup(config: dict, hermes_home): if not (missing_required or missing_optional or missing_config or current_ver < latest_ver): print_success("Everything is configured! Nothing to do.") - print() - print_info("Run 'hermes setup' and choose 'Full Setup' to reconfigure,") - print_info("or pick a specific section from the menu.") + _info(None, "Run 'hermes setup' and choose 'Full Setup' to reconfigure,", + "or pick a specific section from the menu.") return # Handle missing required env vars diff --git a/hermes_cli/setup_summary.py b/hermes_cli/setup_summary.py index 8b0bed6de1..53aefc9d52 100644 --- a/hermes_cli/setup_summary.py +++ b/hermes_cli/setup_summary.py @@ -255,8 +255,8 @@ def _print_section_header(title): def _print_setup_summary(config: dict, hermes_home): """Print the setup completion summary.""" from hermes_cli.setup import ( - print_header, print_info, print_warning, get_config_path, get_env_path, - get_nous_subscription_features, Colors, color, + Colors, color, get_config_path, get_env_path, get_nous_subscription_features, _info, print_header, + print_info, print_warning, ) # Provider readiness — the one thing setup absolutely must produce. Previously a user # could cancel the API-key prompt mid-wizard (Enter → "Cancelled."), watch the wizard @@ -272,9 +272,9 @@ def _print_setup_summary(config: dict, hermes_home): if not _provider_ready: print() print_warning("No inference provider is configured — Hermes cannot chat yet.") - print_info(" Finish this one step with either of:") - print_info(" hermes model (pick any provider/model)") - print_info(" hermes setup --portal (Nous Portal OAuth, no API key)") + _info(" Finish this one step with either of:", + " hermes model (pick any provider/model)", + " hermes setup --portal (Nous Portal OAuth, no API key)") print() print_header("Tool Availability Summary") diff --git a/hermes_cli/setup_terminal.py b/hermes_cli/setup_terminal.py index 1697420c7e..99e78a7c8f 100644 --- a/hermes_cli/setup_terminal.py +++ b/hermes_cli/setup_terminal.py @@ -18,13 +18,11 @@ _SANDBOX_IMAGE = "nikolaik/python-nodejs:python3.11-nodejs20" def _prompt_vercel_sandbox_settings(config: dict): """Prompt for Vercel Sandbox settings without exposing unsupported disk sizing.""" from hermes_cli.setup import ( - get_env_value, print_info, print_warning, prompt, remove_env_value, save_env_value, + get_env_value, _info, print_info, print_warning, prompt, remove_env_value, save_env_value, ) terminal = config.setdefault("terminal", {}) - print() - print_info("Vercel Sandbox settings:") - print_info(" Filesystem persistence uses Vercel snapshots.") - print_info(" Snapshots restore files only; live processes do not continue after sandbox recreation.") + _info(None, "Vercel Sandbox settings:", " Filesystem persistence uses Vercel snapshots.", + " Snapshots restore files only; live processes do not continue after sandbox recreation.") from tools.terminal_tool import _SUPPORTED_VERCEL_RUNTIMES @@ -57,9 +55,7 @@ def _prompt_vercel_sandbox_settings(config: dict): ) terminal["container_disk"] = 51200 - print() - print_info("Vercel authentication:") - print_info(" Use a long-lived Vercel access token plus project/team IDs.") + _info(None, "Vercel authentication:", " Use a long-lived Vercel access token plus project/team IDs.") linked = _read_nearest_vercel_project() if linked: print_info(" Found defaults in nearest .vercel/project.json.") @@ -136,7 +132,7 @@ def _setup_backend_local(config: dict) -> None: def _setup_backend_docker(config: dict) -> None: - from hermes_cli.setup import print_info, print_success, print_warning, prompt_yes_no + from hermes_cli.setup import _info, print_info, print_success, print_warning, prompt_yes_no print_success("Terminal backend: Docker") docker_bin = shutil.which("docker") if not docker_bin: @@ -147,13 +143,10 @@ def _setup_backend_docker(config: dict) -> None: # Image and resource limits use defaults; tune via `hermes setup terminal`. config["terminal"].setdefault("docker_image", _SANDBOX_IMAGE) - print() - print_info("Docker sandboxes can be protected with the egress credential firewall.") - print_info( - "It routes sandbox traffic through iron-proxy so containers receive " - "proxy tokens instead of real API keys." - ) - print_info(" Docker only for now; Modal, SSH, Daytona, and Singularity are not wired yet.") + _info(None, "Docker sandboxes can be protected with the egress credential firewall.", + "It routes sandbox traffic through iron-proxy so containers receive " + "proxy tokens instead of real API keys.", + " Docker only for now; Modal, SSH, Daytona, and Singularity are not wired yet.") if prompt_yes_no(" Enable egress firewall for Docker sandboxes?", False): proxy_cfg = config.setdefault("proxy", {}) proxy_cfg["enabled"] = True @@ -179,7 +172,7 @@ def _setup_backend_singularity(config: dict) -> None: def _setup_backend_modal(config: dict) -> None: from hermes_cli.setup import ( - cfg_get, get_env_value, get_nous_subscription_features, managed_nous_tools_enabled, + cfg_get, get_env_value, get_nous_subscription_features, _info, managed_nous_tools_enabled, print_info, print_success, prompt_choice, prompt_yes_no, ) print_success("Terminal backend: Modal") @@ -217,9 +210,7 @@ def _setup_backend_modal(config: dict) -> None: config["terminal"]["modal_mode"] = "direct" print_info("Requires a Modal account: https://modal.com") _ensure_sdk("modal", "uv pip install modal") - print() - print_info("Modal authentication:") - print_info(" Get your token at: https://modal.com/settings") + _info(None, "Modal authentication:", " Get your token at: https://modal.com/settings") if get_env_value("MODAL_TOKEN_ID"): print_info(" Modal token: already configured") if not prompt_yes_no(" Update Modal credentials?", False): @@ -229,11 +220,11 @@ def _setup_backend_modal(config: dict) -> None: def _setup_backend_daytona(config: dict) -> None: - from hermes_cli.setup import get_env_value, print_info, print_success, prompt_yes_no + from hermes_cli.setup import get_env_value, _info, print_info, print_success, prompt_yes_no print_success("Terminal backend: Daytona") - print_info("Persistent cloud development environments.") - print_info("Each session gets a dedicated sandbox with filesystem persistence.") - print_info("Sign up at: https://daytona.io") + _info("Persistent cloud development environments.", + "Each session gets a dedicated sandbox with filesystem persistence.", + "Sign up at: https://daytona.io") _ensure_sdk("daytona", "uv pip install daytona", show_stderr=True) print() if get_env_value("DAYTONA_API_KEY"): diff --git a/hermes_cli/setup_tts.py b/hermes_cli/setup_tts.py index 0567202883..76f14a2589 100644 --- a/hermes_cli/setup_tts.py +++ b/hermes_cli/setup_tts.py @@ -46,7 +46,7 @@ _ESPEAK_INSTALL = { def _install_neutts_deps() -> bool: """Install NeuTTS dependencies with user approval. Returns True on success.""" - from hermes_cli.setup import print_info, print_success, print_warning, prompt_yes_no + from hermes_cli.setup import _info, print_info, print_success, print_warning, prompt_yes_no if not _check_espeak_ng(): hint, install_cmd = _ESPEAK_INSTALL.get(sys.platform, _ESPEAK_INSTALL["linux"]) print() @@ -64,23 +64,19 @@ def _install_neutts_deps() -> bool: else: print_warning("espeak-ng is required for NeuTTS. Install it manually before using NeuTTS.") - print() - print_info("Installing neutts Python package...") - print_info("This will also download the TTS model (~300MB) on first use.") - print() + _info(None, "Installing neutts Python package...", + "This will also download the TTS model (~300MB) on first use.", None) return _pip_install_tts_package("neutts", ["-U", "neutts[all]", "--quiet"], "uv pip install -U 'neutts[all]'") def _install_kittentts_deps() -> bool: """Install KittenTTS dependencies with user approval. Returns True on success.""" - from hermes_cli.setup import print_info + from hermes_cli.setup import _info wheel_url = ( "https://github.com/KittenML/KittenTTS/releases/download/" "0.8.1/kittentts-0.8.1-py3-none-any.whl" ) - print() - print_info("Installing kittentts Python package (~25-80MB model downloaded on first use)...") - print() + _info(None, "Installing kittentts Python package (~25-80MB model downloaded on first use)...", None) return _pip_install_tts_package( "kittentts", ["-U", wheel_url, "soundfile", "--quiet"], f"uv pip install -U '{wheel_url}' soundfile", )