Skip to content

fix(skills): follow symlinks with cycle guard across all skill discovery paths#7634

Open
graysonzeng wants to merge 1 commit into
NousResearch:mainfrom
graysonzeng:fix/skill-scan-follow-symlinks
Open

fix(skills): follow symlinks with cycle guard across all skill discovery paths#7634
graysonzeng wants to merge 1 commit into
NousResearch:mainfrom
graysonzeng:fix/skill-scan-follow-symlinks

Conversation

@graysonzeng

@graysonzeng graysonzeng commented Apr 11, 2026

Copy link
Copy Markdown

Summary

Skills installed via directory symlink (e.g. ln -s /path/to/skill ~/.hermes/skills/my-skill) are silently ignored because Python's pathlib.Path.rglob() does not follow symlinked directories by default. This affects all skill discovery paths — not just slash command registration but also skills list, skills categories, skill_view fallback search, skill_manager operations, and gateway unavailable-skill hints.

Root Cause

Path.rglob() skips symlinked directories during traversal (documented Python behavior). When a skill directory itself is a symlink, rglob("SKILL.md") never enters it, so the skill is invisible to the entire system.

Fix

Introduce a single shared walk_skill_files() function in agent/skill_utils.py that:

  1. Uses os.walk(followlinks=True) to traverse into symlinked directories
  2. Maintains a (st_dev, st_ino) visited set to detect and break symlink cycles
  3. Excludes .git, .github, .hub directories (consistent with existing behavior)

Then replace all rglob("SKILL.md") calls across the skill discovery surface:

File Function Change
agent/skill_utils.py iter_skill_index_files Delegates to walk_skill_files
agent/skill_commands.py scan_skill_commands rglobwalk_skill_files
tools/skills_tool.py _find_all_skills rglobwalk_skill_files
tools/skills_tool.py skills_categories rglobwalk_skill_files
tools/skills_tool.py skill_view (name search) rglobwalk_skill_files
tools/skill_manager_tool.py _find_skill rglobwalk_skill_files
gateway/run.py _check_unavailable_skill rglobwalk_skill_files
hermes_cli/profiles.py _count_skills rglobwalk_skill_files

Not changed (repo-internal dirs, no user symlinks expected):

  • tools/skills_hub.py — scans optional-skills/ bundled in repo
  • tools/skills_sync.py — scans bundled skills
  • hermes_cli/dump.py — export utility

Reproduction

# Create a symlinked skill
ln -s /path/to/my-skill ~/.hermes/skills/my-skill

# Before fix: skill invisible everywhere
# After fix: skill discovered in all paths (slash commands, list, categories, etc.)

Cycle Safety

A symlink cycle (e.g. skill-dir/loop -> ~/.hermes/skills/) would cause os.walk(followlinks=True) to recurse infinitely. The walk_skill_files function tracks visited directories by (st_dev, st_ino) and prunes already-seen directories, preventing infinite loops.

Test Plan

Two new tests added to tests/agent/test_skill_commands.py:

  • test_finds_symlinked_skill — symlinked skill directory is discovered by scan_skill_commands()
  • test_symlink_cycle_does_not_hang — directory cycle does not cause infinite recursion

Existing 26 tests continue to pass.

Known Limitation

Symlinked skills whose resolved path falls outside ~/.hermes/skills/ will trigger a security warning log ("skill file is outside the trusted skills directory"). This is cosmetic and does not affect functionality. Addressing this is out of scope for this PR.

@graysonzeng graysonzeng force-pushed the fix/skill-scan-follow-symlinks branch from e18682e to 9971ddd Compare April 11, 2026 09:05
@graysonzeng graysonzeng changed the title fix(skills): follow symlinks when scanning skill directories fix(skills): follow symlinks with cycle guard across all skill discovery paths Apr 11, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Likely duplicate of #12624 — same fix: os.walk(followlinks=True) with cycle guard for symlinked skill directories.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants