mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat(plugins): add slash command registration for plugins (#2359)
Plugins can now register slash commands via ctx.register_command() in their register() function. Commands automatically appear in: - /help and COMMANDS_BY_CATEGORY (under 'Plugins' category) - Tab autocomplete in CLI - Telegram bot menu - Slack subcommand mapping - Gateway dispatch Handler signature: handler(args: str) -> str | None Async handlers are supported in gateway context. Changes: - commands.py: add register_plugin_command() and rebuild_lookups() - plugins.py: add register_command() to PluginContext, track in PluginManager._plugin_commands and LoadedPlugin.commands_registered - cli.py: dispatch plugin commands in process_command() - gateway/run.py: dispatch plugin commands before skill commands - tests: 5 new tests for registration, help, tracking, handler, gateway - docs: update plugins feature page and build guide
This commit is contained in:
parent
36079c6646
commit
8da410ed95
7 changed files with 326 additions and 2 deletions
|
|
@ -137,7 +137,7 @@ COMMAND_REGISTRY: list[CommandDef] = [
|
|||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Derived lookups -- rebuilt once at import time
|
||||
# Derived lookups -- rebuilt once at import time, refreshed by rebuild_lookups()
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _build_command_lookup() -> dict[str, CommandDef]:
|
||||
|
|
@ -161,6 +161,58 @@ def resolve_command(name: str) -> CommandDef | None:
|
|||
return _COMMAND_LOOKUP.get(name.lower().lstrip("/"))
|
||||
|
||||
|
||||
def register_plugin_command(cmd: CommandDef) -> None:
|
||||
"""Append a plugin-defined command to the registry and refresh lookups."""
|
||||
COMMAND_REGISTRY.append(cmd)
|
||||
rebuild_lookups()
|
||||
|
||||
|
||||
def rebuild_lookups() -> None:
|
||||
"""Rebuild all derived lookup dicts from the current COMMAND_REGISTRY.
|
||||
|
||||
Called after plugin commands are registered so they appear in help,
|
||||
autocomplete, gateway dispatch, Telegram menu, and Slack mapping.
|
||||
"""
|
||||
global GATEWAY_KNOWN_COMMANDS
|
||||
|
||||
_COMMAND_LOOKUP.clear()
|
||||
_COMMAND_LOOKUP.update(_build_command_lookup())
|
||||
|
||||
COMMANDS.clear()
|
||||
for cmd in COMMAND_REGISTRY:
|
||||
if not cmd.gateway_only:
|
||||
COMMANDS[f"/{cmd.name}"] = _build_description(cmd)
|
||||
for alias in cmd.aliases:
|
||||
COMMANDS[f"/{alias}"] = f"{cmd.description} (alias for /{cmd.name})"
|
||||
|
||||
COMMANDS_BY_CATEGORY.clear()
|
||||
for cmd in COMMAND_REGISTRY:
|
||||
if not cmd.gateway_only:
|
||||
cat = COMMANDS_BY_CATEGORY.setdefault(cmd.category, {})
|
||||
cat[f"/{cmd.name}"] = COMMANDS[f"/{cmd.name}"]
|
||||
for alias in cmd.aliases:
|
||||
cat[f"/{alias}"] = COMMANDS[f"/{alias}"]
|
||||
|
||||
SUBCOMMANDS.clear()
|
||||
for cmd in COMMAND_REGISTRY:
|
||||
if cmd.subcommands:
|
||||
SUBCOMMANDS[f"/{cmd.name}"] = list(cmd.subcommands)
|
||||
for cmd in COMMAND_REGISTRY:
|
||||
key = f"/{cmd.name}"
|
||||
if key in SUBCOMMANDS or not cmd.args_hint:
|
||||
continue
|
||||
m = _PIPE_SUBS_RE.search(cmd.args_hint)
|
||||
if m:
|
||||
SUBCOMMANDS[key] = m.group(0).split("|")
|
||||
|
||||
GATEWAY_KNOWN_COMMANDS = frozenset(
|
||||
name
|
||||
for cmd in COMMAND_REGISTRY
|
||||
if not cmd.cli_only
|
||||
for name in (cmd.name, *cmd.aliases)
|
||||
)
|
||||
|
||||
|
||||
def _build_description(cmd: CommandDef) -> str:
|
||||
"""Build a CLI-facing description string including usage hint."""
|
||||
if cmd.args_hint:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue