sb-inventory について
sb-inventory は、生成したインベントリデータからプロジェクトのコンポーネント、デザイン情報、Storybook のカバレッジを調べるスキルです。独自コンポーネントとベンダー・サポート用ファイルを区別し、ページ上の利用状況を追跡できます。
主な機能
- 01プロジェクトインベントリ JSON の生成
- 02独自・ベンダー・サポート用コンポーネントの分類
- 03コンポーネントの props と呼び出し箇所の抽出
- 04コンポーネントとページの利用グラフ作成
- 05ストーリーカバレッジと未作成分の追跡
活用シーン
- 01ストーリー計画用に独自コンポーネントを特定
- 02アプリで未使用の宣言済み props を確認
- 03コンポーネントを使うページと親を特定
- 04トークン利用とストーリーカバレッジを確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ strongeron/ storybook-workbench --skill sb-inventoryインストール後、Claude Code で/sb-inventoryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CONTEXT.mdagents/openai.yamldecorators/withLayoutFrame.tsxreferences/field-learnings.mdscripts/build-component-pages.pyextract-component-usage.shinventory-project.shrefresh-usage.shscaffold-usage-mdx.pyscaffold-wrapper.shtoken-usage.pytemplates/usage-profile.mdxwrappers/ComponentUsage.tsxicons.tsxProjectInventory.tsxReportIntro.tsxresolve-paint.tsusage-index.tsxusage-stamp.tsx詳細
sb-inventory — 信頼ではなく、実際の状態を把握する
雰囲気でコーディングされたアプリには、約30%の粗雑なコードが含まれます。このスキルは、「AGENTS.md / CLAUDE.md / DESIGN.mdを信頼する」を、「実際にインポートされているものを見る」に置き換えます。実際/ベンダー/未使用/種別の用語については、CONTEXT.mdを読んでください。
(ここで主流のデザインシステムは、ドキュメントではなく、コードのシグナルから判断します。DESIGN.mdの概要がある場合、sb-healthは記載された色をコードと照合します。AGENTS.mdと同様に、内容が古くなったり、実態と異なったりするためです。)
実行方法
SKILL=${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}
CORE=${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}
"$SKILL/scripts/inventory-project.sh" # writes .storybook/project-inventory.json (atomic)
このスクリプトが正しい情報源です。手作業で再計算しないでください。JSONを読み、そのフィールドを根拠として示してください。既知の検出器の注意点(検証実行を通じて、検出スクリプトが正しく判定するものと誤判定するもの)については、カウントが予想外に見える場合に限り、references/field-learnings.mdを読み込んでください(0、外れ値、孤立ファイルの誤検出が疑われる場合)。初回のインベントリが問題なく、数値も妥当に見える場合は読み込まないでください。JSONのフィールドだけで判断できます。
報告(以下の順にフィールドを確認)
- スタック —
libraries(react/vite/tailwind v4|v3/shadcn/radix/baseui/r3f)。 - 主流のデザインシステム —
designSystem.dominant(tailwind-v4/shadcn/dtcg/css-vars/noneのいずれか)とmixedフラグ。mixedがtrueの場合、プロジェクトは移行中であるとユーザーに伝え、sb-healthを提案してください。 - ユーザー独自のコンポーネント —
components.realCount/deadCountはドメインコンポーネントのみを対象とします。ストーリー作成の優先リストとして、components.real[](インポート数の多い順)を示してください。 - ベンダー —
components.vendorCount+byKind.vendorは、shadcnのcomponents/ui/プリミティブです。これらは分けて説明してください。「インストール済みのshadcnプリミティブがN個あります。ユーザーのコードではなく、粗雑なコードでもありません。まずこれらのストーリーを書く必要はありません。」 - モジュール/サポート/スキャフォールド —
moduleCount(byKind.module= types/helpers/hooks/utils/lib/api — コンポーネント以外のソース)、supportCount(byKind.scaffold|support= テスト/ファクトリー/モック + SB初期化チュートリアル)。これらは見出しの数値と、インポート数上位のreal[]リストのどちらにも含まれません。そのため、リストに含まれるのは、どこからでもインポートされるtypes.tsではなく、本番用のコンポーネントです。0以外の場合は言及してください。 - トークン — 使用中と孤立。ストーリー —
orphanStories(存在しなくなったコンポーネントをインポートするストーリー)と、components.storyCoverage(作成済みの独自コンポーネント数と、必要な数:{real, storyFiles, withColocatedStory, withStory, needsCount, needsStory[]})。カバレッジは**withColocatedStory(独自の<name>.stories.*を持つコンポーネント)と、storyFiles(存在する個別のストーリーファイル数)から読み取ってください。これらが確定数です。withStoryは大まかな上限値**です。どのストーリーでもインポートされていればカバー済みとして数えるため(たとえば、フォームをモックするためにダイアログのストーリーがフォームをインポートする場合など)、実際より多く数えます。withColocatedStory ≤ real_coverage ≤ withStoryとして扱ってください。needsStory[]は「必要なストーリー」の作業リストです(orphanStoriesの逆)。refresh-usage.shが最新の状態を保つので、sb-storiesはneedsStory[]を順番に進められます。
ここで重要な修正点:「実際」のリストと見出しには、ユーザーが作成したコンポーネントのみを含めます。shadcnプリミティブ40個や、型、ヘルパーは含めません。これはインベントリに関する最大の不満点でした。見出しを明確に保つために、vendor/support/scaffoldの分類があります。
ストーリーとして表示する
エージェントとデザイナーが同じビューを共有できるよう、ProjectInventoryラッパーをスキャフォールドします。
"$CORE/scripts/scaffold-wrapper.sh" ProjectInventory # → .storybook/wrappers/ (+ icons.tsx)
タイトルはFoundations/Inventoryです。ラッパーは同じJSONを読み込みます。絵文字は使いません(共通のアイコンセットを使います)。
実際のプロパティの使用状況(実際のものと水増しの比較だけではありません)
importers から分かるのはコンポーネントが使われていることだけで、アプリが実際にどのプロパティ/バリアントをレンダリングするかは分かりません。shadcn の Button は6つのバリアントを宣言していても、本番環境で使われるのは2つだけかもしれません。残りはカタログ用の水増しです。インベントリの作成後に、使用状況の抽出を実行してください。
"$CORE/scripts/extract-component-usage.sh" # → .storybook/component-usage.json (atomic)
コンポーネントごと(独自のものと shadcn のプリミティブ。PascalCase のエクスポート名をキーにします)に、JSX の呼び出し箇所から次の情報を記録します。
props.<name>.{count, values{value:n}, exprCount}、callSites、files[]、および declaredButUnused(宣言されているものの渡されていないプロパティ/文字列ユニオンの値。プロパティ単位の水増しです)。さらに pages のロールアップも記録します。これらのフィールドを引用してください。再度 grep しないでください。sb-stories はこれを読み込み、実際に使われている状態を優先し、未使用のものにタグを付けます。
コンポーネントの作業リスト — インポートからシステムを監査する(ComponentUsage)
システム全体を把握するには、実際の UI コンポーネントを呼び出し箇所の数でランク付けし、どのコンポーネント内にネストされているか(parents)/何をレンダリングするか(children)、そしてどのルーティングされたページに(直接または間接的に)表示されるかを確認します。コンポーネントとページのインポートグラフを生成してください。これは3つの検出用 JSON を組み合わせるもので、新たなスキャンは行いません。
"$CORE/scripts/build-component-pages.py" # project-inventory + component-usage + flows → component-pages.json
# (or just run "$CORE/scripts/refresh-usage.sh" — it writes all four together)
build-component-pages.py は、component-usage.json(callSites/props/declaredButUnused と各コンポーネントの files)、project-inventory.json(各コンポーネントの file/kind)、flows.json(ルートの path と access ロール)を読み込み、.storybook/component-pages.json に書き出します。コンポーネントごとに {callSites, props, declaredButUnused, parents[], children[], pages[{path, title, role, storyId}]} が含まれます。また、コンポーネント自身のファイルがルーティングされたページのサーフェスである場合は、isPage:true と route(そのファイルが提供するパス)も含まれます。ページはルーターによって設定値({ component: X })としてマウントされ、JSX としてマウントされることはありません。そのため、構造上、呼び出し箇所数は0、プロパティ数も0です(正確な値であり、欠落ではありません)。route があれば、誤解を招く「0」ではなく、ドキュメントに「/scheduler を提供」と記載できます。route はそのページが提供するルートであり、pages[] には含めません。「表示されるページ」の一覧にそのページ自身を記載すると、循環しているように見えるためです。
これには、双方向の使用エッジも組み合わせます。これにより、1つのファイルで、どの方向からでも「X はどこで使われているか」に答えられます。
fileIndex(<src file> → {component, kind, pages[]})、tokens(<--token> → {category, count, components[], pages[]} — 順方向)、およびコンポーネントごとの tokens[] 配列(逆方向。コンポーネントが使用するトークン)です。tokens には宣言されたすべてのトークン(使用中、プリミティブのみ、または孤立したもの)が一覧表示されます。デザインシステムのプリミティブ(インベントリでは vendor/module に分類され、追跡対象のコンポーネントではない src/components/ui/*.tsx)だけが使用するトークンも、ひそかに除外されず、使用先のプリミティブに紐付けられます。そのため、エクスプローラーにはアプリのコンポーネントに届くトークンだけでなく、パレット全体が表示されます。usage-index ヘルパー(resolveUsage/UsageDetail/UsageDisclosure)は fileIndex を読み込み、TokenMatrix と Foundations のスウォッチで、生のパスをクリック可能なコンポーネント名/ページ名に変換します。UsageExplorer ラッパーはグラフ全体を読み込み、「これはどこで使われているか?」を検索できる単一の画面(トークン ⇄ コンポーネント ⇄ ページ)として表示します。これにより、以前のアプリ単位の audit-map.json(並行するコンポーネントとページのグラフ)は不要になります。
ComponentUsage ラッパーはこれを作業リストとしてレンダリングします。"$CORE/scripts/scaffold-wrapper.sh" ComponentUsage でひな形を作成してください。ページはレンダーグラフをたどって解決されるため(Rating が /dashboard の CourseCard 内にネストされていれば、/dashboard と表示されます)、「何があり、どれほど使われ、どこにネストされ、どの画面に表示され、まだストーリーが必要なものは何か?」という問いに答えられます。これは静的なドラフトです。バレル/動的/エイリアス付きインポートではリンクが不足することがあるため、重要なエッジはソースで確認してください。
使用状況の提示(整合性のとれたフロー — Storybook でもこれに合わせる)
使用状況は、確認する場所に表示します。各セクションの Docs に埋め込み、独立した「使用状況」ページやセクションは設けません。
-
各 Docs ページに 1 つの Docs ブロック(デフォルトでファイル追加不要)。
UsageSectionラッパーを用意し、.storybook/preview.tsのグローバルな autodocs レイアウトに追加します。これにより、対応する監査出力ファイルを読み込み、各セクションの Docs に実際の使用状況セクションを追加します(そのため、Foundations にはtags: ['autodocs']が必要です)。- Components →
component-pages.json—ComponentContextの「使用箇所」マップ(表示されるページ、内包しているもの、レンダリングするもの、使用するトークン)に加え、src/の実際のコンポーネントを単独でレンダリングします。デフォルトでは折りたたまれています。すばやく確認できるコンパクトな「使用箇所」見出しと状況に応じたメタ情報は常に表示されます(ゼロ以外の件数のみを表示します。Docs のタイトルにコンポーネント名がすでに含まれているため、冗長な大きな<h2>は表示しません)。マップは必要に応じて展開できます(ネイティブの<details>を使用し、単独の監査ページでは開いた状態にします)。メタ情報はページの種類に応じて変わります。実際のコンポーネントでは「呼び出し箇所 5 件 · props 2 件 · ストーリーあり」と表示される一方、ルーティングされたページ(isPage)では呼び出し箇所と props を省き、対応するルートを先頭に表示します。「/scheduler を提供 · レンダリング 5 件 · ストーリーあり」のようになります。「これは何か、どこから来たか」を示す来歴バンドはその上に配置され、Docs ページではデフォルトで非表示です(各コンポーネントで繰り返し表示されるためです。setProvenance(true)で表示できます)。Usage エクスプローラーのスタンプを再利用します。 - Pages/* →
src/の実際のアプリページを、プロバイダー/Inertia のモック(sb-setup)を通してレンダリングします。モックアップではなく、実際のページです。「これは何か?」を示す来歴バンドは Docs ページではデフォルトで非表示です(ページのストーリーにコンテンツが含まれており、setProvenance(true)で再表示できます)。 - Foundations →
UsageSectionからの追加はありません。各 Foundation はそれぞれ独立したストーリーをレンダリングします。Colors→TokenMatrix(値 · マッピング · 採用状況 — 健全性ではありません。healthprop は指定しないでください。デフォルトは false です)、Health→DesignSystemHealth(検出結果の全項目)、Icons→IconMatrix、Typography/Scales→ トークン表示です。以前の Foundation ごとの「このアプリでの実際の使用状況」バンドは削除しました。Health ではDesignSystemHealthストーリーと内容が重複し、トークンごとの「使用箇所」は type/scale では解決できないためです(宣言された名前と参照される名前は一致しません)。その情報は Usage エクスプローラーと Colors の採用状況列で確認できます。
サイドバーを煩雑にせず、独立したページも作成しません。
import { Title, Subtitle, Description, Primary, Controls, Stories } from '@storybook/addon-docs/blocks'; import { UsageSection } from './wrappers/UsageSection'; // parameters.docs.page = () => (<><Title/><Subtitle/><UsageSection/><Description/><Primary/><Controls/><Stories/></>) // ↑ order is yours — UsageSection here puts the real-usage status band at the TOP; move it last for the bottom.refresh-usage.shで JSON を最新の状態に保ちます。Storybook を再ビルドすると、その内容が反映されます。ジェネレーターはデフォルトでは何も生成しません。scripts/scaffold-usage-mdx.py --per-componentは、templates/usage-profile.mdxからレンダリングする独立した<Name>.usage.mdxページを生成するためのオプトイン用の手段です(必要になることはほとんどありません)。 - Components →
-
視覚的な使用状況グリッドは、コンポーネント自身の CSF 内に FLAT なストーリーとして追加します。独立したファイルにはしません(ファイルを新規作成すると、サイドバーのフォルダーも新規作成されます)。
usageprop を使って、他の export と並べて追加します。<StateGrid component={X} usage={usageJson.components.X} states={…} />(またはStateMatrix)。トークンには<TokenUsageGrid>を使用します。
制限事項:{...spread}/動的な値は exprCount(「変数から設定」)としてカウントされ、解決はされません(静的解析)。
セットアップ全体の確認
このスキルがインベントリを管理します。残りの検出には、兄弟スキルを実行してください(それぞれが独自のスクリプトを管理し、アトミックな JSON を書き出すため、サブエージェントは不要です)。まず sb-flows(ルート/エッジ/ナビゲーション → flows.json)を実行し、続いて必要に応じて sb-stories が component-states.json と prop-shapes.json を生成します。
JSON を読み込み、ソースを grep し直さないでください。
禁止事項(および、その理由)
- ドキュメントから支配的なデザインシステムを決めてはいけません — インポートや呼び出し箇所など、コードの兆候を確認してください。
DESIGN.md/AGENTS.mdに書かれているのは主張にすぎず、実態とずれていきます。実際に出荷されるものを示すのはインポートです。 - 手作業で数を数え直したり、ソースを再度grepしたりしてはいけません — 検出スクリプトはすでに
.storybook/*.jsonに結果を書き出しています。フィールドを参照してください。目視で数え直すと、ラッパーが描画する内容とずれていき、JSONがすでに担っている情報にコンテキストを浪費します。 - エッジケースの件数を鵜呑みにしてはいけません —
0、外れ値、または孤立コンポーネントの誤検出が疑われる場合は、references/field-learnings.mdを読み込んでください(BSD-sedの\s、Tailwindユーティリティトークンの消費、コンポーネントとページの混同)。静的スキャンには既知の見落としがあり、現場のメモにその内容が記されています。 - 使用状況グリッドを独立したファイルにしてはいけません — これはコンポーネント自身のCSFに含めるFLATストーリーです。新しいファイルを作ると、サイドバーに新しいフォルダーができます。グリッドは、その要約対象となる状態の隣に置いてください。
次の手順
.storybook/audit/findings.mdに1行の所見を追記してから、ハブ(/sb-hub)に依頼してください。通常はsb-flows(アプリマップの作成)またはsb-stories <TopComponent>を使います。