Chapter 9. Building a Claude Code Skill Pack That Compounds
A skill library compounds when each correction I make to an agent
becomes a skill, when every new public repo I create boots the pack from
a committed .claude/settings.json, and when a linter fails
on routes that point at skills the loaded surface does not have. It rots
through copying: installation copies rather than links, so a loaded root
starts diverging from its source repo the day after install.
The format is one directory with a file named SKILL.md
in it. The directory name is the skill’s identifier, the frontmatter
tells the agent when to load it, and the body tells the agent what to do
once it has. Everything else in this chapter is about what happens when
you write dozens of these, wire them to route to each other, publish
them, and let a fleet of agent sessions run against them for months. The
format stays tiny. The estate around it does not.
I run my whole software operation through this system. Each correction I make to an agent, each process I want repeated the same way twice, each “no, do it like this” ends up as a skill or an edit to one. The pack is where feedback stops being a conversation and becomes infrastructure.
The counts for one skill library, and what the gaps mean
Here is the inventory on this machine as of 2026-07-27, taken by reading the filesystem, not by trusting any manifest:
| Surface | Count | Location |
|---|---|---|
| Loaded user-level skills | 31 | ~/.claude/skills |
| Published public pack | 29 | ~/code/suede-creator-skills/skills |
| Authored source repo | 74 | ~/code/suede-personal-skills |
| Installed plugin cache | 27 | plugin cache, version 0.6.2 |
| Subagent definitions | 61 | ~/.claude/agents |
| Standalone slash commands | 3 | ~/.claude/commands |
Five different numbers for what a casual observer would call “the skills.” Each row is a different lifecycle stage, and the gaps between rows are the operational story.
The 74-to-31 gap is editorial. The source repo holds 74 authored
SKILL.md files; the loadable user root holds 31.
Forty-three skills exist that this machine cannot invoke. Some of that
is deliberate curation and some of it I cannot reconstruct from the
filesystem alone, which is its own finding: if you cannot tell a curated
absence from a failed install, your install process is not leaving
enough evidence.
The 29-to-27 gap is drift. The public pack’s source repo declares version 0.8.0 with 29 skills. The installed cache on this machine sits at 0.6.2 with 27. The two skills present in source and absent from the install are suede-deslop and suede-full-send. The marketplace manifest says “29 MIT-licensed skills” and the plugin manifest says “Installs all 29 skills,” and both claims are accurate against source while overstating what a machine on 0.6.2 receives. I wrote those claims. They are true and misleading at the same time, which is the exact failure mode a version-pinned distribution system produces when nobody schedules the update.
The mechanism behind the drift: installation copies, it does not
link. Not one entry under ~/.claude/skills is a symlink.
The loaded root starts diverging from its source repo the day after
install, and nothing complains. A copy-based install with no freshness
check is a drift machine, and I say that as the person who built
one.
The 61 subagents are the other half of the system: flat
.md files named by domain, 32 engineering, 10 security, 9
design, 8 testing, plus one stray. They share the frontmatter idea but
not the naming convention: skill names match their directory slug in all
60 skills across both roots, while 60 of the 61 agent files declare a
display name that does not match their filename. Two conventions grew up
side by side on one machine because nothing enforced either. Pick one on
day one.
Two fields
carry the whole pack: name and
description
Frontmatter across the whole estate reduces to two universal fields:
name and description. All 29 public-pack
skills carry exactly those two and nothing else. Optional fields exist
(model, allowed-tools,
argument-hint, user-invocable,
version, license, a metadata
map), but the load-bearing fact is that only one of them changed how I
operate: model: opus is pinned on five skills, and those
five are suede-think, suede-spec, suede-plan, suede-product, and
suede-debug. The judgment lane runs on the strongest model; execution
lanes inherit whatever the session is running. That one line of YAML is
the cheapest quality lever in the whole pack.
The description field does all the routing work. Body
sections never participate in trigger matching; a beautifully organized
body under a vague description is a skill that never fires. The
convention that survived contact with real use is three parts packed
into that one field: a plain statement of the job the skill owns, a “Use
when” clause listing the literal phrasings a user types, and a routing
clause pointing near-misses somewhere better. The routing clause has a
fixed shape:
NOT FOR: proving a finished feature works (use suede-verify);
reviewing a diff for quality (use suede-code-review).
That is the actual clause from suede-debug. The all-caps
NOT FOR: token followed by semicolon-separated pairs of
near-miss and referral is a house convention, not a platform
requirement, and adoption tells you how conventions decay: 14 of my 31
user-level skills carry it, 5 of 29 public ones do, and lowercase
variants (Not for, Don't use for) have crept
in. A convention that lives only in your head degrades one skill at a
time.
Organizing the pack into lanes
The pack is organized by lane, and a lane is a promise: any task in this category has a defined owner and a defined next step. Thirty-plus skills as a flat list is unusable, for me and for the model choosing among them.
The planning lane runs think, spec, plan, execute, debug: suede-think shapes a loose idea, suede-spec turns it into a PRD with acceptance criteria, suede-plan produces file-level tasks another agent can run without interpretation, suede-execute works the plan one commit at a time, and suede-debug takes over when something breaks. These live at the user level because they are how I work, not what I publish.
The quality lane is the public pack’s center of gravity: suede-code-review finds the bugs a diff can ship, suede-code-grader issues an A-F verdict, suede-code does both in one pass, suede-ship-gate wires CI and branch protection, suede-mcp-qa catches server drift, suede-ai-eval audits model-facing behavior. Copy has suede-copy for writing and suede-deslop for stripping generated-text patterns before anything goes public. SEO has suede-seo-audit, suede-visibility-grader, and suede-site-alchemy. Launch has suede-launch-packaging and suede-release-linter. The creator lane, the pack’s original audience, holds suede-campaign-in-a-box, suede-sync-packaging, suede-rights-passport, and suede-rights-audit.
The lane structure is what makes NOT FOR writable. You
can only route a near-miss when you know which neighbor owns it, and you
only know that when the neighborhood has a map. A pile of skills
produces descriptions that all claim slightly too much territory; a
laned pack produces descriptions that hand work off at the border.
Linting the pack: dangling routes and duplicate owners
Once skills reference each other by name, those references are dependencies, and dependencies rot. The pack carries its own enforcement: a bundled linter invoked as
python3 suede-skill-forge/scripts/lint_skill_estate.py <target-root> --external-root <path>which exits nonzero when a use X referral, a
NOT FOR ... (use X) route, or a $X sigil
points at a skill absent from the target surface, and when a relative
path under scripts/, references/,
agents/, or the other bundle directories points at a file
that does not exist. A second auditor,
python3 suede-skill-forge/scripts/audit_skill_overlap.py <root> \
--exclusive-root <label>=<path> --compare <label>=<path> --strictfails when the same skill name exists in two loadable roots, on the
stated grounds that two loadable owners make routing depend on loader
order. For public packs there is an escape hatch: the literal phrase
private Suede Labs companion, not in this pack: <skill-name>
lets a published skill acknowledge a private neighbor without failing
the dangling-route check. Publishing a subset of a private estate forces
that distinction, because your public skills will want to route to
companions the public cannot install.
Now the part that keeps this chapter honest: my estate currently fails its own audits. The machine’s project instructions advertise ten slash commands that resolve to nothing on any loadable surface: suede-verify, suede-progress, suede-roadmap, suede-arch, suede-docs, suede-ui, suede-map, suede-growth, suede-visual-qa, suede-public-claim-check. Two more names, suede-verification-law and suede-fable-standard, are routed to by other skills and also missing. suede-debug, a skill I use weekly, routes to suede-verify in its own frontmatter, and suede-verify does not exist here. Four skill names live in both the user root and the enabled public plugin at once: suede-codex-fleet, suede-design, suede-deslop, suede-full-send. That is the exact two-owners condition the overlap auditor exists to fail on, and by accident two of the four resolve anyway, because the stale 0.6.2 cache lacks deslop and full-send. The other two load twice, and which body a bare invocation gets is untested.
None of this shows up in daily use. When a route dangles, the agent falls back to doing the work inline, worse and without the checklist, and the session still ends with output. Routing debt is invisible at the point of failure. The linter is the only component in the system that can see a dangling edge, which means running it has to be a habit, not a reaction. I built the enforcement and then stopped running it, and the filesystem accumulated each class of defect the tooling was written to catch. Build the linter and put it in a loop.
Publishing the pack, and installing it from committed settings
A pack compounds when distribution stops being a per-repo decision. The mechanics, bottom to top:
A marketplace is a git repo with a
.claude-plugin/marketplace.json declaring plugins. One repo
can publish several plugins from the same source, carved by a
skills[] subset: suede-creator-skills declares three,
suede-skills with no skills[] key (which means all 29),
suede-agent-workflows with 4, and suede-code with 4. A plugin’s identity
is the composite <plugin>@<marketplace>, and
its skills surface at runtime under a colon namespace, so the public
code reviewer is invoked as suede-skills:suede-code.
On any given machine, registration lives in the runtime file
~/.claude/plugins/known_marketplaces.json. Mine lists five:
claude-plugins-official (anthropics/claude-plugins-official),
superpowers-dev (obra/superpowers), google-labs-code-stitch-skills
(google-labs-code/stitch-skills), suede
(JasonColapietro/suede-creator-skills), and addy-agent-skills
(addyosmani/agent-skills). Enablement is a flat boolean map under
enabledPlugins in ~/.claude/settings.json, 12
entries on this machine, keyed by the composite id.
The runtime file is machine state. The committed form is different,
and the committed form is the standard: every new public repo I create
gets a .claude/settings.json checked in on first commit,
registering the marketplace and enabling the pack:
{
"extraKnownMarketplaces": {
"suede": {
"source": { "source": "github", "repo": "JasonColapietro/suede-creator-skills" }
}
},
"enabledPlugins": { "suede-skills@suede": true }
}The effect is that any agent session opened in any of my public repos
boots with the full quality, copy, SEO, launch, and creator lanes
available, without anyone remembering to install anything. A skill
written once propagates to each future repo automatically. That is the
compounding in the chapter title, and it is a policy, not a technology:
the platform gives you extraKnownMarketplaces; the payoff
comes from making the committed file a non-negotiable part of
scaffolding.
The distribution layer has the same drift hazard as the install layer, and I documented it above: this machine runs the pack at 0.6.2 against a 0.8.0 source. A distribution standard needs a freshness check the same way a lockfile needs an update cadence. Committed registration solves “is the pack present”; nothing in it solves “is the pack current.”
Pruning a pack: keep the mechanic, drop the audience
On 2026-07-12 I cut 14 music-creator skills down to 3 broad ones. The 14 were built for musicians, my original audience thesis, and the usage data was blunt: musicians did not use Claude skills. Not “used them less than projected.” Did not use them.
The wrong response would have been deleting the work. The mechanics inside those skills were sound: scanning content for viral hooks, resurrecting old material, building a coherent brand world. What was wrong was the addressing. Fourteen descriptions all began, in effect, “for musicians who,” and the selector never had a musician in the session. So the surviving 3 keep the mechanic and drop the audience qualifier : virality-scanner, content-resurrection, and brand-world-builder. A hook-scanner works on a founder’s changelog as well as a chorus. The rewrite cost a fraction of the original build.
The routing system makes over-specific skills worse than useless. Each loaded description competes for the selector’s attention on each routing decision; 14 entries aimed at an audience that never arrives are pure noise in every session, tax without revenue. Pruning is not cleanup. It is what keeps the descriptions that do earn their slot from being crowded out. A pack compounds through the editing as much as the authoring, and the 74-to-29 gap between what I have written and what I ship is that editing made visible.
Other people’s packs, and what not to duplicate
Three of the five marketplaces registered on this machine are other people’s. Building your own pack and installing other people’s are the same discipline at different trust levels.
The first-party skills are the ones worth knowing by name, because
they cover the file formats that turn up in almost every workflow:
pdf for reading, filling, merging, and OCRing PDFs,
docx, xlsx, and pptx for the
Office formats, and skill-creator for authoring and testing
new skills. Those ship from Anthropic and are the baseline your own pack
should not duplicate. My own published pack covers the lanes those do
not: suede-code-review and suede-code-grader
for review and ship verdicts, suede-deslop for stripping AI
writing patterns before anything goes public,
suede-seo-audit and suede-visibility-grader
for search surfaces, suede-plan and
suede-execute for planned implementation, and
suede-debug for root-cause work.
The gap between those two sets is where your pack belongs. Do not write a fourth PDF skill. Write the one that encodes the correction you keep making.
What to check before installing someone else’s skill
A skill is instructions your agent will follow with your credentials, in your repos, on your machine. Curation on a list is not a review. Mine is:
Read the whole SKILL.md, not the description. The
description is marketing aimed at a router; the body is what the model
executes once loaded. If the body is too long to read before installing,
that is itself the finding: you are about to hand your agent standing
instructions you have not read.
Check what it executes. Look for a scripts/ directory
and read what is in it. Look for an allowed-tools field in
the frontmatter; the observed syntax is a tool-plus-glob permission
pattern, like Bash(npx impeccable *) or
Bash(node .claude/skills/impeccable/scripts/*). Those globs
are standing permission grants. Know what the commands inside them do,
and treat a glob broad enough to cover arbitrary commands as a refusal
reason.
Check what it phones home to. Plugins can ship MCP servers via a root
.mcp.json, and a server entry is a process launch: a
command, an args array, paths through
${CLAUDE_PLUGIN_ROOT}. Read them. A server is a capability
the pack runs alongside your session, and a skill whose job needs no
network should declare no server. Where the manifest gives the server a
name and description, check that the launch args match the story.
Prefer skills whose gates are advisory. The best third-party skills behave like a senior reviewer: they produce findings, checklists, and stop-and-confirm moments, and they leave the decision with you. Be slower to adopt skills that install themselves as hard blockers in your workflow or fire side effects on their own triggers. An advisory gate that is wrong wastes a read; an enforcing gate that is wrong halts work in a way you now have to debug inside someone else’s prompt engineering. Authority over your pipeline should be added by you, deliberately, after the skill has earned it in advisory mode.
And know the exit before the entrance. Deactivation in this system is
a move, not an uninstall: my disabled skills sit in a sibling
~/.claude/skills.disabled/ directory under dated names, and
retired subagents sit in ~/.claude/agents-archive/. The
dated directory name is the changelog. Cheap, reversible removal is what
makes trying a third-party skill a low-stakes decision in the first
place.
What the library is worth, and what upkeep it owes
The pack is the one asset in my estate that appreciates with use. Products drift toward their next redesign; skills accumulate corrections. When an agent gets something wrong and I fix the skill, the fix applies to each future session in each repo that boots the pack, and the committed-settings standard keeps widening that footprint one scaffold at a time.
The same review found the appreciation and the rot in one pass: 74
authored, 29 published, 27 installed, ten advertised commands that
resolve to nothing, four duplicate names, one broken symlink where a
slash command points at a SKILL.md that no longer exists.
Both lists came from the same filesystem read, and both are the normal
condition of a system that compounds. The pack pays for its own upkeep.
The counts at the top of this chapter are the receipt, gaps included,
and the linter run I owe the estate is scheduled before the next skill
gets written.