Skip to content

fix(skills): follow symlinks when discovering SKILL.md#12624

Closed
yaooqinn wants to merge 1 commit into
NousResearch:mainfrom
yaooqinn:fix/skill-discovery-follow-symlinks
Closed

fix(skills): follow symlinks when discovering SKILL.md#12624
yaooqinn wants to merge 1 commit into
NousResearch:mainfrom
yaooqinn:fix/skill-discovery-follow-symlinks

Conversation

@yaooqinn

@yaooqinn yaooqinn commented Apr 19, 2026

Copy link
Copy Markdown

Summary

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 to:

  • /<skill> slash command registration (agent/skill_commands.py)
  • skill_view, skill_manage, skills_list tools (tools/skills_tool.py, tools/skill_manager_tool.py)
  • Gateway unavailable-skill resolver (gateway/run.py)
  • Optional skills hub (tools/skills_hub.py)
  • Bundled-skills sync (tools/skills_sync.py)
  • CLI /dump and hermes profiles skill 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

  • Add hermes_constants.rglob_follow(directory, pattern) — a thin wrapper around os.walk(followlinks=True) + fnmatch that behaves like Path.rglob but traverses symlinked directories.
  • Replace the 8 .rglob("SKILL.md") call sites in skill-discovery paths with rglob_follow(...).
  • Switch the os.walk(...) in agent/skill_utils.iter_skill_index_files to followlinks=True for 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 rglob are untouched.

Test Plan

  • New TestRglobFollow class in tests/test_hermes_constants.py covers:
    • Regular subdirectory traversal (baseline)
    • Symlinked directory traversal (the bug) — the key assertion
    • Glob pattern matching (*.md)
    • Empty-result behaviour
    • Gracefully pytest.skips on platforms where the test cannot create symlinks (Windows without dev mode)
  • pytest tests/test_hermes_constants.py -v passes all cases (new + existing).

Notes

  • No behaviour change for users without symlinked skills — os.walk(followlinks=True) produces the same file list as Path.rglob for trees with no symlinks.
  • EXCLUDED_SKILL_DIRS / .git / .github / .hub filtering is preserved at every call site, same as before.
  • Symlink cycles are not explicitly guarded against — os.walk itself does not detect them, but in practice skill directories don't form cycles and existing EXCLUDED_SKILL_DIRS filtering avoids the common cases.

@yaooqinn yaooqinn force-pushed the fix/skill-discovery-follow-symlinks branch from fd8e3d4 to 6d03f3b Compare April 19, 2026 17:00
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).
@yaooqinn yaooqinn force-pushed the fix/skill-discovery-follow-symlinks branch from 6d03f3b to 49ee376 Compare April 19, 2026 17:02
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/skills Skills system (list, view, manage) labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to existing PRs #9253, #7634, #8769 all addressing symlink traversal in skill discovery. This PR is the most comprehensive (covers all 8 call sites).

@yaooqinn

Copy link
Copy Markdown
Author

Thank you @alt-glitch for the review

@redpiggy-cyber

Copy link
Copy Markdown
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.

@yaooqinn

Copy link
Copy Markdown
Author

Closing — superseded by other work / no longer pursuing this fix on this branch.

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

Labels

P2 Medium — degraded but workaround exists 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.

3 participants