feat(skills): 专项包、整仓导入与 Dashboard 三表联动 - #731
Conversation
- 新增 skill-pack-store 持久化与 /api/skill-packs CRUD API(含 revision 冲突 409、IN_USE 409、force 覆盖) - policy 解析器支持 pack:* 选择器,direct skill 永远优先于 pack(分区展开 + dedup 保留首次) - bot-registry / im-command / cli-admin 兼容 pack: 前缀,间接删除分析接线 - Dashboard 拆为页面壳 + 四标签组件(专项包/Skill库/Bot分配/投递设置) - 专项包 tab:列表/新建编辑抽屉/健康状态/引用预览,删除走 in-use 确认流程 - Skill 库 tab:已安装列表 + 分步安装向导(按来源渐进展示高级字段) - Bot 分配 tab:紧凑表格 + 编辑抽屉带展开去重预览与来源标注 - 投递设置 tab:全局 delivery(auto/prompt/native) + per-bot injection(global/prompt/off) 分开展示 - 新增 6 个 UI 测试 + 31 个后端测试,tsc 干净
- 专项包 tab 改为响应式网格(auto-fill minmax 260px),新建按钮为虚线占位 tile - 卡片视觉优化:健康状态圆点图标、meta 数字加粗、tag/ref chip 样式、缺失项警告条 - Bot 分配 tab 改为左右双栏:左侧可拖拽 Skill/Pack 调色板,右侧 Bot 表格为 drop target - 拖拽到 Bot 行高亮反馈,松手即保存分配(direct skill 或 pack 单次原子写入) - 保留原编辑按钮用于精细调整 - 新增 dragHint i18n + 配套 CSS
- 安装向导与建包弹窗改用 showModal() 渲染为真正的模态层,不再内联悬浮 - 专项包编辑器的 Skill 列表限制高度可滚动,描述截断为单行 - Skill 库卡片描述截断为 3 行,网格高度对齐 - 新增 pack/skill chip 样式:圆角 pill、max-width 截断、hover 反馈 - 拖拽调色板 chips 独立成 pill,不再挤成一行
- Bot 分配调色板:搜索框 + tag 二级过滤按钮,快速定位 Skill - 拖拽提示改为高亮卡片样式 + 拖拽图标,更醒目 - 安装源识别:支持 agentbuddy 完整命令、http(s) 通用 git URL、.zip 文件 - 避免在源码硬编码内网域名(通过域名审计)
- 安装向导 step 2→3 自动触发 discover,step 3 展示来源摘要+发现的 Skill 列表(带勾选),不再盲装 - 粘贴浏览器风格 git URL(含 /tree/branch/path)自动拆分为 repo URL + ref + path - step 3 显示扫描中/结果/状态,安装按钮根据是否有选择走对应流程
- 去掉安装向导里对浏览器URL的自动拆分(用户不希望硬解析) - 发送给后端时,非github的http(s) URL自动加git+前缀,让后端按通用git remote处理 - 解决 bits.bytedance.net 等内网仓库被当作local路径导致 ENOENT 的问题
- dashboardSkillCliIds 不再只返回已配置 Bot 的 CLI,而是包含所有已知 CLI ID - 即使没配 codex/trae 的 Bot,也能在「发现本机 CLI Skill」里看到这些目录的 skill - 目录去重逻辑不变(coco/traex 共享 ~/.trae/skills 只显示一个 tab)
- 包含的Skill区域增加搜索框,按名称/描述过滤 - 增加全选/取消全选复选框 - 显示已选/总数 (3/5) - 弹窗加宽到 560px,列表项间距优化,描述截断1行 - skill多时不再逼仄,可快速定位选择
- packs/skills 未传或未加载时回退为 [],避免 .length/.map 抛错 - 替换组件内 props.skills 直接访问为本地安全变量 - 修复 test/skill-packs-ui.test.ts 3 用例在 props.packs 未传时的崩溃
解析与来源层: - 新增 parseAgentbuddyIdentifier,实现文档早已承诺但从未实现的 `agentbuddy:collection/<uid>` 与 `agentbuddy:<group>/<skill>[@Version]`; 此前这两种写法会一路穿透到 local 分支,被当作本机路径安装而失败。 - 抽出 formatAgentbuddyIdentifier 作为保存格式与解析格式的唯一来源, 保证 registry 里存下的 source 能原样反解回同一目标。 - 兼容 marketplace 复制命令的 `skill add <group>/<skill>` 合并路径写法, 保留原有 `--skill` / `--version` 显式形式。 - 裸 `owner/repo[/path]` GitHub 简写改由后端归一化;同名本机目录优先。 - 其余 http(s) 一律按 git remote 处理,不再退化成本机路径;未知 scheme 直接报错。 - 不再用「字符串含 agentbuddy」这类子串启发式判定来源:含该词的 URL 或 本机路径不会被误判为 agentbuddy 源。marketplace URL 在拿到真实格式前不予支持。 - frontmatter 读取支持 YAML 块标量(>、>-、|、|- 等)并容忍 BOM 与前导空行。 此前 `description: >-` 会把字面量 ">-" 存成描述,BOM/前导空行则导致整块 frontmatter 静默丢失,Skill 卡片退化成只有来源链接、没有描述。 测试:test/skill-sources.test.ts 扩充规范标识符、round-trip、子串不劫持、 GitHub 简写与 http 兜底用例;新增 test/skill-frontmatter.test.ts 覆盖块标量、 BOM、前导空行及原有行为回归。
安装流程层: - 浅扫只看源根目录及 `skills/*`、`.agents/skills/*`、`.botmux/skills/*` 的 直接子目录。真实 skill 集合普遍多一层分类目录(如 mattpocock/skills 的 `skills/<category>/<skill>/SKILL.md`),导致整仓导入扫出 0 个、提示 「没有发现可安装的 Skill」。fullDepth 递归扫描的能力一直存在,但 dashboard 从未传过该字段,等于一段调用不到的死代码。 - 现改为浅扫命中即走快路径,扫不到才回退递归扫描;git/github 在同一次 clone 内完成回退,不重复拉取。 - 回传 deepScanned 标记,安装时沿用同一深度,避免「扫得到却装不上」的竞态。 - SkillSourceDiscovery 增加 deepScanned 字段供 UI 提示。 影响面:dashboard 安装/发现路径;CLI 与 IM 的安装入口共用 parseSkillInstallSource, 行为与 dashboard 保持一致。 测试:新增 test/skill-discovery-deep-scan.test.ts,覆盖浅扫漏检、自动回退并标记、 浅扫命中时不回退、显式 fullDepth 跳过浅扫四种情形。
UI 联动层: - 三步安装向导改为单页表单(对齐 3.8 的可用性):来源、仓库内路径、Ref 同屏, 附格式说明与深度扫描开关。原向导存在空步骤(agentbuddy 源在第 2 步无内容)、 步骤指示器三态缺失、以及「下一步」会触发发现并在单候选时直接安装的问题。 - 输入后回显后端语义的识别结果与部署机运行条件;安装失败按 Git / agentbuddy / 本机路径给出可执行的诊断卡片。 - 统一数据层 useSkillsData:四个标签共用一份 skills/packs/bots,取消各标签自取数。 - 新增 buildSkillGraph 作为 Skill↔专项包↔Bot 的唯一关系模型,保留「被引用但未安装」 的 Skill 节点;各处健康度、最终能力数、引用计数均由其派生。 - 三表可互相跳转:Skill 卡片的包/Bot 用量、专项包的 Bot 引用与缺失 Skill、 Bot 行的包/Skill 芯片。缺失 Skill 只跳转并预填安装入口,绝不自动安装。 - 顶部配置健康总览条:被引用但未安装的 Skill、未分配 Bot 的专项包、缺失能力的 Bot, 点击进入对应预筛选视图。 - 专项包接口非 404 失败时保留上次数据并提示;首次加载即失败时 packsKnown=false, 相关健康度按「未知」呈现,不会误报 pack_missing,也不会显示「配置健康」。 - 修复 Skills UI 使用不存在的 CSS 变量(--tag-bg/--card-bg/--input-bg/--code-bg 等) 导致浅色主题下芯片与卡片渲染成深色;补齐此前完全没有样式的对话框、按钮与空状态。 影响面:仅 dashboard web 层,不涉及 daemon/worker/CLI 适配器/IM 通道。 测试:新增 skill-graph、skills-cross-link-ui、skill-source-parity 测试; 同步更新 dashboard-skills-ui、skill-library-workflow-ui。
原两列 grid 把「导入地址」与「仓库内路径」拆到左右两列,左列堆 4 个区块、 右列只有 1 个字段,主操作按钮被挤到右列中部,视觉上与任何字段都无关联, 下方深度扫描说明又占了两行。 - 改为单列:导入地址是主角,占满宽度。 - 仓库内路径 / Ref / 深度扫描收进「高级选项」折叠区(默认收起)——三者都只 在特定场景需要,深度扫描后端本就会自动回退。 - 常用格式示例改为可点击填入,取代只能照着抄的静态文本。 - 操作行回到表单底部:次要入口「浏览本机 Skill」在左,主按钮在右并改为 primary 样式。 既有 data-install / data-action 选择器保持不变,UI 测试不受影响。
`keeps advanced install fields visible beside the install action` 断言 `path.parent?.parent === installGrid`,在高级项折叠进 <details> 后层级变深必然 失败;而 `expect(a).toBe(b)` 作用于两个 React TestInstance 时,vitest 会序列化 两棵含循环引用的内部树来生成 diff,直接耗尽 fork worker 的堆——表现为 「Worker exited unexpectedly / Timeout terminating forks worker」,而不是一条 普通的用例失败。全量测试因此在 12393 passed 的情况下仍以非零码退出。 - 用例改为断言「path/ref/full-depth 位于 data-install-advanced 容器内」「安装 按钮在页脚且为 primary」,只校验归属关系,不再依赖精确层级。 - 比较 TestInstance 一律用 `expect(a === b).toBe(true)`,避免失败时的深序列化。 - 新增示例可点击填入的用例。
|
复审进度(Codex,head 目前确认的边角:
已核对:direct |
deepcoldy
left a comment
There was a problem hiding this comment.
复审结论:请求修改。head 2494788ed;pack 的 direct 优先/跨 pack 去重主逻辑与 agentbuddy 结构化 argv 安全边界核对通过,但发现 2 个会影响核心承诺的正确性问题,以及 1 个大仓性能问题。
Blocking
P2:混合目录布局下,“整仓导入”会静默漏 Skill
src/dashboard/skill-install-request.ts:120-123 只在浅扫结果为 0 时回退深扫。若仓库同时包含:
skills/flat/SKILL.mdskills/category/nested/SKILL.md
默认 discovery 只返回 flat,不会设置 deepScanned;显式 fullDepth: true 才返回 flat + nested。这不是“空仓误报”,而是无提示的不完整候选集,用户也没有线索去打开隐藏在高级选项里的深扫。
建议:至少在已知浅扫根下还存在非 Skill 子目录时继续深扫,或用其它方式保证整仓候选完整;补“浅层 + 深层混合布局”回归测试。
P2:语义损坏的 packs.json 可打崩新会话解析
src/services/skill-pack-store.ts:53-65 对合法 JSON 直接按 Record<string, SkillPack> 信任;src/core/skills/policy.ts:111-113 随后无类型守卫调用 member.startsWith()。例如 include: [123] 会抛:
TypeError: member.startsWith is not a function
这与 store 注释的“corrupt packs.json 不影响 skill pipeline”不符。API 正常写入会校验,但手工编辑、磁盘/兼容性输入仍可阻断 session skill resolution。
建议:read 时做完整 schema 归一化/丢弃非法 pack,且 resolver 保留 typeof member === 'string' 的防御;补合法 JSON 但非法字段类型的测试。
需要本次确认的性能问题
P2:git 批量安装对每个 Skill 重复 fetch/checkout
installGitSkillsFromSourceAsync() 在 src/services/skill-registry-store.ts:894-904 先 checkout+discover,随后每个 candidate 又进入 installGitSkillAsyncLocked() → checkoutGitSourceAsync()。我用本地 2-Skill git repo 跑 trace,已看到初次 clone/fetch/checkout 后,对 a、b 各重复一轮 fetch/checkout;含 Git plumbing 共启动 31 个 git process。大仓整仓导入会按 Skill 数量线性放大远端往返。
这段循环是既有实现,但本 PR 新打通的深层整仓导入正是会把它推到真实大集合上的入口。建议本次收敛为一次 checkout 内复制全部 selected candidates、一次 registry write;如果决定拆后续,至少请在 PR 说明里明确评估和上限。
非阻塞一致性项
detectSourceType()把文档支持的skills add owner/repo/npx skills@latest add .../add-skill ...显示为“本地”,后端实际判为 GitHub;建议补 parity 用例并修 visual hint。docs/setup/skills.md:142仍写skills.include“只支持 skill:”,与新增pack:<id>冲突。
验证
pnpm build✅- PR 新增/相关 14 个测试文件:169 tests passed ✅
git diff --check✅git merge-tree --write-tree origin/master HEAD✅(当前 master 合并无文本冲突)- 额外定向复现:混合布局默认漏深层 Skill;非法 pack member 触发 TypeError;2-Skill git 批装 trace 证实逐项重复 fetch/checkout。
未执行 merge;仍遵守“申晗确认前不合码”。
feat(skills): 专项包、整仓导入与 Dashboard 三表联动
背景
现有 Skill 管理在“安装、组合、分配、诊断”四段链路上彼此割裂:多个 Skill 需要逐个分配给 Bot;GitHub 整仓地址遇到分类目录时扫不到 Skill;文档承诺的
agentbuddy:标识符从未实现;部分标准 frontmatter 安装后丢失描述;Dashboard 安装向导还有空步骤,并可能在“下一步”阶段触发安装。本 PR 以 Skill Pack(专项包)为组合单元,把核心模型、解析安装、Dashboard 管理和回归测试一次贯通。虽然改动横跨 core / dashboard / CLI / IM,但都服务于同一条 Skill 管理工作流,因此保持为一个 PR,并按以下逻辑层次组织 review。
改了什么
1. Skill Pack 核心模型与管理入口
~/.botmux/skills/packs.json)及 CRUD:校验 id/name/include/tags,成员只允许skill:*,禁止 pack 嵌套;写入串行化,并用 revision 防止编辑器覆盖并发更新。skills.include新增pack:<id>selector。会话解析时先解析显式skill:*,再按策略顺序展开pack:*并按 Skill 名去重;同一 Skill 同时被直接引用和专项包引用时,直接引用优先。botmux skills pack list/show/create/update/delete;现有/skills attach|detach只修改直接 Skill,并原样保留 pack selector,避免 IM 操作误清空专项包分配。/api/skill-packsCRUD,并把 Skill 管理拆成 Skill 库、专项包、Bot 分配、投递设置四个标签:支持专项包搜索/全选、Bot 搜索与 tag 过滤、双向拖拽分配(拖入 Bot 添加、将行内已展示芯片拖回明确移除区取消),以及全局工作区 Skill / delivery 配置。重复投放会显示“已在配置中”而非静默失败,跨单元格移动也不会让投放高亮闪烁。紧凑表格折叠的第 4 个及以后直接 Skill、以及键盘操作,继续通过“选择”编辑器完整管理。2. 来源解析与元数据兼容
agentbuddy:collection/<uid>与agentbuddy:<group>/<skill>[@version];抽出formatAgentbuddyIdentifier,保证 registry 保存格式可以反解回同一目标。skill add <group>/<skill>合并路径写法,并保留--skill/--version显式形式与npm_config_registry=... npx -y agentbuddy@latest ...前缀。execFile传参,不执行用户粘贴的 shell 前缀。owner/repo[/path]GitHub 简写改由后端归一化,同名本机目录优先;其它 http(s) 按 Git remote 处理;未知 scheme 明确报错。Dashboard、CLI、IM 因此使用同一套服务端分类。>、>-、|、|-)并容忍 BOM 与前导空行,修复描述被存成字面量">-"或整块元数据静默丢失。3. 整仓发现与安装一致性
skills/*、.agents/skills/*、.botmux/skills/*的直接子目录;真实集合常见的skills/<category>/<skill>/SKILL.md因此会漏检。deepScanned,安装阶段沿用相同fullDepth,避免“预览扫得到、提交时装不上”。显式深度扫描仍可跳过浅扫。4. Dashboard 安装体验、三表联动与健康状态
useSkillsData,四个标签共用 skills / packs / bots,避免各标签独立取数和状态漂移。buildSkillGraph作为 Skill ↔ 专项包 ↔ Bot 的关系模型,保留“被引用但未安装”的节点;最终能力数、引用数和健康度统一由 graph 派生。packsKnown=false,相关结论显示为“未知”,不会误报pack_missing或“配置健康”,也不会泄漏虚假的 0。5. 测试稳定性
expect(a).toBe(b)直接比较两个 React TestInstance 时,失败会让 Vitest 为 diff 序列化两棵带循环引用的内部树,最终耗尽 fork worker 的堆并表现为Worker exited unexpectedly。测试改为先判断归属关系,再断言布尔值,避免生成巨型对象 diff。此外,本分支已 rebase 到最新
upstream/master。上游新增/fork命令后,命令实现与注册表已经落地,但中英文 Slash Command 文档尚未同步,导致仓库既有的文档同步测试失败;本 PR 仅补齐对应两行说明,不改动/fork行为。影响面评估
src/core/skills/sources.ts、policy.ts、references.ts、frontmatter.ts和 Skill Pack store 是共享核心路径,会影响 Dashboard、Skill 管理 CLI、IM 快捷命令及新会话的 priority Skill 解析;对应行为均有专项测试。test/skill-source-parity.test.ts锁定 Dashboard 提示与后端分类不发散,但最终判定仍以后端为准。src/dashboard/web/**;后端和共享核心改动已在前述条目单列。测试验证
重点新增/扩展测试:
test/skill-pack-store.test.ts/skill-pack-policy.test.ts/skill-pack-references.test.ts— 专项包持久化、策略展开、selector 保真与引用行为test/skill-frontmatter.test.ts— 块标量、BOM、前导空行及原有格式回归test/skill-discovery-deep-scan.test.ts— 浅扫漏检、自动回退、显式 fullDepth 与 shallow fast pathtest/skill-source-parity.test.ts/skill-sources.test.ts— 三入口来源分类、Agentbuddy round-trip、Git 安全边界test/skill-graph.test.ts/skills-cross-link-ui.test.ts— 关系图、三表跳转、导航不触发安装、Pack API 错误及未知态test/dashboard-skills-ui.test.ts— 单页安装表单、高级项折叠、可访问来源帮助、诊断与选择流程test/bot-assignments-ui.test.ts— 左侧拖入分配、重复投放反馈、行内高亮边界、已分配 Skill / 专项包拖回取消、跨 Bot 拖放严格 no-op、Firefox 原生拖拽数据,以及移除时其它 selector 保真手工预览未执行
switch:here,也未重启 daemon 和现有 Bot。功能主体此前已使用真实数据检查单页安装表单、高级选项、Skill / 专项包 / Bot 三表跳转、健康总览,以及grill-me描述展示;本轮来源帮助浮层则在独立临时 HOME、空数据目录和隔离端口的 Dashboard 中验收。全程未提交安装请求,也未在真实 registry 执行 Agentbuddy 写入。Agentbuddy 解析、GitHub 整仓深扫与 frontmatter 兼容由自动化测试覆盖。截图
Skill 库:单页安装、健康总览与解析后的描述
导入来源帮助:默认收起,按需查看完整格式与 Agentbuddy 命令
专项包:组合能力与 Bot 引用
Bot 分配:专项包、直接 Skill、最终能力与健康状态
部署说明
~/.botmux/.env配置BOTMUX_AGENTBUDDY_CMD="npx -y agentbuddy@latest";私有 npm registry 由部署机 npmrc/env 提供。npm_config_registry=... npx...前缀只用于识别来源,不会覆盖部署机执行环境,也不会作为 shell 命令执行。