sb-hub — onboarding check · orchestrator · navigator

SkillDev tools

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

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the sb-hub skill

What this skill tells your AI

The instructions your AI receives, as published by strongeron/storybook-workbench in skills/sb-hub/SKILL.md and read by ahel’s review.

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.

Resolve the bundle

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

Pick the mode

User intentMode
"is this ready", "where do I start", fresh/unknown repo0 · 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 ledger2 · Navigate (default)
"this is wrong", "strange behavior", "report a bug", a skill misfired3 · Report

Mode 0 — Onboarding check (phase-0 diagnose, read-only)

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 by refresh-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.

Mode 1 — Orchestrate (run the whole audit → stories)

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

  1. 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.
  2. sb-inventory — real-vs-slop + dominant design system. GATE: .storybook/project-inventory.json exists.
  3. 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.
  4. sb-flows — route map + nav edges. GATE: .storybook/flows.json exists.
  5. 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.
  6. 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".
  7. 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.)

Mode 2 — Navigate (default — name the single next step)

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:

StateNext stepSkill
NO_STORYBOOKdefer bootstrap to npm create storybook@latest + npx storybook skills setup, then alignsb-setup
No project-inventory.jsondiscover real-vs-slop firstsb-inventory
Inventory done, no design-system-health.jsoncheck health before authoringsb-health
Health done, no flows.jsoncapture navigation + app-mapsb-flows
Inventory clean, want storiesauthor ONE componentsb-stories <Component>
New/redesigned component, undecided — iterating optionssandboxed iteration (+ Figma node)sb-explore
Approved Figma design — map foundation tokens (color/spacing/type) or deliver an approved componentFigma → production Storybooksb-figma
Check Figma↔code token parity / "is the design system in sync with Figma"parity + drift reportsb-figma
Connect components back to Figma (Code Connect) / "show my code in Figma Dev Mode"code → design (reverse)sb-figma
Periodic checkdrift survey + decision boardsb-audit
Explore meets graduation gatepropagate to productionsb-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-figma writes the Foundation token stories itself; for an approved component it extracts the design then authors via sb-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.

Mode 3 — Report (a skill misfired / strange behavior)

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

Add --gaps when the trouble came from sb-figma: the draft then lists the open gaps from .storybook/figma/gaps.json as counts per kind (no names or values).

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.

Never (orchestration failure modes, and why)

  • NEVER advance past a failed/empty gate in Mode 1 — each phase's .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.
  • NEVER re-derive CONTEXT by shell-scanning what a script already wrote — the .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.
  • NEVER treat a half-written prior-session artifact as complete — writes are atomic per file, but an interrupted run may have stopped mid-pipeline. Check status.md and re-run the first incomplete step.
  • NEVER author here — the hub inspects and routes. Writing a story/wrapper from the hub bypasses the focused skill's own checks and anti-patterns; invoke the skill instead.
  • NEVER hand-duplicate CONTEXT.md — every skill reads the one canonical file (CONTEXT.md); a copy is drift waiting to happen.

Cross-agent

  • Claude: this skill is /sb-hub (Mode 2 / "what's next" is the default; /sb-hub onboard or /sb-hub audit this app selects Mode 0 / 1).
  • Codex: $sb-hub what's next · $sb-hub onboard · $sb-hub audit this app.
  • The loop: a verb runs → appends to .storybook/audit/findings.md → ask sb-hub again (Mode 2) → it names the next surgical skill → run it.

Signals

GitHub stars
37
Last commit
Oct 2026
Advanced
Item type
skill
Key
sb-hub
Source
github.com/strongeron/storybook-workbench