DEV Community

Zhengxin
Zhengxin

Posted on

Claude Code Skills Deep Dive (3): From a Directory to Claude's Candidate List

TL;DR: Before a Skill can reach Claude's candidate list, Claude Code must discover its directory, combine sources, resolve names, and apply path and invocation visibility rules. Personal, project, enterprise, and plugin locations represent different ownership models, not merely four installation paths. Nested Skills and paths narrow “available in this project” to “relevant to the files being worked on.”

Part 2: From Description to Full Instructions followed one Skill through three loading stages: metadata for discovery, full instructions on activation, and supporting resources when needed.

But where does that metadata come from? Claude Code does not search every SKILL.md on your computer. First, it must answer a more basic question:

Which Skills are eligible to be candidates in a session started from this working directory?

Discovery has three gates

Creating a Skill on disk does not mean Claude can use it. At least three decisions stand between the file and execution:

Filesystem discovery
  Does Claude Code load Skills from this location?
        ↓
Candidate exposure
  May this Skill appear among Claude's available capabilities?
        ↓
Task matching and activation
  Should this request load its full instructions?
Enter fullscreen mode Exit fullscreen mode

People often call all three “automatic discovery.” That hides distinct failures:

  • Put the file in the wrong location, and the first gate never opens.
  • Make a Skill user-invocable only, and the file may exist without being advertised to the model for automatic selection.
  • Write a description that does not match the task, and Claude may know the Skill exists but not select it.
  • Apply a path condition that does not match the current files, and a project Skill is not relevant yet.

When a Skill fails to trigger, do not repeatedly rewrite its description until you have confirmed that it actually entered the candidate set.

Four scopes, four ownership relationships

The Claude Code Skills documentation identifies four principal distribution sources:

Scope Typical location Maintainer Reach
Enterprise / managed Managed settings directory, .claude/skills/<name>/SKILL.md Administrator Users whose machines receive the managed configuration
Personal ~/.claude/skills/<name>/SKILL.md Individual user That user's local projects
Project .claude/skills/<name>/SKILL.md Project team Sessions in the repository
Plugin <plugin>/skills/<name>/SKILL.md Plugin author Environments where the plugin is enabled

These may look like four paths, but they encode four different kinds of ownership.

Personal: my general way of working

Personal Skills fit reusable practices that cross projects without requiring everyone on the team to install them: a private review routine, a note-organizing workflow, a generic commit-message helper, or a wrapper around tools available on your machine.

They follow the user, not the repository. A teammate who clones the repository does not automatically get them.

Project: a practice owned by this repository

Project Skills can be versioned with the code: a release checklist, a repository-specific migration procedure, an internal architecture-document generator, or a test workflow tailored to the project.

This scope says that people working on the project may need the capability. Committing it also makes changes reviewable alongside code.

Enterprise: an organization-provided capability

Managed Skills suit centrally deployed processes, knowledge, and governance requirements. The author and user may be different: a user can invoke a Skill without being able to edit its source.

This is about organizational consistency, not personal reuse. Central distribution of instructions, however, should not be mistaken for enforcement; permissions and execution boundaries are separate concerns.

Plugin: a capability in an installable component

A plugin can distribute Skills with supporting scripts, assets, subagents, hooks, MCP server configuration, and other extension content. Its Skills use the plugin namespace, such as /plugin-name:skill-name, rather than competing for a bare name with personal or project Skills.

The first question when choosing a scope is who owns this capability?, not which folder is easiest to write to.

Scope is not synchronization

A personal Skill being available in local projects does not mean its files automatically reach another machine or Claude surface. Likewise:

  • A project Skill travels with Git only if it is committed.
  • A plugin Skill travels through plugin installation and versioning.
  • A local Claude Code Skill does not automatically become a Claude API or claude.ai Skill.
  • A cloud session cannot simply read your machine's ~/.claude/skills/.

Claude Code now also supports Skills synced from a claude.ai account in eligible sessions, but that is a separate sync mechanism, not a property of every local personal Skill. Scope answers who should see a capability in one environment; synchronization answers how its files get there.

Project discovery does not stop at the current directory

In a monorepo, reading only the current directory's .claude/skills/ would be surprising. If a session starts in repo/apps/web/, should the root-level release Skill disappear? Claude Code searches the start directory and its parents up to the repository root:

repo/apps/web/        ← session starts here
      ↑ parent
repo/apps/
      ↑ parent
repo/                 ← repository root
└── .claude/skills/release-check/
Enter fullscreen mode Exit fullscreen mode

The shared root capability remains available regardless of which package you start in. Upward discovery solves inheritance, but monorepos also need the opposite: a package may have its own local Skills.

Nested Skills appear with the subdirectory

Suppose the frontend package has a distinct deployment procedure:

repo/
├── .claude/skills/deploy/SKILL.md
└── apps/web/
    ├── .claude/skills/deploy/SKILL.md
    └── src/
Enter fullscreen mode Exit fullscreen mode

