Vercel Ship
26

How /extract-ds-skill works

A meta-skill that turns a real design system into an installable Claude Code skill. Three phases, one human gate, every rule cited.

What it produces

A directory at .claude/skills/<slug>/ inside your project. It contains a SKILL.md, an AGENTS.md, per-component reference files, a tokens file, an assets file, and a verbatim design-craft reference. You commit it alongside your code, review it in PRs, and roll it back with git.

It is not a copy of your DS docs. It is an adapter. It tells the agent what to read, what APIs are public, what sources are authoritative, and how to verify that generated UI actually uses the system. See Anatomy of a produced skill for every file that lands in the tree.

Three phases, one gate

Phase 1 · Discovery

Inspect, classify, propose

The skill reads the sources you point at and classifies each by role: [code], [docs], [docs:foundation], [example:project], [storybook], [figma], [private-blocker]. It auto-discovers the component exports and renders a single discovery summary inline, capped at 30 lines.

One line in that summary matters most: Components found (N), proposing (M). You see the full surface and the proposed slice. You adjust the slice before anything else happens.

The summary closes with a confirmation prompt: "Confirm or adjust? Reply 'go' to accept defaults."

Cutoff: fresh session, resume with validate:. See Handoffs and fresh sessions.

Phase 2 · Validate (scratch only)

Prove the extraction grounds in real source

Everything lives in .extract-ds-skill-scratch/, which is gitignored. Nothing writes to .claude/skills/<slug>/ yet. Iteration is cheap and partial state never lands in your repo.

scripts/validate.sh runs a deterministic check. It typechecks the extracted component contracts against the DS package's published types. It greps every cited token name against the source token file. It greps every cited asset name against the asset package. It counts [VERIFY] markers.

The output is a single proof-point line you can read at a glance:

14 props verified, 47 tokens grep-resolved,
0 assets in scope, 6 foundation-rules extracted,
TOKEN_COVERAGE=PASS, 0 hallucinations,
3 open [VERIFY] markers

◼ The single human gate ◼

Phase 3 · Persist

First and only write to .claude/skills/<slug>/

A slug-collision check runs first. If the slug is taken, the skill asks you what to do. It never silently appends -2. After the check, the scaffolder writes the skill files. Then scripts/check-skill-docs.sh runs, and its stdout is the audit result. On exit 0 you get a closing message with two or three example prompts and the final [VERIFY] tally.

Why only one gate? Phase 1 is read-only inspection, so there is nothing to gate. Phase 3 writes to disk and offers no rollback inside the skill itself. The boundary between Phase 2 and Phase 3 is the only place where the cost of being wrong is high and there is something concrete to judge: the proof-point line. Adding a second gate would train reviewers to rubber-stamp both.

The source-of-truth contract

This rule explains why the skill exists in this shape:

Every extracted rule cites source by file:line, or carries a [VERIFY] marker, or is dropped. A rule with no citation is a hallucination waiting to surface in someone else's PR.

When code and docs disagree, code wins. A docs page that claims a prop the types file does not export is a docs bug, not a feature. The divergence gets logged in the discovery summary so the DS team can fix it.

Copy rules belong elsewhere. Title Case button labels and action-oriented placeholder phrasing are routed to a sibling copy skill. They are recognised during extraction and surfaced in the summary, but never written into the DS skill.

The reflexive audit

There is no separate audit verb. The skill audits itself in three layers.

LayerWhenWhat
A · pre-emitJust before scaffold.shThe agent re-reads the audit section and pastes a visible six-point tick-list into the message: all eight required sections present, every rule cited, scope clean, routing rows resolve, slugs follow the registry, examples lift from real files.
B · in-flightDuring extractionAny rule the agent cannot back up with source code gets a literal [VERIFY] inline. The running tally surfaces in the Phase 2 proof point and again in the Phase 3 closing message.
C · post-emitAfter Phase 3 writesscripts/check-skill-docs.sh asserts every routing row resolves, every slug is registered, every component file has all eight sections, every [VERIFY] is surfaced. The exit code is the audit result.

Why fresh sessions between phases

Each phase cutoff prints a handoff path and exits. You re-enter Phase 2, and later Phase 3, by starting a fresh Claude Code session and running:

/extract-ds-skill validate: .extract-ds-skill-scratch/handoffs/phase-1.md
/extract-ds-skill persist:  .extract-ds-skill-scratch/handoffs/phase-2.md

The handoff captures every decision, so the new session does not re-ask. A fresh context window gives Phase 2 room for heavy WebFetch calls, token resolves, and exemplar lifts. It gives Phase 3 room for the slug check, the scaffold, and the post-emit script.

A continue inline override exists if you want to push through in one session, but the default is fresh, and the default is almost always right.

What to take away

  1. The shape. Three phases, one gate, scratch then persist.
  2. The contract. Cite file:line or mark [VERIFY]. No exceptions.
  3. The role taxonomy. [code] beats [docs] on conflict. [docs:foundation] opts into prose-rule extraction. [example:project] opts into verbatim wiring lift.
  4. The audit. The rules are the rubric. The script's exit code is the truth.
  5. The output. A committed, reviewable, rollback-able directory in your own repo.

Invocation shape

From your project root, pointed at your DS:

/extract-ds-skill make a skill from @acme/ui,
  using https://acme.dev/docs/foundations
  and the reference project at apps/marketing/

You will get a discovery summary back. Read it. Adjust the proposing set if needed. Reply go.


Primary source: .claude/skills/extract-ds-skill/SKILL.md in this repo. References are loaded progressively on gate. The skill does not pre-read them, and neither should you.