Why your Claude Code skill never triggers
By Randy Olson, Co-Founder & CTO, Goodeye
Goodeye tunes a skill's description against a labeled set of real prompts, but the description is only one of four reasons a skill stays quiet. Check in order: is the skill loaded, is the description winning the match, is the copy on that machine current, and is the agent simply declining to use it. Each one has a different fix, and rewriting a description that was never loaded wastes an afternoon.
You installed a skill, the work came back as though it did not exist, and nothing anywhere told you why. There was no error, no log line, and no "skill not found". The agent simply answered without it.
That silence is the real difficulty. Four unrelated failures produce exactly the same symptom, and they take four different fixes, so the first job is finding out which one you have. Rewriting a description that was never loaded is a wasted afternoon.
The four gates a skill has to clear
A skill changes nothing you can see until it passes all four of these.
It has to load. The harness reads skills from specific directories. The file has to sit in one of them.
It has to win the match. Once loaded, your skill sits beside every other loaded skill, and the agent chooses one by reading descriptions. Yours can lose that comparison without anything being wrong with it.
It has to be current on the machine doing the work. Your copy and your teammate's copy are two different files on two different disks. Only one of them may have your fix.
It has to actually get chosen. Even when the first three are right, the agent can work around a skill it can see. That is a real and measured behavior, not a fault in your file.
Each gate fails with its own signature. Walk them in order, because each one is cheaper to rule out than the one after it.
Gate 1: confirm it loaded
Ask the agent what it can see:
What skills do you have access to?
If your skill is not in the list it returns, stop here. Nothing downstream matters yet. Three things keep a skill off that list, and all three are silent.
The file is in a directory the harness does not read. Claude Code reads ~/.claude/skills for personal skills, and project skills from .claude/skills in the directory where you start it and in every parent directory up to the repository root, plus plugin, enterprise, and --add-dir locations. Codex reads ~/.agents/skills. Cursor reads ~/.cursor/skills and ~/.agents/skills. Those are the vendors' locations rather than ours, so check their current docs if a skill is missing:
ls ~/.claude/skills/my-skill/SKILL.md
Unpacking an archive is the usual way this goes wrong, because it leaves you with ~/.claude/skills/my-skill/my-skill/SKILL.md and the extra level is enough to hide it.
The filename is not SKILL.md. Uppercase stem, lowercase .md extension. Do not rely on the filesystem to forgive skill.md: it may resolve on a case-insensitive macOS volume and then fail on Linux or in CI, which is a confusing bug to chase later.
The skills directory did not exist when the session started. Claude Code watches ~/.claude/skills/ and the project .claude/skills/ and picks up added, edited, and removed skills inside a running session, so you do not normally need to restart. The exception is a top-level skills directory created after the session began, which it is not watching yet. Create the directory, then restart.
Note what is not on that list. Broken front matter does not keep a skill off it. In Claude Code every front matter field is optional: name defaults to the directory name, and a missing description falls back to the first paragraph of the body. A skill with mangled YAML still loads, carrying a description you did not write. That belongs to Gate 2, and it is the most commonly misfiled cause of all.
If you sync from goodeye, this gate mostly stops being something you check by hand:
goodeye skills sync target add --preset claude --scope all
goodeye skills sync pull
Each in-scope skill is written to <target>/<slug>/SKILL.md, so the layout is correct by construction. --scope all matters: the default scope is owned, which mirrors only the skills that account published. A colleague who holds the skill through a grant pulls nothing without it.
Gate 2: it loaded and still lost the match
The skill is in the list and stays idle anyway. This is a description problem almost every time.
Your agent reads your description next to every other loaded description at the same moment, then picks one. It has not opened your skill body, so a capability documented only in the body is invisible at the moment of the decision. Anything you never named in the description cannot be chosen.
Two patterns account for most of it. The first is a description written to explain what the skill is rather than the situation someone is in when they need it, which leaves the agent nothing to match a prompt against. The second is a neighboring skill claiming the same ground more specifically than you claimed it, which shows up as a skill that fires sometimes and not others. Both, and the measured evidence behind them, are covered in write a skill description your agent actually uses.
You will also see imperative phrasing recommended for this, along the lines of writing "this skill MUST be invoked when the user says X". It circulates because it does help some people. Treat it as a hypothesis about your own skill rather than a rule, because what decides is how your description reads beside the specific set of skills you have installed, and no phrasing convention can tell you that from the outside.
To grade the skill against the authoring rubric, including whether the description names the job and its trigger and whether the body has drifted off the task the description claims, audit it:
goodeye skills audit my-skill
Run it with no id to audit a local skill file that is not on goodeye yet. What comes back is a pack of instructions your agent carries out locally.
That tells you whether the description is well formed. It does not tell you whether it wins against the other skills on your disk. For that, route prompts at it:
goodeye skills optimize-description my-skill
Your agent writes prompts that should fire the skill and near-misses that should not, takes the right answers from the skill body rather than from the description under test, judges each one blind, and reports which skill actually loaded for each prompt. The rows that went the wrong way are your edit list. It changes the description only, spends no goodeye credits, and saves nothing until you approve it. Your own agent's tokens still bill through whatever model you run it on.
Gate 3: it fires for you and not for your teammate
This one gets misdiagnosed as Gate 2 constantly, and the misdiagnosis is expensive: you rewrite a description that was working, and now it is worse for everybody.
The tell is that the skill behaves correctly on the machine you fixed it on. Nothing about a description explains a result that changes with whose laptop runs it. What changes is which local skill file is on that disk, and whether it matches the hosted skill.
Check what is actually there, on the machine that is quiet:
goodeye skills sync status
That reads only, and reports each skill as clean, modified-local (edited on disk), behind-server (older than the hosted copy), conflict (both sides moved), deleted-on-server, or untracked. Anything other than clean is your answer, and the command names the move it recommends in its next_action column.
behind-server is the easy one: a pull brings your fix.
goodeye skills sync pull
modified-local is the one worth slowing down on, because a plain pull will skip that skill by design: it preserves local edits. Somebody changed the file on disk instead of publishing, so that machine has quietly forked away from the version everyone else runs. You have two ways out, and they are opposites:
goodeye skills sync push
goodeye skills sync pull --force
Push if the local edit was the good one and should become the hosted version. Force-pull to discard it and take the hosted copy. Decide which before you run either.
Then check the simplest explanation of all, which is that the teammate never had the skill:
goodeye skills grants my-skill
Access is granted per user or per team, and a person who was never granted anything has nothing to sync.
Gate 4: everything is correct and it still does not fire
Sometimes the skill is loaded, the description is right, and the copy is current, and the agent still works around it. This is real, it is documented, and it is worth knowing before you spend another evening rewriting a description that was never the problem.
An open Claude Code issue, #9716, collects reports of the assistant not being aware of skills present in .claude/skills/. More usefully, Scott Spence measured it: 20 fresh sessions against a UserPromptSubmit hook that explicitly instructed the agent to use a skill returned 4 of 10 at global level and 5 of 10 at project level. His summary is that it is basically a coin flip.
Loading is deterministic. Choosing is not.
Two things follow. When a skill has to run, name it in the prompt and the decision disappears. When it has to run often, the lever is how far ahead your description sits of the alternatives at the moment of the choice, which is exactly what optimize-description measures. It does not make the choice deterministic. It moves the odds, and it shows you the rows where they are still against you.
What else people reach for
Worth knowing what the rest of the field does, because no single tool covers all four gates. Asking the agent to list its skills is the fastest Gate 1 check and costs nothing. Naming the skill in the prompt is the only fully deterministic answer to Gate 4. UserPromptSubmit hooks that inject a reminder are widely used, and Scott Spence's measured coin flip is the honest read on how far they get you. Tuning the description is the only one of these that addresses cause rather than symptom, which is why it is the one worth automating.
Where to start
Do this before you change a line of the description. Open the agent, ask what skills it can see, and note whether yours is in the list. That one answer sends you to Gate 1 or Gate 2 and costs thirty seconds.
If the skill only misbehaves for other people, skip both and run goodeye skills sync status on one of their machines instead.
Once the skill fires reliably, the next thing worth pinning down is what its output has to be right about, rather than whether it ran at all: run a rubric check on AI output from the CLI covers writing a check the work has to pass and deploying it so everyone runs that exact one.
Frequently asked questions
Why does my Claude Code skill never trigger?
Four different failures produce that same silence, and they need four different fixes. The skill may never have loaded, which is a path or directory problem. It may have loaded and lost the match, because the agent reads your description beside every other loaded description and picks one. The machine that is quiet may hold an older copy than the one you fixed. Or everything may be correct and the agent is declining to use the skill anyway, which is a documented and measured behavior rather than a fault in your file. Check them in that order, cheapest first. Start by asking the agent what skills it can see: if yours is missing, the problem is placement, not wording.
Do I need to restart Claude Code after adding a skill?
Usually not. Claude Code watches ~/.claude/skills/, the project .claude/skills/, and any .claude/skills/ inside an --add-dir directory, and picks up added, edited, and removed skills inside the running session without a restart. One case does need one: if the top-level skills directory did not exist when the session started, Claude Code is not watching it yet, so create the directory and then restart. Live change detection also covers the text of SKILL.md only, so a skill folder that is also a plugin behaves differently. Restarting is still a reasonable first move when you are unsure, because it costs seconds and removes a variable.
Does a Claude Code skill still load if the front matter is broken?
Yes, and that is the trap. In Claude Code every front matter field is optional: name falls back to the directory name, and if description is missing the skill uses the first paragraph of the markdown body instead. So a skill with absent or unparseable front matter still loads. It simply loads carrying a description you did not write, which is then what the agent matches prompts against, and it usually loses. The symptom is identical to a missing skill even though the skill is present. Confirm the YAML parses and that description says when the skill should fire, rather than assuming a broken header takes the skill out of play.
Why does a skill work on my machine but not my teammate's?
Because those are two different files on two different disks, and only yours has the fix. This is the failure most often misdiagnosed as a description problem, and rewriting the description makes it worse for everyone. Goodeye has each machine mirror one hosted skill rather than hold an independent copy, so goodeye skills sync status on the quiet machine reports what is actually there: clean, modified-local, behind-server, conflict, deleted-on-server, or untracked. Anything other than clean explains the symptom. Check two more things: that they were granted the skill at all, with goodeye skills grants my-skill, and that their sync target uses --scope all, since the default scope mirrors only skills that account owns.
Do Claude Code skills activate automatically, or do I have to invoke them?
Automatic activation is real but not reliable, so treat invoking a skill by name as the deterministic option. Loading is deterministic; choosing is not. Scott Spence ran 20 fresh Claude Code sessions against a UserPromptSubmit hook that explicitly told the agent to use a skill and landed 4 of 10 at global level and 5 of 10 at project level, which he summarized as basically a coin flip. An open Claude Code issue collects reports of the assistant not being aware of skills sitting in .claude/skills/. When a skill has to run, name it in the prompt. When it has to run often, the lever is how far ahead its description is of the alternatives, which is what tuning measures.