Merge PR #810: fix(cli): handle unquoted multi-word session names in -c/--continue and -r/--resume
This commit is contained in:
commit
bdce33e239
2 changed files with 156 additions and 1 deletions
|
|
@ -1777,6 +1777,44 @@ def cmd_update(args):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _coalesce_session_name_args(argv: list) -> list:
|
||||||
|
"""Join unquoted multi-word session names after -c/--continue and -r/--resume.
|
||||||
|
|
||||||
|
When a user types ``hermes -c Pokemon Agent Dev`` without quoting the
|
||||||
|
session name, argparse sees three separate tokens. This function merges
|
||||||
|
them into a single argument so argparse receives
|
||||||
|
``['-c', 'Pokemon Agent Dev']`` instead.
|
||||||
|
|
||||||
|
Tokens are collected after the flag until we hit another flag (``-*``)
|
||||||
|
or a known top-level subcommand.
|
||||||
|
"""
|
||||||
|
_SUBCOMMANDS = {
|
||||||
|
"chat", "model", "gateway", "setup", "whatsapp", "login", "logout",
|
||||||
|
"status", "cron", "doctor", "config", "pairing", "skills", "tools",
|
||||||
|
"sessions", "insights", "version", "update", "uninstall",
|
||||||
|
}
|
||||||
|
_SESSION_FLAGS = {"-c", "--continue", "-r", "--resume"}
|
||||||
|
|
||||||
|
result = []
|
||||||
|
i = 0
|
||||||
|
while i < len(argv):
|
||||||
|
token = argv[i]
|
||||||
|
if token in _SESSION_FLAGS:
|
||||||
|
result.append(token)
|
||||||
|
i += 1
|
||||||
|
# Collect subsequent non-flag, non-subcommand tokens as one name
|
||||||
|
parts: list = []
|
||||||
|
while i < len(argv) and not argv[i].startswith("-") and argv[i] not in _SUBCOMMANDS:
|
||||||
|
parts.append(argv[i])
|
||||||
|
i += 1
|
||||||
|
if parts:
|
||||||
|
result.append(" ".join(parts))
|
||||||
|
else:
|
||||||
|
result.append(token)
|
||||||
|
i += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main entry point for hermes CLI."""
|
"""Main entry point for hermes CLI."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
|
@ -2515,7 +2553,11 @@ For more help on a command:
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Parse and execute
|
# Parse and execute
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
args = parser.parse_args()
|
# Pre-process argv so unquoted multi-word session names after -c / -r
|
||||||
|
# are merged into a single token before argparse sees them.
|
||||||
|
# e.g. ``hermes -c Pokemon Agent Dev`` → ``hermes -c 'Pokemon Agent Dev'``
|
||||||
|
_processed_argv = _coalesce_session_name_args(sys.argv[1:])
|
||||||
|
args = parser.parse_args(_processed_argv)
|
||||||
|
|
||||||
# Handle --version flag
|
# Handle --version flag
|
||||||
if args.version:
|
if args.version:
|
||||||
|
|
|
||||||
113
tests/hermes_cli/test_coalesce_session_args.py
Normal file
113
tests/hermes_cli/test_coalesce_session_args.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
"""Tests for _coalesce_session_name_args — multi-word session name merging."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from hermes_cli.main import _coalesce_session_name_args
|
||||||
|
|
||||||
|
|
||||||
|
class TestCoalesceSessionNameArgs:
|
||||||
|
"""Ensure unquoted multi-word session names are merged into one token."""
|
||||||
|
|
||||||
|
# ── -c / --continue ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_continue_multiword_unquoted(self):
|
||||||
|
"""hermes -c Pokemon Agent Dev → -c 'Pokemon Agent Dev'"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "Pokemon", "Agent", "Dev"]
|
||||||
|
) == ["-c", "Pokemon Agent Dev"]
|
||||||
|
|
||||||
|
def test_continue_long_form_multiword(self):
|
||||||
|
"""hermes --continue Pokemon Agent Dev"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["--continue", "Pokemon", "Agent", "Dev"]
|
||||||
|
) == ["--continue", "Pokemon Agent Dev"]
|
||||||
|
|
||||||
|
def test_continue_single_word(self):
|
||||||
|
"""hermes -c MyProject (no merging needed)"""
|
||||||
|
assert _coalesce_session_name_args(["-c", "MyProject"]) == [
|
||||||
|
"-c",
|
||||||
|
"MyProject",
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_continue_already_quoted(self):
|
||||||
|
"""hermes -c 'Pokemon Agent Dev' (shell already merged)"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "Pokemon Agent Dev"]
|
||||||
|
) == ["-c", "Pokemon Agent Dev"]
|
||||||
|
|
||||||
|
def test_continue_bare_flag(self):
|
||||||
|
"""hermes -c (no name — means 'continue latest')"""
|
||||||
|
assert _coalesce_session_name_args(["-c"]) == ["-c"]
|
||||||
|
|
||||||
|
def test_continue_followed_by_flag(self):
|
||||||
|
"""hermes -c -w (no name consumed, -w stays separate)"""
|
||||||
|
assert _coalesce_session_name_args(["-c", "-w"]) == ["-c", "-w"]
|
||||||
|
|
||||||
|
def test_continue_multiword_then_flag(self):
|
||||||
|
"""hermes -c my project -w"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "my", "project", "-w"]
|
||||||
|
) == ["-c", "my project", "-w"]
|
||||||
|
|
||||||
|
def test_continue_multiword_then_subcommand(self):
|
||||||
|
"""hermes -c my project chat -q hello"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "my", "project", "chat", "-q", "hello"]
|
||||||
|
) == ["-c", "my project", "chat", "-q", "hello"]
|
||||||
|
|
||||||
|
# ── -r / --resume ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_resume_multiword(self):
|
||||||
|
"""hermes -r My Session Name"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-r", "My", "Session", "Name"]
|
||||||
|
) == ["-r", "My Session Name"]
|
||||||
|
|
||||||
|
def test_resume_long_form_multiword(self):
|
||||||
|
"""hermes --resume My Session Name"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["--resume", "My", "Session", "Name"]
|
||||||
|
) == ["--resume", "My Session Name"]
|
||||||
|
|
||||||
|
def test_resume_multiword_then_flag(self):
|
||||||
|
"""hermes -r My Session -w"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-r", "My", "Session", "-w"]
|
||||||
|
) == ["-r", "My Session", "-w"]
|
||||||
|
|
||||||
|
# ── combined flags ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_worktree_and_continue_multiword(self):
|
||||||
|
"""hermes -w -c Pokemon Agent Dev (the original failing case)"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-w", "-c", "Pokemon", "Agent", "Dev"]
|
||||||
|
) == ["-w", "-c", "Pokemon Agent Dev"]
|
||||||
|
|
||||||
|
def test_continue_multiword_and_worktree(self):
|
||||||
|
"""hermes -c Pokemon Agent Dev -w (order reversed)"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "Pokemon", "Agent", "Dev", "-w"]
|
||||||
|
) == ["-c", "Pokemon Agent Dev", "-w"]
|
||||||
|
|
||||||
|
# ── passthrough (no session flags) ───────────────────────────────────
|
||||||
|
|
||||||
|
def test_no_session_flags_passthrough(self):
|
||||||
|
"""hermes -w chat -q hello (nothing to merge)"""
|
||||||
|
result = _coalesce_session_name_args(["-w", "chat", "-q", "hello"])
|
||||||
|
assert result == ["-w", "chat", "-q", "hello"]
|
||||||
|
|
||||||
|
def test_empty_argv(self):
|
||||||
|
assert _coalesce_session_name_args([]) == []
|
||||||
|
|
||||||
|
# ── subcommand boundary ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_stops_at_sessions_subcommand(self):
|
||||||
|
"""hermes -c my project sessions list → stops before 'sessions'"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "my", "project", "sessions", "list"]
|
||||||
|
) == ["-c", "my project", "sessions", "list"]
|
||||||
|
|
||||||
|
def test_stops_at_setup_subcommand(self):
|
||||||
|
"""hermes -c my setup → 'setup' is a subcommand, not part of name"""
|
||||||
|
assert _coalesce_session_name_args(
|
||||||
|
["-c", "my", "setup"]
|
||||||
|
) == ["-c", "my", "setup"]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue