iter(g151): verify 校验 MCP 工具清单
MCP_TOOL_NAMES 收敛为单一来源,cmdMcp 启动时自证注册表与常量一致。 verify 再查 README / AGENTS / CLI 清单是否逐个写出工具名,缺失即报错。
This commit is contained in:
@@ -26,6 +26,7 @@ const INDEX_FILE = path.join(INDEX_DIR, 'index.json');
|
||||
const LLMS_FILE = path.join(ROOT, 'llms.txt');
|
||||
const LLMS_FULL = path.join(ROOT, 'llms-full.txt');
|
||||
const RECIPES_FILE = path.join(ROOT, 'packages', 'tools', 'cli', 'recipes.md'); // 组合配方单一来源(人类版与 MCP 共用)
|
||||
const MCP_TOOL_NAMES = ['list_components', 'get_component', 'search_components', 'get_tokens', 'list_recipes', 'get_recipe']; // MCP 工具单一来源:注册表与三处文档都对它校验
|
||||
|
||||
/*** 汇总各包 ua.manifest.json 与 package.json 版本(扫描 packages/ 两层目录) ***/
|
||||
function collectManifests() {
|
||||
@@ -580,6 +581,9 @@ function cmdMcp() {
|
||||
|
||||
const recipes = parseRecipes(); // 配方清单:MCP 侧只读一次
|
||||
const compIds = collectComponents(packages).map((x) => x.comp.id); // 组件全集:把配方里的组件名回指到注册表
|
||||
if (tools.map((t) => t.name).join() !== MCP_TOOL_NAMES.join()) {
|
||||
throw new Error(`MCP 工具注册表与 MCP_TOOL_NAMES 不一致:${tools.map((t) => t.name).join(',')}`); // 常量是文档校验的基准,先自证
|
||||
}
|
||||
|
||||
/*** 从配方正文里认出用到的组件 id(按词边界匹配,避免 toast 命中 toastError) ***/
|
||||
const recipeComponents = (r) =>
|
||||
@@ -987,6 +991,26 @@ function checkIterations() {
|
||||
return issues;
|
||||
}
|
||||
|
||||
/*** MCP 工具清单校验:README / AGENTS / CLI 清单必须逐个写出实际注册的工具名(新增工具漏改文档会命中) ***/
|
||||
function checkMcpDocs() {
|
||||
const issues = [];
|
||||
const files = [
|
||||
['README.md', path.join(ROOT, 'README.md')],
|
||||
['AGENTS.md', path.join(ROOT, 'AGENTS.md')],
|
||||
['packages/tools/cli/ua.manifest.json', path.join(ROOT, 'packages', 'tools', 'cli', 'ua.manifest.json')],
|
||||
];
|
||||
for (const [label, file] of files) {
|
||||
if (!fs.existsSync(file)) {
|
||||
issues.push(`${label} 不存在:MCP 工具清单无处登记`);
|
||||
continue;
|
||||
}
|
||||
const text = fs.readFileSync(file, 'utf8');
|
||||
const miss = MCP_TOOL_NAMES.filter((n) => !text.includes(n));
|
||||
if (miss.length) issues.push(`${label} 的 MCP 工具清单缺少:${miss.join(' / ')}(改了 ua mcp 的工具就要同步这三处)`);
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
/*** 配方结构校验:编号连续不重复、每条都有最小代码块与要点、通用约定段存在(MCP 与 llms-full 都按这个结构取用) ***/
|
||||
function checkRecipes() {
|
||||
const issues = [];
|
||||
@@ -1093,6 +1117,7 @@ function cmdVerify() {
|
||||
if (demoSource) issues.push(...checkDemoBundle(anchors)); // 产物与源码同步校验
|
||||
if (!demoSource) console.log('verify: 未找到 apps/demo/main.ts,跳过 demo 锚点校验');
|
||||
issues.push(...checkRecipes()); // 配方结构:编号连续 + 骨架 + 要点,保证 MCP 取得到
|
||||
issues.push(...checkMcpDocs()); // MCP 工具清单:改注册表必须同步 README / AGENTS / CLI 清单
|
||||
|
||||
for (const pkg of pkgs) {
|
||||
const comps = pkg.docs?.components ?? []; // 清单已被 collectManifests 摊平
|
||||
@@ -1141,7 +1166,7 @@ function cmdVerify() {
|
||||
console.error(`${issues.length} violation(s)`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌,README 组件清单与分组同步,backlog ↔ 留痕一致,组合配方结构完整)');
|
||||
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌,README 组件清单与分组同步,backlog ↔ 留痕一致,组合配方结构完整,MCP 工具清单三处一致)');
|
||||
}
|
||||
|
||||
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
||||
|
||||
Reference in New Issue
Block a user