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.
Summary
Two CLI surfaces in
hermes_cli/plugins_cmd.pyfilter outentry-point-discovered plugins (the
hermes_agent.pluginsgroup),even though
PluginManager.discover_and_load()andPluginManager._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, butthe 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-pointplugin 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 listfilters out entry-point pluginshermes_cli/plugins_cmd.py:670defines_discover_all_plugins()—walks
_plugins_dir()(user) andget_bundled_plugins_dir()(bundled)looking for
plugin.yaml/plugin.yml. Entry-point plugins have noon-disk manifest, so they're invisible.
cmd_list()(line 723) calls_discover_all_plugins(). Sohermes plugins listshows only directory-based plugins.Reproduction
Suggested fix
Extend
_discover_all_plugins()to also includePluginManager._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/disablerejects entry-point pluginshermes_cli/plugins_cmd.py:684defines_plugin_exists(name)—same coverage as
_discover_all_plugins()(user dir + bundled dir).Returns False for entry-point plugins.
cmd_enable(name)(line 631) andcmd_disable(name)(line 658) gateon
_plugin_exists()and exit non-zero with"Plugin 'X' is not installed or bundled.".Reproduction
…even though
_get_enabled_set()/_save_enabled_set()(lines603-628) would happily round-trip the value through
~/.hermes/config.yaml::plugins.enabledif_plugin_existsletus in.
Suggested fix
Extend
_plugin_exists()to also checkPluginManager._scan_entry_points()for a manifest with matchingname. 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.tomlis exactly the shape Python's packaging toolingrecommends, and Hermes'
PluginManageralready supports it cleanlyat 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 ownsetup wizard that does it for them — which is what we do in
hermes-a365's
interactive_setup, see satscryption/Hermes-A365slice 19r-a).
Related
deployments. Same upstream-team scope.
PluginManager._scan_entry_points()path itself was alreadycorrect in v0.13.0 (PR perf(cli): skip eager plugin discovery on known built-in subcommands #22120) — this issue is purely about the
CLI-surface helpers in
plugins_cmd.pynot consuming that path.Reproduction environment
hermes-a365from PyPI(https://pypi.org/project/hermes-a365/)
Happy to spec or contribute a PR if direction is positive.