diff --git a/.ua/backlog.json b/.ua/backlog.json index 8c0bae8..eb2e981 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -3257,6 +3257,15 @@ "motivation": "多步向导是后台最高频的表单形态之一,现有 19 条配方没有覆盖「步进条 + 分步字段 + 步间校验 + 回跳」的组合:stepper 的 active 双向绑定与 formItem 句柄的 validate() 恰好能拼出这个场景,缺一条示范怎么把校验拦在步与步之间,而不是最后一步集体报错。", "acceptance": "① recipes.md 新增配方 20(编号连续、含代码块与要点,代码块能被 ua recipes 认出 stepper / formItem / button / card / toast 组件);② 配方代码与真实签名一致(FormRule.message、button disabled 收 Signal、formItem 句柄 validate);③ ua recipes 20 与 ua search 均能命中;④ npm run check 通过。", "notes": "" + }, + { + "id": "g319", + "domain": "tooling", + "status": "open", + "title": "ua info 支持逗号分隔批量查询多个组件", + "motivation": "Agent 拼一个界面通常要 3–5 个组件的参数,现在只能 `ua info button`、`ua info select` 一条条查(MCP get_component 同样单查),每轮多花好几次往返;逗号分隔批量一次拿全,文本 / --md / --json 三种模式都覆盖。", + "acceptance": "① `ua info button,select --md` 一次输出两个组件的 Markdown 片段(片段间以分隔线隔开);② `--json` 多值时输出数组、单值保持对象(不破坏现有脚本消费);③ 不存在的 id 报错并指明是第几个;④ manifest usage 串同步更新;⑤ npm run check 通过。", + "notes": "" } ] } diff --git a/AGENTS.md b/AGENTS.md index 7747d08..801a2e2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,7 @@ 可用工具:`list_components` / `get_component`(传 `format: "md"` 直接拿 Markdown 片段,等同 `ua info --md`)/ `search_components` / `get_tokens`(返回 --ua-* 令牌清单,避免硬编码颜色间距)/ `list_recipes` / `get_recipe`(取跨组件组合配方:骨架代码 + 要点 + 用到的组件 id,配好再逐个 `get_component` 查参数)。 -2. **CLI**:`node packages/tools/cli/bin/ua.js loop [--json](迭代循环诊断:锁心跳 / 上次留痕 / 待办数) | iter done (迭代收尾:置 done + 写留痕,ts 由本机时钟生成) | iter add <条目json>(补自选题) | list [--json|--components] | search <关键词> | info [--json|--md](--md 出可粘贴的 Markdown 片段:标题 + 参数表 + 用法 + 示例) | recipes [编号] [--json] | index | docs | style | verify | changelog`(`--json` 给脚本与 Agent 用,不必解析文本);`search` 覆盖 id / 标题 / 参数名 / 参数说明 / 步骤五层 + 配方三层(标题 / 配方组件 / 要点,命中行直接给 `ua recipes <编号>` 指路),命中非 id 标题时会标出命中字段(如「← 参数说明含『处理中』」),`--json` 输出 `{ components, recipes }` 两数组、各项带 `matched` 字段 +2. **CLI**:`node packages/tools/cli/bin/ua.js loop [--json](迭代循环诊断:锁心跳 / 上次留痕 / 待办数) | iter done (迭代收尾:置 done + 写留痕,ts 由本机时钟生成) | iter add <条目json>(补自选题) | list [--json|--components] | search <关键词> | info [--json|--md](name 支持逗号分隔一次查多个;--md 出可粘贴的 Markdown 片段:标题 + 参数表 + 用法 + 示例,批量片段以水平线分隔;--json 单值为对象、多值为数组) | recipes [编号] [--json] | index | docs | style | verify | changelog`(`--json` 给脚本与 Agent 用,不必解析文本);`search` 覆盖 id / 标题 / 参数名 / 参数说明 / 步骤五层 + 配方三层(标题 / 配方组件 / 要点,命中行直接给 `ua recipes <编号>` 指路),命中非 id 标题时会标出命中字段(如「← 参数说明含『处理中』」),`--json` 输出 `{ components, recipes }` 两数组、各项带 `matched` 字段 3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌 / 组合配方,无图形纯文本,喂给 Agent 首选)**。 diff --git a/llms-full.txt b/llms-full.txt index ae7d5f6..7eb4ffd 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T16:55:18.044Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T17:03:14.418Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -2764,7 +2764,7 @@ API 速览: 一行用法: ``` -node packages/tools/cli/bin/ua.js [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor +node packages/tools/cli/bin/ua.js [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor ``` 约定: diff --git a/packages/tools/cli/bin/ua.js b/packages/tools/cli/bin/ua.js index 0307428..3d6d103 100644 --- a/packages/tools/cli/bin/ua.js +++ b/packages/tools/cli/bin/ua.js @@ -315,31 +315,56 @@ function cmdSearch(words = [], args = []) { console.log(`\n共 ${parts.join('、')}命中;查参数:ua info <组件id>,看骨架:ua recipes <编号>`); } -/*** 输出单包或单组件信息:默认人读摘要,--json 输出原始 JSON ***/ +/*** 输出包或组件信息:默认人读摘要,--json 输出原始 JSON;name 支持逗号分隔一次查多个(单值保持原输出结构不破坏脚本) ***/ function cmdInfo(name, args = []) { const { packages } = loadIndex(); const asJson = args.includes('--json'); - const pkg = packages.find((x) => x.name === name || x.name === `@ui-arch/${name}`); - if (pkg) { - if (asJson) return console.log(JSON.stringify(pkg, null, 2)); - return console.log( - `${pkg.name} v${pkg.version} [${pkg.type}] ${pkg.description}\n(--json 看完整清单;按组件查:ua info <组件id>)`, - ); + const asMd = args.includes('--md'); + const names = name.split(',').map((s) => s.trim()).filter(Boolean); // 批量:逗号分隔多组件一次拿全,省 Agent 往返 + const comps = collectComponents(packages); + const resolve = (n) => { // 单个名字 → 包或组件条目;都没命中返回 null 交由调用方报错 + const pkg = packages.find((x) => x.name === n || x.name === `@ui-arch/${n}`); + if (pkg) return { kind: 'pkg', pkg }; + const hit = comps.find((x) => x.comp.id === n); + return hit ? { kind: 'comp', hit } : null; + }; + if (names.length === 1) { + const r = resolve(names[0]); + if (!r) { + const near = nearIds(names[0]); + console.error(`not found: ${names[0]}(包名与组件 id 都没命中)${near.length ? `;相近:${near.join(' / ')}` : ''}`); + return process.exit(1); + } + if (r.kind === 'pkg') { + if (asJson) return console.log(JSON.stringify(r.pkg, null, 2)); + return console.log( + `${r.pkg.name} v${r.pkg.version} [${r.pkg.type}] ${r.pkg.description}\n(--json 看完整清单;按组件查:ua info <组件id>)`, + ); + } + const { hit } = r; + if (asJson) return console.log(JSON.stringify({ package: hit.pkg.name, ...hit.comp, recipes: recipesFor(hit.comp.id) }, null, 2)); + const related = recipesFor(hit.comp.id); + const recipeTail = related.length ? `\n\n出现在配方:${related.map((x) => `${x.id}(${x.title})`).join('、')}(ua mcp 的 get_recipe 可取骨架)` : ''; + if (asMd) return console.log(formatComponentMd(hit.pkg, hit.comp) + recipeTail); // Markdown 片段:贴进文档 / issue / 回复时不用手抄参数表 + return console.log(formatComponent(hit.pkg, hit.comp) + recipeTail); } - const hit = collectComponents(packages).find((x) => x.comp.id === name); - if (!hit) { - const near = nearIds(name); - console.error(`not found: ${name}(包名与组件 id 都没命中)${near.length ? `;相近:${near.join(' / ')}` : ''}`); + const resolved = names.map((n) => resolve(n)); // 批量路径:先全量解析,任一失败整条拒绝(输出一半再报错会让脚本误判成功) + const bad = names.filter((n, i) => !resolved[i]); + if (bad.length) { + const near = nearIds(bad[0]); + console.error(`not found: ${bad.join(' / ')}(第 ${names.indexOf(bad[0]) + 1} 个,包名与组件 id 都没命中)${near.length ? `;相近:${near.join(' / ')}` : ''}`); return process.exit(1); } - if (asJson) return console.log(JSON.stringify({ package: hit.pkg.name, ...hit.comp, recipes: recipesFor(hit.comp.id) }, null, 2)); - const related = recipesFor(hit.comp.id); - const recipeTail = related.length ? `\n\n出现在配方:${related.map((r) => `${r.id}(${r.title})`).join('、')}(ua mcp 的 get_recipe 可取骨架)` : ''; - if (args.includes('--md')) { - // Markdown 片段:贴进文档 / issue / 回复时不用手抄参数表;配方清单压成一行尾注 - return console.log(formatComponentMd(hit.pkg, hit.comp) + recipeTail); + const compMd = (r) => { // 批量输出统一用组件片段格式:包条目退化为标题行 + if (r.kind === 'pkg') return `## ${r.pkg.name}(${r.pkg.type})\n\n${r.pkg.description}`; + const related = recipesFor(r.hit.comp.id); + const tail = related.length ? `\n\n> 出现在配方:${related.map((x) => `${x.id}(${x.title})`).join('、')}` : ''; + return formatComponentMd(r.hit.pkg, r.hit.comp) + tail; + }; + if (asJson) { + return console.log(JSON.stringify(resolved.map((r) => (r.kind === 'pkg' ? r.pkg : { package: r.hit.pkg.name, ...r.hit.comp, recipes: recipesFor(r.hit.comp.id) })), null, 2)); } - console.log(formatComponent(hit.pkg, hit.comp) + recipeTail); + console.log(resolved.map(compMd).join('\n\n---\n\n')); // 片段间以水平线分隔,方便人读与脚本按线切块 } // ---- 静态文档站生成 ---- @@ -2033,6 +2058,6 @@ switch (cmd) { cmdMcp(); break; default: - console.log('usage: ua |info [--json|--md]|recipes [编号] [--json]|docs|tokens [--json|--unused]|style|sync-counts|verify|doctor|commit-lint|lint-iterations|iter|audit:visual [--json|--baseline]|audit:contrast [--json|--list]|audit:motion [--json]|audit:surface [--json|--list]|loop [--json]|changelog|mcp>'); + console.log('usage: ua |info [--json|--md]|recipes [编号] [--json]|docs|tokens [--json|--unused]|style|sync-counts|verify|doctor|commit-lint|lint-iterations|iter|audit:visual [--json|--baseline]|audit:contrast [--json|--list]|audit:motion [--json]|audit:surface [--json|--list]|loop [--json]|changelog|mcp>'); process.exit(cmd ? 1 : 0); } diff --git a/packages/tools/cli/ua.manifest.json b/packages/tools/cli/ua.manifest.json index f4dd421..67d4be0 100644 --- a/packages/tools/cli/ua.manifest.json +++ b/packages/tools/cli/ua.manifest.json @@ -34,7 +34,7 @@ "MCP tool 入参 schema" ] }, - "usage": "node packages/tools/cli/bin/ua.js [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor", + "usage": "node packages/tools/cli/bin/ua.js [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor", "conventions": [ "零依赖:只用 node 标准库,Node >= 18", "清单是单一来源:代码注释、文档站、llms-full.txt、MCP 全部从 ua.manifest.json 派生", @@ -56,4 +56,4 @@ ], "components": [] } -} +} \ No newline at end of file