diff --git a/.ua/backlog.json b/.ua/backlog.json index dc8a728..f9ddfbe 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -1548,6 +1548,14 @@ "acceptance": "ua mcp 新增 list_recipes(id/title/components)与 get_recipe(骨架代码 + 要点 + 用到的组件)两个工具;配方解析复用 recipes.md 单一来源;verify 增加配方结构校验(编号连续、每条有代码块与要点、有通用约定段);AGENTS/README/CLI 清单文档同步。", "notes": "", "status": "done" + }, + { + "id": "g149", + "title": "CLI 补 ua search(关键词找组件 + 拼错给相近 id)", + "motivation": "组件已达 67 个,只有 ua list 全量平铺与 ua info 精确查询:不记得 id 时只能肉眼扫列表,拼错则只回一句 not found。", + "acceptance": "ua search <关键词> 按 id/标题/props 名/步骤匹配并排序输出,支持 --json;ua info 与 search 未命中时给出编辑距离 ≤3 的相近 id;usage 与 CLI 清单 steps、README、AGENTS 同步。", + "notes": "", + "status": "open" } ] } diff --git a/.ua/index.json b/.ua/index.json index 761d11a..3eebe3d 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T07:47:10.174Z", + "generatedAt": "2026-10-26T07:49:34.129Z", "repoRoot": ".", "packages": [ { @@ -3583,7 +3583,7 @@ "intro": "工程层工具包(private,不发布)。所有 Agent 与人都通过它读取组件库:清单驱动索引、文档、MCP 三件套,外加 style / verify 两道门禁。", "steps": [ "装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify)", - "查组件:ua list 看全量,ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", + "查组件:ua list 看全量,ua search <关键词> 按 id/标题/props/步骤模糊找(拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码", "改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)", "在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe" diff --git a/AGENTS.md b/AGENTS.md index 955ccbf..4fe067b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,7 +50,7 @@ 可用工具:`list_components` / `get_component` / `search_components` / `get_tokens`(返回 --ua-* 令牌清单,避免硬编码颜色间距)/ `list_recipes` / `get_recipe`(取跨组件组合配方:骨架代码 + 要点 + 用到的组件 id,配好再逐个 `get_component` 查参数)。 -2. **CLI**:`node packages/tools/cli/bin/ua.js list | info | index | docs | style | verify | changelog` +2. **CLI**:`node packages/tools/cli/bin/ua.js list | search <关键词> | info | index | docs | style | verify | changelog` 3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌 / 组合配方,无图形纯文本,喂给 Agent 首选)**。 diff --git a/README.md b/README.md index 19cdd7d..57d97f5 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ accordion · affix · alert · anchor · autoComplete · avatar · avatarGroup ## Agent 接入 - 静态:`llms.txt`(速览)/ **`llms-full.txt`(全量:逐组件参数 / 回调 / 手把手 / 令牌)** -- 命令行:`node packages/tools/cli/bin/ua.js list | info <包名|组件id> | docs | tokens | verify | mcp` +- 命令行:`node packages/tools/cli/bin/ua.js list | search <关键词> | info <包名|组件id> | docs | tokens | verify | mcp` - MCP:在 `~/.workbuddy/mcp.json` 注册 `ua mcp`,可用 `list_components` / `get_component` / `search_components` / `get_tokens` / `list_recipes` / `get_recipe` 规范见 `AGENTS.md`(接入手册)、`ARCHITECTURE.md`(分层与依赖方向)、`CODING.md`(编码规范)、`ITERATION.md`(自动迭代协议)。 diff --git a/llms-full.txt b/llms-full.txt index c54e975..e4f09bd 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T07:47:10.174Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T07:49:34.129Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -41,7 +41,7 @@ ### @ui-arch/cli 1. 装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify) -2. 查组件:ua list 看全量,ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单) +2. 查组件:ua list 看全量,ua search <关键词> 按 id/标题/props/步骤模糊找(拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单) 3. 查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码 4. 改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料) 5. 在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe diff --git a/packages/tools/cli/bin/ua.js b/packages/tools/cli/bin/ua.js index 4f410ef..f6167d5 100644 --- a/packages/tools/cli/bin/ua.js +++ b/packages/tools/cli/bin/ua.js @@ -146,6 +146,62 @@ function formatComponent(pkg, comp) { return lines.join('\n'); } +/*** 编辑距离(截断版):只用来给拼错的名字找相近候选,超过 3 直接当无建议 ***/ +function editDistance(a, b) { + const prev = Array.from({ length: b.length + 1 }, (_, j) => j); + for (let i = 1; i <= a.length; i++) { + let diag = prev[0]; + prev[0] = i; + for (let j = 1; j <= b.length; j++) { + const tmp = prev[j]; + prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1)); + diag = tmp; + } + } + return prev[b.length]; +} + +/*** 未命中时给相近组件 id(编辑距离 ≤ 3),避免只回一句 not found ***/ +function nearIds(name) { + return collectComponents() + .map((x) => [x.comp.id, editDistance(String(name).toLowerCase(), x.comp.id.toLowerCase())]) + .filter(([, d]) => d <= 3) + .sort((x, y) => x[1] - y[1]) + .slice(0, 3) + .map(([id]) => id); +} + +/*** 按关键词搜组件:id 命中优先于 title,其次 props 名与步骤文案;--json 输出结构化结果 ***/ +function cmdSearch(keyword = '', args = []) { + const kw = String(keyword).toLowerCase().trim(); + if (!kw) return console.error('usage: ua search <关键词> [--json]'); + const hits = collectComponents() + .map(({ pkg, comp }) => { + const id = String(comp.id ?? '').toLowerCase(); + const title = String(comp.title ?? '').toLowerCase(); + const props = (comp.props ?? []).map((p) => String(p.n ?? '').toLowerCase()).join(' '); + const steps = (comp.steps ?? []).join(' ').toLowerCase(); + let score = 0; // 分数只用于排序:id 命中 > 标题 > props 名 > 步骤 + if (id === kw) score = 4; + else if (id.includes(kw)) score = 3; + else if (title.includes(kw)) score = 2; + else if (props.includes(kw)) score = 1; + else if (steps.includes(kw)) score = 0.5; + return score ? { pkg, comp, score } : null; + }) + .filter(Boolean) + .sort((a, b) => b.score - a.score || String(a.comp.id).localeCompare(String(b.comp.id))); + if (!hits.length) { + const near = nearIds(kw); + return console.error(`no match: ${keyword}${near.length ? `(相近:${near.join(' / ')})` : ''}`); + } + if (args.includes('--json')) { + return console.log(JSON.stringify(hits.map((h) => ({ package: h.pkg.name, ...h.comp })), null, 2)); + } + for (const h of hits) console.log(`${String(h.comp.id).padEnd(16)} ${h.comp.title ?? ''} (${h.pkg.name})`); + console.log(`\n共 ${hits.length} 个组件命中;查参数:ua info <组件id>`); +} + /*** 输出单包或单组件信息:默认人读摘要,--json 输出原始 JSON ***/ function cmdInfo(name, args = []) { const { packages } = loadIndex(); @@ -159,8 +215,9 @@ function cmdInfo(name, args = []) { } const hit = collectComponents(packages).find((x) => x.comp.id === name); if (!hit) { - console.error(`not found: ${name}(包名与组件 id 都没命中)`); - process.exit(1); + const near = nearIds(name); + console.error(`not found: ${name}(包名与组件 id 都没命中)${near.length ? `;相近:${near.join(' / ')}` : ''}`); + return process.exit(1); } console.log(asJson ? JSON.stringify({ package: hit.pkg.name, ...hit.comp }, null, 2) : formatComponent(hit.pkg, hit.comp)); } @@ -1182,6 +1239,9 @@ switch (cmd) { case 'list': cmdList(); break; + case 'search': + cmdSearch(args[0], args.slice(1)); + break; case 'info': if (!args[0]) { console.error('usage: ua info <包名|组件id> [--json]'); @@ -1217,6 +1277,6 @@ switch (cmd) { cmdMcp(); break; default: - console.log('usage: ua |docs|tokens [--json]|style|sync-counts|verify|commit-lint|lint-iterations|changelog|mcp>'); + console.log('usage: ua |info |docs|tokens [--json]|style|sync-counts|verify|commit-lint|lint-iterations|changelog|mcp>'); process.exit(cmd ? 1 : 0); } diff --git a/packages/tools/cli/ua.manifest.json b/packages/tools/cli/ua.manifest.json index 667efb2..2d5c06a 100644 --- a/packages/tools/cli/ua.manifest.json +++ b/packages/tools/cli/ua.manifest.json @@ -36,7 +36,7 @@ "intro": "工程层工具包(private,不发布)。所有 Agent 与人都通过它读取组件库:清单驱动索引、文档、MCP 三件套,外加 style / verify 两道门禁。", "steps": [ "装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify)", - "查组件:ua list 看全量,ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", + "查组件:ua list 看全量,ua search <关键词> 按 id/标题/props/步骤模糊找(拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码", "改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)", "在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe"