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> --strict

fails 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.