Agent Skills
Package reusable know-how as Skills Claude loads on demand: SKILL.md, triggering descriptions, progressive disclosure (with a cost model), bundled helpers, distribution, and an org Skills library.
Learning objectives
- Explain what an Agent Skill is and when it beats a prompt or CLAUDE.md.
- Write a SKILL.md with a triggering description and bundled files.
- Understand progressive disclosure and why it keeps Skills cheap.
- Distribute and govern Skills across a team.
code/ak4-agent-skills/. Python runs offline; configs are ready to use.1 · What a Skill is essential
You've explained the same thing to Claude fifteen times this month. "Write the incident report the way we do it — timeline in UTC, cite every claim, no blame language, action items with owners." You paste that paragraph into a fresh chat, tweak it, paste it again next week. It's tribal knowledge trapped in your head and your clipboard, and every teammate re-invents their own slightly-different version. A Skill is the fix: you write that know-how down once, in a file, and from then on Claude reaches for it automatically whenever the task shows up.
Here's the analogy that makes it click. Think of a Skill as a recipe card in a kitchen's recipe box. The chef doesn't memorise every dish or keep all the cards open on the counter — that would be chaos. Instead each card has a title ("Beef Wellington") they can glance at, and only when tonight's order matches does the chef pull that one card and follow it step by step. The full recipe — the technique, the timings, the reference photo — stays folded away until it's actually needed. A Skill works exactly this way: a short label Claude can scan cheaply, and a detailed body it opens only on a match.
The mistake beginners make is reaching for a Skill for everything, or confusing it with the tools they already know. A Skill is not a one-off instruction (that's just a prompt) and it's not always-on project context (that's CLAUDE.md). It's specifically for recurring, specific know-how — a task you do the same way again and again — that you want packaged so it can be versioned, reviewed, and shared like code, yet costs almost nothing when it's not in use. Get that distinction right and Skills become a superpower; get it wrong and you've just made a folder full of prompts nobody triggers. With that framing, here's the precise definition.
An Agent Skill is a folder with a SKILL.md that packages reusable know-how — instructions, and optionally scripts and reference files — that Claude loads on demand when a task matches. It turns "the way we do X" into a versioned, shareable capability.
This picture shows the life of a request when Skills are installed — and the one idea that makes Skills cheap: Claude does not read every Skill up front. It reads only a tiny label for each, and opens the full Skill only when your task actually matches it.
- Box 1 · Task arrives — you send a normal request ("write me an incident report").
- Box 2 · name+desc match? — for each installed Skill, Claude compares your task against just the Skill's
nameand one-linedescription. The caption calls this a "cheap check" because those labels are tiny (a few words), so checking them costs almost nothing even with dozens of Skills. - Box 3 · load SKILL.md body — only on a match does Claude read the full instructions. A Skill that doesn't match is never opened, so you don't pay for it.
- Box 4 · use bundled files — once loaded, the Skill can pull in its extra files (templates, scripts) as the instructions call for them.
- Read it left to right: each arrow is "next step". The flow narrows from "look at every Skill's label" down to "fully load just the one that fits".
In short: "Progressive disclosure" = reveal detail only when needed. Labels are always visible (cheap); the heavy body appears just-in-time. That is why a team can install many Skills without slowing every chat.
2 · Anatomy of a Skill essential
The good news you're about to discover: there's no framework to learn here. People hear "Agent Skill" and brace for a config schema, a registry, a build step. There is none. A Skill is a folder with a Markdown file in it. If you can create a directory and write a to-do list, you can write a Skill — and that low ceremony is deliberate, because the whole point is that ordinary teammates author these, not just platform engineers.
Picture it like a labelled folder in a filing cabinet. On the front is a sticky note saying what's inside and when to grab it — that's the metadata. Inside are the actual documents: the main instructions, plus any supporting handouts and a calculator you keep clipped to it. The folder's name is the Skill's name; the sticky note is the trigger; the pages inside are what Claude reads and follows once it opens the folder. Nothing more exotic than that.
Two pieces do all the work, and it's worth naming them before you see the code so you know what to look for. First, the front-matter — a tiny header of name and description — which is the sticky note Claude scans to decide whether this Skill is relevant. Second, the body — plain Markdown instructions below the header — which is the how Claude follows once the Skill fires. The common mistake is pouring all your effort into a beautiful body while writing a lazy one-line description; but a Skill with a perfect body and a vague description is a recipe card with a blank title — it never gets pulled, so its brilliance is never seen. Treat the description as first-class. The layout and the SKILL.md below show both pieces in a real, minimal Skill.
SKILL.md splits into front-matter (name + description — the trigger deciding whether to fire) and a body (the instructions for how); reference files and scripts ride along optionally.
Read it left to right: the left box is what you create on disk; the arrow points to the right, which zooms inside the one required file.
- The left folder
incident-report/is the whole Skill. Its folder name becomes the Skill's name. Inside sits the highlightedSKILL.md(required) plus an optional reference file and ascripts/helper. - The top-right blue box is the front-matter —
name+description. This is the sticky note Claude scans to decide whether the Skill is relevant. - The bottom-right green box is the body — plain Markdown steps Claude follows once the Skill fires: the how.
- The payoff is the split itself: "whether" (front-matter) and "how" (body) are separate jobs, which is why a lazy description dooms even a perfect body.
In short: Folder = the Skill; SKILL.md = front-matter (trigger) + body (procedure). Two pieces, two different jobs.
layout.txt.claude/skills/
└── incident-report/
├── SKILL.md # instructions + trigger metadata
├── template.md # a reference the body points to
└── scripts/
└── gather_logs.sh # an optional helper the Skill can run
A Skill is nothing exotic — it is just a folder on disk with a specific shape. This is a directory tree (the └── and ├── lines are branches, like a family tree of files). Reading it tells you exactly what files to create.
.claude/skills/is where Claude looks for Skills. Each Skill gets its own sub-folder — hereincident-report/, whose name becomes the Skill's name.SKILL.mdis the required file: the instructions plus the trigger metadata (covered next). Every Skill must have exactly one.template.mdis an optional reference file — extra material the instructions can point to (a house format, a checklist). It loads only if the body asks for it.scripts/gather_logs.shis an optional helper script the Skill can run. The#text after each file is just a comment explaining its purpose, not part of the name.
Try this: Recreate this tree yourself: mkdir -p .claude/skills/incident-report/scripts and add an empty SKILL.md. That empty folder is already a (do-nothing) Skill.
SKILL.md---
name: incident-report
description: >
Write a postmortem/incident report in our house format. Use whenever the user
asks for an incident report, postmortem, or RCA writeup.
---
# Incident report
1. Use the structure in `template.md` (timeline, impact, root cause, actions).
2. If logs exist, run `scripts/gather_logs.sh <service>`.
3. Timeline entries are UTC, newest last; every claim cites a log line or ticket.
4. "Action items" need an owner and a due date each.
5. No blame language — describe systems and events, not people.
This is the heart of a Skill: the SKILL.md file. It has two parts — a small metadata header (between the --- lines) that tells Claude when to use the Skill, and a Markdown body that tells Claude how to do the task.
- The block fenced by
---at top and bottom is YAML front-matter (a tiny key: value settings header).nameidentifies the Skill;descriptionis the all-important trigger line. - The
description: >uses a>so the text can wrap onto several indented lines and still count as one value. Notice it says "Use whenever the user asks for an incident report, postmortem, or RCA..." — it names the exact words a user would say, which is what makes Claude fire the Skill. - Everything below the closing
---is the body: ordinary Markdown instructions. The# Incident reportis a heading; the numbered list is the step-by-step procedure Claude follows. - The body can reference the bundled files: step 1 points at
template.mdand step 2 runsscripts/gather_logs.sh— the same files from the folder tree above. The rules (UTC timeline, cite every claim, owners on action items, no blame language) capture "how we do it" once, reusably.
What the output means: There is no program output here — SKILL.md is instructions, not code. Its effect is that asking Claude for a postmortem now produces one in exactly this house format.
Try this: Rewrite the description to be vague ("Writes reports") and you'll see the problem the lesson warns about — a vague description often never triggers. Keep the concrete "use whenever..." phrasing.
name + description alone. Write the description around when to use it ("use whenever the user asks for X") — vague descriptions never fire; precise ones do.3 · Why progressive disclosure matters intermediate
Imagine if your kitchen chef had to read every recipe card in the box, cover to cover, before cooking a single dish. Fifty cards, thousands of words, every night, whether or not any of them applied to tonight's order. The kitchen would grind to a halt — and worse, you'd pay for all that reading even on nights you only served toast. That is exactly the trap a naive Skills system would fall into: load every Skill's full instructions into context on every request. The clever idea that avoids it has a name — progressive disclosure — and it's the single reason a team can install dozens of Skills without every chat getting slower and more expensive.
The principle is simple: reveal detail only when it's needed. What Claude keeps in view at all times is just the sticky note — each Skill's name and one-line description, a handful of tokens apiece. Those tiny labels are enough to answer the only question that matters up front: is this Skill relevant to what the user just asked? If the answer is no, the Skill's full body is never opened, so you never pay for it. If the answer is yes, then Claude reads the full SKILL.md — and only then do any bundled reference files get pulled in, and only if the body actually points at them. It's a three-tier ladder of cost: labels always, body on match, extras on demand.
Why does this deserve its own section rather than a footnote? Because it inverts the intuition most people carry about context. Newcomers assume "more installed Skills = a heavier, slower agent," so they hoard know-how in their head to keep sessions lean — and defeat the whole purpose. The truth is the opposite: because dormant Skills cost only their label, adding the fifty-first Skill is nearly free until the day a task finally matches it. The common mistake, then, isn't installing too many Skills — it's writing bodies so bloated that when one does match, the on-match cost balloons. Keep bodies tight and lean on bundled files for the heavy detail, and the economics stay in your favour at any scale. The little cost model below turns this argument into concrete numbers. Only each Skill's name + description are always in context (tiny). The full body loads only on a match. That's what lets a team ship dozens of Skills without every session paying for all of them — the context-economy lesson from PE2.
Read it top to bottom as a ladder — each rung is a deeper tier of a Skill, and each is narrower to signal that it fires less often but costs more when it does.
- The top green band is Tier 0: every installed Skill's
name+description, a few tokens each, held in context always. This is the cheap "cheap check" from section 1. - The middle blue band is Tier 1: the full
SKILL.mdbody (~500 tokens), loaded only on a match. A Skill that doesn't match never reaches this rung. - The bottom amber band is Tier 2: bundled reference files and scripts, pulled in only when the body actually points at them.
- The payoff is the shrinking width plus the "cost grows ↓" label: dormant Skills sit at Tier 0 forever, so adding the fifty-first Skill is nearly free until a task finally hits it.
In short: Labels always, body on match, extras on demand — you pay for detail only when a task reaches for it.
disclosure_cost.pydef context_cost(skills, matched):
"""Always-loaded: name+desc (~20 tokens each). Body (~500) only if matched."""
always = sum(20 for _ in skills)
bodies = sum(500 for s in skills if s in matched)
return always + bodies
skills = ["incident-report", "release-notes", "code-review", "data-clean", "sql-style"]
print("all 5 skills, none matched:", context_cost(skills, set()), "tokens (just metadata)")
print("all 5, one matched: ", context_cost(skills, {"incident-report"}), "tokens")
print("if bodies always loaded: ", 5*520, "tokens (5x waste)")
all 5 skills, none matched: 100 tokens (just metadata)
all 5, one matched: 600 tokens
if bodies always loaded: 2600 tokens (5x waste)
This tiny program puts numbers on why progressive disclosure (the diagram above) saves money. It's a made-up but realistic cost model: it counts context tokens — the units Claude reads and you pay for — under different scenarios.
context_cost(skills, matched)is a function taking a list of Skill names and the set of ones that matched this task. The docstring states the assumption: each Skill's name+desc costs ~20 tokens (always loaded), each full body ~500 tokens (loaded only on a match).always = sum(20 for _ in skills)adds 20 for every Skill — the cheap labels that are always present.bodies = sum(500 for s in skills if s in matched)adds 500 only for the matched ones. The total isalways + bodies.- The three
printlines run three what-ifs: 5 Skills with none matched (labels only), 5 Skills with one matched, and the wasteful world where all 5 bodies load every time (5*520).
What the output means: none matched: 100 tokens (just 5×20 labels); one matched: 600 (100 labels + one 500 body); always loaded: 2600. The point: 100 vs 2600 — loading bodies only on demand is roughly a 5× saving.
Try this: Change matched to two Skills, e.g. {"incident-report","code-review"}, and predict the number before running (100 + 2×500 = 1100). More matches cost more — but you still skip the Skills that didn't match.
4 · Advanced — a Skill that bundles a script advanced
Would you ask a brilliant colleague to add up a column of two hundred numbers in their head? Of course not — you'd hand them a calculator. Yet that's exactly what you're doing when you ask a language model to count criticals in a log dump, sort timestamps, or tally null rates by eyeballing text. It can do it, mostly, but it's slow, it's probabilistic, and one day it will quietly miscount and you won't notice. The fix is to let the model do what it's genuinely great at — reasoning and narrating — and hand the exact, mechanical work to code. Skills let you ship that code inside the Skill itself.
A bundled script is the calculator clipped to the recipe card. When the Skill's instructions say "run scripts/summarize.py on the CSV," Claude executes real, deterministic code and reads back a trustworthy result — the same answer every single time, no drift, no hallucinated totals. Then the model reasons over that reliable output and writes the human-facing summary. You get the best of both worlds: the precision of a program and the fluency of a model, glued together by the Skill. And thanks to progressive disclosure, the script sits idle at zero cost until the body actually references it (that's the Tier-2 loading from the last section) — so bundling helpers never bloats your context, it only pays off when used.
The habit to build — and the mistake to avoid — is treating the helper like throwaway glue. It isn't; it runs with your agent's permissions and it runs unattended, so write it as a clean, testable unit you'd be comfortable importing anywhere (the same discipline as any command-line tool in DF1). Give it a clear input and output, no hidden side effects, and a shape you can unit-test on its own before a Skill ever calls it. A sloppy helper that crashes on odd input turns a helpful Skill into a landmine. The example below is a helper an incident-report Skill might ship — deliberately small, pure, and runnable on its own so you can see exactly what "clean and testable" looks like.
Follow the arrows left to right — it's a small pipeline that hands each job to whoever is best at it.
- The left box is the messy input — a log dump, a CSV, a list of events — the fiddly stuff you don't want a model eyeballing.
- The middle green box is the bundled script: real code that counts, sorts, and tallies. Its label "exact · same every run" is the whole point — deterministic, no drift, no hallucinated totals.
- The right blue box is the model: it reads the script's trustworthy output and does what it's genuinely great at — reasoning and writing the readable summary.
- The payoff is the division of labour under the boxes: deterministic computation on the left, fluent narration on the right — the precision of a program glued to the fluency of a model.
In short: Hand the exact counting to code and the wording to the model — you get reliable numbers and a good write-up.
skill_helper.py# scripts/summarize_incidents.py — a helper the incident-report Skill calls
def summarize(events):
"""events: [(ts, service, severity)] -> a timeline + a headline count."""
events = sorted(events) # chronological
crit = sum(1 for _,_,sev in events if sev == "critical")
timeline = [f"{ts} UTC — {svc} ({sev})" for ts, svc, sev in events]
return {"critical_count": crit, "timeline": timeline}
r = summarize([("10:05","api","critical"), ("10:01","db","warning"), ("10:12","api","critical")])
print("criticals:", r["critical_count"])
for line in r["timeline"]: print(" ", line)
criticals: 2
10:01 UTC — db (warning)
10:05 UTC — api (critical)
10:12 UTC — api (critical)
This is an example of the bundled helper script a Skill can ship (the scripts/ file from the folder tree). The point is that a Skill isn't limited to prose — it can carry real, runnable code that does the fiddly work reliably every time.
summarize(events)takes a list of incident events. The docstring shows the shape: each event is a tuple(ts, service, severity)— a timestamp, a service name, and how bad it was.events = sorted(events)puts them in chronological order (sorting tuples sorts by the first item, the timestamp). This guarantees a correct timeline no matter what order they arrived.crit = sum(1 for _,_,sev in events if sev == "critical")counts how many werecriticalby adding 1 for each match — the headline count. Thetimelineline builds a readable string per event with an f-string.- It
returns a dictionary with both results. The code below the function calls it on three sample events and prints the critical count, then each timeline line.
What the output means: criticals: 2 (two of the three events were critical), followed by the three events printed in time order — 10:01 before 10:05 before 10:12 — proving the sorted() worked even though 10:01 was listed second in the input.
Try this: Add a fourth event like ("10:08","db","critical") to the list and re-run: the count becomes 3 and the new line slots into the right chronological spot automatically.
5 · Distributing Skills advanced
A Skill that lives only on your laptop helps exactly one person. You wrote the perfect incident-report Skill, it fires beautifully, your postmortems are consistent and fast — and your five teammates still each hand-roll their own, because they've never seen yours. The whole value proposition of Skills is that know-how becomes shared: "the way we do X" turns into a default the whole team inherits without a meeting. That only happens if the Skill lands somewhere everyone's Claude can find it — which is what distribution is about.
The key idea is scope: where a Skill lives determines who gets it, and it maps cleanly onto how a company shares anything. A Skill committed into a repo's .claude/skills/ is like a document in the team's shared drive — clone the repo and it's just there, same version for everyone. A personal Skill in your own user config is like a note in your private notebook — it travels with you across every project but nobody else sees it. And a plugin-bundled Skill is like a published library on an internal registry — anyone across the org who installs the bundle picks it up. Same file, three different blast radii, chosen by where you put it.
The mistake here is thinking of distribution as a mechanical afterthought — just git add and move on. It's actually a governance decision, and picking the wrong scope has real consequences. Commit a half-baked Skill to the project scope and you've foisted your experiment on everyone's session; keep a genuinely useful one personal and the team keeps duplicating it. There's also a security dimension the moment a Skill leaves your machine: a shared Skill's bundled scripts run with each installer's agent permissions, so distributing one is closer to publishing a dependency than emailing a doc — it deserves the same review. The table below lays the three scopes side by side; match the scope to how widely the know-how should actually travel.
.claude/skills/ ships to everyone who clones it; a plugin-bundled Skill reaches anyone across the org who installs it. Same file, chosen reach.
Read it top to bottom: each bar is one scope, and each is wider than the one above to show a bigger audience — its "blast radius".
- The narrow top bar is Personal: a Skill in your own user config. It follows you across every project, but no teammate sees it.
- The middle bar is Project: committed into a repo's
.claude/skills/. Clone the repo and it's just there — same version for everyone on that repo. - The widest bottom bar is Plugin: bundled and published so anyone across the org who installs it picks it up.
- The payoff is that the same Skill file gets a different reach purely by where you place it — so choosing scope is a governance decision, not an afterthought.
In short: Personal → you; Project → your repo; Plugin → the org. Match the width to how far the know-how should really travel.
| Scope | Location | Who gets it |
|---|---|---|
| Project | .claude/skills/ (in the repo) | everyone on the repo |
| Personal | your user Claude config | you, everywhere |
| Plugin | a shared plugin bundle | anyone who installs it |
6 · Professional — write a triggering description professional
Here's the most heartbreaking way a Skill fails: it works perfectly and never runs. The body is flawless, the bundled script is tested, the folder is committed — and then it just… sits there. Weeks pass. Nobody's postmortems improve. The Skill is dormant, because the one line that decides whether Claude ever opens it — the description — was too vague to match anything. All that effort, invisible. This is far and away the number-one Skill bug in practice, and the whole cure lives in a single sentence you have to get right.
Recall from the anatomy section that the description is the only part of a Skill always in context — the sticky note on the folder. That makes it the entire trigger signal: Claude fires the Skill by matching your task against that one line and nothing else. So the description isn't a summary, it's a trigger, and those are different jobs. A summary answers "what is this?" ("Helps with spreadsheets"). A trigger answers "when should I reach for this?" ("Use whenever the user asks to create, edit, or export an Excel file"). The failing descriptions are the ones written as summaries — they describe a capability floating in the abstract, giving Claude no situational cue for when it applies, so it never applies.
The reliable pattern is to name the situation in the user's own words: lead with "use when" or "use whenever the user asks for…" and then list the concrete nouns a real person would type — report, review, summary, postmortem, RCA, clean, format. Those task words are the hooks Claude latches onto. The opposite mistake, worth guarding against too, is a description so broad it fires on everything and collides with your other Skills — a trigger that's always on is as useless as one that's never on. Aim for the narrow band in between: specific enough to fire exactly when it should, and not a token more. The lab below is a small "linter" that scores a description on precisely these traits, so you can catch a dead trigger before you ship it rather than months later.
Two side-by-side panels answer the same question — "does this Skill fire on the task at the top?" — with opposite outcomes.
- The left red panel is a description written as a summary: "Helps with spreadsheets." It says what the Skill is but gives no cue for when to use it, so the arrow leads to "never fires · dormant".
- The right green panel is a description written as a trigger: "use whenever the user asks for a report…". It names the concrete nouns a real person types, so the arrow leads to "fires at the right moment".
- The colours carry the verdict: red = the heartbreaking dead trigger, green = the working one. The only difference is phrasing.
In short: Write the description around when ("use whenever the user asks for X") with real task words — that is the entire difference between a Skill that runs and one that silently never does.
desc_score.pydef score_description(desc):
d = desc.lower(); score, notes = 0, []
if "use when" in d or "use whenever" in d: score += 1
else: notes.append("say WHEN to use it (\"use whenever...\")")
if len(desc) >= 40: score += 1
else: notes.append("too short — name concrete triggers")
if any(w in d for w in ["report","review","summary","writeup","clean","format"]):
score += 1
else: notes.append("name the task nouns users will say")
return score, notes
print(score_description("Writes stuff"))
print(score_description("Write an incident report/postmortem. Use whenever the user asks for an RCA or postmortem writeup."))
(0, ['say WHEN to use it ("use whenever...")', 'too short — name concrete triggers', 'name the task nouns users will say'])
(3, [])
The lesson's biggest warning is that a Skill with a weak description never triggers. This little program is a linter for descriptions — it scores one out of 3 and tells you what's missing, so you can fix a trigger before shipping it.
d = desc.lower()makes a lowercase copy so the checks are case-insensitive.score, notes = 0, []starts the tally at 0 with an empty list of complaints.- Each
ifawards one point for a good trait: it contains "use when/whenever" (says when to fire); it's at least 40 characters (specific, not a stub); and it mentions a concrete task noun (report, review, summary...). When a check fails, theelseappends a note explaining the fix. - It
returns the pair(score, notes). The twoprintlines score a deliberately bad description ("Writes stuff") and a good one.
What the output means: (0, [...three complaints...]) for "Writes stuff" — it earns no points and gets told exactly what to add. The concrete one scores (3, []): full marks, no complaints. The exercise asks you to reach a score of at least 2.
Try this: Paste your own draft description into a third print(score_description(...)) and aim for 3. Adding the words "use whenever the user asks for a ... report" usually flips all three checks to pass at once.
7 · Tech-lead — a Skills library for the org tech-lead
Think about what walks out the door every time a senior engineer leaves. The way they wrote incident reports, the SQL conventions they enforced in review, the release-note format everyone quietly copied from them — years of "how we do things here" that lived only in their habits. Onboarding a new hire means someone re-teaching all of it by osmosis, imperfectly, over months. A curated Skills library is the tech-lead's answer to that leak: it captures the team's recurring know-how as code you can commit, so it survives turnover and every new person inherits it on day one instead of year one.
The mental model that matters at this altitude is that a Skills library is a shared codebase, and it needs the same governance. A folder of uncoordinated Skills rots exactly like a folder of uncoordinated scripts: nobody owns them, two of them quietly overlap and fight to trigger on the same task, and half of them stopped firing months ago and no one noticed. So the lead's job isn't to write every Skill — it's to run the library like a maintainer runs a repo. One clear owner per Skill. Descriptions deliberately kept non-overlapping so triggering stays predictable and two Skills never collide. Review before merge, versioning, and a quiet deprecation path for Skills that have gone dark.
The judgement call underneath all of it — and the common mistake juniors make — is deciding what even deserves to be a Skill. The temptation is to skill-ify everything, which just recreates the sprawl you were trying to avoid. The discipline is a decision rule: promote know-how to a Skill only when it recurs, is situation-specific enough to write a clean trigger for, and ideally benefits from a deterministic bundled step. One-off work is cheaper as a direct instruction; borderline cases often belong in a slash command or a line of CLAUDE.md, not a full Skill. Get that gate right and the library stays lean and trustworthy; skip it and you're back to tribal knowledge, just with more folders. The exercises that follow put both halves — the decision rule and the governance policy — into practice.
CLAUDE.md. The gate is what stops skill-ifying everything.
Follow the arrows from the top box down — it's a two-question filter that routes each piece of know-how to the right home.
- The top box asks the first question: does this recur? If no, the arrow goes left to a red box — it's cheaper as a one-off direct instruction, not a Skill.
- If yes, you drop to the second question: is it situation-specific enough for a clean trigger, or does it need a deterministic (code) step?
- A clear yes routes to the green → a Skill box — this is the only path that earns a full Skill.
- A borderline case routes to the amber box: a slash command or a line of
CLAUDE.md— lighter than a full Skill. - The payoff is that most know-how doesn't reach the green box, which is exactly what keeps the org library from sprawling.
In short: Recurs + specific/needs-code → Skill; one-off → prompt; in-between → slash command or CLAUDE.md.
Exercise AK4.1 — Author & score a Skill
Context: Authoring a real skill forces the two things that make skills work: a description specific enough to trigger reliably, and a bundled helper that does deterministic work the model can reason over.
Your task: Author a SKILL.md for a recurring task with a precise trigger (score it ≥2 with desc_score), bundle one reference file and one runnable helper, and confirm it fires only when it should.
Requirements:
- The skill targets a recurring task you already do a particular way
- The
descriptionis a precise trigger — it scores ≥2 withdesc_score - It bundles one reference file and one runnable helper script, and the helper is tested
- Confirm the skill fires when you ask for that task
- Confirm it stays dormant on unrelated tasks
💡 Hint: Test both directions — that it triggers on the target task and that it stays quiet otherwise — a skill that always fires is as broken as one that never does.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: A Skill is just a folder with a SKILL.md: frontmatter that says what it is and when to load it, plus a body of guidance — the minimum viable unit of reusable know-how.
Your task: Write a minimal SKILL.md for a skill that formats commit messages, including its name and description frontmatter.
Requirements:
- The skill is a folder containing a
SKILL.md(e.g..claude/skills/…/SKILL.md) - Frontmatter includes a
nameand adescription - The
descriptionsays when to load the skill, not just what it does - The body holds the actual guidance (the commit-message format rules)
- Verify the exact frontmatter keys and folder location in the docs
💡 Hint: Name + description + body is the whole minimum — the description is what tells Claude to reach for it.
Show solution
The whole skill is a folder plus a SKILL.md with frontmatter + body:
# .claude/skills/commit-style/SKILL.md
---
name: commit-style
description: Write conventional-commit messages. Use when composing a git
commit message from a diff.
---
Format: `type(scope): summary` where type is feat|fix|docs|refactor|test.
- Summary in imperative mood, <=72 chars, no trailing period.
- Add a body only when the "why" isn't obvious from the summary.
The name identifies the skill and the description tells Claude when to load it; the body is the actual guidance. That is the minimum viable skill. Verify the exact frontmatter keys and folder location in the docs.
Context: Progressive disclosure is how a large skill library stays cheap: only the one-line description sits in context by default, and detail loads on demand.
Your task: Explain the progressive-disclosure tiers and model which parts of a skill are in context at each stage.
Requirements:
- Tier 0 (always): only the skill's
descriptionline is resident - Tier 1 (on use): the full
SKILL.mdbody loads when the task matches - Tier 2 (on demand): bundled files/scripts load only when the body references them
- Model the context cost per stage (idle vs triggered vs deep)
- Explain the payoff: you pay for detail on demand, not up front
💡 Hint: Three stages, growing context: description only, then + body, then + referenced files.
Show solution
Progressive disclosure keeps only the description resident until the skill is needed:
Tier 0 (always): the skill's `description` line only -> cheap, always in context
Tier 1 (on use): the full SKILL.md body -> loaded when relevant
Tier 2 (on demand): bundled files/scripts the body points to -> read as needed
def context_cost(stage):
return {"idle": "description only",
"triggered": "description + SKILL.md body",
"deep": "description + body + referenced files"}[stage]
print(context_cost("idle")) # description only
print(context_cost("triggered")) # description + SKILL.md body
Only the one-line description sits in context by default; the body loads when the task matches, and bundled files load only when the body references them. That is how a large skill library stays cheap — you pay for detail on demand, not up front.
Context: Bundling a script beside a skill lets it do deterministic work — parsing, stats — instead of asking the model to eyeball it, and the script costs nothing until the body references it (Tier 2).
Your task: Sketch a skill folder that bundles a Python helper script and references it from SKILL.md.
Requirements:
- The folder holds
SKILL.mdplus a bundled script (e.g. underscripts/) - The
SKILL.mdbody tells the model to run the script and how to invoke it - The script does deterministic work; the model reasons over its reliable output
- The script loads only when the body references it (Tier 2), so it's free until used
- Verify path conventions in the docs
💡 Hint: Let the script do the exact computation and let the model narrate its output — point to it from the body so it stays lazy-loaded.
Show solution
Bundle the script beside SKILL.md and point to it from the body:
.claude/skills/csv-report/
SKILL.md
scripts/summarize.py <- bundled helper
# SKILL.md body excerpt:
To summarize a CSV, run the bundled script:
python scripts/summarize.py <path-to-csv>
It prints row count, column types, and per-column null rates.
Read its output, then write the narrative summary.
Bundling code lets the skill do deterministic work (parsing, stats) instead of asking the model to eyeball it — the model runs the script and reasons over reliable output. The script loads only when the body references it (Tier 2), so it costs nothing until used. Verify path conventions in the docs.
Context: Because the description is the only part always in context, it is the entire trigger signal — a skill that names the situation fires precisely, while a vague one sits unused or fires on the wrong task.
Your task: Write a strong versus weak skill description and explain why the strong one triggers correctly.
Requirements:
- The weak version is vague about capability and gives no situational cue
- The strong version names concrete triggering situations (what the user asks for)
- It prescribes when to use the skill, not only what it does
- Explain that the description is the only always-in-context part, so it is the whole trigger
- Tie the specificity to firing at the right moment and not on the wrong task
💡 Hint: Describe the user's situation the skill answers ("when the user asks to…"), not the feature list.
Show solution
The description must name the situation, not just the capability:
Weak: description: "Helps with spreadsheets."
-> too vague; the model can't tell when it applies.
Strong: description: "Generate .xlsx spreadsheets with formulas and charts.
Use when the user asks to create, edit, or export an Excel file
or a report as a spreadsheet."
-> names concrete triggers (create/edit/export .xlsx), so the
model loads it exactly when relevant.
Because the description is the only part always in context, it is the entire trigger signal. Prescribe when to use the skill (the user-facing situations) — not just what it does — so it fires precisely and doesn't sit unused or fire on the wrong task.
Context: A distributed skill runs with your agent's permissions, so vetting its bundled scripts is a security step, not a formality; versioning and pinning stop an upstream change from silently altering everyone's behavior.
Your task: Decide the packaging, versioning, and pre-install checks for sharing a skill across the org.
Requirements:
- The skill is a self-contained folder (
SKILL.md+ any scripts) - The description is specific enough to trigger correctly
- Bundled scripts are reviewed like code — they run with the agent's permissions
- No secrets and no calls to untrusted hosts inside the scripts
- Versioned in a shared repo/registry and pinned on install
- Ships with a short README: what it does, when it triggers, what it can touch
- Verify the current distribution/registry mechanism in the docs
💡 Hint: Treat it like importing a dependency that runs as you — review the code, pin the version, document the blast radius.
Show solution
Package it as a versioned folder and review it like code:
Distribution checklist:
[ ] Skill is a self-contained folder (SKILL.md + any scripts).
[ ] Description is specific enough to trigger correctly (see expert rung).
[ ] Bundled scripts are reviewed — they run with the agent's permissions.
[ ] No secrets, no network calls to untrusted hosts inside scripts.
[ ] Versioned in a shared repo / registry; pin a version when installing.
[ ] A short README: what it does, when it triggers, what it can touch.
A distributed skill runs with your agent's permissions, so vetting the bundled scripts is a security step, not a formality. Version and pin it so an upstream change can't silently alter behavior for everyone. Verify the current distribution/registry mechanism in the docs.
Context: A skill earns its place when the know-how recurs and is clearly triggerable; the library's health depends on non-overlapping descriptions so two skills don't fight to trigger, plus single ownership and review.
Your task: As tech lead, write the decision rule for what becomes a skill and the governance policy for an org Skills library.
Requirements:
- The decision rule keys on: repeated, situation-specific, and whether it needs a deterministic step
- One-off work is not a skill — a direct instruction is cheaper
- Recurring + clearly-triggered → yes (bundle a script if deterministic work is needed)
- Borderline cases may be a slash command or a
CLAUDE.mdline instead of a full skill - Governance: one owner per skill, non-overlapping descriptions, review + version in a shared repo, deprecate skills that stop triggering
💡 Hint: Two tests decide it: does the know-how recur, and can you write a description that won't collide with another skill's trigger?
Show solution
Make skills for repeated, situation-specific know-how; govern to avoid sprawl:
def is_skill_worthy(repeated, situation_specific, needs_deterministic_step):
if not repeated:
return "NO — one-off; a direct instruction is cheaper than a skill"
if situation_specific or needs_deterministic_step:
return "YES — recurring, clearly-triggered; bundle a script if it "\
"needs deterministic work"
return "MAYBE — could be a slash command instead of a full skill"
print(is_skill_worthy(True, True, True)) # YES
print(is_skill_worthy(False, True, False)) # NO
# Governance: one owner per skill; non-overlapping descriptions (so two
# skills don't fight to trigger); review + version in a shared repo;
# deprecate skills that stop triggering.
A skill earns its place when the know-how recurs and is clearly triggerable; otherwise a slash command or a line in CLAUDE.md is lighter. The library's health depends on non-overlapping descriptions (so triggering stays predictable), single ownership, and review — the same discipline as a shared code library.
✓ Checkpoint — you can move on when you can…
- Explain when a Skill beats a prompt/CLAUDE.md.
- Write a valid SKILL.md with a triggering description.
- Explain progressive disclosure and its cost model.
- Distribute Skills and curate an org library.
Knowledge check check yourself
How does progressive disclosure keep Agent Skills cheap, and what is loaded into context before a Skill matches?
Show answer
name and description (tiny metadata labels) stay in context at all times; the full SKILL.md body loads only when a task matches. So comparing five Skills with none matched costs only the metadata (~100 tokens) instead of ~2600 tokens if every body were loaded.Why is the description in a Skill's YAML front-matter considered the trigger, and what makes one reliable versus one that never fires?