Your Skill's Description Is the Whole Ballgame
Agent skills run on progressive disclosure: at the start of a session the model sees only each skill's name and description — not its body. From that one sentence it decides whether your skill is relevant. Only if it bites does the full SKILL.md load into context. Which means two things most people writing skills get wrong:
- A vague description is a skill that never fires. You can write the most useful skill in the world; if its description is "Helps with database stuff," the model won't reach for it, because nothing in that sentence tells it when. The skill is dead weight — present in the directory, absent from every session.
- A bloated body taxes every session that does use it. When the skill triggers, the whole
SKILL.mdloads. A 10,000-token skill body is 10,000 tokens you pay on every task that touches its domain.
Both are mechanical and checkable, so we wrote skill-lint.
What it checks
$ skill-lint myskill/
skill-lint — myskill/SKILL.md (desc 3w · body ~9 tokens)
● HIGH description is only 3 words — too thin to trigger reliably
say what it does AND when to use it (the exact tasks/keywords that should invoke it).
● INFO description states what it does but not WHEN to use it
add a 'use when…' / 'trigger when…' clause so the model knows to reach for it.
● INFO `name` (helper) doesn't match the directory (myskill)
The description, as a trigger. The model matches on it, so skill-lint measures it as one: a description under ~10 words is flagged HIGH (it can't carry both what and when); under ~20 is a WARN; and a description that says what the skill does but never says when to use it gets an INFO nudge to add a "use when…" clause. The skills that fire reliably are the ones whose description spells out the trigger — the file patterns, the user phrasings, the exact tasks. Look at a well-built skill and the description reads like a routing rule, because that's what it is.
The body, as a recurring cost. skill-lint reports the body's token size and flags it past ~5,000, with the fix the docs recommend: keep SKILL.md a lean overview and push the detail into referenced files that load only when needed. That's progressive disclosure applied one level down — the skill itself shouldn't dump everything into context the moment it's chosen.
Stale bundled references. Skills ship helper files (scripts/…, references/…), and SKILL.md points at them. When one moves or is deleted, the pointer rots and the skill sends the model after a file that isn't there. skill-lint checks the bundled-path references against the skill directory.
● WARN 1 referenced file(s) not found in the skill
fix or remove: `scripts/run.sh`
A note on getting this right, because it's the kind of check that's easy to do badly: a naive version flagged every backtick that looked file-ish — including code identifiers like Anthropic.Tool and example filenames like app.py mentioned in prose. Those aren't bundled files, and nagging about them makes the tool noise. The fix is to only treat a reference as a file when it's a path (it has a slash) or an explicit markdown link — and a test pins exactly that, so a real, well-documented skill comes back clean instead of drowning in false positives.
Using it
skill-lint # ./SKILL.md
skill-lint path/to/skill/ # pass the dir; it finds SKILL.md
skill-lint ~/.claude/skills/*/ # audit a whole library
It exits non-zero on findings, so it slots into CI for a skills repo. Run it across your skills directory and you'll usually find the same two things: a couple of skills with descriptions too thin to ever trigger, and one that grew a 12,000-token body nobody noticed was loading on every related task.
The honest limits
It checks shape, not quality. It can tell you a description is 4 words and has no trigger clause; it can't tell you a 40-word one is actually a good router — that the keywords match how people really ask. "Has a trigger cue" is a heuristic for "is discoverable," not a guarantee. And body size is a proxy for cost, not a verdict on whether the content earns it. Treat skill-lint as the smoke detector for the failure modes that are mechanical — undiscoverable, over-heavy, pointing at missing files — and keep writing the description like the routing decision it is.
The real lesson
A skill isn't code that runs when called; it's a candidate the model chooses from a one-line pitch. So the highest-leverage thing in the whole file is the description — and the most common failure isn't a bug in the skill, it's a skill that's invisible because its pitch never said when to use it. Write the description as a trigger, keep the body lean enough that loading it isn't a tax, and let a linter catch the day either one drifts.
We build and run agent skills and fleets on Claude Code in production. If you're building a skills library, talk to us.
Get the tool → skill-lint in agent-ops — open source (MIT), a single dependency-free script, part of our toolkit for running coding agents in production.
Found this useful?
- You're Running Five Agents on One Repo. Who Stops Them Overwriting Each Other?
- Your Agent Did the Work, Then It Vanished. Here's How to Get It Back.
- Your Claude Code Session Is Permanently Stuck on a 400. Don't /clear — Do This.
- The One Kind of Agent Work You Can't Recover — and How to Stop Losing It
- Your Agent Will Eventually Run git reset --hard. Stop It at the Door.
- Your Agent Reads Text You Can't See. Frisk It First.
- You're Running a Dozen Agents. Which One Is Stuck?
- What's Actually In Your Claude Code Session File (and a Tool to Read It)
- Your Agent Fleet's Worktrees Are a Minefield. Here's How to See It.
- The Agent Wrote the Code in Minutes. Reviewing It Takes Hours. Here's Why.
- Your Claude Agent Re-Reads Its Whole Prompt Every Turn. Here's the 10× You're Leaving On the Table.
- Your Agent Can Read Everything Your Shell Can. Have You Looked?
- The Loosest Line in Your Repo Is in .claude/settings.json
- The Most Expensive File in Your Repo Runs on Every Single Turn
- Your Repo Already Knows How To Build Itself. Put That In the CLAUDE.md.
Related posts
The boring part: governance that enforces, not displays
Most "AI governance" is a dashboard. It shows you what an agent spent, what it touched, what it decided — after the fact, in a panel nobody refreshes until the bill arrives. A display is not a control. A control is the t…
Your Launch Deserves a Real Demo Video, Not a Screen Recording With Captions
Most product launches skip the demo video entirely, and the ones that don't ship one usually reach for the fastest option: a raw screen recording, maybe some auto-generated captions, posted as-is. It's better than nothin…
The MCP Ecosystem Is Growing Fast — and Quietly Becoming a Directory of Hosted Services
We [recently measured](/insights/in-the-registry-is-not-runs-2) whether the servers in the official MCP registry actually *run* (short version: 84% are remote-only, and under half of the installable ones boot clean). Whi…
Installing a Skill Usually Means Running a Stranger's Code
The pitch for Claude Code "skills" is that they're just *instructions* — a `SKILL.md` file with some markdown telling the agent how to do a task. That framing makes them feel safe to install. A prompt can't hurt you; wor…