Repository navigation
Discover skills under grouping directories, bounded and symlink-safe - #99
Merged
Merged
Conversation
DetectSkills looked only at the immediate subdirectories, so a catalog laid out as skills/<product>/<skill>/ reported "no skills found" and had to be validated one product at a time (#77). The agentskills.io client implementation guide tells scanners to walk 4-6 levels and stop at 2000 directories. Add skillcheck.DiscoverSkills(root, maxDepth), which searches breadth-first to a depth bound (default 6) and returns a Discovery: the mode, the sorted skill directories, each one's depth below the root, and whether the scan hit the directory budget. DetectSkills keeps its signature and calls it with the default depth. Symlinks are still followed, since a skills directory assembled from links to folders elsewhere is a supported layout, but each real directory is visited once, keyed by its resolved path, so cycles and aliases neither loop nor report a skill twice. A skill is detected the moment its parent is listed and the budget counts listings, so a wide or linked sibling tree cannot use up the budget before a real skill is found. A skill's own subtree is not searched, hidden directories are skipped, and so are node_modules and __pycache__. The CLI gains a persistent --max-depth flag, prints a warning when the budget was hit, and names the depth in the "no skills found" error. The README states that only the given path is searched: the guide's home-directory and .agents/skills scanning is an agent's concern, not a validator's, and .gitignore is not consulted. Fixes #77
There was a problem hiding this comment.
🟡 Changes recommended
Relative library roots can cause symlink aliases to report the same skill twice.
1 open finding
What changed in this PR
Adds bounded discovery of skills under grouping directories, addressing #77 while preserving the DetectSkills API.
Changes:
- Adds breadth-first discovery with depth limits, a listing budget, and symlink deduplication.
- Exposes
--max-depthand truncation warnings through the CLI. - Documents discovery rules and adds regression coverage.
| File | Description |
|---|---|
| skillcheck/validator.go | Implements bounded nested skill discovery. |
| skillcheck/validator_test.go | Covers depth, exclusions, budgets, and symlinks. |
| README.md | Documents discovery behavior and limits. |
| cmd/root.go | Adds depth configuration and discovery diagnostics. |
| cmd/exitcode_integration_test.go | Tests CLI depth handling and errors. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
With a relative root, an ordinary child's identity key was built from the root's relative resolved path, while a symlink to an absolute target resolved to an absolute key. The same directory got two keys, so a skill and its alias were both reported. The CLI passes an absolute root and never hit this; library callers of DetectSkills with a relative path did. Make the root absolute before resolving it, and resolve symlinked children through the parent's resolved path, so every key is absolute. Reported paths stay as the caller gave them. Add a regression test with a relative root and an absolute-target alias.
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.

Fixes #77.
What was wrong
DetectSkillslooked only at the immediate subdirectories of the given path. A catalog laid out asskills/<product>/<skill>/(the NVIDIA layout from the issue) reported "no skills found" and had to be validated one product directory at a time. The agentskills.io client implementation guide tells scanners to walk 4-6 levels and stop at 2000 directories.What changed
skillcheck.DiscoverSkills(root, maxDepth)searches breadth-first to a depth bound (default 6,skillcheck.DefaultMaxDepth) and returns aDiscovery: the mode, the sorted skill directories, each one's depth below the root, and aTruncatedflag set when the scan hit the 2000-listing budget.DetectSkillskeeps its signature and calls it with the default depth, so library callers are unaffected.loop -> .,up -> ..) and aliases neither loop nor report a skill twice. Per-skill containment inutil.SafeReadFileis unchanged.SKILL.mdinside a skill's examples or references is not a second skill. Hidden directories,node_modules, and__pycache__are skipped..gitignoreis not consulted; the budget already bounds the cost of scanning build output.--max-depthflag on every command (0searches the given path only,1is the previous behavior), a stderr warning when the budget was hit, and the depth named in the "no skills found" error..agents/skillsscanning is an agent's session-start concern, not a validator's.The
Depthsfield is there so a later platform-compatibility check can say that a skill found at depth 2 will not be listed by a harness that reads direct children only.Verification
Tests cover the two-level layout, mixed depths, the depth boundary on both sides, no descent into a skill, hidden and dependency directories, budget truncation, cycle to root, cycle to parent, alias dedupe, a link to a skill outside the root, broken and file links, and the starvation case. A CLI test exercises
--max-depthend to end. Symlink tests skip on Windows as the existing ones do.Against fixtures with a self-loop, a parent loop, a link to a 2500-directory tree, and a link to
/, the build reports each skill once, keeps the skill behind the big link, and finishes the link-to-root case in under a second.go test -race ./...andgolangci-lint runare clean.No CHANGELOG entry, following the release-commit pattern.