fix(skills): ignore support docs in skill discovery

Support files under references/, templates/, assets/, and scripts/ are progressive-disclosure data loaded through skill_view(..., file_path=...). They should not be treated as standalone skills during discovery or collision checks.

This prevents archived skill packages or support markdown files inside a real skill from shadowing active skills with the same name while still allowing top-level categories named scripts/templates/assets/references.

Tests cover:
- pruning nested SKILL.md files inside skill support directories
- preserving support-named top-level categories
- avoiding skill_view collisions from support markdown
- keeping archived package SKILL.md files accessible only through file_path
This commit is contained in:
Wolfram Ravenwolf
2026-06-16 13:08:34 -07:00
committed by Teknium
parent 7493de7fc3
commit 9137b86a52
4 changed files with 211 additions and 15 deletions
+28 -6
View File
@@ -79,7 +79,10 @@ from typing import Dict, Any, List, Optional, Set, Tuple
from tools.registry import registry, tool_error
from hermes_cli.config import cfg_get
from utils import env_var_enabled
from agent.skill_utils import EXCLUDED_SKILL_DIRS as _EXCLUDED_SKILL_DIRS
from agent.skill_utils import (
EXCLUDED_SKILL_DIRS as _EXCLUDED_SKILL_DIRS,
is_skill_support_path as _is_skill_support_path,
)
logger = logging.getLogger(__name__)
@@ -1020,9 +1023,15 @@ def skill_view(
# Strategy 1: direct path (e.g., "mlops/axolotl" or bare "axolotl"
# at the top of the dir).
direct_path = search_dir / name
if direct_path.is_dir() and (direct_path / "SKILL.md").exists():
if (
not _is_skill_support_path(direct_path)
and direct_path.is_dir()
and (direct_path / "SKILL.md").exists()
):
_record(direct_path, direct_path / "SKILL.md")
elif direct_path.with_suffix(".md").exists():
elif direct_path.with_suffix(".md").exists() and not _is_skill_support_path(
direct_path.with_suffix(".md")
):
_record(None, direct_path.with_suffix(".md"))
# Strategy 1b: categorized form for plugin namespace fall-through
@@ -1030,9 +1039,17 @@ def skill_view(
# tries the on-disk path "myplugin/explore").
if local_category_name:
categorized_path = search_dir / local_category_name
if categorized_path.is_dir() and (categorized_path / "SKILL.md").exists():
if (
not _is_skill_support_path(categorized_path)
and categorized_path.is_dir()
and (categorized_path / "SKILL.md").exists()
):
_record(categorized_path, categorized_path / "SKILL.md")
elif categorized_path.with_suffix(".md").exists():
elif categorized_path.with_suffix(
".md"
).exists() and not _is_skill_support_path(
categorized_path.with_suffix(".md")
):
_record(None, categorized_path.with_suffix(".md"))
# Strategy 2: recursive by directory name (catches nested skills
@@ -1053,8 +1070,13 @@ def skill_view(
_record(found_skill_md.parent, found_skill_md)
# Strategy 3: legacy flat <name>.md files anywhere under the dir.
# Exclude skill support docs: references/templates/assets/scripts
# are loaded through skill_view(skill, file_path=...) and must not
# shadow or collide with real skills that share the same basename.
for found_md in search_dir.rglob(f"{name}.md"):
if found_md.name != "SKILL.md":
if found_md.name != "SKILL.md" and not _is_skill_support_path(
found_md
):
_record(None, found_md)
if len(candidates) > 1: