Case Study · Claude Code Plugin for Design Systems

Every component change costs half a day.
What if it took thirty seconds?

Forge is a Claude Code plugin that lets any engineer drive an entire React+TypeScript component library by talking to Claude — recommend the right component, add a prop, scaffold a new one, retire an old one, or score the whole thing. Every change ships as a real GitHub PR with docs that regenerate from source, so the design system can't drift.

See the source
Muzeeb Urrahaman Solo — spec, plugin, all scripts v1.0.0 · Installable Claude Code · MV3-style plugin
5
Skills · use / edit / create / delete / audit
59
Components in the source-verified brain
456
Design tokens auto-documented
4
Real PRs shipped in the pilot
22/22
Playwright e2e green · 0 console errors
0
Secrets can leak · scoped staging
In one line

Forge is a Claude Code plugin that lets an engineer talk their component library into shape — recommend a component with real props, add a prop non-breaking, scaffold a new house-style component, or score the whole library against a written standard — and every write ends by regenerating docs from source and opening a real PR.

The pain: every component library, six months in, has docs that lie. Someone shipped a loading prop; Storybook doesn't know. A team renamed a variant; the design token page still shows the old name. New engineers waste days reading a spec that no longer matches the code. Forge stops that decay by inverting the flow: you don't write docs after the code — every edit, create, and delete regenerates the doc for that component from source, in the same commit. The docs literally cannot drift because the same action that changes the code rewrites its section.

The signal came from watching it happen at OmnisAI. Storybook lagged real PRs by weeks, and new engineers kept rebuilding primitives that already existed because they couldn't find them in a spec that no longer matched the code. The instinct is to add more doc discipline — more review, more reminders. I concluded the opposite: discipline decays, so the only durable fix is to make drift structurally impossible. That decision — regenerate from source in the same commit — is the whole product, and it came from the failure mode, not a feature list.

Problem

  • Design-system docs drift silently from code. Storybook lags every real PR by weeks.
  • New engineers reinvent primitives because they can't find the ones that already exist.
  • Adding a prop or fixing a token is a half-day round-trip: code, then docs, then a PR, then a review, then updating tokens.

Solution

  • Five skills over a single markdown brain: use / edit / create / delete / audit.
  • Every write regenerates the brain from source, then opens a PR. Docs and code ship together, or not at all.
  • A written standard (naming, tokens, a11y, deprecation) is enforced on every action.

Results

  • 4 real PRs shipped in the pilot — Button standardization, Divider, Drawer, OTPInput — every one non-breaking.
  • Foundation tokens (spacing, radius, type) added; 461 off-vocabulary references catalogued for migration.
  • Whole library audited: 59 components scored, ranked fix queue, tokens documented — health finally measurable.
The wedge
Why this exists

Everyone writes docs after the code. Regenerating docs from the code is the whole product.

Design-system docs — Storybook, MDX, whatever — are written after the fact. An engineer ships a component or a new prop, and later someone (maybe them, usually nobody) updates the doc. The two are always slightly out of sync, and the gap widens with every merge. Six months in, the spec and the code disagree on names, defaults, and even which components exist, and every new engineer hits it on day one.

Forge's bet is that the answer isn't better docs — it's not writing them separately at all. There's one markdown brain, DESIGN-SYSTEM.md, and every write skill ends by regenerating the touched component's section from real source. Skills read it before they answer, so they cannot recommend a prop that doesn't exist or a component that isn't in the library. When a new Toast lands, /forge:use knows about it the next second. The docs and the code are one artifact, updated by the same action.

The brain is source-derived, not source-of-truth. Real code stays the truth. The brain is generated by a Python script (rebuild_brain.py) that parses every component's props interface, JSDoc, and CSS tokens — so it lags source by at most one commit. Skills never invent props; they can only quote the brain, and the brain can only quote source. That chain is the whole product.
What it does
The job, plainly

Five verbs. That's the whole surface.

One skill per real design-system task, plus a matching slash command. They all read the same brain and the same written standard, so the answers are consistent whether an engineer types /forge:audit or says "score the library."

Use

Recommend the right component + props

Answers "which component for a confirm dialog?" or "what props does Button take?" — only from the brain. Cannot invent props. Returns a wiring snippet lifted from real stories.

Edit

Add a prop, non-breaking, then PR

Adds a typed prop with a safe default, keeps old props as @deprecated aliases, updates JSDoc + tests + brain section, opens a real PR. Never renames in place.

Create

Scaffold house-style, brain-first

Reads 2–3 sibling components for style, scaffolds tsx + css + tests, references only tokens (no raw hex/px), adds the brain section, opens a PR. New components are born standard-compliant.

Delete

Retire safely, deprecate-first

Greps every call site, defaults to marking @deprecated and keeping it exported, hard-deletes only after explicit "yes break it" confirmation. Consumers can't be surprised.

Audit

Score the library, ranked fix queue

Scores every component against the standard: naming, token vocabulary, size scale, prop completeness. Emits a ranked fix list ready to hand back to edit-component.

Self-updating

The docs regenerate themselves

Every edit/create/delete ends by running sync_design_system.py — the component's brain section is rewritten from source in the same commit as the code. There is no "update the docs" step to forget.

Config

One plugin, any library

A per-repo forge.config.json declares package name, components directory, tokens CSS, PR base branch, naming rule. Point Forge at any React+TS library and it adapts.

Standard

Rules the whole team reads

component-standard.md encodes naming, tokens, a11y gates, deprecation policy, prop completeness by type. Skills load it every run; audits score against it.

How it works
The flow

Talk to Claude. Real files change. A PR opens.

Nothing hides behind a service. The plugin installs at the user level; each write is deterministic — the skills read the standard, mutate real files, run a Python script to regenerate the brain, and shell out to gh to open the PR. Here's the whole path for a typical /forge:edit:

1

Load the rules

The skill reads forge.config.json, the written component-standard.md, and standing-guidance.md (durable corrections). It refuses to work on a component that isn't in the brain.

2

Read the source + siblings

Opens the target component's tsx, css, stories and tests. Opens 2–3 stylistically-nearest siblings so the change matches house style, not generic React defaults.

3

Make the change non-breaking

Adds new props with safe defaults; keeps old names as @deprecated aliases. Never renames in place. Migrates raw hex/px to tokens opportunistically. Fixes dead props if it sees any.

4

Regenerate the brain section

Runs sync_design_system.py. The component's ## Name block in DESIGN-SYSTEM.md is rewritten from the just-changed source — props, defaults, JSDoc, tokens referenced, usage snippet from stories.

5

Open the PR into main

open_pr.sh branches (forge/edit/<name>-<slug>), stages only the files Forge touched via --paths (never git add -A), commits, pushes, runs gh pr create. If gh is missing, it prints exact commands rather than fail silently.

The self-improvement loop

Two distinct loops. Neither one drifts.

Docs update mechanically; corrections stick as durable rules. Forge separates the two on purpose so neither depends on the other holding.

● Mechanical sync

After every edit/create/delete, sync_design_system.py rewrites the touched component's brain section from real source. Zero prose. No LLM involved.

docs = source, always
● Guidance capture

When a reviewer says "we always X, never Y," record_guidance.py appends a scoped rule to standing-guidance.md. Every future skill run reads it.

the mistake stops recurring
● Scoped staging

open_pr.sh --paths stages only files Forge touched. A local .env, WIP scratch file, or private key cannot get swept into a PR.

safe on shared machines
● Per-repo config

forge.config.json declares package name, component dir, tokens file, PR base, naming rule. One installed plugin adapts to any React+TS library.

install once, use anywhere
Before vs Forge
The difference that matters

"Add a loading prop to Button." Two ways.

The task itself takes ten seconds either way. What varies is everything around it — the doc drift, the review latency, the token debt, the sibling inconsistency. Left is the usual roundtrip; right is one command:

The usual roundtrip · ~4 hours
// 1. Open Button.tsx, add prop
interface ButtonProps { /* … */
  loading?: boolean;
}

// 2. Update the CSS — hope you match
//    the sibling's spinner class conv.

// 3. Update the Storybook MDX doc
//    (usually skipped — "later")

// 4. Update the design-token file
//    if you used a new color

// 5. Open a PR. Reviewer bounces
//    because the naming is off.
// 6. Fix. Push. Merge. Docs still lag.
With Forge · ~30 seconds
$ /forge:edit add loading to CommonButton

// Forge:
//   ✓ read standard + sibling siblings
//   ✓ add prop non-breaking (default false)
//   ✓ update JSDoc + stories
//   ✓ regenerate brain section
//   ✓ commit + push + gh pr create

// You get:
→ PR #42 · main ← forge/edit/commonbutton-…
// Docs already accurate. Reviewer
// reviews. Merge.

The honest trade-off: Forge cannot judge taste. It won't tell you a Toast is the wrong pattern or that a prop should be a variant instead. It can enforce naming, tokens, a11y baselines and non-breaking rules — the mechanical stuff that eats a review. Taste stays with the reviewer; drift stops.

These are real runs from the pilot — same engine, four different verbs:

Judgment calls
The interesting decisions

