关于 sb-inventory
sb-inventory 是一项技能,通过生成的清单数据检查项目组件、设计信号和 Storybook 覆盖情况。它帮助团队区分自有组件与供应商、支持文件,并追踪组件在页面中的使用。
主要功能
- 01生成项目清单 JSON
- 02区分自有、供应商和支持组件
- 03提取组件属性与调用位置
- 04构建组件到页面的使用关系图
- 05追踪故事覆盖及待补故事
适用场景
- 01识别规划故事所需的自有组件
- 02检查应用中未使用的已声明属性
- 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,了解 real/vendor/dead/kind 这些术语的含义。
(这里的主导设计系统来自代码信号,而非文档。如果存在 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 并引用其中的字段。
有关已知的检测器陷阱(在验证运行中,发现脚本哪些地方做对了、哪些地方做错了),只有在某个计数看起来意外时(例如 0、异常值或疑似误报的孤立项),才加载 references/field-learnings.md。如果首次盘点结果正常、数字看起来合理,不要加载它——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= shadcncomponents/ui/基础组件。 请单独指出这些组件:“你还安装了 N 个 shadcn 基础组件——它们不是你的代码,也不是粗制滥造代码,先别急着为它们写故事。” - 模块 / 支持文件 / 脚手架 —
moduleCount(byKind.module= 类型/辅助函数/钩子/工具函数/lib/api——非组件源码),supportCount(byKind.scaffold|support= 测试/工厂/模拟 + SB 初始化教程)。这些都不计入总览数字,也不计入real[]导入次数最多的列表,因此该列表展示的是生产环境中的组件,而不是被到处导入的types.ts。如果计数非零,请说明。 - 设计令牌 — 已使用与孤立的令牌。故事 —
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按清单逐项处理。
此处最重要的修正:“真实组件”清单和总览数字只包含用户编写的组件——不包含 40 个 shadcn 基础组件、类型或辅助函数。这是盘点反馈中最常见的问题;vendor / support / scaffold 分类正是为了让总览保持准确。
将其呈现为故事
搭建 ProjectInventory 封装组件,让 Agent 和设计师看到同一视图:
"$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 能让文档写出“提供 /scheduler 服务”,而不是容易误导人的“0”。route 是页面提供服务的路由,与 pages[] 分开存放(把页面列在“它出现在哪些页面上”中会显得循环)。
这与双向使用关系配套,因此一个文件就能从任意方向回答“X 在哪里使用”:
fileIndex(<src file> → {component, kind, pages[]})、tokens(<--token> → {category, count, components[], pages[]}——正向),以及每个组件的 tokens[] 数组(反向——该组件使用的内容)。tokens 会列出所有已声明的令牌(已使用、仅基础组件使用或孤立的令牌)——即使某个令牌只被设计系统基础组件(src/components/ui/*.tsx,清单会将其归入 vendor/module,而非受跟踪组件)使用,它也会解析到该基础组件作为使用方,而不会被悄然丢弃,因此浏览器会展示完整调色板,而不只是能追溯到应用组件的令牌。usage-index 辅助工具(resolveUsage/UsageDetail/UsageDisclosure)会读取 fileIndex,让 TokenMatrix 和 Foundations 色板将原始路径转换为可点击的组件/页面名称;UsageExplorer 封装组件会读取完整关系图,作为一个可搜索的“此项在哪里使用?”界面(令牌 ⇄ 组件 ⇄ 页面)。这样一来,旧的每应用 audit-map.json(一个平行的组件↔页面关系图)就不再需要了。
ComponentUsage 封装组件会将其渲染为工作列表——使用 "$CORE/scripts/scaffold-wrapper.sh" ComponentUsage 来搭建。页面会通过渲染关系图解析(嵌套在 /dashboard 上 CourseCard 中的 Rating 会显示 /dashboard),因此它能回答*“我们有哪些组件、使用频率如何、嵌套在哪里、出现在哪些屏幕上,还有哪些仍需要 story?”* 这是一份静态草稿——barrel/动态/别名导入可能导致关系链接不完整;请对照源码验证关键关系。
呈现用法(对齐后的流程——在 Storybook 中与此一致)
用法就在你查看内容的地方——嵌入各个部分的 Docs 中,无需单独的“用法”页面/部分。
-
每个 Docs 页面都有一个文档区块(默认启用,无需新增文件)。 提供
UsageSection包装器,并将其添加到.storybook/preview.ts中的全局 autodocs 布局。它会为每个部分的 Docs 添加真实用法区块,并读取对应的审计输出文件(因此 foundations 需要tags: ['autodocs']):- Components →
component-pages.json—ComponentContext的“使用位置”映射(组件出现在哪些页面中、哪些内容会嵌套它、它会渲染什么、会读取哪些 tokens),以及从src/导入并独立渲染的真实组件。默认折叠,便于快速浏览:始终显示简洁的“使用位置”眉题 + 自适应元信息(仅显示非零计数——不显示多余的大号<h2>,因为 Docs 标题已包含组件名称);映射按需展开(使用原生<details>,在独立审计页面上默认展开)。元信息会根据页面类型调整:真实组件显示“5 处调用 · 2 个 props · 有 story”,而路由页面(isPage)不显示调用次数/props,并优先显示其服务的路由——“服务 /scheduler · 5 次渲染 · 有 story”。元信息上方会显示“这是什么 + 来源”的溯源栏,默认不在 Docs 页面中显示(它会在每个组件中重复出现;使用setProvenance(true)可启用)。复用 Usage-explorer 印章。 - Pages/* → 从
src/导入的真实应用页面,通过 provider/Inertia mocks(sb-setup)渲染——是真实页面,而非模型图。其“这是什么?”溯源栏默认不在 Docs 页面中显示(页面 story 已包含相关内容;使用setProvenance(true)可重新显示溯源栏)。 - Foundations →
UsageSection不提供任何内容。每个 Foundation 都有自己的独立 story:Colors→TokenMatrix(值 · 映射 · 采用情况——不是健康状态;不传入healthprop,它默认值为 false),Health→DesignSystemHealth(完整发现结果),Icons→IconMatrix,Typography/Scales→ tokens 展示。旧版每个 Foundation 中的“此应用中的真实用法”区块已移除:Health 区块会重复展示 DesignSystemHealth story,而每个 token 的“使用位置”无法解析类型/比例(声明名称 ≠ 引用名称)——这部分由 Usage explorer 和 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 中的独立 story——绝不单独创建文件(新文件 = 新的侧边栏文件夹)。在其他导出项旁边添加一个导出,并使用
usageprop:<StateGrid component={X} usage={usageJson.components.X} states={…} />(或StateMatrix);对于 tokens,使用<TokenUsageGrid>。
限制:{...spread}/动态值会计入 exprCount(“从变量设置”),不会被解析(静态分析)。
完整 Setup 扫描
本技能负责清单。其余发现工作请运行同级技能(每个技能管理自己的脚本、原子化写入 JSON,无需子 Agent):sb-flows(路由/边/导航 → flows.json),然后 sb-stories 会在需要时生成 component-states.json + prop-shapes.json。
读取 JSON 文件;不要重新 grep 源码。
切勿(以及不那么显而易见的原因)
- 切勿根据文档判断主导设计系统——应读取代码信号(导入、调用位置)。
DESIGN.md/AGENTS.md是一种说法,会逐渐过时;真正生效的是导入。 - 切勿手动重新计数或重新搜索源代码——发现脚本已将结果写入
.storybook/*.json。请引用字段。目测重新推算的结果会与封装器实际呈现的内容不一致,还会浪费 JSON 已经消耗过的上下文。 - 切勿盲目信任边界情况的计数——遇到
0、异常值或疑似误判为孤立项的情况?此时请加载references/field-learnings.md(BSD-sed 的\s、Tailwind 工具类令牌消耗、组件与页面混淆)。静态扫描存在已知盲点;实地记录会说明这些盲点。 - 切勿将用法网格单独放在一个文件中——它是组件自身 CSF 中的扁平故事。新文件意味着新的侧边栏文件夹;网格应放在它所概述的状态旁边。
下一步
在 .storybook/audit/findings.md 中追加一行发现,然后询问 hub(/sb-hub)——通常是 sb-flows(记录应用映射)或 sb-stories <TopComponent>。