feat(skills): /reload-skills slash command + skills_reload agent tool

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.
This commit is contained in:
Shannon Sands
2026-04-29 13:58:45 +10:00
committed by Teknium
parent 113239f6e3
commit 7966560fb5
10 changed files with 682 additions and 4 deletions

View File

@@ -1512,3 +1512,61 @@ registry.register(
check_fn=check_skills_requirements,
emoji="📚",
)
# ---------------------------------------------------------------------------
# skills_reload — rescan the skills dir without restarting the gateway
# ---------------------------------------------------------------------------
def skills_reload(task_id: str | None = None) -> str:
"""Re-scan ``~/.hermes/skills/`` and clear in-process skill caches.
Use this after installing a skill via the shell during a session so the
new skill becomes visible to ``skills_list`` / ``skill_view`` and the
skill catalogue in the system prompt without a gateway restart.
Returns:
JSON string with ``added``, ``removed``, ``unchanged``, ``total``,
and ``commands`` keys. ``added``/``removed`` are bare skill names
(no leading slash).
"""
try:
from agent.skill_commands import reload_skills as _reload
result = _reload()
except Exception as e:
return json.dumps({"success": False, "error": str(e)})
return json.dumps({
"success": True,
"added": result.get("added", []),
"removed": result.get("removed", []),
"unchanged_count": len(result.get("unchanged", [])),
"total": result.get("total", 0),
"commands": result.get("commands", 0),
})
SKILLS_RELOAD_SCHEMA = {
"name": "skills_reload",
"description": (
"Re-scan the skills directory and clear in-process skill caches. "
"Use after installing or removing a skill mid-session (e.g. via the "
"shell tool or skills_hub) so the new skill becomes visible to "
"skills_list / skill_view without restarting the gateway. Returns "
"the diff of added/removed skill names plus the new total count."
),
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
}
registry.register(
name="skills_reload",
toolset="skills",
schema=SKILLS_RELOAD_SCHEMA,
handler=lambda args, **kw: skills_reload(task_id=kw.get("task_id")),
check_fn=check_skills_requirements,
emoji="🔄",
)