From Storybook Workbench
Hub for the Storybook CSF3 bundle — diagnose a repo, orchestrate the audit→stories pipeline, or name the next sb-* step. Use for 'set up Storybook', 'audit my app', 'where do I start', 'what's next'.
How this skill is triggered — by the user, by Claude, or both
Slash command
/storybook-workbench:sb-hubThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The hub. You don't author here — you **inspect state and route**. Three read-only modes; pick by
The hub. You don't author here — you inspect state and route. Three read-only modes; pick by
intent. Read CONTEXT.md once for shared vocabulary, the storage map, and the resume protocol.
Load by mode (don't pull both refs up front):
references/runbook.md — the navigator engine (state detection → one next step). Load it for
Mode 0 / Mode 2 (diagnose / "what's next").references/end-to-end-flow.md (~220 lines) — the full pipeline worked example for "messy app →
design system". Load it only for Mode 1 (orchestrate a full audit). Do NOT load it for a
Mode-0 onboarding check or a Mode-2 next-step lookup — runbook.md is all those need.Canonical context (one source). The bundle has exactly one CONTEXT.md (shared vocab · STORAGE
MAP · resume). It's never hand-duplicated: in the bundle/plugin install every skill reads this one file
(CONTEXT.md / ${CLAUDE_PLUGIN_ROOT}/CONTEXT.md); a standalone npx skills add <skill> ships a
byte-identical copy the build vendors, refreshed on update. sb-hub is the orchestrator — if skills
are installed separately, route through sb-hub so the pipeline + shared vocabulary stay authoritative.
Scripts live in scripts/. In this repo: ${CLAUDE_PLUGIN_ROOT}/scripts/.
When installed as a plugin, use ${CLAUDE_PLUGIN_ROOT}/scripts/.
CORE=${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}} # shared scripts: "$CORE/scripts/<name>"
| User intent | Mode |
|---|---|
| "is this ready", "where do I start", fresh/unknown repo | 0 · Onboarding check |
| "audit this app", "do the whole thing", "build Storybook for this app" | 1 · Orchestrate |
| "what's next", or after any verb appends to the ledger | 2 · Navigate (default) |
| "this is wrong", "strange behavior", "report a bug", a skill misfired | 3 · Report |
A fresh messy app: probe the stack + readiness before any verb, print a report, recommend the first step. No prompts, no writes — re-runnable.
echo "━━ Phase 0 — readiness ━━"
grep -q '"react"' package.json 2>/dev/null && echo " [ok] React" || echo " [warn] no React in package.json"
grep -qE '"vite"|"@vitejs' package.json 2>/dev/null && echo " [ok] Vite" || echo " [info] not Vite (skill targets React+Vite)"
test -d .storybook && grep -q '"storybook"' package.json 2>/dev/null && echo " [ok] Storybook present" || echo " [warn] NO_STORYBOOK → start with sb-setup"
test -d node_modules && echo " [ok] node_modules present" || echo " [warn] deps not installed — run your package-manager install"
lock=$(ls package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null | head -1); echo " [info] lockfile: ${lock:-none}"
grep -q '@storybook/addon-mcp' package.json 2>/dev/null && test -f .mcp.json && echo " [info] MCP wired → sb-stories uses with-mcp" || echo " [info] no addon-mcp → without-mcp path"
if find .storybook -maxdepth 1 -name '*.json' 2>/dev/null | grep -q .; then
find .storybook -maxdepth 1 -name '*.json' | sed 's/^/ [ok] discovery: /'
else echo " [info] no discovery JSON yet → sb-inventory not run"; fi
grep -q 'storiesLocation:' .storybook/audit/status.md 2>/dev/null && echo " [ok] stories location decided" || echo " [warn] stories location undecided → sb-setup must ASK (isolated .storybook/stories/ vs co-located) before sb-stories"
Worked example — a half-migrated app (Storybook installed, nothing audited yet):
━━ Phase 0 — readiness ━━
[ok] React
[ok] Vite
[ok] Storybook present
[ok] node_modules present
[info] lockfile: pnpm-lock.yaml
[info] no addon-mcp → without-mcp path
[info] no discovery JSON yet → sb-inventory not run
[warn] stories location undecided → sb-setup must ASK …
→ Storybook exists but no discovery JSON and location undecided, so don't jump to sb-stories.
Name sb-setup first (to record the stories location), then sb-inventory. That ordering —
setup before inventory before stories — is the whole point of the gate: the [info]/[warn] lines
are the routing signal, not noise.
Everything the bundle writes lives under .storybook/ (CONTEXT.md STORAGE MAP) — flag if you ever see
outputs elsewhere. The one decision is where stories go; if undecided, name sb-setup (it asks).
Discovery is precomputed. The discovery layers are JSON under
.storybook/:project-inventory(+tokens.map,storyCoverage) ·component-usage·flows·design-system-health·component-states·prop-shapes·page-patterns·runtime. The first four are rendered in Storybook (autodocs embeds + wrappers) and refreshed together byrefresh-usage.sh; the rest are authoring inputs (regenerated on demand). Never re-derive by shell scan what a script already wrote to.storybook/*.json— Read the field.
Then print → Start: <first verb> — sb-setup if NO_STORYBOOK, else sb-inventory. Don't run
the verb; name it and stop. For the full new-project arc, hand to Mode 1.
"Audit this app end to end." Run the pipeline in order, gating between phases — never advance until the prior artifact exists (the discovery scripts write atomically, so an existing JSON is complete). You don't run sibling scripts directly; you invoke each focused skill and verify its output.
sb-setup — if Mode 0 reported NO_STORYBOOK, or stories location is undecided. GATE: .storybook/ + "storybook" in package.json, AND storiesLocation recorded in .storybook/audit/status.md (sb-setup asks the user — isolated .storybook/stories/ vs co-located), AND .storybook/runtime.json exists (discover-runtime.py — provider tree / root-CSS / portals / MSW the shared preview must supply), before step 2.sb-inventory — real-vs-slop + dominant design system. GATE: .storybook/project-inventory.json exists.sb-health — design-system health. GATE: .storybook/design-system-health.json exists; if designSystem.mixed / heavy raw-hex, surface the /ds-runbook handoff before authoring.sb-flows — route map + nav edges. GATE: .storybook/flows.json exists.sb-stories <Component> — author a story for each component that needs one: work down
components.storyCoverage.needsStory[] (own components without a story, top-imported first), each
self-gating with validate-stories.sh. The honest progress meter is storyCoverage.withRegisteredStory/ needsCount when storyCoverage.source == "storybook-index" (reconciled against Storybook's index.json
— the stories Storybook actually registers); otherwise withColocatedStory/needsCount (heuristic; withStory
is a loose upper bound — it also counts components a story merely imports to mock). Re-run sb-inventory
(or sb-audit) after authoring to refresh coverage from the index and shrink needsStory[] — iterate until empty.sb-wrappers — render each step's output as a view: ProjectInventory (after step 2), DesignSystemHealth/TokenMatrix (after step 3's health), AppFlowGraph/JourneyGraph (after step 4), and StateGrid/StateMatrix while authoring stories (step 5). The data wrappers need their step's JSON first — see sb-wrappers "When in the flow".sb-audit — periodic drift survey + decision board once stories land; runs refresh-usage.sh (below).Figma-originated design system (optional branch, after step 1). The pipeline above is code-first
(audit what exists). When the source of truth is an approved Figma file, insert sb-figma to
establish foundation-token parity before authoring: it captures the Figma MCP variables to
.storybook/figma/, writes .storybook/figma-token-parity.json (color/spacing/type, OKLCH→hex), wires the
Foundations/Colors|Tokens|Type stories' figmaVar/figmaHex, and reports drift. It feeds — doesn't
replace — sb-health (step 3): health checks the code-internal token set; sb-figma adds the design↔code
axis health can't see. (Delivering approved components from Figma is also sb-figma, authored via
sb-stories; iterating undecided designs stays sb-explore.)
Append a one-line finding to .storybook/audit/findings.md after each phase. Stop on the first GATE
that fails and tell the user which verb to fix. (Pattern: ordered pipeline with hard gates, like lfg.)
Keep rendered data fresh (the usage layer). The four rendered JSONs (inventory · component-usage ·
flows · design-system-health) feed the autodocs usage embed — UsageSection wired once into
preview.ts docs.page adds "Real usage in this app" to every component's Docs and each Foundation
(Colors/Semantic/Typography/Scales = token tables, Health = audit findings). refresh-usage.sh --docs
re-runs all four together so a rebuild reflects reality; sb-audit does this each pass, and it belongs in
CI before storybook build. (Setup wires the docs.page composition — see sb-setup install-wizard.)
Inspect state + ledger, recommend exactly one next surgical skill (whose prerequisites are met).
test -d .storybook && grep -q '"storybook"' package.json 2>/dev/null && echo STORYBOOK_PRESENT || echo NO_STORYBOOK
ls .storybook/*.json 2>/dev/null # which discovery JSONs exist
cat .storybook/audit/status.md 2>/dev/null # ledger / resume point
# DRIFT — source/story files changed since the last discovery (newer than the inventory JSON). Plain
# `find -newer` → cross-agent (no git, no Claude hook), works on uncommitted/untracked files. Lists the
# exact files that moved this session; if any, the discovery JSONs are stale → refresh before routing.
if [ -f .storybook/project-inventory.json ]; then
find src app components .storybook/stories \( -name '*.tsx' -o -name '*.ts' \) \
-newer .storybook/project-inventory.json 2>/dev/null | grep -v '\.storybook/.*\.json' | head -20
fi
# COVERAGE — READ the field the script already wrote (CONTEXT.md doctrine: never re-derive). Extract
# ONLY storyCoverage so the FULL object surfaces complete — never a truncated dump of the whole
# inventory. Prefer the authoritative `withRegisteredStory` (source=storybook-index, reconciled against
# index.json); fall back to the heuristic `withColocatedStory`. needsStory[] is the iterate list.
if [ -f .storybook/project-inventory.json ]; then
python3 - <<'PY'
import json
try: c = json.load(open(".storybook/project-inventory.json"))["components"]["storyCoverage"]
except Exception: raise SystemExit
src = c.get("source", "heuristic")
done = c.get("withRegisteredStory") if src == "storybook-index" else c.get("withColocatedStory", 0)
print(f"coverage: {done}/{c.get('real',0)} components have a story [{src}] · {c.get('needsCount',0)} need one")
if c.get("needsStory"):
extra = " …" if c.get("needsCount", 0) > 8 else ""
print("iterate next (sb-stories): " + ", ".join(c["needsStory"][:8]) + extra)
PY
fi
If DRIFT lists files, the discovery JSONs no longer match the code — route to sb-inventory (or
sb-audit for a periodic pass) to refresh before naming the next step: its refresh-usage.sh re-runs
inventory/usage/flows AND reconciles story coverage against Storybook's own index.json (authoritative,
not a basename guess). Then route — name exactly one:
| State | Next step | Skill |
|---|---|---|
NO_STORYBOOK | defer bootstrap to npx storybook ai setup, then align | sb-setup |
No project-inventory.json | discover real-vs-slop first | sb-inventory |
Inventory done, no design-system-health.json | check health before authoring | sb-health |
Health done, no flows.json | capture navigation + app-map | sb-flows |
| Inventory clean, want stories | author ONE component | sb-stories <Component> |
| New/redesigned component, undecided — iterating options | sandboxed iteration (+ Figma node) | sb-explore |
| Approved Figma design — map foundation tokens (color/spacing/type) or deliver an approved component | Figma → production Storybook | sb-figma |
| Check Figma↔code token parity / "is the design system in sync with Figma" | parity + drift report | sb-figma |
| Connect components back to Figma (Code Connect) / "show my code in Figma Dev Mode" | code → design (reverse) | sb-figma |
| Periodic check | drift survey + decision board | sb-audit |
| Explore meets graduation gate | propagate to production | sb-ship |
EXPLORE vs DELIVER (both are Figma-aware — route by stage, not by "uses Figma"): undecided / trying options →
sb-explore(Lab) →sb-ship(graduate). Already approved in Figma →sb-figma(deliver direct to prod).sb-figmawrites the Foundation token stories itself; for an approved component it extracts the design then authors viasb-stories' rules. It captures every Figma MCP output to.storybook/figma/(manifest + per-tool files) so the pipeline is reproducible/iterable, not MCP-bound.
Resume rule (CONTEXT.md): read status.md + check which JSONs exist; resume from the first
incomplete step. Never treat a file half-written in a prior session as complete — re-run that step.
When the user says a skill did something wrong, surprising, or broken, help them file a useful report — don't just apologize. Draft it with the agent-native reporter, then hand them the submit command.
CORE=${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}
"$CORE/scripts/report-issue.sh" --asked "<what they asked>" --observed "<what happened>" --expected "<what they wanted>"
It writes a sanitized draft (versions + .storybook/*.json shapes/counts only — never source, token
values, or component names) and prints a gh issue create … command + a blank-issue URL. It makes no
network call — the user submits. Fill --asked/--observed/--expected from the conversation, show them
the draft path + the gh line, and (only if they ask) offer to run gh for them. To target a different
repo: SB_ISSUE_REPO=owner/name. A good report becomes a reproduction → a new eval case → a fix → a
field-learnings.md entry — the loop that keeps the next run from re-hitting the same bug.
.storybook/*.json is the
precondition for the next (foundation tokens before components, flows before the app map). Skip one
and every later phase builds on missing ground truth and silently under-delivers..storybook/*.json
files ARE the state. Hand-grepping drifts from what the scripts captured and what the wrappers render;
cite the field, don't recompute it.status.md and re-run the first incomplete step.CONTEXT.md — every skill reads the one canonical file (CONTEXT.md);
a copy is drift waiting to happen./sb-hub (Mode 2 / "what's next" is the default; /sb-hub onboard or
/sb-hub audit this app selects Mode 0 / 1).$sb-hub what's next · $sb-hub onboard · $sb-hub audit this app..storybook/audit/findings.md → ask sb-hub again (Mode 2)
→ it names the next surgical skill → run it.npx claudepluginhub strongeron/storybook-workbench --plugin storybook-workbenchGuides writing, updating, and reviewing Storybook stories with a strict workflow. Pulls story-writing rules from the MCP, ensures a running preview, and prevents unverified stories.
Storybook 9+ setup, CSF3 story authoring, args/controls, decorators, play-function testing, a11y axe-core integration, autodocs, design tokens, Figma linking, Chromatic deployment, CI caching, and on-demand build performance.
Periodic Storybook audit — naming-drift survey, archived/decision-board review, lifecycle tagging, usage refresh. Use for 'audit my Storybook', 'find drift', 'show pending decisions', or a periodic catalog health check.