Skip to content

feat(skills): /reload-skills slash command (salvage of #17670)#17744

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-1c315fcf
Apr 30, 2026
Merged

feat(skills): /reload-skills slash command (salvage of #17670)#17744
teknium1 merged 2 commits into
mainfrom
hermes/hermes-1c315fcf

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvage of #17670 by @shannonsands. Adds /reload-skills (CLI + gateway + Discord) that rescans ~/.hermes/skills/ and queues a one-shot note for the next user turn listing what changed.

Summary

/reload-skills lets a user make newly-installed skills visible mid-session without a gateway restart — critical for agent-driven skill installation (an agent drops a skill via the shell, then runs /reload-skills to pick it up).

What changes vs. the original PR #17670

  • No prompt-cache invalidation. Skills are invoked at runtime via /skill-name, skills_list, or skill_view — they don't need to live in the system prompt for the model to use them. Keeping the cache intact preserves prefix caching across the reload. (MCP has to break the cache because tool schemas must be known at conversation start; skills do not.)
  • No skills_reload agent tool. Dropped the tool, schema, and four toolset enumerations. The model doesn't need a new schema entry to see a just-installed skill.
  • No phantom user turn written to the transcript. Replaced the conversation_history.append({role: 'user', ...}) / session_store.append_to_transcript calls with a one-shot queued note:
    • CLI: self._pending_skills_reload_note (same pattern as _pending_model_switch_note, prepended to the next API call and cleared).
    • Gateway: self._pending_skills_reload_notes[session_key] (consumer in _run_agent_turn prepends to the next message and pops the entry).
    • Message alternation stays intact; nothing persists to the session transcript out-of-band.
  • Diff carries full descriptions. reload_skills() now returns added/removed as [{"name", "description"}, ...]. The description is the skill's SKILL.md frontmatter description: field, verbatim — same string the system prompt renders.
  • Note format matches the system prompt. Pre-existing skills render as - name: description; the reload note uses the same shape so the model reads the diff in its original catalog format:
[USER INITIATED SKILLS RELOAD:

Added Skills:
    - alpha: Run alpha to do xyz
    - beta: Run beta to do abc

Removed Skills:
    - gamma: Old gamma skill

Use skills_list to see the updated catalog.]
  • Empty diff → zero side effects. Prints "No new skills detected" and queues nothing.

Files changed

File Change
agent/skill_commands.py New reload_skills() helper — rescans, returns diff with name + description
cli.py /reload-skills handler + queued note consumer
gateway/run.py Handler + per-session queued note consumer in _run_agent_turn
gateway/platforms/discord.py Discord app command registration
hermes_cli/commands.py CommandDef("reload-skills", ...) with /reload_skills alias
tests/agent/test_skill_commands_reload.py 6 cases — return shape, diff detection, description passthrough, prompt-cache preservation regression guard
tests/cli/test_cli_reload_skills.py 3 cases — CLI handler queue semantics
tests/gateway/test_reload_skills_command.py 4 cases — gateway handler queue semantics + dispatcher routing

Validation

Targeted Surrounding skill/command tests
13 new tests pass 370 pass, zero regressions

Closes #17670.
Authorship preserved via cherry-pick + rebase-merge — commit bf5d011b5 stays @shannonsands, follow-up cb9393152 is @teknium1.

shannonsands and others added 2 commits April 29, 2026 20:34
Adds a public reload path for the in-process skill caches so newly
installed (or removed) skills become visible mid-session without a
gateway restart. Mirrors the shape of /reload-mcp.

Three surfaces:
* /reload-skills slash command — CLI (cli.py) and gateway (gateway/run.py),
  with /reload_skills alias for Telegram autocomplete and an explicit
  Discord registration.
* skills_reload agent tool (tools/skills_tool.py) — lets agents/subagents
  pick up freshly-installed skills via tool call.
* agent.skill_commands.reload_skills() — shared helper that clears
  _skill_commands, _SKILLS_PROMPT_CACHE (in-process LRU), and the
  on-disk .skills_prompt_snapshot.json, then returns an added/removed
  diff plus the new total count.

Tested:
* tests/agent/test_skill_commands_reload.py (9 cases)
* tests/cli/test_cli_reload_skills.py       (3 cases)
* tests/gateway/test_reload_skills_command.py (4 cases)

Use case: NemoClaw / OpenShell-style sandboxed orchestrators that drop
skills into ~/.hermes/skills mid-session, plus agentic flows where the
agent itself installs a skill via the shell tool and needs it bound
without a gateway restart. The Python helper
clear_skills_system_prompt_cache(clear_snapshot=True) already exists
internally — this PR just exposes it via slash command and tool.
…ation + agent tool

Salvage-follow-up to @shannonsands's /reload-skills PR. Trims the feature to
match the design: user-initiated rescan, no prompt-cache reset, no new
schema surface, no phantom user turn, and the next-turn note carries each
added/removed skill's 60-char description (not just its name).

Changes vs the original PR:

* Drop the in-process skills prompt-cache clear in reload_skills(). Skills
  are invoked at runtime via /skill-name, skills_list, or skill_view —
  they don't need to live in the system prompt for the model to use them.
  Keeping the cache intact preserves prefix caching across the reload so
  /reload-skills pays no cache-reset cost. (MCP has to break the cache
  because tool schemas must be known at conversation start; skills do not.)

* Drop the skills_reload agent tool and SKILLS_RELOAD_SCHEMA from
  tools/skills_tool.py, plus the four skills_reload enumerations in
  toolsets.py. No new schema surface — agents can already see a freshly-
  installed skill via skill_view / skills_list the moment it's on disk.

* Replace the phantom 'role: user' turn injection with a one-shot queued
  note. CLI uses self._pending_skills_reload_note (same pattern as
  _pending_model_switch_note, prepended to the next API call and cleared).
  Gateway uses self._pending_skills_reload_notes[session_key]. The note
  is prepended to the NEXT real user message in this session, so message
  alternation stays intact and nothing out-of-band is persisted to the
  transcript.

* reload_skills() now returns added/removed as
  [{'name': str, 'description': str}, ...] (description truncated to 60
  chars — matches the curator / gateway adapter budget). The injected
  next-turn note formats each entry as 'name — description' so the model
  can actually reason about which new skills to call without running
  skills_list first.

* Only emit the note when the diff is non-empty. On empty diff, print
  'No new skills detected' and do nothing else.

* Tests rewritten to cover the queue semantics, the description payload,
  and a regression guard that the prompt-cache snapshot is preserved.
@teknium1 teknium1 merged commit dd2d1ba into main Apr 30, 2026
11 of 12 checks passed
@teknium1 teknium1 deleted the hermes/hermes-1c315fcf branch April 30, 2026 04:07
@alt-glitch alt-glitch added comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have tool/skills Skills system (list, view, manage) type/feature New feature or request labels Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have tool/skills Skills system (list, view, manage) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants