diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 26a0f3c37..2291758f7 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -2922,19 +2922,33 @@ def run_setup_wizard(args): _offer_launch_chat() +def _resolve_hermes_chat_argv() -> Optional[list[str]]: + """Resolve argv for launching ``hermes chat`` in a fresh process.""" + hermes_bin = shutil.which("hermes") + if hermes_bin: + return [hermes_bin, "chat"] + + try: + if importlib.util.find_spec("hermes_cli") is not None: + return [sys.executable, "-m", "hermes_cli.main", "chat"] + except Exception: + pass + + return None + + def _offer_launch_chat(): """Prompt the user to jump straight into chat after setup.""" print() - if prompt_yes_no("Launch hermes chat now?", True): - from hermes_cli.main import cmd_chat - from types import SimpleNamespace - cmd_chat(SimpleNamespace( - query=None, resume=None, continue_last=None, model=None, - provider=None, effort=None, skin=None, oneshot=False, - quiet=False, verbose=False, toolsets=None, skills=None, - yolo=False, source=None, worktree=False, checkpoints=False, - pass_session_id=False, max_turns=None, - )) + if not prompt_yes_no("Launch hermes chat now?", True): + return + + chat_argv = _resolve_hermes_chat_argv() + if not chat_argv: + print_info("Could not relaunch Hermes automatically. Run 'hermes chat' manually.") + return + + os.execvp(chat_argv[0], chat_argv) def _run_first_time_quick_setup(config: dict, hermes_home, is_existing: bool): diff --git a/tests/hermes_cli/test_setup.py b/tests/hermes_cli/test_setup.py index 0eac69bac..4a3f5151f 100644 --- a/tests/hermes_cli/test_setup.py +++ b/tests/hermes_cli/test_setup.py @@ -4,6 +4,8 @@ import json import sys import types +import pytest + from hermes_cli.auth import get_active_provider from hermes_cli.config import load_config, save_config from hermes_cli.setup import setup_model_provider @@ -362,3 +364,52 @@ def test_modal_setup_persists_direct_mode_when_user_chooses_their_own_account(tm assert config["terminal"]["backend"] == "modal" assert config["terminal"]["modal_mode"] == "direct" + + +def test_resolve_hermes_chat_argv_prefers_which(monkeypatch): + from hermes_cli import setup as setup_mod + + monkeypatch.setattr(setup_mod.shutil, "which", lambda name: "/usr/local/bin/hermes" if name == "hermes" else None) + + assert setup_mod._resolve_hermes_chat_argv() == ["/usr/local/bin/hermes", "chat"] + + +def test_resolve_hermes_chat_argv_falls_back_to_module(monkeypatch): + from hermes_cli import setup as setup_mod + + monkeypatch.setattr(setup_mod.shutil, "which", lambda _name: None) + monkeypatch.setattr(setup_mod.importlib.util, "find_spec", lambda name: object() if name == "hermes_cli" else None) + + assert setup_mod._resolve_hermes_chat_argv() == [sys.executable, "-m", "hermes_cli.main", "chat"] + + +def test_offer_launch_chat_execs_fresh_process(monkeypatch): + from hermes_cli import setup as setup_mod + + monkeypatch.setattr(setup_mod, "prompt_yes_no", lambda *_args, **_kwargs: True) + monkeypatch.setattr(setup_mod, "_resolve_hermes_chat_argv", lambda: ["/usr/local/bin/hermes", "chat"]) + + exec_calls = [] + + def fake_execvp(path, argv): + exec_calls.append((path, argv)) + raise SystemExit(0) + + monkeypatch.setattr(setup_mod.os, "execvp", fake_execvp) + + with pytest.raises(SystemExit): + setup_mod._offer_launch_chat() + + assert exec_calls == [("/usr/local/bin/hermes", ["/usr/local/bin/hermes", "chat"])] + + +def test_offer_launch_chat_manual_fallback_when_unresolvable(monkeypatch, capsys): + from hermes_cli import setup as setup_mod + + monkeypatch.setattr(setup_mod, "prompt_yes_no", lambda *_args, **_kwargs: True) + monkeypatch.setattr(setup_mod, "_resolve_hermes_chat_argv", lambda: None) + + setup_mod._offer_launch_chat() + + captured = capsys.readouterr() + assert "Run 'hermes chat' manually" in captured.out