{"repo":"NousResearch/hermes-agent","public_id":"34034","display_label":"Plugin manifests missing from PyPI package releases","summary":"A packaging defect caused plugin.yaml manifest files to be absent from distributed wheels and sdists, preventing platform adapters (Discord, IRC, Teams, etc.) from being discovered. Multiple fix attempts and related refactoring efforts address manifest inclusion and plugin discovery via entry points.","subsystem":"gateway","counts":{"members":7,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":3,"merged_prs":1,"attempt_prs":3},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":34484},"resolution":{"state":"partially_resolved","by":"pr-34484"},"freshness":{"state":"verified","last_verified_at":"2026-05-29T08:23:28+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.22476190476190475},"fix_edges":[{"issue_number":28149,"pr_number":34484,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":34034,"pr_number":34484,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":34034,"title":"v0.15.0 PyPI release breaks ALL platforms — plugin.yaml manifests missing from package","url":null,"state":"closed"},{"kind":"issue","number":34511,"title":"[Bug]: Discord gateway adapter not registered/discovered in PyPI v0.15.1","url":null,"state":"closed"},{"kind":"issue","number":34576,"title":"Platform plugins (Discord, IRC, Teams, etc.) missing plugin.yaml in pip package — adapters cannot be discovered","url":null,"state":"closed"},{"kind":"pr","number":34061,"title":"fix(packaging): include plugin manifests in wheels","url":null,"state":"closed","close_reason":{"category":"silent_close","implication":"active"},"merged":false},{"kind":"pr","number":34072,"title":"fix(packaging): include plugin.yaml in PyPI package-data (#34034)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#34484","number":34484},{"kind":"pr","value":"#28170","number":28170},{"kind":"pr","value":"#28162","number":28162},{"kind":"pr","value":"#31753","number":31753}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":34484,"title":"fix(packaging): ship bundled plugin.yaml manifests in wheel and sdist","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["MANIFEST.in","pyproject.toml","tests/test_packaging_metadata.py"],"fix_hunks":[{"file":"MANIFEST.in","hunks":["@@ -1,4 +1,9 @@\n graft skills\n graft optional-skills\n+# Bundled plugin manifests (plugin.yaml / plugin.yml). Without these the\n+# PluginManager scan (hermes_cli/plugins.py) finds zero plugins on installs\n+# built from the sdist (e.g. Homebrew, downstream packagers). package-data\n+# below covers the wheel; this covers the sdist. See #34034 / #28149.\n+recursive-include plugins plugin.yaml plugin.yml\n global-exclude __pycache__\n global-exclude *.py[cod]"]},{"file":"pyproject.toml","hunks":["@@ -226,6 +226,14 @@ plugins = [\n   \"*/dashboard/manifest.json\",\n   \"*/dashboard/dist/*\",\n   \"*/dashboard/dist/**/*\",\n+  # Plugin discovery (hermes_cli/plugins.py) reads a plugin.yaml/plugin.yml\n+  # manifest from each bundled plugin directory to register it. Wheels only\n+  # carry files declared here, so without this glob the wheel ships every\n+  # plugin's Python code but none of its manifests — the scan finds zero\n+  # plugins and all gateway platforms fail with \"No adapter available for\n+  # <platform>\" (#34034), web-search providers go missing (#28149), etc.\n+  \"**/plugin.yaml\",\n+  \"**/plugin.yml\",\n ]\n \n [tool.setuptools.packages.find]"]},{"file":"tests/test_packaging_metadata.py","hunks":["@@ -20,3 +20,42 @@ def test_manifest_includes_bundled_skills():\n \n     assert \"graft skills\" in manifest\n     assert \"graft optional-skills\" in manifest\n+\n+\n+def test_bundled_plugin_manifests_ship_in_both_wheel_and_sdist():\n+    \"\"\"Regression test for #34034 / #28149.\n+\n+    Plugin discovery (hermes_cli/plugins.py) registers each bundled plugin by\n+    reading its ``plugin.yaml`` / ``plugin.yml`` manifest. Those manifests are\n+    data files, not Python modules, so they only reach installed packages when\n+    declared explicitly:\n+\n+    - wheel  -> ``[tool.setuptools.package-data]`` ``plugins`` glob\n+    - sdist  -> ``MANIFEST.in`` (Homebrew and other downstream packagers build\n+                from the sdist)\n+\n+    v0.15.0 declared neither, so the wheel shipped every adapter's Python code\n+    but none of its manifests, and *every* gateway platform failed with\n+    \"No adapter available for <platform>\". Both channels must cover manifests.\n+    \"\"\"\n+    # There must actually be manifests on disk for the globs to match.\n+    on_disk = list((REPO_ROOT / \"plugins\").rglob(\"plugin.yaml\")) + list(\n+        (REPO_ROOT / \"plugins\").rglob(\"plugin.yml\")\n+    )\n+    assert on_disk, \"expected bundled plugin manifests under plugins/\"\n+\n+    # Wheel channel: package-data must declare a glob that matches plugin\n+    # manifests anywhere under the plugins package.\n+    data = tomllib.loads((REPO_ROOT / \"pyproject.toml\").read_text(encoding=\"utf-8\"))\n+    plugins_pkg_data = data[\"tool\"][\"setuptools\"][\"package-data\"].get(\"plugins\", [])\n+    assert any(\n+        g.endswith(\"plugin.yaml\") or g.endswith(\"plugin.yml\")\n+        for g in plugins_pkg_data\n+    ), \"pyproject package-data 'plugins' must ship plugin.yaml/plugin.yml (wheel)\"\n+\n+    # Sdist channel: MANIFEST.in must recursively include the manifests so\n+    # downstream packagers building from the sdist also get them.\n+    manifest = (REPO_ROOT / \"MANIFEST.in\").read_text(encoding=\"utf-8\")\n+    assert \"recursive-include plugins\" in manifest and \"plugin.yaml\" in manifest, (\n+        \"MANIFEST.in must recursive-include plugins plugin.yaml/plugin.yml (sdist)\"\n+    )\n"]}]},{"kind":"pr","number":34533,"title":"fix(cli): discover pip-installed plugins via entry points in list/enable/disable","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#23802","number":23802},{"kind":"pr","value":"#23814","number":23814},{"kind":"pr","value":"#25040","number":25040}],"category":"duplicate_of","implication":"duplicate"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}