The interesting parts were about restraint and refusing shortcuts.

Anyone can auto-generate React components. The decisions that make Forge trustworthy in a real repo were about what it refuses to do:

The self-documenting brain is the moat. Anyone can write a "generate component" GPT wrapper. Forge's edge is rebuild_brain.py + sync_design_system.py + the audit engine — Python that parses real TypeScript props interfaces, JSDoc, destructuring defaults, and CSS token usage into a source-verified markdown brain, on every write. That measurement layer is the slow part for anyone to copy.
The stack
Under the hood

A Claude Code plugin, five skills, seven scripts.

No framework, no build step for the plugin itself. Plain JSON manifests, markdown SKILL files, and Python/bash scripts a reviewer can read top to bottom. Every file has one job:

File / folderWhat it does
.claude-plugin/plugin.jsonPlugin manifest — name, version, repository. Read by Claude Code on install.
.claude-plugin/marketplace.jsonMarketplace entry — enables /plugin marketplace add OmnisAIOrg/OmnisDesign-OS. Name matches the github clone dir for zero-friction install.
skills/{use,edit,create,delete}-component/SKILL.mdThe four write/read skills, each with a frontmatter description that routes natural language to it. Full workflow in prose — Claude reads and executes.
skills/audit-library/SKILL.mdRead-only scorer. Wraps audit_components.py + component_readiness.py.
skills/_shared/references/component-standard.mdThe written standard. Naming, prop API, canonical token vocabulary, ARIA gates, deprecation policy, prop completeness by type. Every skill reads this before acting.
skills/_shared/references/standing-guidance.mdDurable corrections — "we always X, never Y." Appended by record_guidance.py. Read on every run.
skills/_shared/DESIGN-SYSTEM.mdSeed brain shipped with the plugin. The canonical brain lives in each consumer repo, regenerated on every write.
skills/_shared/scripts/rebuild_brain.pyThe core engine. Walks components, parses props interfaces + destructuring defaults + JSDoc, extracts token vocabulary from the CSS, emits a fully-source-verified brain.
skills/_shared/scripts/sync_design_system.pySingle-component brain refresh. Runs at the end of every edit/create/delete.
skills/_shared/scripts/audit_components.pyScores components vs the standard's §9 checklist. Emits ranked fix queue.
skills/_shared/scripts/component_readiness.pyMeasures token debt (raw hex, off-vocab, unparseable interfaces) across the whole library.
skills/_shared/scripts/forge_init.pyOne-time setup — auto-detects package name, components dir, tokens CSS, writes forge.config.json.
skills/_shared/scripts/record_guidance.pyAppends a scoped correction to standing-guidance.md.
skills/_shared/scripts/open_pr.shBranch → scoped stage (never git add -A) → commit → push → gh pr create. Injection-safe config read. --no-push option.
commands/{use,edit,create,delete,audit}.mdSlash-command wrappers. Route /forge:X to the matching skill.
Try it in three commands

Install once. Init per repo. Then talk to Claude.

The whole install is public. Two /plugin commands from any Claude Code session, one forge init in the target repo, done:

# 1. In any Claude Code session — two separate submits:
/plugin marketplace add OmnisAIOrg/OmnisDesign-OS
/plugin install forge@OmnisAIOrg-OmnisDesign-OS

# 2. In the component-library repo, tell Claude:
"initialize forge for this repo"
   # forge_init.py auto-detects paths, writes forge.config.json,
   # regenerates the brain from source. One-time.

# 3. Now just talk:
"add a loading prop to Button"       # /forge:edit
"create a Toast component"            # /forge:create
"which component for a confirm dialog?" # /forge:use
"audit the library"                   # /forge:audit
Results
Where it stands

Shipping, installed, four real PRs in.

Forge v1.0.0 is installed at OmnisAI, tested end-to-end with a real component library, and has shipped four real PRs during the pilot — every one non-breaking, every one with source-verified docs updated in the same commit. The plugin passes claude plugin validate and installs from github with a single command.

The leverage is the point: this isn't a tool only I use. Any engineer on the team drives the whole 59-component library in plain language — no design-system gatekeeper in the loop — and every change ships as a reviewable PR. It turns a one-person standard into something the whole team maintains without me, which is the difference between output and leverage.

Its own edge states, handled: audit on an empty or unconfigured library returns a clear "nothing to score yet" rather than crashing; a change that would break consumers is blocked — it deprecates first and only hard-deletes after an explicit "yes, break it"; and a failed generation or PR step aborts cleanly without half-writing files.
4

real PRs in the pilot

CommonButton standardization, Divider, Drawer, and OTPInput — all non-breaking, all with brain sections regenerated in the same commit. Zero manual doc updates.

