From 20e22eafd4902c4ac285fb573c6db5d846c0d821 Mon Sep 17 00:00:00 2001 From: Zhengshou Lai Date: Wed, 15 Jul 2026 10:34:01 +0800 Subject: [PATCH] fix(hermes): align myhermes resume flags with myclaude UX Hermes -r/--resume requires a session id and has no -c shorthand, so bare 'myhermes -r' failed with an argparse error. Translate myagents-style flags like the codex backend: - bare -r/--resume -> 'hermes sessions browse' (interactive picker) - -r/--resume -> '--resume --no-restore-cwd' - -c/--continue/--last -> '--continue --no-restore-cwd' --no-restore-cwd keeps resumed sessions in the launch cwd, matching myclaude semantics instead of jumping to the session's original cwd. --- myagents/launcher.py | 29 +++++++++++++++++++++- tests/test_cli.py | 51 +++++++++++++++++++++++++++++++++++++-- tests/test_entrypoints.py | 6 ++++- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/myagents/launcher.py b/myagents/launcher.py index 6421558..bc472ad 100644 --- a/myagents/launcher.py +++ b/myagents/launcher.py @@ -103,6 +103,28 @@ def _translate_codex_extra(extra: list[str]) -> list[str]: return extra +def _translate_hermes_extra(extra: list[str]) -> list[str]: + """Map myagents-style resume options to native hermes syntax. + + Hermes ``-r``/``--resume`` requires a session id (no bare picker), has no + ``-c`` shorthand, and restores the session's original cwd by default. + This keeps ``myhermes`` UX consistent with ``myclaude``: bare ``-r`` + opens the interactive picker, ``-c`` continues the latest session, and + resumed sessions stay in the launch cwd. + """ + if not extra: + return extra + head = extra[0] + tail = extra[1:] + if head in ("-r", "--resume"): + if not tail or tail[0].startswith("-"): + return ["sessions", "browse", *tail] + return ["--resume", tail[0], "--no-restore-cwd", *tail[1:]] + if head in ("-c", "--continue", "--last"): + return ["--continue", *tail, "--no-restore-cwd"] + return extra + + def _launch(backend: str, chat_cwd: Path, extra: list[str]) -> None: """Run backend CLI in chat_cwd, forwarding extra args. Never returns.""" config = _BACKENDS[backend] @@ -113,6 +135,8 @@ def _launch(backend: str, chat_cwd: Path, extra: list[str]) -> None: if backend == "codex": extra = _translate_codex_extra(extra) + elif backend == "hermes": + extra = _translate_hermes_extra(extra) cmd = [binary, *config["default_args"], *extra] proc = subprocess.run(cmd, cwd=str(chat_cwd), check=False) @@ -283,6 +307,8 @@ def _resume_syntax(backend: str) -> str: return "--session, -S " if backend == "codex": return "-r, --resume or resume " + if backend == "hermes": + return "-r, --resume (bare -r opens the session picker)" return "--resume, -r " @@ -335,7 +361,8 @@ def _list_sessions_hermes(chat_cwd: Path) -> None: f"[dim]{mtime:%Y-%m-%d %H:%M}[/dim] {snippet or '[dim](empty)[/dim]'}" ) console.print( - "\n[dim]Resume with[/dim] [green]hermes --resume [/green]" + "\n[dim]Resume with[/dim] [green]myhermes -r [/green] " + "[dim](bare myhermes -r opens the picker)[/dim]" ) diff --git a/tests/test_cli.py b/tests/test_cli.py index 60e3608..4384ff5 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -473,11 +473,58 @@ class TestHermesPassthrough: def test_resume_with_session_id(self, tmp_path: Path) -> None: cmd = self._invoke(["--resume", "abc123"], tmp_path) - assert cmd == ["/usr/bin/hermes", "--yolo", "--resume", "abc123"] + assert cmd == [ + "/usr/bin/hermes", + "--yolo", + "--resume", + "abc123", + "--no-restore-cwd", + ] + + def test_bare_r_flag_opens_session_picker(self, tmp_path: Path) -> None: + cmd = self._invoke(["-r"], tmp_path) + assert cmd == ["/usr/bin/hermes", "--yolo", "sessions", "browse"] + + def test_bare_resume_flag_opens_session_picker(self, tmp_path: Path) -> None: + cmd = self._invoke(["--resume"], tmp_path) + assert cmd == ["/usr/bin/hermes", "--yolo", "sessions", "browse"] + + def test_r_flag_with_session_id(self, tmp_path: Path) -> None: + cmd = self._invoke(["-r", "abc123"], tmp_path) + assert cmd == [ + "/usr/bin/hermes", + "--yolo", + "--resume", + "abc123", + "--no-restore-cwd", + ] + + def test_c_flag_maps_to_continue(self, tmp_path: Path) -> None: + cmd = self._invoke(["-c"], tmp_path) + assert cmd == [ + "/usr/bin/hermes", + "--yolo", + "--continue", + "--no-restore-cwd", + ] def test_continue_flag_passes_through(self, tmp_path: Path) -> None: cmd = self._invoke(["--continue"], tmp_path) - assert cmd[-1] == "--continue" + assert cmd == [ + "/usr/bin/hermes", + "--yolo", + "--continue", + "--no-restore-cwd", + ] + + def test_last_flag_maps_to_continue(self, tmp_path: Path) -> None: + cmd = self._invoke(["--last"], tmp_path) + assert cmd == [ + "/usr/bin/hermes", + "--yolo", + "--continue", + "--no-restore-cwd", + ] class TestHermesListSessions: diff --git a/tests/test_entrypoints.py b/tests/test_entrypoints.py index 2e314cc..a197fe5 100644 --- a/tests/test_entrypoints.py +++ b/tests/test_entrypoints.py @@ -138,4 +138,8 @@ class TestMyhermesEntrypoint: hermes_cli, ["--cwd", str(tmp_path), "--resume", "abc123"] ) assert result.exit_code == 0 - assert mock_run.call_args[0][0][-2:] == ["--resume", "abc123"] + assert mock_run.call_args[0][0][-3:] == [ + "--resume", + "abc123", + "--no-restore-cwd", + ]