fix(skills): follow symlinks with cycle guard across all skill discovery paths#7634
Open
graysonzeng wants to merge 1 commit into
Open
fix(skills): follow symlinks with cycle guard across all skill discovery paths#7634graysonzeng wants to merge 1 commit into
graysonzeng wants to merge 1 commit into
Conversation
e18682e to
9971ddd
Compare
9971ddd to
e1a8540
Compare
This was referenced Apr 22, 2026
Collaborator
|
Likely duplicate of #12624 — same fix: os.walk(followlinks=True) with cycle guard for symlinked skill directories. |
This was referenced May 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Skills installed via directory symlink (e.g.
ln -s /path/to/skill ~/.hermes/skills/my-skill) are silently ignored because Python'spathlib.Path.rglob()does not follow symlinked directories by default. This affects all skill discovery paths — not just slash command registration but alsoskills list,skills categories,skill_viewfallback search,skill_manageroperations, 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 inagent/skill_utils.pythat:os.walk(followlinks=True)to traverse into symlinked directories(st_dev, st_ino)visited set to detect and break symlink cycles.git,.github,.hubdirectories (consistent with existing behavior)Then replace all
rglob("SKILL.md")calls across the skill discovery surface:agent/skill_utils.pyiter_skill_index_fileswalk_skill_filesagent/skill_commands.pyscan_skill_commandsrglob→walk_skill_filestools/skills_tool.py_find_all_skillsrglob→walk_skill_filestools/skills_tool.pyskills_categoriesrglob→walk_skill_filestools/skills_tool.pyskill_view(name search)rglob→walk_skill_filestools/skill_manager_tool.py_find_skillrglob→walk_skill_filesgateway/run.py_check_unavailable_skillrglob→walk_skill_fileshermes_cli/profiles.py_count_skillsrglob→walk_skill_filesNot changed (repo-internal dirs, no user symlinks expected):
tools/skills_hub.py— scansoptional-skills/bundled in repotools/skills_sync.py— scans bundled skillshermes_cli/dump.py— export utilityReproduction
Cycle Safety
A symlink cycle (e.g.
skill-dir/loop -> ~/.hermes/skills/) would causeos.walk(followlinks=True)to recurse infinitely. Thewalk_skill_filesfunction 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 byscan_skill_commands()test_symlink_cycle_does_not_hang— directory cycle does not cause infinite recursionExisting 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.