Lesson 6 / 26
The Description: Your Most Important Sentence
Write descriptions that make Claude choose the skill at the right time.
What it does, when to use it, trigger phrases
Claude decides whether to use a skill mostly from its description, which is always visible, so it must carry the whole decision. A strong description has three parts: what the skill does (one clear sentence), when to use it ("Use when asked to write a changelog or release notes"), and trigger phrases users actually say ("Triggers on: release notes, changelog, what changed"). Write in the third person, be specific about inputs and outputs, mention distinctive keywords and file types, and keep it under the 1,024-character limit. Vague descriptions ("helps with stuff") never trigger; over-broad ones trigger on everything and waste context; missing "when" clauses make Claude guess. Treat the description as the skill's API: test it with real requests, including ones that should not trigger it.
Validating a SKILL.md, run
I ran this with plain Python 3 (standard library only). The validator parses the frontmatter and checks the rules from this topic: lowercase hyphenated name matching the folder, non-empty description under 1,024 characters that says when to use the skill, and a body under 500 lines. The good skill passes. The bad one has an uppercase name with a space, a name that does not match its folder, and a description that never says when to use it. These are the conventions taught here, and your tool version may check more or fewer things.
import re
def parse_skill(text):
"""Split a SKILL.md into (frontmatter dict, body). Handles simple `key: value` lines."""
m = re.match(r"^---\n(.*?)\n---\n?(.*)$", text, re.S)
if not m: return None, text
meta = {}
for line in m.group(1).splitlines():
if ":" in line:
k, v = line.split(":", 1)
meta[k.strip()] = v.strip().strip('"')
return meta, m.group(2)
def validate(text, folder):
meta, body = parse_skill(text)
problems = []
if meta is None: return ["no frontmatter block between --- lines"]
name, desc = meta.get("name", ""), meta.get("description", "")
if not re.fullmatch(r"[a-z0-9]+(-[a-z0-9]+)*", name): problems.append("name must be lowercase letters, digits and hyphens")
if len(name) > 64: problems.append("name longer than 64 characters")
if name != folder: problems.append(f"name '{name}' does not match folder '{folder}'")
if not desc: problems.append("description is empty")
if len(desc) > 1024: problems.append("description longer than 1024 characters")
if desc and not re.search(r"\b(use|when|triggers?)\b", desc, re.I): problems.append("description never says WHEN to use the skill")
if len(body.splitlines()) > 500: problems.append("body over 500 lines: move detail into reference files")
return problems or ["ok"]
good = """---
name: release-notes
description: "Draft release notes from merged PRs since the last tag. Use when asked to write a changelog, release notes or what's new. Triggers on: release notes, changelog, what changed."
---
# release-notes
1. Find the last tag.
2. List merged PRs since then.
3. Group by feature, fix, chore.
"""
bad = """---
name: Release Notes
description: helps with stuff
---
Do things.
"""
print("good:", validate(good, "release-notes"))
print("bad :", validate(bad, "release-notes"))
Output:
good: ['ok'] bad : ['name must be lowercase letters, digits and hyphens', "name 'Release Notes' does not match folder 'release-notes'", 'description never says WHEN to use the skill']
How descriptions steer selection (simulation), run
I ran this with plain Python 3 (standard library only). This is a simple simulation of the idea, not how Claude actually decides: Claude uses its own judgement over the descriptions, which is why clear descriptions matter. A crude word-overlap score picks the skill whose description best matches each request (a minimum of 2 shared words is needed). The specific release-notes description wins for the changelog request, the browser description wins for the console-errors request, the vague "helps with stuff" description never wins, and the unrelated refactoring request matches nothing.
import re
STOP = {"a", "an", "the", "to", "of", "for", "and", "or", "in", "on", "is", "it", "my", "me", "please", "this", "that", "use", "when", "with", "from"}
def words(text): return {w for w in re.findall(r"[a-z0-9]+", text.lower()) if w not in STOP and len(w) > 2}
SKILLS = {
"vague": "Helps with stuff and general tasks.",
"specific": "Draft release notes from merged PRs since the last tag. Use when asked to write a changelog, release notes or what changed. Triggers on: release notes, changelog, whats new.",
"browser": "Load a page in a headless browser and report console errors and failed requests. Use to verify web changes. Triggers on: does it render, console errors, check the page.",
}
requests = ["write the changelog for this release", "does the page render without console errors", "refactor the payment module"]
def best_match(request):
scores = {name: len(words(request) & words(desc)) for name, desc in SKILLS.items()}
name, score = max(scores.items(), key=lambda kv: kv[1])
return (name if score >= 2 else None), scores
for r in requests:
chosen, scores = best_match(r)
print(f"{r!r:48} -> {chosen} scores={scores}")
Output:
'write the changelog for this release' -> specific scores={'vague': 0, 'specific': 3, 'browser': 0}
'does the page render without console errors' -> browser scores={'vague': 0, 'specific': 0, 'browser': 5}
'refactor the payment module' -> None scores={'vague': 0, 'specific': 0, 'browser': 0}Write the trigger phrases users really say
Collect real requests from your team ("check the page", "what shipped?") and put those words in the description.
Quick check: Which description is most likely to trigger at the right time?
- "Version 2 of my helper."
- "Helps with stuff."
- "A very useful skill."
- "Draft release notes from merged PRs. Use when asked for a changelog. Triggers on: release notes, changelog."
Answer
"Draft release notes from merged PRs. Use when asked for a changelog. Triggers on: release notes, changelog." — It says what, when and which phrases, in concrete words.