fix(skills): follow symlinks when discovering SKILL.md#12624
Closed
yaooqinn wants to merge 1 commit into
Closed
Conversation
fd8e3d4 to
6d03f3b
Compare
Path.rglob() does not follow directory symlinks (confirmed on Python
3.11 and 3.12; 3.13 adds an opt-in recurse_symlinks=True parameter
but the default is still false). Every skill-discovery code path in
hermes-agent uses .rglob("SKILL.md"), so any skill whose directory
is a symlink — at any depth, including directly under the scan root —
is silently invisible. As a result:
- /<skill> slash commands are not registered
- skill_view, skill_manage, and skills_list can't find them
- The gateway, CLI dump, and profile skill counts all under-report
This affects setups that symlink skill directories into a scan root
(e.g. sharing a single on-disk skill across multiple agent frameworks,
or organising skills via symlinked category folders).
Add a small helper rglob_follow(directory, pattern) in hermes_constants
that wraps os.walk(followlinks=True) + fnmatch, and replace the eight
.rglob("SKILL.md") call sites across skill discovery with it. Also
switch the os.walk() in agent/skill_utils.iter_skill_index_files to
followlinks=True.
Tests (tests/test_hermes_constants.py::TestRglobFollow) cover the
symlinked-directory case, glob matching, and the empty-result path,
with a graceful skip on platforms where creating symlinks is
restricted (e.g. Windows without dev mode).
6d03f3b to
49ee376
Compare
Collaborator
Author
|
Thank you @alt-glitch for the review |
This was referenced Apr 23, 2026
8 tasks
Contributor
|
+1 on this PR. We hit the same issue (symlinked skill directories invisible to skill_manage/skill_view) and independently wrote a similar fix before discovering this PR. Our local version uses the same os.walk(followlinks=True) approach and has been working well. Would love to see this merged — the symlink pattern (skills/ -> workspace/skills/) is a natural setup for users managing skills across multiple agent frameworks. |
Author
|
Closing — superseded by other work / no longer pursuing this fix on this branch. |
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
Path.rglob()does not follow directory symlinks (confirmed on Python 3.11 and 3.12; 3.13 adds an opt-inrecurse_symlinks=Trueparameter but the default is still false). Every skill-discovery code path in hermes-agent uses.rglob("SKILL.md"), so any skill whose directory is a symlink — at any depth, including directly under the scan root — is silently invisible to:/<skill>slash command registration (agent/skill_commands.py)skill_view,skill_manage,skills_listtools (tools/skills_tool.py,tools/skill_manager_tool.py)gateway/run.py)tools/skills_hub.py)tools/skills_sync.py)/dumpandhermes profilesskill counts (hermes_cli/dump.py,hermes_cli/profiles.py)This affects any setup that symlinks skill directories into a scan root — e.g. sharing a single on-disk skill across multiple agent frameworks, or organising skills via symlinked category folders.
Fix
hermes_constants.rglob_follow(directory, pattern)— a thin wrapper aroundos.walk(followlinks=True)+fnmatchthat behaves likePath.rglobbut traverses symlinked directories..rglob("SKILL.md")call sites in skill-discovery paths withrglob_follow(...).os.walk(...)inagent/skill_utils.iter_skill_index_filestofollowlinks=Truefor the same reason.The helper is narrowly scoped and only used where skill discovery needs to cross symlink boundaries — non-skill code paths that use
rglobare untouched.Test Plan
TestRglobFollowclass intests/test_hermes_constants.pycovers:*.md)pytest.skips on platforms where the test cannot create symlinks (Windows without dev mode)pytest tests/test_hermes_constants.py -vpasses all cases (new + existing).Notes
os.walk(followlinks=True)produces the same file list asPath.rglobfor trees with no symlinks.EXCLUDED_SKILL_DIRS/.git/.github/.hubfiltering is preserved at every call site, same as before.os.walkitself does not detect them, but in practice skill directories don't form cycles and existingEXCLUDED_SKILL_DIRSfiltering avoids the common cases.