@wisdomgarden/torque-knowledge
v1.0.7
Published
Machine-readable knowledge artifact for @wisdomgarden/torque — component catalog (SQLite) and AI coding rules (Markdown). Consumed by CI code review and local AI tooling, not by application runtime.
Downloads
465
Keywords
Readme
@wisdomgarden/torque-knowledge
这个包是给机器读的,不是给人读的。 人读的组件文档在
@wisdomgarden/torque-doc(文档站),组件本身在
@wisdomgarden/torque。
它把组件库的知识打包成一个可发布、可版本寻址的 artifact,供两个消费端取用。
包里不含任何运行时代码,不要 import 它 —— 没有 main / exports,
消费端是 npm pack 之后解包取文件。
产物
| 文件 | 给谁 | 是什么 |
| --- | --- | --- |
| dist/torque-components.md | diting(lms-web 的 CI code review) | 选型决策树 + 可用组件清单 + 硬约束 + 禁止事项。只有清单和策略,没有组件细节 —— 细节查 components.db。带 diting 认的 frontmatter(单行 keywords / globs),解包进它的 rule_dirs 就能被召回 |
| dist/components.db | lms-web 的本地 MCP + diting | SQLite 组件目录,MCP search_component() 和 diting query_ui_components 共同的数据源。44 个组件的导出名(含 Combobox.InfiniteDropdown 这类点号子组件名)、英文描述、keywords 和完整 props |
| dist/.meta.json | CI / 下游漂移检测 | { knowledge, torque, generatedAt } —— 这份知识是哪个版本、描述的又是哪个 torque 版本 |
版本与 @wisdomgarden/torque 相互独立
四个包各自独立版本号,这个包的 version 没有和 torque 相等的义务 ——
[email protected] 描述 [email protected] 是完全正常的状态。
下游取的是 latest(npm pack @wisdomgarden/torque-knowledge@latest),
不经过 lockfile —— 因为把它装成 devDependency 会让 lms-web 的 base image
(1642 个包)在每次知识更新时重建,而这个包有时只改了几句描述。
代价是版本对齐从「精确」降成「有窗口」,靠漂移检测兜住:dist/.meta.json 的
torque 字段记着这份知识描述的是哪个版本,下游拿它和自己装的 torque 比对。
所以那个字段是必须的,不是锦上添花 —— 上游不落地 .meta.json,
下游的漂移检测就是恒静默的,不报错也不会有人发现。
构建
pnpm --filter @wisdomgarden/torque-knowledge builddist/ 是构建产物,不入库。仓库根的 pnpm build 会自动带上这个包。
源与数据流
packages/torque/src/index.ts ← 组件清单的唯一来源(不是 readdir)
packages/torque/src/components/*/readme.md ← 39 篇,props 和中文描述
packages/torque/src/provider/readme.md ← 第 40 篇
src/fallback.json ← 全部 44 个组件的英文描述(手工维护)
src/handwritten/*.md ← 人工提炼的正文片段
│
└──> scripts/gen.mjs ──> dist/机械的部分(扫源码、解析 props、产 db 和组件清单)在 gen.mjs 里,每次 pnpm build
都跑。需要判断力的部分(读懂 readme 提炼选型/约束、复核英文描述)在
.claude/skills/torque-knowledge/ 这个 skill 里。
scripts/check-changes.mjs 是两者之间的闸门:
node scripts/check-changes.mjs --check-only # 只问「有活吗」,不写缓存、不生成
node scripts/check-changes.mjs # 有变化就顺便重新生成退出码是契约:0 有变化 / 1 无变化 / 2 有变化但生成失败。
--check-only 的意义是让调用方先判断要不要花一次 AI 调用 —— lms-web 实测 22 个生产日里
有 21 天没活干。缓存在 .cache/changes.json(gitignore)。
gen.mjs 和 check-changes.mjs 共用 scripts/lib/parse-barrel.mjs 解析组件清单。
别在任一个里重写一遍正则 —— 两份必然漂移,一个认得 StickyBox 另一个不认得,
变更检测就会报出永远修不掉的假变化。
几件容易踩的事,改 gen.mjs 前先看一眼:
组件清单从 barrel 推,不要
readdir。components/sticky-box.tsx是裸文件、 没有目录也没有 readme,但公开导出StickyBox;_inner/则不该暴露。barrel 两个问题都没有。components.db的 schema 是硬契约,而且有两个消费端。lms-web的 MCP 和 diting 的query_ui_components都按字段名取值,改了名两边都静默返回undefined,不会报错。 表名components也是契约 —— diting 不认文件名,它在rule_dirs下逐个 SQLite 文件试探「有没有components表」来发现这份目录,所以文件名能改、表名不能。一份文案只发一份。
when_to_use列保留但写NULL—— 它曾经是description的逐字节副本(44/44 个组件一字不差),同一段话发两遍不只是费 token,挂在两个字段名 下还会让模型当成两条独立信息。fallback.json只有一份文案,就只写description; 列不能删,消费方读到空值自己回落。子组件名要进
exported_names。Combobox.InfiniteDropdown/Tabs.List/Popover.Target在 barrel 里查不到(挂在父组件上),但调用方写的就是这个名字。gen.mjs扫组件源码的X.Sub = Y和Object.assign(Root, {...})两种写法把它们 补进去 —— 不补的话下游精确查询 miss、fuzzy 落回父组件,模型拿不到答案就去翻源码, 而 torque 在node_modules里根本翻不到。md 清单不列它们:数量不定又被slice(0, 4)截断,漏出随机的一两个比一个不列更误导。db 保持默认的
DELETEjournal 模式,不要设 WAL。 WAL 标记会持久化进 db header, 产物在只读目录下会直接打不开。md 只放「有什么」和「怎么选」,组件细节一律留给
components.db。 清单里只有 组件名和导出名,没有描述 —— 用途、完整 props、readme 里的注意事项都按需查 db。 两条界线:- 不能砍到只剩组件名。 kebab → Pascal 有一串不规则的(
form-wrapper→FormItemWrapper、provider→UiProvider、tab→Tabs、message→messages),而且好几个组件导出多个名字。少了导出名,模型要么多查一次 db 才能写import,要么猜错一个标识符。这一列 1 KB,买的是「不用查就能正确导入」。 - 也不能整节砍掉。 模型不知道有什么就不会去查 —— 「不知道自己不知道」。 这份文件的全部意义就是防止它自己造一个组件或直接用 mantine 原生的。
- 要明说「一次能查多个」。 清单没有描述,模型选型时手上是一串候选,逐个查就是 逐个 round trip。这句话不绑参数形态(空格分隔还是数组由两端实现决定),只说能力。
- 不能砍到只剩组件名。 kebab → Pascal 有一串不规则的(
torque-components.md的 frontmatter 是 diting 的硬契约:keywords/globs必须单行逗号分隔(它用^keywords:\s*(.*?)$解析,多行 YAML 数组会静默变成空)。不设
alwaysApply: true,但这份 md 事实上每次 review 都全量注入。 别以为alwaysApply: false拦住了什么。召回是 keywords 驱动的,而且是对整个 diff 做子串匹配 ——rule_loader.py的load_matched_rules把「改动文件路径 + 完整 diff」 拼成一个search_text,然后if kw in search_text_lower。我们的 keywords 里有ui/text/tab,而build含ui、context含text、table含tab—— 任何非空 diff 基本必中。而 keywords 一命中就continue,globs那条路径根本走不到。globs本身也很宽,只是宽得没那么彻底:_match_glob把**/*压成*再交给 Pythonfnmatch,而fnmatch的*是匹配/的,所以globs: src/**/*.{tsx,ts}等于「路径里含src/的任何.ts/.tsx」—— lms-web 有 5974 个这样的文件,另有 296 个在testing/下(那些只有 keywords 能召回)。 全量注入本身是可以接受的(组件选型规则本来就该对每个 tsx 生效,14.5 KB 不是大头), 但想收敛只能剪 keywords,收窄globs一个字节都省不下来。 而剪掉ui/component这类泛词,模型主动search_project_rules时的召回也跟着掉 —— 同一个旋钮的两个方向, 所以现状是刻意的选择,不是疏漏。别在
.md里写某一端专有的工具名(search_component()之类)。这份 md 是给 diting 读的,而那个名字只存在于 lms-web 的 MCP,diting 里没有;写了模型会照着调, 拿一个 "tool not found" 白烧一轮探索预算。指数据文件(components.db)两端都成立。gen.mjs不写回src/fallback.json。 缺 entry 就报错退出,由人补。 写回会让pnpm build变成改动被追踪源文件的操作。不要给这个包加 npm 依赖。 它会被解包进 CI 环境,只用 Node 内置模块。
新增组件之后
- 在
packages/torque/src/index.ts里导出它(gen.mjs从这里认组件) - 往
src/fallback.json加一条英文的whenToUse+keywords—— 不加的话pnpm build会直接报错退出,并把建议的起手式打出来 - 组件有 readme 的话,props 会自动解析出来(表格或
- \name?: Type`: 说明` 列表都认) - 如果它带来了新的硬约束或禁止事项,补进
src/handwritten/