22/22

Playwright e2e green

End-to-end tests hit the demo pages for every Forge-generated component. Zero console errors on any surface. Also caught two pre-existing library bugs (InputField hydration, Calendar SVG attrs) which Forge fixed on the way.

13

bugs found + fixed in QA

Seven parallel agents edge-tested each script (single-line interfaces, unicode, empty repos, path traversal). All confirmed bugs fixed. Security-reviewed: scoped staging, injection-safe config read, no secret handling anywhere.

The PRs Forge shipped

Real changes, on the real library.

Each of these was one prompt, then Forge did the rest — code, tests where present, brain section, PR body:

PR #42
CommonButton standardization

Added Button alias, colorType axis, canonical sm|md|lg. All non-breaking. Existing consumers unaffected.

PR #43
Divider primitive

New component from scratch. Token-referencing CSS, ARIA role="separator", keyboard-accessible. House-style matched from siblings.

PR #44
Foundation tokens

Filled a hole in the design system — added missing spacing, radius, and type-scale tokens. 461 off-vocabulary references catalogued for future migration.

PR #45
OTPInput + Drawer

Two new house-style components. OTPInput has auto-advance, numeric guard, aria-labelled cells. Drawer opens with role="dialog" and escape-to-close.

Reflection
Reflection

The approach I inverted — and what still isn't solved.

Every prior attempt at this, mine included, wrote docs after the code and watched them rot. I stopped patching that and inverted the flow: the same action that changes a component regenerates its doc in the same commit, so drift is structurally impossible. The honest gap that remains: the plugin runs real git and shell, so a careless action could leak secrets or push to the wrong place — that's why writes are scoped to staging and destructive ops need explicit confirmation. It's contained, not fully solved.

What I'd do differently: I validated it on one library — OmnisAI's — so the standard is opinionated toward that house style. Before calling it general, I'd run it against a second, differently-structured library to find exactly where the "one standard" assumption breaks.

What's next
What I'd do next

Ordered by value, not novelty.

The through-line. Everything future goes through the same door: docs regenerate from source, PRs are non-breaking, tokens are the vocabulary, the standard is the referee. If a proposed feature can't respect all four, it doesn't ship.
Interview brief
Anticipated questions

The things people probe for.

Isn't this just Storybook autodocs with extra steps?
No — Storybook autodocs still assumes the doc is a separate artifact you generate and deploy. Forge's brain is read by the skills on every action. When /forge:use answers "which component for X," it's quoting the brain, which was regenerated on the last edit — so it cannot recommend a prop that doesn't exist yet or forget one that was just added. Storybook lags; Forge's brain does not.
What if Claude picks the wrong sibling and matches the wrong style?
It can happen — that's why the standard exists as a separate file. Even if the sibling pick is off, the change still has to pass the standard's checklist (naming, tokens, size scale, a11y). And every write ships as a PR — a human reviewer catches taste-level issues before merge. Forge is a good engineer, not the final reviewer.
Can it push directly to main and skip review?
No. Every write ends by opening a PR into whatever git.prBase is configured (usually main, but any branch). Forge never merges. The engineer (or CI) merges after review. The plugin's job stops at the PR.
Doesn't the --paths stage-list make it fragile?
It's the safest thing about it. Every write skill knows exactly which files it touched — the tsx, css, brain, barrel, sometimes tests — and passes them explicitly. If a developer has a local .env, WIP scratch file, or a secret in the working tree, none of it can leak into a Forge PR. This is verified by a security test that puts a fake .env in a repo and asserts it's never staged.
What happens when the library is huge — thousands of components?
The brain rebuild is per-component when triggered by a write (sync one section), and full-rebuild only when explicitly asked. Full rebuild on ~60 components takes about a second. At thousands, it'd take a few seconds — still fine. The bigger question is whether a single markdown file is the right shape at that scale; a segmented brain (per category) is a straightforward extension.
Does it work on any React library or just OmnisAI's?
Any React+TS library that has components as directories under a configurable root. forge.config.json declares the package name, components directory, tokens CSS, PR base, naming rule, and token prefixes. Run forge init in a new repo, it auto-detects, and you're done. Nothing about the plugin is hardcoded to OmnisAI.
What's the failure mode when things go wrong?
Visible, not silent. If gh is missing, open_pr.sh prints the exact push + PR commands rather than failing. If a component isn't in the brain, the write skill refuses instead of inventing one. If the standard and a component disagree, the audit says so and asks you to fix the migration map, not special-case the check. Every wrong-answer branch is designed to say "I don't know" rather than guess.