Skip to content

hermes plugins enable/list filters out entry-point-discovered plugins #23802

@satscryption

Description

@satscryption

Summary

Two CLI surfaces in hermes_cli/plugins_cmd.py filter out
entry-point-discovered plugins (the hermes_agent.plugins group),
even though PluginManager.discover_and_load() and
PluginManager._scan_entry_points() discover and load them correctly.

Result: operators who install a Hermes plugin via pip install <package> see the plugin's adapter / hooks / CLI commands work, but
the management surfaces (hermes plugins list, hermes plugins enable/disable) behave as if the plugin doesn't exist.

I'm encountering this while shipping hermes-a365 (an entry-point
plugin distributed via PyPI; satscryption/Hermes-A365), but it
generalises to any third-party plugin author who picks the
entry-point distribution path.

Gap 1 — hermes plugins list filters out entry-point plugins

hermes_cli/plugins_cmd.py:670 defines _discover_all_plugins()
walks _plugins_dir() (user) and get_bundled_plugins_dir() (bundled)
looking for plugin.yaml/plugin.yml. Entry-point plugins have no
on-disk manifest, so they're invisible.

cmd_list() (line 723) calls _discover_all_plugins(). So
hermes plugins list shows only directory-based plugins.

Reproduction

pip install hermes-a365   # entry-point plugin
hermes plugins list       # agent365 does NOT appear

# But the plugin IS loaded:
python -c "
from hermes_cli.plugins import get_plugin_manager, discover_plugins
discover_plugins(force=True)
for k, lp in sorted(get_plugin_manager()._plugins.items()):
    print(k, lp.manifest.source, lp.enabled, lp.error)
"
# → agent365 entrypoint True None

Suggested fix

Extend _discover_all_plugins() to also include
PluginManager._scan_entry_points() results, deduplicating by name.
Bundled + user overrides still take precedence; entry-point fills in
the rest. A 5-line addition.

Gap 2 — hermes plugins enable/disable rejects entry-point plugins

hermes_cli/plugins_cmd.py:684 defines _plugin_exists(name)
same coverage as _discover_all_plugins() (user dir + bundled dir).
Returns False for entry-point plugins.

cmd_enable(name) (line 631) and cmd_disable(name) (line 658) gate
on _plugin_exists() and exit non-zero with "Plugin 'X' is not installed or bundled.".

Reproduction

pip install hermes-a365
hermes plugins enable agent365
# → Plugin 'agent365' is not installed or bundled.   (exit 1)

…even though _get_enabled_set() / _save_enabled_set() (lines
603-628) would happily round-trip the value through
~/.hermes/config.yaml::plugins.enabled if _plugin_exists let
us in.

Suggested fix

Extend _plugin_exists() to also check
PluginManager._scan_entry_points() for a manifest with matching
name. Same pattern as Gap 1.

Why this matters

Entry-point plugin distribution is the modern Python path — pip install foo + [project.entry-points."hermes_agent.plugins"]
in pyproject.toml is exactly the shape Python's packaging tooling
recommends, and Hermes' PluginManager already supports it cleanly
at the runtime layer. The CLI surfaces are the only thing lagging.

Fixing both gaps would unblock a class of third-party plugins that
otherwise need workarounds like manually editing
~/.hermes/config.yaml::plugins.enabled (or shipping their own
setup wizard that does it for them — which is what we do in
hermes-a365's interactive_setup, see satscryption/Hermes-A365
slice 19r-a
).

Related

Reproduction environment

Happy to spec or contribute a PR if direction is positive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/cliCLI entry point, hermes_cli/, setup wizardcomp/pluginsPlugin system and bundled pluginstype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions