Skip to content
Closed
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
92 changes: 84 additions & 8 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5597,6 +5597,34 @@ def _handle_resume_command(self, cmd_original: str) -> None:
else:
_cprint(f" ↻ Resumed session {target_id}{title_part} — no messages, starting fresh.")

def _handle_sessions_command(self) -> None:
"""Handle /sessions — list and browse saved sessions."""
# Fix for Issue #22951: /sessions unknown command in classic REPL mode
if not self._session_db:
from hermes_state import format_session_db_unavailable
_cprint(f" {format_session_db_unavailable()}")
return

try:
sessions = self._session_db.list_sessions()
except Exception as exc:
_cprint(f" Error reading sessions: {exc}")
return

if not sessions:
_cprint(" No saved sessions yet. Sessions are created automatically as you chat.")
return

try:
from hermes_cli.main import _session_browse_picker
selected = _session_browse_picker(sessions)
except Exception as exc:
_cprint(f" Error browsing sessions: {exc}")
return

if selected is not None:
self._relaunch(session_id=selected)

def _handle_branch_command(self, cmd_original: str) -> None:
"""Handle /branch [name] — fork the current session into a new independent copy.

Expand Down Expand Up @@ -5869,11 +5897,18 @@ def _ask():

if self._app:
from prompt_toolkit.application import run_in_terminal
import threading
was_visible = self._status_bar_visible
self._status_bar_visible = False
self._app.invalidate()
try:
run_in_terminal(_ask)
# Fix for Issue #22970: RuntimeWarning when slash commands run
# from background threads. run_in_terminal() returns a coroutine
# that can't be scheduled when not on the main thread.
if threading.current_thread() is threading.main_thread():
run_in_terminal(_ask)
else:
_ask()
finally:
self._status_bar_visible = was_visible
self._app.invalidate()
Expand Down Expand Up @@ -6922,8 +6957,12 @@ def process_command(self, command: str) -> bool:
self.new_session(title=title)
elif canonical == "resume":
self._handle_resume_command(cmd_original)
elif canonical == "sessions":
self._handle_sessions_command()
elif canonical == "model":
self._handle_model_switch(cmd_original)
elif canonical == "indicator":
self._handle_indicator_command(cmd_original)
elif canonical == "gquota":
self._handle_gquota_command(cmd_original)

Expand Down Expand Up @@ -7897,6 +7936,48 @@ def _toggle_yolo(self):
" — all commands auto-approved. Use with caution."
)

def _handle_indicator_command(self, cmd_original: str) -> None:
"""Pick the TUI busy-indicator style (kaomoji/emoji/unicode/ascii).

Usage:
/indicator → show current style
/indicator kaomoji → set to kaomoji (default)
/indicator emoji → set to emoji
/indicator unicode → set to unicode (braille spinner)
/indicator ascii → set to ascii
"""
from hermes_cli.config import load_config
from hermes_cli.colors import Colors as _Colors

VALID_STYLES = {"kaomoji", "emoji", "unicode", "ascii"}

# Parse arg
arg = ""
try:
parts = (cmd_original or "").strip().split(None, 1)
if len(parts) > 1:
arg = parts[1].strip().lower()
except Exception:
arg = ""

cfg = load_config() or {}
current = ((cfg.get("display") or {}).get("tui_status_indicator", "kaomoji"))

if arg in ("status", "?", ""):
_cprint(f" {_Colors.BOLD}Busy indicator:{_Colors.RESET} {current}")
_cprint(f" Available: {', '.join(sorted(VALID_STYLES))}")
return

if arg not in VALID_STYLES:
_cprint(f" Unknown indicator style: {arg}")
_cprint(f" Available: {', '.join(sorted(VALID_STYLES))}")
return

if save_config_value("display.tui_status_indicator", arg):
_cprint(f" Busy indicator set to: {_Colors.GREEN}{arg}{_Colors.RESET} (saved)")
else:
_cprint(f" Busy indicator set to: {arg}")

def _handle_reasoning_command(self, cmd: str):
"""Handle /reasoning — manage effort level and display toggle.

Expand Down Expand Up @@ -8267,13 +8348,8 @@ def _show_usage(self):
logging.getLogger(noisy).setLevel(logging.WARNING)
else:
logging.getLogger().setLevel(logging.INFO)
# NOTE: We deliberately do NOT raise per-logger levels for
# tools/run_agent/etc. in quiet mode. Setting logger.setLevel
# above the file handler level filters records before they
# reach handlers, so agent.log / errors.log lose visibility
# into stream-retry events, credential rotations, etc.
# Console quietness is enforced by hermes_logging not
# installing a console StreamHandler in non-verbose mode.
for quiet_logger in ('tools', 'run_agent', 'trajectory_compressor', 'cron', 'hermes_cli'):
logging.getLogger(quiet_logger).setLevel(logging.ERROR)

def _show_insights(self, command: str = "/insights"):
"""Show usage insights and analytics from session history."""
Expand Down