Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2949,10 +2949,57 @@ def show_config(self):
print(f" Config File: {config_path} {config_status}")
print()

def _list_recent_sessions(self, limit: int = 10) -> list[dict[str, Any]]:
"""Return recent CLI sessions for in-chat browsing/resume affordances."""
if not self._session_db:
return []
try:
sessions = self._session_db.list_sessions_rich(
source="cli",
exclude_sources=["tool"],
limit=limit,
)
except Exception:
return []
return [s for s in sessions if s.get("id") != self.session_id]

def _show_recent_sessions(self, *, reason: str = "history", limit: int = 10) -> bool:
"""Render recent sessions inline from the active chat TUI.

Returns True when something was shown, False if no session list was available.
"""
sessions = self._list_recent_sessions(limit=limit)
if not sessions:
return False

from hermes_cli.main import _relative_time

print()
print("+" + "-" * 86 + "+")
print("|" + " " * 25 + "Recent Sessions" + " " * 46 + "|")
print("+" + "-" * 86 + "+")
print(f" {'Title':<30} {'Preview':<30} {'Last Active':<12} ID")
print(f" {'─' * 30} {'─' * 30} {'─' * 12} {'─' * 18}")
for session in sessions:
title = (session.get("title") or "—")[:30]
preview = (session.get("preview") or "")[:30]
last_active = _relative_time(session.get("last_active"))
print(f" {title:<30} {preview:<30} {last_active:<12} {session['id']}")

print()
if reason == "history":
print("(._.) No messages in the current chat yet.")
print(" Above are recent sessions you can resume with /resume <session id or title>.")
else:
print(" Use /resume <session id or title> to switch to one of these sessions.")
print()
return True

def show_history(self):
"""Display conversation history."""
if not self.conversation_history:
print("(._.) No conversation history yet.")
if not self._show_recent_sessions(reason="history"):
print("(._.) No conversation history yet.")
return

preview_limit = 400
Expand Down Expand Up @@ -3077,6 +3124,8 @@ def _handle_resume_command(self, cmd_original: str) -> None:

if not target:
_cprint(" Usage: /resume <session_id_or_title>")
if self._show_recent_sessions(reason="resume"):
return
_cprint(" Tip: Use /history or `hermes sessions list` to find sessions.")
return

Expand Down
54 changes: 54 additions & 0 deletions tests/test_cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,60 @@ def test_history_numbers_only_visible_messages_and_summarizes_tools(self, capsys
assert "A" * 250 in output
assert "A" * 250 + "..." not in output

def test_history_shows_recent_sessions_when_current_chat_is_empty(self, capsys):
cli = _make_cli()
cli.session_id = "current"
cli._session_db = MagicMock()
cli._session_db.list_sessions_rich.return_value = [
{
"id": "current",
"title": "Current",
"preview": "Current preview",
"last_active": 0,
},
{
"id": "20260401_201329_d85961",
"title": "Checking Running Hermes Agent",
"preview": "check running gateways for hermes agent",
"last_active": 0,
},
]

cli.show_history()
output = capsys.readouterr().out

assert "Recent Sessions" in output
assert "Checking Running Hermes Agent" in output
assert "20260401_201329_d85961" in output
assert "No messages in the current chat yet." in output
assert "Current preview" not in output

def test_resume_without_target_lists_recent_sessions(self, capsys):
cli = _make_cli()
cli.session_id = "current"
cli._session_db = MagicMock()
cli._session_db.list_sessions_rich.return_value = [
{
"id": "current",
"title": "Current",
"preview": "Current preview",
"last_active": 0,
},
{
"id": "20260401_201329_d85961",
"title": "Checking Running Hermes Agent",
"preview": "check running gateways for hermes agent",
"last_active": 0,
},
]

cli._handle_resume_command("/resume")
output = capsys.readouterr().out

assert "Recent Sessions" in output
assert "Checking Running Hermes Agent" in output
assert "Use /resume <session id or title> to switch" in output


class TestRootLevelProviderOverride:
"""Root-level provider/base_url in config.yaml must NOT override model.provider."""
Expand Down
Loading