From 2b244762e14a04b39efdb74433ff36a939c9d1ca Mon Sep 17 00:00:00 2001 From: teknium1 Date: Tue, 10 Mar 2026 23:49:03 -0700 Subject: [PATCH] feat: add missing commands to categorized /help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-merge follow-up to PR #752 — adds 10 commands that were added since the PR was submitted: Session: /title, /compress, /rollback Configuration: /provider, /verbose, /skin Tools & Skills: /reload-mcp (+ full /skills description) Info: /usage, /insights, /paste Also preserved existing color formatting (_cprint, _GOLD, _BOLD, _DIM) and skill commands section from main. --- hermes_cli/commands.py | 12 ++++++++- hermes_cli/skills_config.py | 19 +++++++------- hermes_cli/tools_config.py | 52 ++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index e1b23d872..0d9a796ba 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -23,22 +23,32 @@ COMMANDS_BY_CATEGORY = { "/save": "Save the current conversation", "/retry": "Retry the last message (resend to agent)", "/undo": "Remove the last user/assistant exchange", + "/title": "Set a title for the current session (usage: /title My Session Name)", + "/compress": "Manually compress conversation context (flush memories + summarize)", + "/rollback": "List or restore filesystem checkpoints (usage: /rollback [number])", }, "Configuration": { "/config": "Show current configuration", "/model": "Show or change the current model", + "/provider": "Show available providers and current provider", "/prompt": "View/set custom system prompt", "/personality": "Set a predefined personality", + "/verbose": "Cycle tool progress display: off → new → all → verbose", + "/skin": "Show or change the display skin/theme", }, "Tools & Skills": { "/tools": "List available tools", "/toolsets": "List available toolsets", - "/skills": "Search, install, inspect, or manage skills", + "/skills": "Search, install, inspect, or manage skills from online registries", "/cron": "Manage scheduled tasks (list, add, remove)", + "/reload-mcp": "Reload MCP servers from config.yaml", }, "Info": { "/help": "Show this help message", + "/usage": "Show token usage for the current session", + "/insights": "Show usage insights and analytics (last 30 days)", "/platforms": "Show gateway/messaging platform status", + "/paste": "Check clipboard for an image and attach it", }, "Exit": { "/quit": "Exit the CLI (also: /exit, /q)", diff --git a/hermes_cli/skills_config.py b/hermes_cli/skills_config.py index 0e97f8e40..256f7ba52 100644 --- a/hermes_cli/skills_config.py +++ b/hermes_cli/skills_config.py @@ -111,7 +111,7 @@ def _prompt_checklist(title: str, items: List[str], disabled_items: Set[str]) -> curses.use_default_colors() curses.init_pair(1, curses.COLOR_GREEN, -1) curses.init_pair(2, curses.COLOR_YELLOW, -1) - curses.init_pair(3, curses.COLOR_RED, -1) + curses.init_pair(3, 8, -1) # dim gray cursor = 0 scroll_offset = 0 while True: @@ -121,28 +121,27 @@ def _prompt_checklist(title: str, items: List[str], disabled_items: Set[str]) -> hattr = curses.A_BOLD | (curses.color_pair(2) if curses.has_colors() else 0) stdscr.addnstr(0, 0, title, max_x - 1, hattr) stdscr.addnstr(1, 0, " ↑↓ navigate SPACE toggle ENTER confirm ESC cancel", max_x - 1, - curses.color_pair(3) if curses.has_colors() else curses.A_DIM) - stdscr.addnstr(2, 0, " [✓] enabled [✗] disabled", max_x - 1, curses.A_DIM) + curses.A_DIM) except curses.error: pass - visible_rows = max_y - 4 + visible_rows = max_y - 3 if cursor < scroll_offset: scroll_offset = cursor elif cursor >= scroll_offset + visible_rows: scroll_offset = cursor - visible_rows + 1 for draw_i, i in enumerate(range(scroll_offset, min(len(items), scroll_offset + visible_rows))): - y = draw_i + 4 + y = draw_i + 3 if y >= max_y - 1: break is_disabled = i in selected - check = "✗" if is_disabled else "✓" + check = " " if is_disabled else "✓" arrow = "→" if i == cursor else " " line = f" {arrow} [{check}] {items[i]}" attr = curses.A_NORMAL if i == cursor: - attr = curses.A_BOLD | (curses.color_pair(1) if curses.has_colors() else 0) - elif is_disabled and curses.has_colors(): - attr = curses.color_pair(3) + attr = curses.A_BOLD + if curses.has_colors(): + attr |= curses.color_pair(1) try: stdscr.addnstr(y, 0, line, max_x - 1, attr) except curses.error: @@ -179,7 +178,7 @@ def _numbered_toggle(title: str, items: List[str], disabled: Set[str]) -> Set[st print() print(color(f"{title}", Colors.BOLD)) for i, item in enumerate(items, 1): - mark = "✗" if item in current else "✓" + mark = "✓" if item not in current else " " print(f" {i:3}. [{mark}] {item}") print() print(color(" Number to toggle, 's' save, 'q' cancel:", Colors.DIM)) diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index 19288bf59..f334f9a17 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -481,7 +481,7 @@ def _prompt_toolset_checklist(platform_label: str, enabled: Set[str]) -> Set[str while True: stdscr.clear() max_y, max_x = stdscr.getmaxyx() - header = f"Tools for {platform_label} — ↑↓ navigate, SPACE toggle, ENTER confirm" + header = f"Tools for {platform_label} — ↑↓ navigate, SPACE toggle, ENTER confirm, ESC cancel" try: stdscr.addnstr(0, 0, header, max_x - 1, curses.A_BOLD | curses.color_pair(2) if curses.has_colors() else curses.A_BOLD) except curses.error: @@ -941,22 +941,68 @@ def tools_command(args=None, first_install: bool = False, config: dict = None): platform_choices.append(f"Configure {pinfo['label']} ({count}/{total} enabled)") platform_keys.append(pkey) + if len(platform_keys) > 1: + platform_choices.append("Configure all platforms (global)") platform_choices.append("Reconfigure an existing tool's provider or API key") platform_choices.append("Done") + # Index offsets for the extra options after per-platform entries + _global_idx = len(platform_keys) if len(platform_keys) > 1 else -1 + _reconfig_idx = len(platform_keys) + (1 if len(platform_keys) > 1 else 0) + _done_idx = _reconfig_idx + 1 + while True: idx = _prompt_choice("Select an option:", platform_choices, default=0) # "Done" selected - if idx == len(platform_keys) + 1: + if idx == _done_idx: break # "Reconfigure" selected - if idx == len(platform_keys): + if idx == _reconfig_idx: _reconfigure_tool(config) print() continue + # "Configure all platforms (global)" selected + if idx == _global_idx: + # Use the union of all platforms' current tools as the starting state + all_current = set() + for pk in platform_keys: + all_current |= _get_platform_tools(config, pk) + new_enabled = _prompt_toolset_checklist("All platforms", all_current) + if new_enabled != all_current: + for pk in platform_keys: + prev = _get_platform_tools(config, pk) + added = new_enabled - prev + removed = prev - new_enabled + pinfo_inner = PLATFORMS[pk] + if added or removed: + print(color(f" {pinfo_inner['label']}:", Colors.DIM)) + for ts in sorted(added): + label = next((l for k, l, _ in CONFIGURABLE_TOOLSETS if k == ts), ts) + print(color(f" + {label}", Colors.GREEN)) + for ts in sorted(removed): + label = next((l for k, l, _ in CONFIGURABLE_TOOLSETS if k == ts), ts) + print(color(f" - {label}", Colors.RED)) + # Configure API keys for newly enabled tools + for ts_key in sorted(added): + if (TOOL_CATEGORIES.get(ts_key) or TOOLSET_ENV_REQUIREMENTS.get(ts_key)): + if not _toolset_has_keys(ts_key): + _configure_toolset(ts_key, config) + _save_platform_tools(config, pk, new_enabled) + save_config(config) + print(color(" ✓ Saved configuration for all platforms", Colors.GREEN)) + # Update choice labels + for ci, pk in enumerate(platform_keys): + new_count = len(_get_platform_tools(config, pk)) + total = len(CONFIGURABLE_TOOLSETS) + platform_choices[ci] = f"Configure {PLATFORMS[pk]['label']} ({new_count}/{total} enabled)" + else: + print(color(" No changes", Colors.DIM)) + print() + continue + pkey = platform_keys[idx] pinfo = PLATFORMS[pkey]