NousResearch/hermes-agent

Platform-disabled skills appearing in <available_skills> prompt

Cluster 46201 · skills. Data as of

Nothing open on GitHub. Fix merged in #46236 on 14 Jun 2026. Check it covers your case.

The short answer

0open issues
0open PRs
4items grouped
Known?
Yes. 4 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #46236. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #46236. That is separate from the fix event.
Still open
Nothing open on GitHub.

All four members concern disabled skills still showing up in the <available_skills> prompt; one is a bug report that also mentions false loads, and three reference #46201 with proposed fixes. One of them also mentions unifying resolution sites. Their status is not established here.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #46236 fix(skills): platform-disabled skills still appear in <available_skills> + unify all resolution sites (#46201)

    merged to main

    files: agent/prompt_builder.py, agent/skill_utils.py, hermes_cli/skills_config.py, tests/hermes_cli/test_skills_config.py, tools/skills_tool.py

    Show the diff
    • agent/prompt_builder.py
      @@ -1164,7 +1164,7 @@ def build_skills_system_prompt(
               or get_session_env("HERMES_SESSION_PLATFORM")
               or ""
           )
      -    disabled = get_disabled_skill_names()
      +    disabled = get_disabled_skill_names(_platform_hint or None)
           cache_key = (
               str(skills_dir.resolve()),
               tuple(str(d) for d in external_dirs),
    • agent/skill_utils.py
      @@ -278,8 +278,10 @@ def get_disabled_skill_names(platform: str | None = None) -> Set[str]:
           Args:
               platform: Explicit platform name (e.g. ``"telegram"``).  When
                   *None*, resolves from ``HERMES_PLATFORM`` or
      -            ``HERMES_SESSION_PLATFORM`` env vars.  Falls back to the
      -            global disabled list when no platform is determined.
      +            ``HERMES_SESSION_PLATFORM`` env vars.  Returns the global
      +            disabled list, unioned with the platform-specific list when a
      +            platform is resolved (a globally-disabled skill stays disabled
      +            on every platform).
       
           Reads the config file directly (no CLI config imports) to stay
           lightweight.
      @@ -305,13 +307,14 @@ def get_disabled_skill_names(platform: str | None = None) -> Set[str]:
               or os.getenv("HERMES_PLATFORM")
               or get_session_env("HERMES_SESSION_PLATFORM")
           )
      +    global_disabled = _normalize_string_set(skills_cfg.get("disabled"))
           if resolved_platform:
               platform_disabled = (skills_cfg.get("platform_disabled") or {}).get(
                   resolved_platform
               )
               if platform_disabled is not None:
      -            return _normalize_string_set(platform_disabled)
      -    return _normalize_string_set(skills_cfg.get("disabled"))
      +            return global_disabled | _normalize_string_set(platform_disabled)
      +    return global_disabled
       
       
       def _normalize_string_set(values) -> Set[str]:
    • hermes_cli/skills_config.py
      @@ -25,7 +25,13 @@
       # ─── Config Helpers ───────────────────────────────────────────────────────────
       
       def get_disabled_skills(config: dict, platform: Optional[str] = None) -> Set[str]:
      -    """Return disabled skill names. Platform-specific list falls back to global."""
      +    """Return disabled skill names: the global list unioned with the
      +    platform-specific list when a platform is given.
      +
      +    A globally-disabled skill stays disabled on every platform, so the
      +    platform list adds to the global list rather than replacing it. This
      +    mirrors ``agent.skill_utils.get_disabled_skill_names``.
      +    """
           skills_cfg = config.get("skills", {})
           global_disabled = set(skills_cfg.get("disabled", []))
           if platform is None:
      @@ -33,7 +39,7 @@ def get_disabled_skills(config: dict, platform: Optional[str] = None) -> Set[str
           platform_disabled = cfg_get(skills_cfg, "platform_disabled", platform)
           if platform_disabled is None:
               return global_disabled
      -    return set(platform_disabled)
      +    return global_disabled | set(platform_disabled)
       
       
       def save_disabled_skills(config: dict, disabled: Set[str], platform: Optional[str] = None):
    • tests/hermes_cli/test_skills_config.py
      @@ -22,7 +22,19 @@ def test_reads_platform_disabled(self):
                   "disabled": ["skill-a"],
                   "platform_disabled": {"telegram": ["skill-b"]}
               }}
      -        assert get_disabled_skills(config, platform="telegram") == {"skill-b"}
      +        # Union of global + platform: a globally-disabled skill stays disabled
      +        # on every platform, and the platform list adds to it.
      +        assert get_disabled_skills(config, platform="telegram") == {"skill-a", "skill-b"}
      +
      +    def test_platform_list_unions_with_global(self):
      +        from hermes_cli.skills_config import get_disabled_skills
      +        config = {"skills": {
      +            "disabled": ["global-skill"],
      +            "platform_disabled": {"telegram": []}
      +        }}
      +        # An explicit empty platform list does NOT re-enable a globally-disabled
      +        # skill (matches issue #46201 — global disables hold everywhere).
      +        assert get_disabled_skills(config, platform="telegram") == {"global-skill"}
       
           def test_platform_falls_back_to_global(self):
               from hermes_cli.skills_config import get_disabled_skills
      @@ -102,14 +114,27 @@ def test_platform_disabled(self, mock_load):
               assert _is_skill_disabled("tg-skill", platform="telegram") is True
       
           @patch("hermes_cli.config.load_config")
      -    def test_platform_enabled_overrides_global(self, mock_load):
      +    def test_globally_disabled_stays_disabled_on_platform(self, mock_load):
      +        mock_load.return_value = {"skills": {
      +            "disabled": ["skill-a"],
      +            "platform_disabled": {"telegram": ["tg-skill"]}
      +        }}
      +        from tools.skills_tool import _is_skill_disabled
      +        # Union: a globally-disabled skill stays disabled on a platform that
      +        # has its own platform_disabled list (matches issue #46201).
      +        assert _is_skill_disabled("skill-a", platform="telegram") is True
      +        assert _is_skill_disabled("tg-skill", platform="telegram") is True
      +
      +    @patch("hermes_cli.config.load_config")
      +    def test_empty_platform_list_keeps_global_disabled(self, mock_load):
               mock_load.return_value = {"skills": {
                   "disabled": ["skill-a"],
                   "platform_disabled": {"telegram": []}
               }}
               from tools.skills_tool import _is_skill_disabled
      -        # telegram has explicit empty list -> skill-a is NOT disabled for telegram
      -        assert _is_skill_disabled("skill-a", platform="telegram") is False
      +        # An explicit empty platform list does NOT re-enable a globally-disabled
      +        # skill — global disables hold on every platform.
      +        assert _is_skill_disabled("skill-a", platform="telegram") is True
       
           @patch("hermes_cli.config.load_config")
           def test_platform_falls_back_to_global(self, mock_load):
      @@ -164,7 +189,7 @@ def test_explicit_platform_param(self, tmp_path, monkeypatch):
       
               from agent.skill_utils import get_disabled_skill_names
               result = get_disabled_skill_names(platform="telegram")
      -        assert result == {"tg-only-skill"}
      +        assert result == {"tg-only-skill", "global-skill"}
       
           def test_session_platform_env_var(self, tmp_path, monkeypatch):
               """HERMES_SESSION_PLATFORM should be used when HERMES_PLATFORM is unset."""
      @@ -183,7 +208,7 @@ def test_session_platform_env_var(self, tmp_path, monkeypatch):
       
               from agent.skill_utils import get_disabled_skill_names
               result = get_disabled_skill_names()
      -        assert result == {"discord-skill"}
      +        assert result == {"discord-skill", "global-skill"}
       
           def test_hermes_platform_takes_precedence(self, tmp_path, monkeypatch):
               """HERMES_PLATFORM should win over HERMES_SESSION_PLATFORM."""
    • tools/skills_tool.py
      @@ -583,11 +583,15 @@ def _is_skill_disabled(name: str, platform: str = None) -> bool:
               config = load_config()
               skills_cfg = config.get("skills", {})
               resolved_platform = platform or os.getenv("HERMES_PLATFORM") or _get_session_platform()
      +        global_disabled = skills_cfg.get("disabled", [])
               if resolved_platform:
                   platform_disabled = cfg_get(skills_cfg, "platform_disabled", resolved_platform)
                   if platform_disabled is not None:
      -                return name in platform_disabled
      -        return name in skills_cfg.get("disabled", [])
      +                # A globally-disabled skill stays disabled on every platform;
      +                # the platform list adds to it rather than replacing it. Keep
      +                # in sync with agent.skill_utils.get_disabled_skill_names.
      +                return name in platform_disabled or name in global_disabled
      +        return name in global_disabled
           except Exception:
               return False
       

2 PRs closed without merging

From the close comments: done elsewhere 2

  • #46203 fix: platform-disabled skills still appear in <available_skills> prompt (#46201) · done elsewhere → 7bbe702, #46201, #46236
  • #46220 fix(skills): filter platform-disabled skills from prompt (#46201) · done elsewhere → 7bbe702, ce19fdb, #46236
Closed issues · 1
  • Closed#46201[Bug]: Disabled skills still appear in <available_skills> prompt and trigger false loads
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.50: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
skills
Merged PRs
#46236
Closed issues
1
Attempts
2 PRs closed without merging
Fix link checked
14 Jun 2026

For your agent

The same facts, typed.