Skip to content

fix(skills): use symlink-following traversal in _find_skill - #75225

Open
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/skill-find-symlink
Open

fix(skills): use symlink-following traversal in _find_skill#75225
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/skill-find-symlink

Conversation

@JonthanaHanh

Copy link
Copy Markdown
Contributor

Fixes #75130

rglob("SKILL.md") does not descend into directory symlinks, making symlinked skill directories invisible to skill_manage operations (create, patch, delete) while the prompt loader (which uses os.walk(followlinks=True)) still sees them.

The reporter measured: rglob found 393 skills, os.walk(followlinks=True) found 399 -- 6 symlinked skills were invisible to the tool.

Changes

Replace rglob("SKILL.md") with iter_skill_index_files(skills_dir, "SKILL.md") in both:

  • _find_skill() (line 657) -- primary skill lookup used by all skill_manage actions
  • _find_skill_in_other_profiles() (line 796) -- cross-profile lookup for error messages

iter_skill_index_files already uses os.walk(followlinks=True) and is the canonical skill enumeration path used by the prompt builder and sync subsystem. This makes the tool's visibility match what the model sees.

Known remaining sites

The same rglob("SKILL.md") pattern exists in ~25 other files (skills_sync.py, skills_hub.py, skill_usage.py, profiles.py, gateway/run.py, etc.). These affect skill counting, sync, and usage tracking but not the core skill-manage tool path. A follow-up PR could migrate those to iter_skill_index_files for full consistency.

Test plan

  • python -m pytest tests/tools/test_skill_manager_tool.py -xvs passes
  • Skills installed via symlink are now found by skill_manage

rglob('SKILL.md') does not descend into directory symlinks, making
symlinked skill directories invisible to skill_manage operations
(create, patch, delete) while the prompt loader (which uses
os.walk(followlinks=True)) still sees them.

Replace rglob with iter_skill_index_files in both _find_skill and
_find_skill_in_other_profiles. iter_skill_index_files already uses
os.walk(followlinks=True) and is the canonical skill enumeration
path used by the prompt builder and sync subsystem.

Fixes NousResearch#75130
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for aligning the skill-manager lookup with the established symlink-following discovery path. Current main still uses rglob("SKILL.md") in _find_skill() (tools/skill_manager_tool.py:657) and _find_skill_in_other_profiles() (:796), while prompt discovery uses iter_skill_index_files() (agent/prompt_builder.py:1669), whose walk follows links (agent/skill_utils.py:880).

Problems

  • Please add a skill_manage regression test. Existing symlink coverage proves slash-command discovery (tests/agent/test_skill_commands.py:61), but does not exercise _find_skill() or a write through the manager.
  • The PR description should not imply that deletion is enabled. _delete_skill() still calls _validate_delete_target() (tools/skill_manager_tool.py:1219), which explicitly refuses a symlinked skill directory (:234-239). That safety behavior should remain.

Suggested changes

  • Add a test that patches a SKILL.md reached through a directory symlink under the skills root.
  • Narrow the description to lookup/non-delete mutations, or explicitly document the retained delete refusal.

Automated hermes-sweeper review.

@alt-glitch alt-glitch added type/bug Something isn't working tool/skills Skills system (list, view, manage) comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have labels Jul 31, 2026
@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 31, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

34 PRs address or reference this issue complex, but none implements #75130's queue cap, TTL, notification, stale-anchor detection, or conflict handling. Most diffs concern external write roots or symlink-aware skill discovery; #75225 addresses only the measured manager-visibility contributor by replacing two residual rglob lookups with the shared iterator.

Related pull requests

Duplicates

Reader-discovery chain: #8357, #8769, #9253, #11847, #12624, #14476, and #14668 converge on merged #14230/#14740. Manager-lookup chain: #35213, #35244, #35299, #44210, #54200, #56420, #56423, #60960, and #75225 overlap #67748, while creation-root #21812/#22236/#23562 overlaps the broader #8177 design.

Suggested consolidation

Author action on #75225: add a platform-safe regression that patches SKILL.md through a directory symlink, narrow the claimed scope to non-delete mutations, and retain _validate_delete_target's symlink refusal; it remains only a partial mitigation for #75130, whose queue lifecycle needs a separate fix. Keep #67748 and #8177 open on their distinct salvage paths, close the listed manager-lookup and creation-root competitors as duplicates, and do not reopen the already implemented or salvaged closed PRs.

Complex graph

flowchart TD
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I35184(["issue #35184 (open)"])
    I75130(["issue #75130 (open)"])
    subgraph Dup35213 ["PRs duplicating each other"]
        P35213["PR #35213 (open)"]
        P35244["PR #35244 (open)"]
        P44210["PR #44210 (open)"]
        P54200["PR #54200 (closed)"]
        P56420["PR #56420 (closed)"]
        P56423["PR #56423 (open)"]
        P60960["PR #60960 (open)"]
        P67748["PR #67748 (open)"]
        P75225["PR #75225 (open)"]
    end
    P75225 -->|fixes| I35184
    P75225 -->|best fix| I75130
    class I35184 open
    class I75130 open
    class P35213 open
    class P35244 open
    class P44210 open
    class P54200 closed
    class P56420 closed
    class P56423 open
    class P60960 open
    class P67748 open
    class P75225 open
    class P67748 best
    class P67748 best
    class P67748 best
    class P67748 best
    class P75225 best
    class P75225 target
    click I35184 "https://github.com/NousResearch/hermes-agent/issues/35184"
    click I75130 "https://github.com/NousResearch/hermes-agent/issues/75130"
    click P35213 "https://github.com/NousResearch/hermes-agent/pull/35213"
    click P35244 "https://github.com/NousResearch/hermes-agent/pull/35244"
    click P44210 "https://github.com/NousResearch/hermes-agent/pull/44210"
    click P54200 "https://github.com/NousResearch/hermes-agent/pull/54200"
    click P56420 "https://github.com/NousResearch/hermes-agent/pull/56420"
    click P56423 "https://github.com/NousResearch/hermes-agent/pull/56423"
    click P60960 "https://github.com/NousResearch/hermes-agent/pull/60960"
    click P67748 "https://github.com/NousResearch/hermes-agent/pull/67748"
    click P75225 "https://github.com/NousResearch/hermes-agent/pull/75225"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 34 pull requests and 8 issues in this complex. Each diff was read against this issue; Assessment working set: 435 kB of PR diffs, 101 kB of issue/PR text, 46 kB of discussion (78 comments), 65 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

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

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data 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.

Pending skill-proposal queue grows unbounded and self-invalidates when skills.write_approval is enabled (357 in 8 days, 21% dead)

5 participants