From 4b3839a8ee34b221c05f0d3f131465780b8b529d Mon Sep 17 00:00:00 2001 From: emozilla Date: Mon, 11 May 2026 15:53:50 -0400 Subject: [PATCH] fix(cli): seed bundled skills on dashboard + gateway entrypoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sync_skills(quiet=True)` was only being called from inside `cmd_chat`, which meant `hermes dashboard` (the desktop GUI's backend) and `hermes gateway` (Telegram/Discord/Slack/etc daemons) never seeded the bundled skill library into ~/.hermes/skills/. This surfaced as "No skills found" in the desktop GUI's skills panel on fresh installs, despite the agent having access to the full bundled library when invoked via `hermes chat`. scripts/install.ps1 worked around it by running skills_sync.py as part of Copy-ConfigTemplates, but that's not part of the desktop installer's bootstrap chain. Fix - Extract the skills-sync block from cmd_chat into a module-level `_sync_bundled_skills_quietly()` helper. - Call the helper from cmd_chat (preserving existing behavior), cmd_dashboard (after the --status/--stop early-return paths and fastapi import check, so we don't run skills_sync on management commands or when deps aren't installed), and cmd_gateway. Why these three entrypoints - cmd_chat: the user's primary CLI entrypoint - cmd_dashboard: the desktop GUI's backend; this is what `hermes dashboard --tui` invokes when the desktop bootstrapper spawns Hermes - cmd_gateway: long-running daemons where the user expects the agent to have full skill access Other entrypoints (cmd_config, cmd_doctor, cmd_login, cmd_status, etc.) are management commands that don't need skill discovery and were never running skills_sync in the first place — leaving them alone. Idempotence - tools/skills_sync.py is manifest-based: skipped skills cost milliseconds. Calling it from multiple entrypoints adds no real cost, and users running `hermes chat` then `hermes dashboard` get two fast no-ops on the second call. Failure handling - Helper wraps skills_sync in try/except. Skills are an enhancement, not a hard dependency — Hermes runs fine with an empty skills/ dir. Files - hermes_cli/main.py: + new helper `_sync_bundled_skills_quietly()` at module level + cmd_chat: replace inline block with helper call + cmd_dashboard: add helper call after fastapi import succeeds + cmd_gateway: add helper call before delegating to gateway_command --- hermes_cli/main.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 5759a0ba9c2..db260e60bb0 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -1342,6 +1342,26 @@ def _pin_kanban_board_env() -> None: pass +def _sync_bundled_skills_quietly() -> None: + """Seed ``~/.hermes/skills/`` with the bundled skill library on first launch. + + Called from any CLI entrypoint that the user might use as their first + interaction with Hermes — chat, dashboard (the desktop GUI's backend), + and gateway. The skills_sync module is manifest-based and idempotent: + skipped skills cost ~milliseconds, so calling this repeatedly is fine. + + Failures are swallowed because skills are an enhancement, not a hard + dependency. Hermes still functions without them; the user just sees an + empty skills library. + """ + try: + from tools.skills_sync import sync_skills + + sync_skills(quiet=True) + except Exception: + pass + + def cmd_chat(args): """Run interactive chat CLI.""" use_tui = getattr(args, "tui", False) or os.environ.get("HERMES_TUI") == "1" @@ -1421,12 +1441,7 @@ def cmd_chat(args): pass # Sync bundled skills on every CLI launch (fast -- skips unchanged skills) - try: - from tools.skills_sync import sync_skills - - sync_skills(quiet=True) - except Exception: - pass + _sync_bundled_skills_quietly() # --yolo: bypass all dangerous command approvals if getattr(args, "yolo", False): @@ -1504,6 +1519,8 @@ def cmd_chat(args): def cmd_gateway(args): """Gateway management commands.""" + _sync_bundled_skills_quietly() + from hermes_cli.gateway import gateway_command gateway_command(args) @@ -8973,6 +8990,12 @@ def cmd_dashboard(args): print(f"Import error: {e}") sys.exit(1) + # Seed bundled skills on first dashboard launch so the desktop GUI's + # skills picker / agent skill discovery sees the bundled library. + # cmd_chat does this in its own pre-dispatch block; the dashboard + # backend is the desktop's primary entrypoint and needs the same. + _sync_bundled_skills_quietly() + if "HERMES_WEB_DIST" not in os.environ: if not _build_web_ui(PROJECT_ROOT / "apps" / "dashboard", fatal=True): sys.exit(1)