fix(cli): wire /sessions slash command in the classic CLI

The 'sessions' command has been registered in the central command
registry since #20805 (May 2025) and surfaces in /help and tab-completion,
but the classic CLI's process_command() never had an elif branch for it.
The canonical name fell through and printed 'Unknown command: sessions'.
The TUI side was wired up correctly via the SessionPicker overlay; only
the legacy CLI was missing the dispatch.

Adds _handle_sessions_command() which mirrors /resume's no-arg behavior
inline (the CLI has no overlay primitive equivalent to the TUI picker):

- /sessions and /sessions list  → print the recent-sessions table
- /sessions <id_or_title>       → delegates to _handle_resume_command

Includes regression tests covering the dispatcher wiring (the original
bug) plus the three handler branches.
This commit is contained in:
Phil Thomas 2026-05-13 14:51:06 -06:00 committed by Teknium
parent 09d970160b
commit d6c488f2dc
2 changed files with 117 additions and 0 deletions

34
cli.py
View file

@ -5961,6 +5961,38 @@ class HermesCLI:
else:
_cprint(f" ↻ Resumed session {target_id}{title_part} — no messages, starting fresh.")
def _handle_sessions_command(self, cmd_original: str) -> None:
"""Handle /sessions [list|<id_or_title>] — browse or resume previous sessions.
Without arguments, prints the same recent-sessions table that /resume
shows when called without a target, and tells the user how to resume.
With an explicit subcommand or target, delegates to the resume flow so
``/sessions <id>`` and ``/resume <id>`` behave identically.
The TUI ships an interactive picker overlay for this command; the
classic CLI prints an inline list because there is no equivalent
overlay primitive here. Without this handler the canonical name
``sessions`` falls through ``process_command``'s elif chain and
prints ``Unknown command: sessions`` even though the command is
registered in the central COMMAND_REGISTRY.
"""
parts = cmd_original.split(None, 1)
arg = parts[1].strip() if len(parts) > 1 else ""
sub = arg.lower()
# Bare /sessions or /sessions list — show recent sessions inline.
if not arg or sub in {"list", "ls", "browse"}:
if not self._session_db:
from hermes_state import format_session_db_unavailable
_cprint(f" {format_session_db_unavailable()}")
return
if not self._show_recent_sessions(reason="sessions"):
_cprint(" (._.) No previous sessions yet.")
return
# /sessions <id_or_title> behaves the same as /resume <id_or_title>.
self._handle_resume_command(f"/resume {arg}")
def _handle_branch_command(self, cmd_original: str) -> None:
"""Handle /branch [name] — fork the current session into a new independent copy.
@ -7540,6 +7572,8 @@ class HermesCLI:
self.new_session(title=title)
elif canonical == "resume":
self._handle_resume_command(cmd_original)
elif canonical == "sessions":
self._handle_sessions_command(cmd_original)
elif canonical == "model":
self._handle_model_switch(cmd_original)
elif canonical == "codex-runtime":