The root deploy is the project default; the one under apps/web is a frontend variant. Recursively scanning the entire monorepo at startup could flood the candidate list with Skills from unrelated packages, examples, and dependency trees.

Instead, Claude Code discovers nested .claude/skills/ directories as work moves into their file trees:

Working only in repo/backend/
  → the apps/web Skill need not appear

Working in repo/apps/web/src/
  → apps/web/.claude/skills/ becomes relevant
Enter fullscreen mode Exit fullscreen mode

A nested Skill is not simply “a higher-priority project-wide setting.” It is a local capability whose relevance follows the work path.

Name collisions are not always simple overrides

For enterprise, personal, and project Skills with the same name, the documented precedence is:

Enterprise > Personal > Project
Enter fullscreen mode Exit fullscreen mode

Nested variants are a different case. Both the root deploy and the apps/web variant can be useful. Silently replacing one with whichever is closest would discard information. Claude Code keeps both: /deploy can identify the root Skill, while a qualified name such as /apps/web:deploy explicitly identifies the nested one.

This preserves three useful distinctions:

  • The bare name remains the main project entry point.
  • The qualified name identifies the directory that owns the variant.
  • Claude can consider which files it is actually working on.

Sometimes a same-named Skill is not an accidental conflict but a package-specific supplement to a global process. Plugin Skills avoid this particular collision by using /plugin-name:skill-name from the start.

paths: relevance without a duplicate Skill folder

Not every path-sensitive capability needs its own nested directory. An API review Skill might be owned at the repository root but become automatically relevant only for interface files:

---
name: api-review
description: Reviews this project's API design and compatibility
paths:
  - "services/api/**"
  - "packages/contracts/**"
---
Enter fullscreen mode Exit fullscreen mode

paths separates ownership from activation:

  • Ownership: The project maintains one shared Skill.
  • Automatic relevance: Claude loads it when working with files that match the patterns.
Mechanism Best fit
Nested Skill A subdirectory owns an independent capability or local variant
Root Skill with paths The project owns one capability that applies only to specific files

paths is not a filesystem permission or sandbox. It governs automatic activation, not which files Claude may read. Treating a relevance filter as a security boundary would overstate what frontmatter can enforce.

--add-dir: a separate route into discovery

--add-dir gives Claude access to another directory, and Claude Code also watches that directory's .claude/skills/ for Skills. This is useful when shared capabilities live outside the product repository:

product-repo/

shared-engineering/
└── .claude/skills/
    ├── incident-review/
    └── architecture-check/
Enter fullscreen mode Exit fullscreen mode

Add shared-engineering when starting the session, and its Skills become available. Do not generalize that into “all Claude configuration in an added directory loads the same way.” The documentation distinguishes Skills from commands, agents, and other configuration, and behavior can depend on settings. This is a Skill-discovery rule, not ordinary file access alone.

Live detection: the list can change during a session

Claude Code watches Skill locations it has included in discovery. Adding a Skill, editing SKILL.md, or removing a Skill can update the candidate list during the session. A caveat: if a top-level Skills directory did not exist when monitoring began and is created only later, a restart may be needed.

Live detection updates the disk definition and candidate listing. It does not rewind a conversation to replace instructions already loaded into context. The source file, candidate metadata, and active instructions are three different states; the later lifecycle article explores this further.

Symlinks: shared source without duplicate capabilities

Enterprise, personal, and project Skill folders can be symlinks to directories elsewhere on disk. Claude Code resolves the target and avoids loading the same Skill twice merely because multiple locations point to it. Plugin symlinks follow their own distribution rules.

For authors, symlinks are convenient during personal development or when maintaining one local source. For stable team distribution, a plugin or an explicitly versioned project Skill is generally easier to review.

The candidate list is not a disk listing

After all these steps, not every Skill that exists on disk is exposed to Claude in the same way:

  • A user-only Skill does not need a description to prompt model invocation.
  • A path-constrained Skill may not match the files currently being worked on.
  • A nested Skill may not have been discovered yet.
  • Visibility settings or runtime modes may hide a capability.
  • Precedence, namespaces, and qualified names may reorganize overlapping sources.

The candidate list is a runtime view of capabilities for this session:

Skill definitions on disk
        ↓ scope / parent / nested / added directory
Discoverable set
        ↓ precedence / namespace / path / visibility
Current candidate list
        ↓ description and user request
Activated Skill
Enter fullscreen mode Exit fullscreen mode

Separating these stages reveals whether a failure lies in installation, scope, exposure, or task matching. Skill discovery is not just find SKILL.md: scope determines ownership, parent and nested lookup determine inheritance, namespaces let variants coexist, and paths determine relevance. Only after a Skill reaches the list can its description perform the routing discussed in Part 2.

Up next

Once the candidate list exists, we can revisit the Tool-like selection behavior. Do /release-check from a user and Claude's own choice of release-check follow the same path? Part 4 will separate manual invocation, model invocation, bundled Skills, and built-in commands.

References

Top comments (0)