Installing a Claude Code skill is, on paper, a one-line operation: drop a SKILL.md into ~/.claude/skills/<slug>/, restart, done. In practice there's a small but real surface area of gotchas — silent YAML parse failures, project-local vs global precedence rules, mid-session reload behavior that surprises people, and a handful of "why isn't Claude picking this up?" failure modes that all look the same on the surface.
This is the guide I wish I'd had after the third or fourth time I installed a skill and couldn't figure out why Claude wasn't using it. We'll walk through how auto-discovery actually works under the hood, the three install methods you'll encounter, the difference between project-local and global skills (with the precedence rule that catches teams off-guard), and a troubleshooting section for the failure modes that bite in practice.
Claude Code's skill loader is mechanically simple, and understanding it cleans up most of the confusion downstream. At the start of a session, Claude Code scans ~/.claude/skills/ (and the project-local equivalent — more on that in a moment), reads the SKILL.md file in each subdirectory, parses the YAML frontmatter at the top, and registers two fields into its internal skill index: name and description.
That's it. The body of the SKILL.md — the actual instructions Claude follows when invoking the skill — is not loaded into context at startup. It's only read when Claude decides to invoke that specific skill. This is why descriptions matter so much: that one-line description: field is the entire signal Claude uses to decide whether your skill is relevant to the current conversation. If it doesn't describe the trigger conditions clearly, your skill won't fire.
The discovery scan happens once per session, at startup. This is the single most important thing to remember:
There's no /reload-skills command. Some people have asked for one, and it might land eventually, but the current contract is: restart on every change. The good news is that Claude Code's startup is fast enough (sub-second on most machines) that this isn't actually a real friction once you internalize it.
You can verify what Claude has actually loaded with the /skills slash command inside a session. It lists every registered skill by name and description. If you installed something and it's not in /skills, the loader didn't see it — almost always a path problem, a YAML problem, or a forgot-to-restart problem.
Internally Claude maintains the skill index as a name → description map, with the file path cached alongside. When a skill fires, Claude reads the body of the SKILL.md fresh from disk. This means if you edit the body of a SKILL.md mid-session (not the frontmatter), the changes do apply on the next invocation — the body is re-read every time. It's only the frontmatter (name/description) that's frozen at startup. This is a useful detail for iterating on skill instructions without restart cycles.
The canonical install flow is three shell commands. Every skill on the catalog has a stable URL at https://claudskills.com/skills/<slug>/SKILL.md that returns the raw markdown.
mkdir -p ~/.claude/skills/<slug>
curl -fsSL https://claudskills.com/skills/<slug>/SKILL.md \
-o ~/.claude/skills/<slug>/SKILL.md
# restart Claude CodeA few notes on the flags. -f makes curl exit non-zero on HTTP errors (otherwise a 404 gets silently written into the file as HTML and Claude Code chokes on the YAML parse). -s suppresses progress meters. -L follows redirects, which matters because the catalog serves SKILL.md files through a CDN rewrite. And -o with an explicit path avoids the curl habit of dumping the file in your current working directory.
If you're installing several skills at once, just loop:
for slug in conventional-comments code-review-rubric pr-summarizer; do
mkdir -p ~/.claude/skills/$slug
curl -fsSL https://claudskills.com/skills/$slug/SKILL.md \
-o ~/.claude/skills/$slug/SKILL.md
doneThe directory name should match the slug, but Claude Code actually doesn't care — the loader keys off the name: field in the frontmatter, not the directory name. That said, mismatched names create confusion when you're grepping later. Keep them aligned.
After restart, two checks confirm everything worked:
/skills in a Claude Code session. Your new skill should appear by name with its description.If /skills shows it but Claude isn't invoking it for relevant queries, the description is probably too narrow or too generic. See Writing a SKILL.md File for how to dial that in. If /skills doesn't show it at all, jump to the troubleshooting section below.
Some skills are part of larger repositories with multiple files (helper scripts, reference documents, example configs). For those, a git clone into ~/.claude/skills/<slug>/ is the right move — Claude Code will still find the SKILL.md at the top level and the supporting files become available as references in the skill body. The catalog's individual SKILL.md endpoint is for the common case where the skill is self-contained in a single file.
If you're installing more than a handful of skills, the curl loop gets old fast. The desktop app at claudskills.com/install/ handles the file-write and restart-prompt for you, plus it keeps track of what you've installed so you can see deltas the next time a skill updates.
The flow is straightforward: browse the catalog inside the app, click Install on any skill, the app writes the SKILL.md to ~/.claude/skills/<slug>/SKILL.md using the same path convention as the manual flow, then prompts you to restart Claude Code. There's no proprietary install format — what the app produces on disk is byte-identical to what you'd get from curl. You can mix and match: install some skills via the app, others via curl, and Claude Code treats them all the same.
The app's main practical advantage besides the click reduction is the Pro Collections feature — bundled installs where one click drops 10-20 thematically related skills at once. If you're setting up a new machine and want, say, the full code-review toolkit or the data-engineering bundle, that's the fastest path. The bundle is just a list of slugs; under the hood it's the same per-skill install flow run in a loop.
A few non-obvious details:
rm -rf on the skill's directory. If you'd customized the SKILL.md after installing it (added a project-specific note, tweaked the description), those edits are gone. Back up first if that matters.~/.claude/skills/ under version control (some people do, to sync skill libraries across machines), the app's writes show up as normal file changes you can commit or revert.The app is free for browsing and installing individual skills. The catalog itself — every SKILL.md, every category, every search — is fully usable without the app via curl and the website. The app is convenience, not gatekeeping.
A growing pattern is shipping multiple related skills as a single plugin — a directory tree rooted at .claude-plugin/ with a plugin.json manifest and a skills/ subdirectory containing one folder per skill. Plugins exist because some toolkits are genuinely multi-skill — a code-review plugin might ship five complementary skills that share helper scripts and reference docs.
The manifest at .claude-plugin/plugin.json looks roughly like this:
{
"name": "service-graph",
"version": "1.2.0",
"description": "B2B service provider lookup toolkit",
"author": "nostrband",
"skills": [
"find-law-firm",
"find-cpa-firm",
"find-marketing-agency"
]
}The skills array enumerates which subdirectories under skills/ are actual SKILL.md-bearing skills. The plugin manager uses this for install/uninstall accounting — it knows which directories to create and which to clean up when you remove the plugin.
Plugin install lands the entire .claude-plugin/ tree at ~/.claude/plugins/<plugin-name>/, and Claude Code's skill loader knows to also scan ~/.claude/plugins/*/skills/ in addition to ~/.claude/skills/. From Claude's perspective, plugin-bundled skills and standalone skills are equivalent at runtime — same discovery, same invocation, same precedence rules.
If you cloned a plugin repo manually:
git clone https://github.com/<author>/<plugin-name>.git \
~/.claude/plugins/<plugin-name>Restart, and Claude Code picks up every skill enumerated in the manifest.
If you're authoring something and trying to decide, the rough rule: plugin if the skills genuinely share dependencies (helper modules, reference data, shared prompts) or if they're cohesive enough that users will always want them together. Individual skills if they're independent and a user might want one without the others. Plugins are heavier to install and update; individual skills are simpler to mix and match. Most things should be individual skills.
Worth flagging: the plugin ecosystem is newer than the skill ecosystem, and not every tool that lists skills is plugin-aware yet. The ClaudSkills catalog indexes plugin-bundled skills individually (each appears as its own catalog entry), which works fine but doesn't currently surface the plugin grouping in the UI. If you're maintaining a plugin, list it by its individual skill slugs in any documentation pointing at the catalog.
Claude Code looks in two places for skills: ~/.claude/skills/ (your global toolkit, available in every session) and ./.claude/skills/ at the root of your current project (available only when you're working in that project). Both follow the same SKILL.md convention.
The precedence rule, and this is the one that catches teams off-guard: project-local wins on name collision. If your global toolkit has a skill called code-review and the project you're working in also has a .claude/skills/code-review/, the project version takes precedence for that session. Claude Code logs which version it loaded at startup if you're paying attention, but most people aren't, and they end up confused when a skill behaves differently in one repo than another.
The pattern that works well: global for general toolkit, project-local for team-specific overrides and context.
~/.claude/skills/../.claude/skills/ in the team repo, checked into git, same name to override.The project-local approach also means new team members get the skills automatically when they clone the repo — no per-developer onboarding step. This is the most undervalued part of the system. If your team has institutional knowledge that ends up in skill bodies (which it should), checking the .claude/skills/ directory into git turns Claude Code into a vehicle for distributing that knowledge.
One subtlety: project-local skills are discovered based on your current working directory when Claude Code starts. If you launch Claude Code from a parent directory, the project-local skills won't be picked up. This is rarely a problem in practice — most people work from a project root — but it's worth knowing if you're scripting Claude Code startup.
People sometimes ask whether to gitignore .claude/skills/ in their projects. The answer depends on intent. If the skills capture team-shared context and conventions, check them in — that's the value. If they're per-developer scratch space (someone's experimental skill they're iterating on locally), gitignore them. I lean toward checking them in by default and gitignoring specific files only when there's a reason. Skills are documentation that happens to be machine-readable; documentation belongs in the repo.
There's no version-aware update mechanism in Claude Code itself. Updating a skill is the same operation as installing it: overwrite the SKILL.md with the new version and restart. The manual flow is identical to install:
curl -fsSL https://claudskills.com/skills/<slug>/SKILL.md \
-o ~/.claude/skills/<slug>/SKILL.md
# restart Claude CodeThe desktop app can flag known-updated skills — it tracks the version you installed against the catalog's current version and shows an indicator when there's drift. You can also diff manually if you're curious what changed:
diff ~/.claude/skills/<slug>/SKILL.md \
<(curl -fsSL https://claudskills.com/skills/<slug>/SKILL.md)The version: frontmatter field is optional and not enforced by the loader. Some skill authors bump it on every change; many don't bump it at all. The catalog's lastmod dates (visible on each skill's page) are the most reliable signal of when a skill actually changed. The catalog stores a content hash and only bumps lastmod when the SKILL.md content actually differs — so a skill that hasn't moved in three months legitimately hasn't moved.
I'd push back gently on the temptation to run a nightly cron that re-pulls every skill in your library. Skill authors occasionally make breaking changes — tighter trigger conditions, removed features, reworded descriptions that now collide with another skill you have installed. Updates should be conscious. Skim the diff, decide whether the change is good, then apply.
If you really want batch updates, the desktop app's bulk-update flow shows you a per-skill diff and lets you pick which ones to apply. The same logic could be scripted, but the diff-then-decide step is the part worth preserving.
If you've edited a SKILL.md locally — common pattern: tightened the description to avoid a collision with another skill, or added a project-specific note in the body — pulling a new version overwrites your edits. There's no merge tool. The workarounds are either: copy the upstream file fresh and reapply your changes, or fork the skill (give it a new slug like code-review-mycompany) so upstream updates don't touch your version. The second approach scales better for anything more than a one-line tweak.
Removing a skill is a directory delete plus a restart:
rm -rf ~/.claude/skills/<slug>/
# restart Claude CodeThat's the whole operation. The next session start won't see the skill in its discovery scan, won't register it in the skill index, and won't invoke it.
One quirk worth knowing: removed skills can still appear in transcripts and Claude's memory of prior sessions. If you ask Claude something like "what skills do you have access to?" right after removing one, the answer should be correct (it reflects the current session's index). But if you're continuing a long conversation that started before the removal, Claude may still reference the old skill by name from its conversation context. This isn't a bug exactly — it's how the conversation history works — but it can be confusing the first time you see it. Starting a fresh session resolves it.
Same caveat applies to in-flight skill invocations. If a skill is mid-execution when you delete its directory, the invocation completes (the body was already read into the working context). Subsequent invocations fail because the file is gone.
Sometimes you want a skill out of the index temporarily without deleting it (you're debugging a collision, or you want to A/B between two versions). The simplest approach: rename the directory so the loader skips it.
mv ~/.claude/skills/<slug> ~/.claude/skills/<slug>.disabledThe loader looks for SKILL.md inside each subdirectory of ~/.claude/skills/ — it doesn't enforce a particular directory naming scheme, but anything that doesn't contain a readable SKILL.md is skipped silently. The .disabled suffix is just a convention; you could use any name. Restart, the skill is gone from the index. Rename it back, restart, it's loaded again.
Reset to zero skills:
rm -rf ~/.claude/skills/*This deletes every installed skill but preserves the parent directory. Restart Claude Code afterward. The desktop app, if you use it, will notice the skills are gone and offer to reinstall the ones you previously had. This can be useful as a clean-slate troubleshooting step if Claude Code is behaving strangely and you suspect a corrupted skill.
These are the failure modes I've hit most often. Each starts with the symptom, then the diagnostic step, then the fix.
Symptom: You installed it, restarted, and /skills doesn't show it.
Diagnose: Check the file actually exists at the path the loader expects: ls ~/.claude/skills/<slug>/SKILL.md. Check the YAML frontmatter parses: head -20 ~/.claude/skills/<slug>/SKILL.md and confirm the first line is ---, there's a closing --- a few lines down, and both name: and description: are present and non-empty.
Fix: Most common cause is the curl wrote an HTML 404 page into the SKILL.md because the URL was wrong and you didn't use the -f flag. Re-curl with -f, verify the file starts with ---, restart.
Symptom: A skill fires when you didn't intend it to, or the wrong skill fires for an obvious query.
Diagnose: Look at the descriptions of the conflicting skills via /skills. If two skills have similar or overlapping descriptions, Claude has to guess.
Fix: Tighten the description on at least one of them — add explicit "use when X" and "do not use when Y" language. See the frontmatter fields guide for description patterns that disambiguate well.
Symptom: ls confirms the file is there, but Claude doesn't see it.
Diagnose: Almost always a YAML parse error. Run a YAML linter on the frontmatter, or just pipe the first 20 lines through yq or python -c 'import yaml,sys; yaml.safe_load(sys.stdin)'. Common offenders: unescaped colons in description values, mismatched quotes, tabs instead of spaces.
Fix: Wrap description in double quotes if it contains colons. Use spaces consistently. Restart.
Symptom: A skill works for you but not for someone else on the team.
Diagnose: Check whether the skill is in ~/.claude/skills/ (global, per-user) or ./.claude/skills/ (project-local, in repo). Global skills aren't shared — every developer installs their own.
Fix: If it should be shared with the team, move it to the project's .claude/skills/ directory and commit. Everyone gets it on next pull.
Symptom: You deleted the skill directory, but Claude is still referencing the skill in the current session.
Diagnose: Expected behavior. Conversation context outlives the discovery scan.
Fix: Start a fresh session. The reference will be gone.
Symptom: Frontmatter looks fine to a human, loader skips the skill silently.
Diagnose: Unquoted strings containing : followed by a space are parsed as nested mappings in YAML. The line description: Use this when: you want X parses as a broken mapping, not a string.
Fix: Wrap in double quotes: description: "Use this when: you want X".
Symptom: Claude invokes the skill (you can see it fire), but the response doesn't follow the instructions in the SKILL.md body.
Diagnose: This usually means the body instructions are at odds with the user's request, or the body is structured in a way Claude can't parse cleanly (huge unstructured wall of text, no clear sections).
Fix: Restructure the body with clear headings and short paragraphs. Treat it like instructions to a smart but new colleague.
A few patterns I've seen work for teams running Claude Code together:
One team I know maintains a private company-skills repo that gets cloned to ~/.claude/skills-shared/ on every developer's machine, and they've symlinked or copy-installed those into ~/.claude/skills/ via a setup script. A weekly cron updates the clone. This gives them version control over their shared skill library without depending on the catalog or any third-party install path.
The setup script is roughly:
#!/bin/bash
repo=~/.claude/skills-shared
if [ ! -d "$repo" ]; then
git clone [email protected]:company/skills.git "$repo"
else
git -C "$repo" pull --ff-only
fi
for skill in "$repo"/skills/*/; do
slug=$(basename "$skill")
ln -sfn "$skill" ~/.claude/skills/"$slug"
doneSymlinks instead of copies mean a git pull is enough to update everyone's skills — no manual sync step. Restart Claude Code afterward.
Another pattern: keep general-purpose skills in the shared repo (linked into ~/.claude/skills/) and put project-specific overrides in each project's ./.claude/skills/. Because project-local wins precedence, a team member working in the auth-service repo gets the auth-service-specific code review skill; a member working in the billing repo gets the billing-specific one. Both inherit the same shared skills for everything not overridden.
This layering scales surprisingly well. The shared library captures "how this company writes code" in skill form. The project-local overrides capture "how this specific service is different." New team members get the full setup automatically when they clone repos.
If you're pair-programming in a Claude Code session or sharing a transcript, run /skills first and skim what's loaded. A teammate's skills can drastically change Claude's behavior, and "why is Claude doing this weird thing?" often resolves to a skill the other person installed that you didn't know about.
One pattern that doesn't work as well as people expect: installing every skill that looks remotely useful, on the theory that more options is better. Claude Code's skill-selection logic gets worse as the index gets noisier — more skills with overlapping descriptions means more chances of the wrong one firing. Lean libraries with tight descriptions beat sprawling libraries with loose descriptions, every time.
I keep my global library to roughly 20-30 skills I actually use. Project-local libraries are usually 3-10 skills tuned to that codebase. Anything beyond those numbers and you're paying a selection-noise tax for skills you rarely invoke. If a skill hasn't fired in a month, uninstall it. You can always reinstall from the catalog in three commands.
Found a bug or want a topic covered? Email [email protected] or open an issue via GitHub.
SKILL.md files, not affiliated with, endorsed by, or sponsored by Anthropic.