Files
ycode/docs/skills-implementation-20261016.md

9.7 KiB
Raw Permalink Blame History

本地技能实施与验收(2026-10-16)

本次实现可实际使用的本地技能库:导入目录/ZIP、读取说明、启停、覆盖、卸载、导出,以及当前空间的技能发现和手动/自动调用上下文。技能页接入真实 SkillsApi。在线市场、云端同步、付费权益和安全扫描服务尚未接入;没有伪造在线卡片、安装量、扫描通过或购买状态。本报告不宣称技能整体 1:1 已完成。

文件与边界

  • src/main/capabilities/skills.ts:本地存储、包校验、发现、生命周期和模型上下文。
  • src/renderer/src/capabilities/SkillsPage.tsx:默认导出技能页;Props 为 {cwd?, onUseSkill?, onCreateSkill?, onFindSkill?}。
  • skills-source.mjs / skills-source.d.mts / skills-source.css / skills-source-manifest.json:固定 REA 源码恢复产物。
  • skills-host.css:宿主容器、可访问交互、本地状态和原版没有对应服务的提示。
  • scripts/test-skills-reference.mjs:固定 SHA 校验及恢复(--write);不读原版安装目录,不操作原窗口。
  • scripts/test-skills-files.mjs、scripts/test-skills-ui.mjs:隔离真实文件系统与原生 Electron 控件验收。

共享契约、IPC、preload、App、ChatPane、CapabilityCenter、包依赖和生产 out 均由主上下文接线;本 worker 未修改这些文件。复用主上下文导出的 CapabilityToolbar / CapabilitySubpageHeader,右侧动作通过 portal 使用唯一共享顶栏。

主进程契约与语义

导出 listSkills(cwd?)、importSkill(path, overwrite=false)、readSkill(id,cwd?)、setSkillEnabled(id,enabled,cwd?)、removeSkill(id)、exportSkill(id,destination)。UI 的 SkillsApi.importPath(undefined,overwrite,kind) 委托根 IPC 展示文件夹或 ZIP picker,不新增手填系统路径流。

存储根为 app.getPath('userData')/capabilities/skills:registry.json 保存注册/禁用状态,packages/<24hex>/ 保存导入副本。user:<name-hash> 保持同名覆盖身份稳定;空间 ID 基于实际包路径,不把两个空间同名技能混为一个。发现当前 cwd 下 .agents/skills 与 .claude/skills,不枚举其他空间。空间包可禁用但不能由卸载 API 删除;导入源始终保留。UI 共享 slash 菜单的 cwd 作用域 pin memory key,保留其他 memory 字段。

SKILL.md 必须为 UTF-8、含 YAML name/description;使用 YAML core schema,支持 folded 字段、版本数值和受限 alias,拒绝重复键及把字符串 "false" 误当布尔值。支持 enabled/disable、user-invocable/userInvocable、disable-model-invocation/disableModelInvocation。未知前置元数据保留于原文件,不重写包内容。

  • buildSkillContext(ids,cwd):显式手选,只允许 enabled + userInvocable;手动专用技能允许注入。
  • buildAvailableSkills(cwd):输出真实 {id,name,description} JSON 目录,只含 enabled 且未 disableModelInvocation 项。
  • loadSkillContext(id,cwd):模型自动加载遵守 enabled + !disableModelInvocation。
  • readSkillResource(id,relative,cwd):仅当前可用包的受限相对 UTF-8 文本,最大 1MB。根工具桥另行约束回合 allowedIds,不能仅凭 model 给出的任意 ID 放行。

单个上下文文档限制 65,536 字符,手选总计 131,072 字符。没有直接执行包内脚本;真实模型可以通过根已有工具执行任务,是否与原版内核同义还需独立验证。

损坏、缺失、被重定向或 YAML 无效的注册包按条目返回 error、enabled:false,保留 ID/name/path 以定位和卸载;不让无关损坏包阻断普通聊天的空选 context 或可用技能目录。空间坏包同样显示不可用。主索引自身损坏仍为明确错误,不静默覆写丢失注册状态。

路径与安装事务

导入目录、SKILL.md 和 ZIP 都先校验;包最多 2,000 文件、展开总量 50MB,ZIP 输入最多 20MB,SKILL.md 最大 1MB。不跟随技能存储祖先、源目录、包或资源中的 symlink/junction。拒绝绝对路径、盘符、反斜杠、dot traversal、Windows 设备名、控制字符及大小写/NFKC 重复 ZIP 路径。ZIP 中央目录预检拒绝多卷/ZIP64/加密/设备/链接;再每 4KB 输入分块解压,核对每项实际输出与声明大小,防止只信声明大小的膨胀包。

使用同级 stage/backup + rename 安装替换,索引原子写入,提交失败回滚。覆盖需要明确 overwrite;UI 勾选“替换已导入的同名技能”。导出以 wx 创建 ZIP,不覆盖已有文件或技能存储。卸载先重命名隔离再提交索引,缺失目录可清理注册,注册路径已被替换为 junction 时只移除链接本身,不触及指向目录。目录遍历防护不承诺抵御具有同等文件权限的进程在所有 lstat/read 间恶意竞态。

原版来源与恢复范围

WorkBuddy 5.8.2,ASAR SHA-256 035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918。所有原实现信息来自已经保存的 REA 导出;先验证 SHA 再解析。

来源 REA Evidence 固定 SHA / 范围
skills-market-Cwf-NvXV.js ev_e8faf60b3d22b99ede302f2d0efdfb3145f53f59789d4a345f5892dc13a589a4 f1434cadabf5ed3762f00fc2a356bc7418b0b4aab27db62dc9fbbe5add6f6dfb,CardFrame UTF-16 [10076,13702) 原函数体
standalone-skill-detail-modal-Bi5X8CZ4.js ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb ee7de2a7123f94110aa01258d3d6b3fcc3ce48f2ec5fd8f91cb3367c817a0f01,8 个字面 SVG 函数,精确 offset/body SHA 见 manifest
skills-market-d_hn_qWB.css selected Evidence 同上 935d55effde68d03b2fbc2529ec94b19a6cd7210d9bc79febd6d115b115f60b6
standalone-skill-detail-modal-fe_BFVdd.css ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94 7967df52bacb7594f934b32d8d5fb24854dbc8c47d0217d4677c7d7da0655f49
src-Bh3YZ35H.css CSS Evidence 同上 88dbdc1cd3f59d0e8b75824512a465a2be714bc7af481d790fe3a9484f85e01a,button/input/switch/dropdown/popover
contexts-BkjwYSZT.css CSS Evidence 同上 50cbd673f7f35fc7f966bdddc6c9f5677fdfe944cb904888d214619fca259380,skill-upload + 对应 keyframes

CSS 声明、顺序、media 和 keyframes 保留;选择器限制到 .yc-capability-center,原 skill-market 顶栏祖先适配到共享 portal。CardFrame 原体仅替换 JSX runtime 引用;React singleton、Tooltip/Avatar/批量 checkbox、原服务数据为显式宿主适配。详情的 header/view/scroll/intro/overview/file 结构由已导出源重新接本地数据,并非整个原详情组件原体复用。导入结构依据 contexts-BMeWSUHk.js 的 SkillUploadModal [4365603,4372182);文件夹/ZIP native picker 取代原宿主 facades。没有拖拽和 web FileList fallback。

原 runtime 对照:市场 ev_c827c8dc7dee0c67c7870f6bf8417b87ec7b4d90d507ecc5dc592b5878579f10;稳定已安装 ev_9d8628a28fed60291e628cebaec55537bfb8274d4b6c8f746bffadcbc277d3fb;添加菜单 ev_ddef9315aaf0ff534cf02573212e205ba70e5b69dfc1b3dcf555b430920faf22。市场和菜单各自 cleanup cancelled,只有动作帧有效;不能宣称 probe 全通过。原已安装 5 个市场/套件数据与本机自定义技能不是同数据集,不作全画面像素相等断言。

验证

  • node scripts/test-skills-reference.mjs --write:6 个固定源码/CSS SHA、9 个函数恢复、4 个输出生成;再不带参数可验证未漂移。
  • node scripts/test-skills-files.mjs:59/59 PASS,.tmp/skills-files-iAkonv/report.json。实际临时 FS,唯一 Electron stub 是隔离 userData 路径。覆盖生命周期、持久化、空间隔离、metadata flags、ZIP roundtrip/traversal/碰撞/symlink/虚报解压大小、损坏/缺失/重定向注册包容错和卸载。
  • node scripts/test-skills-ui.mjs:37/37 PASS,.tmp/skills-ui-fR8rWr/report.json。普通可见 Electron + SwiftShader,1052×901 内容区;真实 main 模块、preload/IPC 和隔离 FS,仅 OS picker 使用受控路径。以鼠标/键盘点击完成目录/ZIP导入、重复冲突、覆盖、搜索、开关、批量、pin、详情 markdown、使用回调、导出、卸载/取消、损坏包 UI、焦点循环/恢复及3秒提示消失;截图同目录。预期冲突/损坏 IPC reject 有日志,不计为 renderer 未处理异常。
  • npm run typecheck:node + web 全通过。主上下文负责统一生产 build 与完整应用 OMP/MCP/重启集成验收,本 worker 没有重复写生产 out。

原生局部实测:受管卡片 padding 20px、radius 16px、shadow rgba(0,0,0,.03) 0px 4px 12px,medium switch 26px。这些验证了恢复声明在宿主生效,不能替代同数据/同状态的原版像素比较。

遗留差异

未接在线推荐/SkillHub/套件目录、URL 导入、云端同步、更新下载与权益/购买流程;当前对应目录显示服务不可用,查找/创建由根真实草稿回调处理。没有安全扫描引擎或风险报告伪造。原版详情的搜索命中、翻译、远程示例/案例、动态价格和权益尚未接入;avatar 为本机名称字母 fallback,Tooltip 使用宿主原生 title,尚未恢复原 tooltip 延迟/浮层。包内图片未通过文字资源 API 越界读取,详情仅渲染可用 markdown 文本及文件清单。暗色及所有状态像素/动效还未完整验收。这些须继续按 REA 证据补齐,不能把本轮本地功能闭环称为技能 1:1 完成。

最终集成由主上下文报告:.tmp/capability-flows-ZJYiyj/report.json,完整应用 UI→真实 OMP 两轮 + MCP + 重启 PASS。该结果属于主上下文验收,不替代本报告的原版像素与未知项边界。成功 notice 是3秒自动消失、不占文档流的宿主 status;已存 REA CSS 未找到可直接恢复的 wb-toast primitive,未声称原版 toast 等价。