[FEATURE] Recursive skill discovery - scan subdirectories in ~/.claude/skills/
Summary
Currently, Claude Code only scans the top-level of ~/.claude/skills/ for skill directories containing SKILL.md files. Skills organized in subdirectories (e.g., ~/.claude/skills/spec-system/spec-creator/) are not discovered automatically.
Problem
When organizing related skills into logical groupings (suites), users must manually create symlinks at the top level for each nested skill:
~/.claude/skills/
├── spec-system/ ← Container directory
│ ├── spec-creator/SKILL.md ← Not discovered!
│ ├── spec-executor/SKILL.md ← Not discovered!
│ └── spec-archiver/SKILL.md ← Not discovered!
├── spec-creator → spec-system/spec-creator ← Manual symlink required
├── spec-executor → spec-system/spec-executor ← Manual symlink required
└── spec-archiver → spec-system/spec-archiver ← Manual symlink required
Without symlinks, calling Skill("spec-creator") fails with "Unknown skill".
Proposed Solution
Recursively scan ~/.claude/skills/ (and .claude/skills/) for directories containing SKILL.md files, regardless of nesting depth.
Option A: Full Recursive Scan
Scan all subdirectories up to a reasonable depth (e.g., 3 levels).
Option B: Configurable Depth
Add a setting like skills.scanDepth: 2 to control how deep to scan.
Option C: Explicit Suite Marker
Only recurse into directories containing a marker file (e.g., SKILL_SUITE.md or .skill-suite).
Benefits
- Better organization - Group related skills (spec-system, gsd-toolkit, etc.)
- No manual symlink maintenance - Skills just work when placed in subdirectories
- Cleaner top-level - Reduce clutter in
~/.claude/skills/ - Suite versioning - Version control entire skill suites as units
Current Workaround
Users must:
- Create symlinks manually:
ln -s spec-system/spec-creator spec-creator - Maintain a validation script to check for missing symlinks
- Remember to create symlinks when adding new nested skills
Environment
- Claude Code version: Latest (Jan 2026)
- Platform: All (Linux, macOS, Windows)
- Skills location:
~/.claude/skills/
Related
This is separate from the "nested skills" issues about skills calling other skills (#17351, #17339) - this is purely about directory structure and discovery.
37 Comments
+1 for this feature. We have a specific use case that makes subdirectory support particularly valuable: Team-wide shared config via symlinks
We maintain a company config repo with shared skills, commands, and rules. Each developer symlinks it into their ~/.claude/:
ln -sfn /path/to/config-repo/skills ~/.claude/skills/acme
Inconsistency with commands: This pattern works perfectly for commands—~/.claude/commands/acme/init-agent.md becomes /acme:init-agent. But skills in ~/.claude/skills/acme/my-skill/SKILL.md aren't discovered.
Our workaround: We now iterate and create individual symlinks with a prefix:
for skill_dir in "$repo/skills"/*/; do
ln -sfn "$skill_dir" ~/.claude/skills/acme-"$(basename "$skill_dir")"
done
Recursive discovery (Option A) would let us use a single symlink like we do for commands.
Fix is coming in 2.1.10 today
@wolffiex it doesn't look like this is working still. https://github.com/anthropics/claude-code/issues/10238#issuecomment-3754882855 -- These issues could likely be merged
I just checked, I can confirm it does not work
Workaround for skill collections:
Until recursive discovery is implemented, here's a script to symlink all skills from a cloned repo:
Usage:
./sync-skills.sh ~/my-skills-repoThis lets you
git pullto update and re-run the script for new skills. Not ideal, but works.+1 for native recursive discovery!
would be even better if we can have dynamic loading of skills upon start working on subdirectories.
Strange , version 2.1.6 changelog already declares that it should be enabled - but even i 2.1.45 i still see no efffect
Still not working. This is so bad.
The nested skills feature is intended for subdirectories in e.g. a monorepo where you have like
./.claude/skillsand./sub-module/.claude/skillsdirectories inside a skill directory are not and have not been supported. what's the use case?
@wolffiex think of global skills, not even project skills, you want to asemble it from different sources. So let's say we create ~/.agents directory ( codex, opencode, cascade already support it ) Now we want to add , personal, work skills . Or even like this we have own personal skills - but for sake of readability we want to structure them properly , like ( tools, integrations, cognitive skills ) and so on. In both cases it's a folder with skills with subfolders..
@wolffiex you can see more in details at https://github.com/agentskills/agentskills/issues/59
Based on Boris response, you supposed to support it it is just a bug.
I have this exact layout. I'm running v2.1.63 and checking skills using
/skills. Interestingly the skills in subdirectories are not eagerly loaded, but do become visible after some progressive directory exploration. If I ask claude to read a random non-skill file insubdirthen it discovers and loads the skills.I tried setting
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1as a means to force more eager loading, but it didn't alter behavior.@jameskoch this is the intended behavior. it's to save context in environments where different modules in the same repo may be used independently. this is also how CLAUDE.md files work
Ok, thanks for the clarification. @ThePlenkov and @yordis I understand this feature request. It's not what I meant by "nested skills" but it's totally valid. Is this part of the emerging skills spec? Do other clients support it?
@wolffiex It is tricky. The reason I created that issue in the agentskills itself is that the spec is not precise enough, so they are diverging in implementation. So, no?!
Which, personally, I do not care what the ultimate solution is, but ideally, all tools behave the same so we can adjust accordingly.
Claude is broken for me, while Cursor isn't broken. Ideally, I would appreciate being allowed to use subnested scanning since a lot of symlinking, personal stuff, and so on live in that directory without having to deal with any plugin system. The feedback loop and maintenance are too slow; those skills are changing as I go.
The use case is better organization. Now we have to have a flat directory structure with skill prefixes as categories, which doesn't make sense.
/skills/design/skill1/
...
/skills/dev/skill1/
...
I had to create another skill with own scripts which updates claude skills with flat structure using symlinks =)
@wolffiex
To add to @yordis 's point about Cursor - I can confirm it supports subfolder discovery. You can symlink multiple skill repos as subdirectories and they're discovered without flattening.
Re: the plugin system as an alternative path - it doesn't solve this either. Plugins cache copies to ~/.claude/plugins/cache/, which breaks the edit→test→commit loop. There's no plugin link for local
development, so you can't point Claude at a working git directory.
The feedback loop issue @yordis mentioned is real. Skills evolve as you use them. The current options are:
Neither supports iterating on skills across multiple source repos cleanly.
+1 from me.
I’d also like
.claude/skills/to support recursive discovery so skills can be organised by category instead of forcing everything into a flat first-level structure.e.g., I’d like to organise related skills like this:
That feels much more natural for larger skill libraries.
A recursive scan that recognises any directory containing
SKILL.mdunder a skills root would make skill organisation much cleaner while keeping existing flat layouts compatible 🤷Current behavior
The skill directory scanner does a single
readdir()and checks each immediate child for aSKILL.md. No recursion.How the loader works
5 skill sources, loaded in priority order:
/Library/Application Support/ClaudeCode/skills/(macOS)~/.claude/skills/.claude/skills/walked upward from cwd to filesystem root--add-dirarguments, each gets.claude/skills/scannedcommands/directories from pluginsAll 5 feed through the same directory scanner function:
The skill name is set to
entry.name(the immediate directory name). After collection, skills are deduplicated by SHA-256 of file content, then split into unconditional (immediately active) and conditional (activated when matching files are touched, via thepaths:frontmatter field).What needs to change
One function — the directory scanner. Same signature
(dir: string, source: string) → Promise<{ skill, filePath }[]>.Make it recursive, but stop descending into a directory once a
SKILL.mdis found there —SKILL.mdacts as a leaf marker (same pattern as.gitorpackage.json):The skill name changes from
entry.nametopath.relative(root, dir)with path separators replaced by:— solint/elixir/SKILL.mdbecomeslint:elixir. This is already the convention for namespaced skills (plugins use it), so flat layouts produce the same single-segment names as today.The dynamic rediscovery codepath also calls the same scanner, so it benefits automatically.
What this enables
Reference implementation
Drop-in replacement for the current scanner — same signature, same return type:
Edge cases to consider
relative()returns just the dir nameSKILL.mdat skills root itself — name falls back tobasename(dir)SKILL.md+ subdirs with their ownSKILL.md— only the parent is discovered (leaf boundary stops descent)isSymbolicLink()check), would work the same in nested dirs@wolffiex I expend some time trying to dig out as much context as possible from the existing compilation, it sounds something the AI would one-shot very easy, please, help us here 🙏🏻 this is the only frustration I have with
claudeat the moment@wolffiex Can you please look into this? Please?
Adding a data point confirming this is still broken on CC 2.1.104 (post the 2.1.10 fix). Verified via a minimal harness:
Launching CC at
parent/:/foo-test→ "unknown slash command"sub-a/first): still not foundsub-a/): ✅ works as expectedReproduces in both interactive and
--printmodes. The phrasing in the skills doc ("Automatic discovery from nested directories") is not accurate in practice today — also tracked in #40640.Genuine question for maintainers: is this lower-priority because the intended path for sharing skills across multiple repos (monorepos, or side-by-side repos opened from a parent dir) is meant to be plugins +
enabledPluginsrather than nested.claude/skills/? The other parts of the CC config model seem to support that interpretation:.mcp.jsonwalks up ancestors (not nested)CLAUDE.mdnests on-demand...which would position subdir
.claude/skills/as a single-project convenience rather than a first-class multi-repo primitive. If that's the design direction, it'd help to:If we're misreading the design and this just needs more time on the roadmap, that'd also be useful to know. Our team is building tooling across two repos and trying to pick the right abstraction to invest in.
+1 on recursive discovery. Sharing a real-world setup where this would be the missing piece, plus a concrete two-track proposal that's non-breaking and, I think, trivial to implement.
Current workaround: parent skill + opaque workflow files
I maintain a multi-workflow skill (
crm:pm) that routes via a table inSKILL.md:SKILL.mdcontains:This works, but the workflow files are invisible to Claude's skill discovery. The only way to reach them is via the parent skill's routing table, which requires the user to phrase the request in a way that matches an entry. Natural-language invocation (
"help me plan the day") doesn't trigger them directly.Proposal — two separable tracks
Track 1: Generalized recursive discovery (primary ask)
Rule: walk any subfolder of a
skills/root until aSKILL.mdis found. ThatSKILL.mdis the skill; don't descend further once matched (preserves skill isolation).Key points:
workflows/convention — the folder name is irrelevant. Any depth, any naming.skills/pm/SKILL.md) still work unchanged. Sibling-flat layouts (skills/auto/SKILL.md,skills/cuts/SKILL.md) still work. Nothing breaks.skills/roots are; just recurse until hittingSKILL.md.Example after Track 1 — same directory tree as above but each workflow is now a folder with its own
SKILL.md:Both invocation paths now work:
description:, so Claude triggers it naturally on"help me plan the day"without the parent being invoked.SKILL.mdkeeps its table with@workflows/daily-planning/SKILL.mdreferences for when the user wants the CRM-specific preamble (context-loading rules, entity format, shared CLI conventions) loaded first.Slash-command naming: reflect the path with colons, e.g.
/crm:pm:daily-planning. Keeps namespacing unambiguous and matches the current/<plugin>:<skill>convention by extension.Frontmatter conflicts between parent and child: child wins. Parent is context, child is the active skill being invoked.
Track 2 (bonus, separable): Auto-attach ancestor
SKILL.mdfiles on the pathWhen a nested child skill is invoked, the harness automatically prepends every
SKILL.mdon the path from theskills/root down to the child into the system prompt.This gives progressive disclosure for free — the shared preamble (auth, conventions, CRM entity format,
pm graphqlusage) is loaded without needing@references in each childSKILL.md. DRY by construction.Why separable from Track 1: Track 1 is a trivial discovery-code change. Track 2 introduces token-cost and ordering questions (below) that could legitimately take longer to design. Shipping Track 1 alone is already hugely valuable — users can opt into Track 2's effect manually via
@../SKILL.mdreferences until it lands natively.Open questions (Track 2)
skills/pluginA/skills/pluginB/child/SKILL.md(or something exotic), shouldpluginA's SKILL.md be attached? My intuition: discovery should never cross plugin boundaries; ancestors are only attached within the same plugin.Please fix the nested-invocation bugs at the same time
If recursive discovery ships (and especially if Track 2 lands), the existing bugs around skill-calls-skill should be addressed in parallel, or hierarchical invocation stays fragile:
TL;DR
Two non-breaking tracks, ship independently:
skills/<plugin>/until hittingSKILL.md; folder names arbitrary. Slash command reflects path (/crm:pm:daily-planning). Existing skills keep working.SKILL.mdon the path into the system prompt. Progressive disclosure without manual@plumbing.Child wins on frontmatter conflicts. Cross-plugin traversal: probably no.
I want this feature so that I can have global symlinks to different source control repositories:
~/.claude/skills/personal -> $REPOS/personal-skills
~/.claude/skills/work -> $REPOS/skills-that-shouldnt-leave-the-corp-environment
I don't want to have to create per-skill symlinks and update them when creating or deleting a skill.
It's unbelievable to observe how much behind other agents claude stands today in terms of unified agents dev experience being stuck with such straightforward requests as this one, or chat history lost.. I can only explain this that they are trying possibly to keep staying a trendmaker and focusing on new features no matter what's in backlog.. But community demand should be also respected guys..
FWIW, my (old) commands are treated as skills in my case.
/company:time-trackingis actually a command, but was automatically loaded and used as a skill for the promptcreate a time tracking entry for yesterday.It resides in a subdirectory btw.
I gave a +1 for this feature back in january, but much has change since and I'd argue this should be closed.
Custom Marketplaces and plugins are much better ways to achieve the use cases that would need recursive skill discovery. Since they both can be local only (don't need to distributed via GitHub), I think there is no reason for skill sub-dirs anymore.
I don't think these address my use case (from above):
Are you saying "marketplace" would solve this problem?
@wolffiex hi Adam, would you mind sharing what the team is internally thinking about this feature, please?
@wolffiex Can we please get some update on this? Please.
+1
+1
@eins78 you don't need to do that. I mean it works but a better solution IMO is to use Vercel's skills CLI and point it at a GitHub repo with a
skills/directory.Highly need this feature. We embed skills in the product repos. Don't need extra plumbing just to distribute them.
Would be very useful together with e.g. agentdeps, which puts its skills into
.agents/skills/_agentdeps_managed/.+1