fix(plugins): discover nested category plugins in hermes plugins list#41081
Open
annguyenNous wants to merge 1 commit into
Open
fix(plugins): discover nested category plugins in hermes plugins list#41081annguyenNous wants to merge 1 commit into
hermes plugins list#41081annguyenNous wants to merge 1 commit into
Conversation
_discover_all_plugins() only iterated immediate subdirectories, missing all category-namespaced plugins (web/tavily, image_gen/openai, browser/browser_use, etc.). Changes: - Replace flat iterdir() with recursive _scan_level() matching PluginManager._scan_directory_level (depth <= 2) - Add path-derived `key` to entry tuple for correct config lookup - Update _plugin_status() to check both manifest name and key - Update all callers (cmd_list, web_server, dashboard) for 6-tuple - Update tests with new entry format Fixes NousResearch#41066
kshitijk4poor
added a commit
to kshitijk4poor/hermes-agent
that referenced
this pull request
Jun 8, 2026
…ble/disable (NousResearch#41066) Salvage consolidating three complementary community PRs into one coherent fix: - NousResearch#41076 (islam666): made `hermes plugins list` discover nested category plugins (e.g. `observability/nemo_relay`, `image_gen/openai`). - NousResearch#41613 (mnajafian-nv): the superset — nested discovery aligned with the runtime loader (reuses `PluginManager._scan_directory`/`_scan_entry_points` so list state can't drift from what actually loads), PLUS the enable/disable mutation side NousResearch#41076 left flat. - NousResearch#41081 (annguyenNous): identified the `web_server.py` dashboard plugins-hub caller of `_discover_all_plugins()`, which must also be updated for the new tuple shape + key-aware status. This combines the best of all three and fixes the mutation-side gap that made nested bundled plugins untoggleable: $ hermes plugins enable nemo_relay Plugin 'nemo_relay' is not installed or bundled. # exit 1 (before) Now `enable`/`disable`/`toggle`, the `hermes plugins list` views, the dashboard plugins-hub endpoint, and the dashboard enable/disable helpers all resolve a bare manifest name OR a full path-derived key to the canonical key the loader gates on, via a single `_resolve_plugin_reference()` normalization point, and persist that key while clearing any stale legacy bare-name alias so the two can't drift. `_plugin_status` is key+legacy-name aware. `_discover_all_plugins` now returns 6-tuples `(key, legacy_name, version, description, source, dir_path)`; ALL call sites (`cmd_list` table/plain/JSON, `_filter_plugin_entries`, `cmd_toggle`, `dashboard_remove_user_plugin`, and `web_server._merged_plugins_hub`) and the `test_plugins_cmd_list.py` fixtures are updated to match. Updating the `web_server.py` caller prevents a `ValueError: too many values to unpack` crash in the dashboard plugins-hub endpoint. Verified e2e on the real CLI + runtime loader (isolated HERMES_HOME): `hermes plugins enable nemo_relay` writes `observability/nemo_relay` to config.yaml and the loader then loads it (`enabled=True, error=None`); a stale bare-name alias is cleared on disable (no contradictory state); the dashboard `_merged_plugins_hub()` runs and lists nested plugins by canonical key. Full `tests/hermes_cli/test_plugins_cmd*` + web_server plugin tests green. Closes NousResearch#41066. Supersedes NousResearch#41076, NousResearch#41081, and NousResearch#41613. Co-authored-by: islam666 <islam666@users.noreply.github.com> Co-authored-by: mnajafian-nv <mnajafian@nvidia.com> Co-authored-by: annguyenNous <annguyenNous@users.noreply.github.com> Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
Collaborator
|
Heads-up: the nested category plugin work landed on main via #42076 (merge commit b99c6c4). Your catch here — that |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #41066
Problem
hermes plugins listonly iterates immediate subdirectories of~/.hermes/plugins/and the bundledplugins/directory. Category-namespaced plugins (web/tavily,image_gen/openai,browser/browser_use, etc.) are invisible to the CLI listing, even though they load correctly at runtime viaPluginManager._scan_directory_level().This means 12+ bundled plugins and any user-installed category plugins never appear in
hermes plugins list.Root Cause
Two separate code paths implement plugin discovery:
PluginManager._scan_directory_level()_discover_all_plugins()Additionally,
_plugin_status()only checks the manifestnameagainst the enabled/disabled sets, but for category plugins the config key is path-derived (e.g.web/tinyfish) while the manifest name differs (e.g.web-tinyfish).Fix
iterdir()in_discover_all_plugins()with a recursive_scan_level()helper that mirrorsPluginManager._scan_directory_level()— recurse into directories withoutplugin.yamlup to 2 levels deepkeyto the entry tuple (6th element) for correct config lookup_plugin_status()to check both manifestnameand path-derivedkeyagainst enabled/disabled setscmd_list(table/plain/JSON),_filter_plugin_entries,dashboard_remove_user_plugin, web server dashboard endpointTesting
test_plugins_cmd_list.pypass (updated for new 6-element tuple)