Files
hermes-agent/tests/hermes_cli/test_banner_skills.py
Teknium b70dd51cfa fix: disabled skills respected across banner, system prompt, slash commands, and skill_view (#1897)
* fix: banner skill count now respects disabled skills and platform filtering

The banner's get_available_skills() was doing a raw rglob scan of
~/.hermes/skills/ without checking:
- Whether skills are disabled (skills.disabled config)
- Whether skills match the current platform (platforms: frontmatter)

This caused the banner to show inflated skill counts (e.g. '100 skills'
when many are disabled) and list macOS-only skills on Linux.

Fix: delegate to _find_all_skills() from tools/skills_tool which already
handles both platform gating and disabled-skill filtering.

* fix: system prompt and slash commands now respect disabled skills

Two more places where disabled skills were still surfaced:

1. build_skills_system_prompt() in prompt_builder.py — disabled skills
   appeared in the <available_skills> system prompt section, causing
   the agent to suggest/load them despite being disabled.

2. scan_skill_commands() in skill_commands.py — disabled skills still
   registered as /skill-name slash commands in CLI help and could be
   invoked.

Both now load _get_disabled_skill_names() and filter accordingly.

* fix: skill_view blocks disabled skills

skill_view() checked platform compatibility but not disabled state,
so the agent could still load and read disabled skills directly.

Now returns a clear error when a disabled skill is requested, telling
the user to enable it via hermes skills or inspect the files manually.

---------

Co-authored-by: Test <test@test.com>
2026-03-18 03:17:37 -07:00

69 lines
2.6 KiB
Python

"""Tests for banner get_available_skills() — disabled and platform filtering."""
from unittest.mock import patch
import pytest
_MOCK_SKILLS = [
{"name": "skill-a", "description": "A skill", "category": "tools"},
{"name": "skill-b", "description": "B skill", "category": "tools"},
{"name": "skill-c", "description": "C skill", "category": "creative"},
]
def test_get_available_skills_delegates_to_find_all_skills():
"""get_available_skills should call _find_all_skills (which handles filtering)."""
with patch("tools.skills_tool._find_all_skills", return_value=list(_MOCK_SKILLS)):
from hermes_cli.banner import get_available_skills
result = get_available_skills()
assert "tools" in result
assert "creative" in result
assert sorted(result["tools"]) == ["skill-a", "skill-b"]
assert result["creative"] == ["skill-c"]
def test_get_available_skills_excludes_disabled():
"""Disabled skills should not appear in the banner count."""
# _find_all_skills already filters disabled skills, so if we give it
# a filtered list, get_available_skills should reflect that.
filtered = [s for s in _MOCK_SKILLS if s["name"] != "skill-b"]
with patch("tools.skills_tool._find_all_skills", return_value=filtered):
from hermes_cli.banner import get_available_skills
result = get_available_skills()
all_names = [n for names in result.values() for n in names]
assert "skill-b" not in all_names
assert "skill-a" in all_names
assert len(all_names) == 2
def test_get_available_skills_empty_when_no_skills():
"""No skills installed returns empty dict."""
with patch("tools.skills_tool._find_all_skills", return_value=[]):
from hermes_cli.banner import get_available_skills
result = get_available_skills()
assert result == {}
def test_get_available_skills_handles_import_failure():
"""If _find_all_skills import fails, return empty dict gracefully."""
with patch("tools.skills_tool._find_all_skills", side_effect=ImportError("boom")):
from hermes_cli.banner import get_available_skills
result = get_available_skills()
assert result == {}
def test_get_available_skills_null_category_becomes_general():
"""Skills with None category should be grouped under 'general'."""
skills = [{"name": "orphan-skill", "description": "No cat", "category": None}]
with patch("tools.skills_tool._find_all_skills", return_value=skills):
from hermes_cli.banner import get_available_skills
result = get_available_skills()
assert "general" in result
assert result["general"] == ["orphan-skill"]