hermes - ✅(Solved) Fix _find_skill rglob leaks into .archive, .git, .github, .hub — no exclusion filtering [4 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#18900Fetched 2026-05-03 04:53:41
View on GitHub
Comments
1
Participants
2
Timeline
10
Reactions
0
Timeline (top)
cross-referenced ×4labeled ×4commented ×1referenced ×1

_find_skill in tools/skill_manager_tool.py uses bare rglob("SKILL.md") with no exclusion filtering. This is inconsistent with iter_skill_index_files (agent/skill_utils.py) which correctly filters EXCLUDED_SKILL_DIRS = {.git, .github, .hub, .archive}.

Root Cause

_find_skill in tools/skill_manager_tool.py uses bare rglob("SKILL.md") with no exclusion filtering. This is inconsistent with iter_skill_index_files (agent/skill_utils.py) which correctly filters EXCLUDED_SKILL_DIRS = {.git, .github, .hub, .archive}.

Fix Action

Fix

Import EXCLUDED_SKILL_DIRS from agent.skill_utils and add a guard in the rglob loop:

if any(p in EXCLUDED_SKILL_DIRS for p in skill_md.parent.parts):
    continue

PR fix notes

PR #18897: fix: exclude .archive/.git/.github/.hub from _find_skill rglob

Description (problem / solution / changelog)

What does this PR do?

_find_skill in tools/skill_manager_tool.py uses bare rglob("SKILL.md") with no exclusion filtering — unlike iter_skill_index_files which filters EXCLUDED_SKILL_DIRS = {.git, .github, .hub, .archive}. This allows skill_manage's five mutating actions (edit, patch, delete, write_file, remove_file) to silently match and modify archived skills or skills inside version-control directories.

Fix: import EXCLUDED_SKILL_DIRS from agent.skill_utils and skip any SKILL.md whose parent directory chain crosses an excluded dir.

Related Issue

Fixes #18900

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • tools/skill_manager_tool.py: Import EXCLUDED_SKILL_DIRS from agent.skill_utils; add exclusion guard in _find_skill rglob loop
  • tests/tools/test_skill_manager_tool.py: Add TestFindSkillExcludesArchiveAndVcs with 7 tests

How to Test

  1. Create skill dirs inside .archive/, .git/, .github/, .hub/ under a temp skills root
  2. Call _find_skill(name) — returns None for all excluded dirs
  3. Call _find_skill(name) for a regular skill — still returns correct path

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature
  • I've run pytest tests/ -q and all tests pass (4595 passed, failures pre-existing)
  • I've added tests for my changes
  • I've tested on my platform: macOS 15.2

Documentation & Housekeeping

  • I've updated relevant documentation — or N/A (updated docstring)
  • I've updated cli-config.yaml.example — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md — or N/A
  • I've considered cross-platform impact — or N/A
  • I've updated tool descriptions/schemas — or N/A

Changed files

  • tests/tools/test_skill_manager_tool.py (modified, +61/-0)
  • tools/skill_manager_tool.py (modified, +9/-1)

PR #18902: Fix: exclude .archive, .git, .github, .hub dirs from _find_skill rglob

Description (problem / solution / changelog)

Fix: Exclude .archive, .git, .github, .hub from _find_skill rglob

Fixes #18900

Problem

_find_skill in tools/skill_manager_tool.py uses a bare rglob("SKILL.md") with no exclusion filtering. This is inconsistent with iter_skill_index_files (agent/skill_utils.py) which correctly filters EXCLUDED_SKILL_DIRS = {.git, .github, .hub, .archive}.

All five skill_manage mutating actions (edit, patch, delete, write_file, remove_file) call _find_skill to locate the skill directory. Since _find_skill has no exclusion filter, it can match skills inside .archive/, .git/, .github/, or .hub/ — silently operating on archived or version-control copies instead of the live skill.

Fix

Imports EXCLUDED_SKILL_DIRS from agent.skill_utils and adds a guard in the rglob loop:

if any(part in EXCLUDED_SKILL_DIRS for part in skill_md.parent.parts):
    continue

This makes _find_skill consistent with the exclusion behavior already used by iter_skill_index_files, skill_view, and skills_list.

Reproduction

  1. Archive a skill foo.archive/foo/SKILL.md
  2. Agent calls skill_manage(name="foo", action="patch", old_string="...", new_string="...")
  3. _find_skill("foo") matches .archive/foo/SKILL.md via skill_md.parent.name == "foo" and modifies the archived copy

Testing

  • Verified the import resolves correctly (EXCLUDED_SKILL_DIRS is a frozenset at agent/skill_utils.py:27)
  • Path parts check works for nested paths (e.g., ~/.hermes/skills/.archive/foo/SKILL.md → parent parts include .archive)

Changed files

  • tools/skill_manager_tool.py (modified, +3/-1)

PR #18937: fix(skills): exclude hidden dirs from skill search, guard cyclic symlinks, handle non-dict quick_commands

Description (problem / solution / changelog)

Fixes #18900, #18809, #18816

#18900 -- _find_skill rglob leaks into .archive, .git, .github, .hub Replace bare rglob in _find_skill with iter_skill_index_files which already excludes those dirs.

#18809 -- os.walk(followlinks=True) infinite-loop on cyclic symlinks Add seen-inode tracking to iter_skill_index_files. If a directory inode pair has already been visited the subtree is pruned. Non-cyclic symlinks are still followed.

#18816 -- quick_commands with non-dict values crashes slash command dispatch Add isinstance(qcmd, dict) guard in both gateway/run.py and cli.py before calling .get(). Misconfigured entries return a clear error message instead of crashing with AttributeError.

14 new tests in tests/test_skill_rglob_and_quick_commands.py, all passing.

Changed files

  • agent/skill_utils.py (modified, +17/-0)
  • cli.py (modified, +6/-0)
  • gateway/run.py (modified, +5/-0)
  • tests/test_skill_rglob_and_quick_commands.py (added, +139/-0)
  • tools/skill_manager_tool.py (modified, +7/-2)

PR #19062: fix: exclude hidden and archive dirs from _find_skill rglob

Description (problem / solution / changelog)

Summary

_find_skill() in tools/skill_manager_tool.py uses a bare rglob("SKILL.md") across skill directories without filtering out hidden or archive paths. This means skill mutations can accidentally match entries inside .git/, .github/, .hub/, or .archive/ directories.

Every other skill-scanning function in the codebase (tools/skills_tool.py, agent/skill_utils.py) already applies EXCLUDED_SKILL_DIRS filtering. This PR adds the same guard to _find_skill.

Changes

  • Import EXCLUDED_SKILL_DIRS alongside get_all_skills_dirs in _find_skill()
  • Add a path-parts exclusion check in the rglob loop, matching the pattern used in tools/skills_tool.py:576

Testing

  • All 85 tests in tests/tools/test_skill_manager_tool.py pass
  • All 36 tests in tests/agent/test_skill_commands.py pass
  • py_compile and ruff check clean (no new warnings)

Closes #18900

Changed files

  • tools/skill_manager_tool.py (modified, +3/-1)

Code Example

if any(p in EXCLUDED_SKILL_DIRS for p in skill_md.parent.parts):
    continue
RAW_BUFFERClick to expand / collapse

Summary

_find_skill in tools/skill_manager_tool.py uses bare rglob("SKILL.md") with no exclusion filtering. This is inconsistent with iter_skill_index_files (agent/skill_utils.py) which correctly filters EXCLUDED_SKILL_DIRS = {.git, .github, .hub, .archive}.

Impact

All five skill_manage mutating actions (edit, patch, delete, write_file, remove_file) call _find_skill to locate the skill directory. Since _find_skill has no exclusion filter, it can match skills inside .archive/, .git/, .github/, or .hub/ — silently operating on archived or version-control copies instead of the live skill.

The prompt builder, skill_view, and skills_list all correctly exclude these directories (they use iter_skill_index_files). So archived skills are invisible to the agent through normal discovery — but the skill_manage write path provides a backdoor where the agent can accidentally mutate the archive.

Steps to Reproduce

  1. Archive a skill foo.archive/foo/SKILL.md
  2. Agent calls skill_manage(name="foo", action="patch", old_string="...", new_string="...")
  3. _find_skill("foo") matches .archive/foo/SKILL.md via skill_md.parent.name == "foo" and modifies the archived copy

Expected Behavior

_find_skill should skip .archive, .git, .github, and .hub directories — matching the exclusion set used by iter_skill_index_files.

Fix

Import EXCLUDED_SKILL_DIRS from agent.skill_utils and add a guard in the rglob loop:

if any(p in EXCLUDED_SKILL_DIRS for p in skill_md.parent.parts):
    continue

extent analysis

TL;DR

The most likely fix is to modify the _find_skill function in tools/skill_manager_tool.py to exclude specific directories by importing EXCLUDED_SKILL_DIRS from agent/skill_utils.py and adding a conditional check.

Guidance

  • Import EXCLUDED_SKILL_DIRS from agent/skill_utils.py into tools/skill_manager_tool.py to ensure consistency in exclusion filtering.
  • Add a guard in the rglob loop of _find_skill to skip directories listed in EXCLUDED_SKILL_DIRS.
  • Verify the fix by attempting to modify an archived skill using skill_manage and confirming that it no longer operates on the archived copy.
  • Review other parts of the codebase that use rglob or similar directory traversal functions to ensure they also apply the necessary exclusion filters.

Example

from agent.skill_utils import EXCLUDED_SKILL_DIRS

# Within the _find_skill function
for skill_md in rglob("SKILL.md"):
    if any(p in EXCLUDED_SKILL_DIRS for p in skill_md.parent.parts):
        continue
    # Rest of the function logic

Notes

This fix assumes that the EXCLUDED_SKILL_DIRS set is correctly defined and up-to-date. If new directories need to be excluded in the future, they should be added to this set.

Recommendation

Apply the workaround by modifying the _find_skill function as described, to prevent accidental modification of archived skills through the skill_manage actions.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING