skill-desc-zh(技能描述中文化)
把 DSH 内置技能与常用斜杠命令的 description(列表/菜单里显示的描述) 自动翻译成中文,并且随时可以一键恢复英文原文。
- 只改描述,不改正文:office 技能通过 global provider 原样透传正文;agent-preset 技能复制整目录、只替换 frontmatter 的
description一行;斜杠命令只改 agent scope 里的描述,执行仍委托原 handler。 - Better Skill 单入口(v0.6.0):DSH 设置左侧只有一个“Better Skill”入口,内含三个 Tab:描述中文化(状态/覆盖清单/自检/应用/还原/刷新/重译)、插件管理(第三方列表/检查更新/一键更新)、技能管理(创建/编辑/禁用/删除/恢复)。三个 Tab 共用同一套 controller 与 HTTP 后端。
- 插件管理(v0.4.0):列出 profile 中已安装的第三方插件,检查 npm 最新版并一键更新;官方 bundle 与
link:本地插件永不被更新,更新安全边界仍由宿主把守。 - 技能管理(v0.5.0):可创建/编辑/删除/禁用/恢复
$HOME/.dsh/skills与<project>/.dsh/skills下的可写技能;内置技能只读;删除只进废纸篓,恢复前检查目标是否已存在。 - 随时可逆:
restore立即下架中文 provider,并把本插件创建的影子目录移入 macOS 废纸篓;再apply即可恢复中文。 - 升级安全:宿主升级后首次启动会先还原旧影子、清空旧译文,再重新翻译;翻译失败就保持英文,绝不写半成品。
- 零运行时依赖:不额外安装 npm 包;
yaml从宿主 profile 加载,客户端只require('react')。 - 删除只走废纸篓:代码中没有
rm/unlink/rmdir,唯一删除通道是~/.dsh/skills/safe-delete-trash/trash.mjs。
| 项 | 值 |
|---|---|
| 源码 | ~/dsh-plugins/skill-desc-zh |
| 包名 / 版本 | dsh-skill-desc-zh@0.6.0 |
| 宿主要求 | DSH ≥ 0.2.1-rc.2(desktop profile) |
| 对外工具 | skill_desc_zh(单工具,8 个 action) |
| 设置页 | 设置 → Better Skill(单入口、三个 Tab:描述中文化/插件管理/技能管理;同一个 settings.section,id skill-desc-zh,order 16) |
| 状态文件 | ~/.dsh/.skill-desc-zh/state.json(v3;v1/v2 自动迁移) |
| 事件日志 | ~/.dsh/.skill-desc-zh/events.ndjson |
设计说明:最初设计是 4 个独立工具,实现时按评审意见合并为一个
skill_desc_zh工具 +action参数,避免工具列表膨胀;下文按实际接口说明。
1. 功能一览
| 能力 | 说明 |
|---|---|
| 自动中文化 | 安装后 bootstrap 捕获 12 个目标技能 + 3 条命令,批量调用宿主 LLM 翻译;失败回落随包词典 lib/dictionary.zh.json |
| Better Skill 设置页(v0.6.0) | 设置左侧单一入口,内含三个 Tab:描述中文化(状态/开关/应用/还原/刷新/重译/自检/15 行覆盖清单)、插件管理(第三方列表/检查更新/一键更新)、技能管理(创建/编辑/禁用/删除/恢复/回收站) |
| 命令菜单中文化 | /acp、/acp-cache、/agent-teams(含动态 agent-teams-<profile>)只在当前 agent 的菜单显示中文;global 视角永远英文,执行行为不变 |
| 一键恢复英文 | restore 把 enabled=false 写入 state,provider 立即下架,4 个影子目录进废纸篓 |
| 随时再中文化 | apply 幂等启用;指纹未变时复用缓存,不重复调模型 |
| 升级自愈 | 检测 hostVersion 变化:先还原旧影子 → 清缓存 → 重译 → 重建影子 |
| 指纹刷新 | refresh 按 hostVersion + name/description/content/resourceBase 指纹判断,变化才重建 |
| 安全删除 | 只有带 .skill-desc-zh.json 标记的影子目录才会被管理;未标记目录绝不覆盖/删除 |
| 自检 | doctor 检查路径、yaml、LLM、废纸篓、覆盖层级 |
覆盖范围(技能 12 个 + 命令 3 条)
| 层 | 技能 | 机制 | 是否写磁盘 |
|---|---|---|---|
| global(office,rank 600) | office、office-docx、office-export-pdf、office-pptx、office-recalculate、office-render、office-xlsx |
注册 global 层 skill provider(rank 50);get() 返回中文 description + 原样透传 content/resourceBase |
否 |
| preset(agent-preset,rank 300) | agent-experience、cordis-composition-reference、cordis-plugin-development、editing-cordis-compositions |
项目级影子 <project>/.dsh/skills/<name>/(文件系统 rank 100,与 preset 同层) |
是,仅项目内 .dsh/skills/ |
| runtime(agent scope,rank 250) | browser-skill |
发现阶段捕获其 runtime 定义,再经 agent.ctx 在该 agent 层注册同名 provider(skill-desc-zh-agent,rank 50 < 250)覆盖描述;content/resourceBase 原样透传 |
否 |
| 用户级(rank 400) | dsh-plugin-preinstall-audit、safe-delete-trash |
本身已是中文,不在目标内 | — |
| 命令(agent scope) | /acp、/acp-cache、/agent-teams;配置含 agent-teams 时自动纳入动态 agent-teams-* |
在每个 agent 的 agent.ctx.get("commands").register() 注册同名命令影子;handler 每次实时委托原实现 |
否 |
2. 安装(先预检,再应用内安装)
2.1 预检(强制)
按 ~/.dsh/AGENTS.md 的约定,安装任何插件前必须先做兼容性/冲突预检:
# 推荐:本仓库的包装脚本(自动补本地目录检查)
node ~/dsh-plugins/skill-desc-zh/scripts/preflight.mjs
# 等价的官方脚本
node ~/.dsh/skills/dsh-plugin-preinstall-audit/audit.mjs --profile desktop \
~/dsh-plugins/skill-desc-zh
退出码:0 干净、2 只有警告(需逐条向用户确认)、1 有阻塞项(禁止安装)。
本地目录包的限制:
audit.mjs无法判定本地目录的“仓库来源 / 发布者签名 / 发布时间 / 隔离期”,scripts/preflight.mjs会固定输出 ⚠️“未验证”。这不等于“没问题”,必须如实转述给用户。本插件为零依赖、无 install/postinstall 生命周期脚本、
private:true的本地 bundle。2026-10-21 的安装预检中,audit 报出的 2 条 ❌ 均指向 profile 中已存在的第三方插件版本(minimumReleaseAge隔离期),且这些版本恰在 profile 的minimumReleaseAgeExclude豁免列表中;用户明确批准 override 后才安装。详见docs/verification.md§1。
2.2 应用内安装
plugin_manager install_bundle
target: ~/dsh-plugins/skill-desc-zh
- 必须用应用内
plugin_manager;CLIdsh plugin --profile desktop add会被桌面端拒绝(“profile "desktop" is managed exclusively by the Electron application”)。 - 本地目录传绝对路径;宿主会写成
link:依赖,不会复制源码。 - 安装后确认:
plugin_manager list_plugins
→ {"entryId":"include:skill-desc-zh","enabled":true,"fiberPhase":"active"}
cordis_inspect_query(platform=host, provider=Tool, method=listTools)
→ 应看到 skill_desc_zh
- 安装 bundle 本身可能由宿主热挂载;但Better Skill 入口(v0.6.0)与其中三个 Tab 都必须重启后才会出现,且模型可见的技能目录快照可能要到下一个会话才刷新。若
link:源码已升级到新版本(本次为 v0.6.0),运行中的进程仍持有旧代码,必须重启 DSH 才会加载新代码和设置页。 - v0.3.0+ 的客户端半边(
exports["./client"]+dsh.client.platform="web")只在宿主启动扫描 bundle 时登记;给已安装插件补设置页,第一次必须重启。重启后左侧设置导航出现“技能描述中文化”“插件管理”“技能管理”,打开后应能读取状态。
2.3 配置(cordis.patch.yml 的 config)
| 键 | 默认 | 说明 |
|---|---|---|
enabled |
true |
插件总开关(restore 会写成 false) |
engine |
"auto" |
auto:LLM 优先,失败回落词典;llm:只用 LLM;dictionary:只用随包词典 |
translatePreset |
true |
是否处理 4 个 agent-preset 技能(需要项目影子) |
restoreOnQuit |
false |
宿主退出时是否自动还原英文(默认保留中文) |
projectAllowlist |
本仓库配置为 ["/Applications/dsh"] |
额外允许写影子的项目根;代码默认 []。$HOME 下的项目默认允许 |
llm.provider / llm.model |
null |
留空用当前会话的 agentDefaultModel.currentSelection() |
llm.timeoutMs |
30000 |
单次批量翻译超时 |
targets.global / targets.preset |
8 + 4 个 | 目标技能名单;global = 7 个 office + browser-skill,preset = 4 个 agent-preset |
targets.commands |
["acp","acp-cache","agent-teams"] |
目标命令名单;设 [] 可单独关闭命令中文化 |
3. 工具用法(skill_desc_zh)
所有操作都通过同一个工具完成,action 必填。v0.6.0 起也可以在 Better Skill 设置页的三个 Tab 里点按钮完成这些动作(语义相同):描述中文化 Tab 见 §5.1,插件管理 Tab 对应 plugins action(§5.2),技能管理 Tab 对应 skills action(§5.3)。
{ "action": "status" }
{ "action": "apply" }
{ "action": "restore", "dryRun": true }
{ "action": "restore" }
{ "action": "retranslate", "names": ["office-render"] }
{ "action": "refresh" }
{ "action": "doctor" }
{ "action": "plugins" }
{ "action": "skills" }
可选参数:
names: string[]:只处理指定技能(默认全部 12 个技能目标;命令由targets.commands控制,不受names影响)。projectRoot: string:显式指定项目根(默认从当前会话工作目录向上找第一个含.git的目录)。dryRun: boolean:restore时只报告会移入废纸篓的目录,不实际执行。force: boolean:apply/retranslate时忽略缓存强制重译。
3.1 status(只读)
技能描述中文化已生效(phase=active):中文 12/12;项目 /Applications/dsh;宿主 0.2.1-rc.2
office [global/provider] 中文 · engine=llm · hash=493220ec71be
…
agent-experience [preset/shadow] 中文 · engine=llm · hash=7fd16448d780
影子 /Applications/dsh/.dsh/skills/agent-experience
同时返回每个技能的机制、指纹、影子路径、warnings。恢复英文后:
已还原英文原文(state.enabled=false);当前无中文覆盖;发现 12 个目标
agent-experience [preset/shadow] 英文 · engine=llm · hash=… · 无影子
3.2 apply(中文化 / 启用)
apply 完成:中文 12/12(office 7,preset 4,runtime 1),影子 4/4;宿主 0.2.1-rc.2
幂等;指纹未变时直接复用 state 缓存与已有影子。已翻译过的技能不会重复调用 LLM。
3.3 restore(一键恢复英文)
已恢复英文原文:4 个影子目录移入废纸篓;provider 已下架,下一步技能列表为英文;命令描述已恢复英文(3 条影子)
enabled=false写入 state,provider 立即invalidate();- 命令影子先注销(数条内存条目,无文件删除);
- 4 个带标记影子目录走
trash.mjs移入~/.Trash; - 不删除 state,随时可以再
apply。
预演:{ "action": "restore", "dryRun": true }。
3.4 retranslate(强制重译)
retranslate 完成:中文 12/12(office 7,preset 4,runtime 1),影子 4/4
忽略缓存,重新调用 LLM;适合手动改了词典、怀疑译文过时,或想换模型时使用。可配 names 只重译单条,force 同义。
3.5 refresh(按指纹刷新)
只在宿主版本/源技能内容/资源变化时重建;未变化时不重写影子、不重新调模型。日常无需手动执行,安装、升级、agent/created 时都会自动跑。
3.6 doctor(自检)
检查:state/路径可读写、cordis.patch.yml 与 profile 配置、LLM 可用性、trash.mjs 是否存在、覆盖层级是否如预期。只读,不改任何文件。
3.7 命令描述(v0.2.0)
/acp、/acp-cache、/agent-teams(以及动态 agent-teams-<profile>)的 agent 菜单描述中文化是 v0.2.0 的独立能力;机制、用法、限制、状态迁移与回滚见 §4。status 的 commands 段和 doctor 的 command-layer 也会一并检查命令层。
3.8 plugins(插件管理 Tab,v0.4.0)
只读列出当前 profile 中已安装的第三方插件(官方 bundle 与自身除外),并返回版本、来源、registry、最新版与可更新标记;不会安装/更新任何东西。Better Skill 的“插件管理”Tab 读取同一份数据,更新动作走单独的 HTTP POST /plugins/skill-desc-zh/plugins/action。完整说明见 §5.2。
3.9 skills(技能管理 Tab,v0.5.0)
只读列出可写技能(用户级/项目级)与只读技能(plugin/preset/bundled/runtime),并返回回收站摘要;不写文件。完整用法、写操作(新建/编辑/禁用/删除/恢复)与错误语义见 §5.3。status 不包含技能管理计数,页面读取 skills 同一载荷。
4. 斜杠命令描述中文化(v0.2.0)
v0.2.0 把 /acp、/acp-cache、/agent-teams 在会话命令菜单里的描述也翻译成中文。它不修改任何命令实现,也不改全局注册表;执行行为保持原样。
设计细节见
docs/command-desc-design.md,机制调查与真实宿主证据见docs/command-desc-investigation.md,验收见docs/verification.md的 v0.2.0 章节。此前“只处理技能描述、斜杠命令不在范围内”的口径自 v0.2.0 起作废。
4.1 覆盖的命令
| 命令 | 中文描述 | 说明 |
|---|---|---|
/acp |
显示 bili 上下文压缩状态 | billion-context 注册的 global 命令 |
/acp-cache |
提示词缓存对账(与 acp_cache 工具同源同报) | billion-context 注册的 global 命令 |
/agent-teams |
以多智能体团队运行目标(你将成为队长) | agent-teams 注册的 global 命令 |
agent-teams-<profile> |
以 AgentTeams「<profile>」配置运行一个团队目标 |
仅当 agent-teams 配置了 profiles 时动态出现;当前 desktop profile 未配置,实际不注册 |
内置的 goal、plan、feedback、compact、permission、export 六条由 GUI 自带本地化,本插件不注册、不修改;即使 targets.commands 误配,也会按 definitionId 跳过。
4.2 机制:只注册 agent scope 影子
宿主对同名命令的全局注册会直接抛错(错误信息会提示改为挂到该 agent 的 agent.ctx)。因此本插件:
- 在每个 agent 的
agent.ctx.get("commands")注册同名影子,只替换description;definitionId、input(含hint/attachments)、recordInput原样保留; handler每次执行时实时commands.find(undefined, name)解析原实现并委托;原实现暂时缺失时用注册期快照兜底,彻底失效则返回明确错误,不 catch、不重写返回值;- global 视角永远英文;agent A 的菜单中文,agent B、其它会话不受影响;
- 只存在内存,零文件写入;命令影子不随插件 fiber 自动回收,
restore/restoreOnQuit/插件dispose都会显式调用注册时返回的 disposer; - 生命周期:
agent/created时同步注册(不阻塞 agent 创建)、startup为已知会话补注册、commands/change用 50ms debounce + 全局 digest 比较避免自触发;每个 agent 用签名去重,重复事件不会重复注册。
commands.register() 通过 layers.effect(this.ctx) 把定义写进调用者所在的 scope 层;list(agent) 按“最近层优先”解析。这正是“global 英文、agent 中文”能同时成立的原因。
4.3 用法
// 查看状态:status.commands 会给出每条命令的中文/引擎/是否 stale/是否缺失
{ "action": "status" }
// 一并恢复英文(命令影子立即注销;技能影子照旧进废纸篓)
{ "action": "restore" }
// 预演 restore,不实际改动
{ "action": "restore", "dryRun": true }
// 重新启用/注册,指纹未变时复用缓存
{ "action": "apply" }
// 强制重译命令描述(忽略缓存)
{ "action": "retranslate" }
status.commands 的常用字段:enabled、targets、translations(每条命令的 zh/engine/stale/missing)、registered(已注册影子数)、missing(未捕获到的命令)、restored/restoredAt。doctor 的 command-layer 会用最近一个 agent 实测“agent 中文 / global 英文 / definitionId 一致 / 非内置”。
只想关闭命令中文化、保留技能中文化:把 cordis.patch.yml 的 targets.commands 设为 [] 后重新加载插件;把 targets.commands 留空不是“翻译失败”,而是明确不注册。
4.4 state v1 → v2 自动迁移
v0.2.0 把 state 版本从 1 升到 2,新增 commands.byName(每条命令的 en/zh/engine/fingerprint/hint 等)与 restoredAt:
- 读取旧 state 时,
normalizeState会补上空的commands.byName/restoredAt并归一化为version=2;技能、项目影子、缓存、历史都原样保留; - 第一次以 v2 保存前,
save()先用trash.snapshot给旧文件留档,再写临时文件并原子rename,权限0600; - state 文件损坏时只告警并使用空状态;原文件在覆盖前同样会留档,不会直接丢弃;
restore会写入commands.restoredAt,但不删除 state;之后apply可重建命令记录。命令译文本身是可重建缓存,删掉 state 不会影响命令实现。
4.5 限制与尚未在线验证的项
- 需要重启/新会话生效:v0.2.0 源码目前通过 profile 的
link:依赖加载;用户重启 DSH 后才会运行新代码,菜单快照也要等新会话或 GUI 刷新。 - GUI 软刷新:
commands/change会让客户端后台重拉,已打开的菜单可能短暂显示旧文案,重新打开即恢复。 - 只覆盖 agent 视角:global 视图始终英文;这是宿主命令注册表的约束,不是失败。
- 不覆盖内置 6 条:
goal/plan/feedback/compact/permission/export的中文由 GUI 自带字典提供,插件不参与。 - 动态 profile 命令:只有 agent-teams 配置了
profiles才会出现agent-teams-<profile>;当前 profile 未配置,故 README 中的模板是离线规则,未在真实宿主触发。 - 真实菜单/真实执行尚未在线验收:v0.2.0 的验收是源码级(真实宿主模块探针 + 生产 e2e);
/acp、/acp-cache、/agent-teams的真实点击、副作用与菜单像素需重启后由用户确认。 - 真实 LLM 翻译命令描述未联网验证:离线词典/模板路径已实测;LLM 路径有单测(fake llm)。
- 多 agent 性能未压测:每个 agent 都会注册一份内存影子;当前 profile 的 agent 数量下无异常。
4.6 回滚
| 目标 | 操作 |
|---|---|
| 只回英文 | skill_desc_zh { "action": "restore" }:注销全部命令影子,并把技能影子移入废纸篓 |
| 临时停用 | plugin_manager set_plugin target="include:skill-desc-zh" enabled=false:插件 dispose 会注销命令影子;技能影子按 restoreOnQuit 语义处理,彻底回英文请先 restore |
| 彻底卸载 | 先 restore,再 plugin_manager remove_bundle target="dsh-skill-desc-zh";源码目录不受影响 |
| 只关命令 | 把 targets.commands 设为 [],保留技能中文化 |
| 源码回退 | 本仓库没有 .git,不能 git revert;改动前把 lib/ 备份到仓库外,回滚时用 trash.mjs --snapshot 留档后覆盖 |
命令影子只存在于内存,回滚不需要删除任何文件;技能影子与 state 的删除/清理仍只能走 trash.mjs。
5. Better Skill 设置页(v0.6.0)
v0.6.0 把设置导航收敛为一个入口、三个 Tab:DSH 设置 → 左侧导航“Better Skill”,进入后用 Tab 切换“描述中文化 / 插件管理 / 技能管理”。三个 Tab 与 skill_desc_zh 调用同一套 controller,页面动作、返回状态与工具结果语义一致;页面只是可视化入口,不改变底层还原/升级自愈逻辑。
状态说明:截至 2026-10-23,源码 v0.6.0 已完成源码级验收(215/215 单测、
check:client17/17、技能/插件/命令/agent 层 e2e),但当前运行进程仍是重启前的旧代码;Better Skill 入口与三个 Tab 都要重启 DSH 后才会出现。本章是重启后的使用说明,“已验证”仅指源码/单测/真实模块探针层,真实页面、认证与真实写操作仍需重启后人工确认。
设计见
docs/settings-ui-design.md、docs/skill-admin-design.md;重启前验收、F1/F2 修复与 I1 基线裁定见docs/verification.md的 v0.3.0/v0.4.0/v0.5.0/v0.6.0 章节。
入口与 Tab 切换
- 重启 DSH。v0.3.0+ 的客户端半边与 HTTP 路由需要宿主启动时重新扫描 bundle;升级
link:源码后不重启,Better Skill 不会出现。 - 打开设置,左侧只有一个“Better Skill”一级项;进入后默认显示“描述中文化”Tab。
- 顶部 Tab 栏可点击,也支持键盘:
←/→在三个 Tab 间循环,Home/End跳到第一个/最后一个;焦点跟随选中项,role=tab/tablist/tabpanel与aria-selected/aria-controls完整。 - 当前 Tab 写入
localStorage的dsh-skill-desc-zh.activeTab;重开设置会恢复到上次的 Tab;值非法/读写抛错时回退到“描述中文化”。 - 只有当前 Tab 的内容会挂载;每个 Tab 的错误边界带
key=active,一个 Tab 崩溃不会让切换永久卡在 fallback。切换 Tab 会卸载旧面板、停止其请求/轮询。
5.1 描述中文化 Tab
第一次使用:重启后进入 Better Skill → 页面顶部应显示运行状态;若显示“无法读取状态”,先看下面的故障排查。需要中文化时点“应用”,已生效会显示 phase=active 与覆盖数量;想回英文点“还原”(二次确认),确认后 provider 下架、影子目录进废纸篓、命令影子注销。
页面只在可见时每 5 秒轮询一次;切到后台会停止,失败按 5→10→20→30 秒退避,不会持续打满宿主。
页面组成
| 区块 | 内容 |
|---|---|
| 状态摘要 | phase、技能/命令中文计数、宿主版本、项目根与来源、warnings |
| 启用开关 | 开:等同 apply;关:等同 restore(先弹确认) |
| 操作按钮 | 应用、还原、刷新、重译、自检 |
| 覆盖清单 | 15 行:8 个全局技能(office ×7 + browser-skill,provider 覆盖)+ 4 个 agent-preset 影子 + 3 条斜杠命令(agent 作用域影子);含当前语言、引擎、stale/missing、影子路径 |
| 自检 | doctor 的 mandatory/optional 检查项与限制 |
| 最近事件 | events.ndjson 的 lastAction 与文件位置 |
| 只读配置 | Host 未通过 state 暴露的 allowlist / restoreOnQuit / 模型路由会显示“Host 未提供”,不会伪造默认值 |
按钮与开关语义
| 操作 | 等价工具调用 | 说明 |
|---|---|---|
| 应用 | skill_desc_zh(action="apply") |
重新捕获/翻译;指纹未变复用缓存;按需重建影子 |
| 还原 | skill_desc_zh(action="restore") |
下架 provider、注销命令影子、影子目录走 trash.mjs;不删除 state,可再应用 |
| 刷新 | skill_desc_zh(action="refresh") |
按 hostVersion/内容指纹检查,只有变化才重建 |
| 重译 | skill_desc_zh(action="retranslate") |
忽略缓存强制重译全部技能与命令 |
| 自检 | skill_desc_zh(action="doctor") |
只读检查路径、yaml、LLM、trash、层级覆盖 |
| 开关关闭 | restore |
会弹确认;取消则不改动 |
| 开关打开 | apply |
若之前是 restore 状态,则重新中文化 |
restore 只管理本插件创建、带 .skill-desc-zh.json 标记的目录;不会改内置技能源文件,也不会碰用户自建技能。
覆盖清单怎么读
清单固定 15 行,分三组:
- 全局技能组(8 行):7 个 office +
browser-skill。office 由 global provider 覆盖(零文件写入,正文原样透传);browser-skill由对应 agent 层的 provider 覆盖。页面上的engine表示译文来源,sourcePath是原始技能位置。 - preset 组(4 行):4 个 agent-preset 技能,通过项目级影子
<project>/.dsh/skills/<name>/覆盖;shadowPath指向影子目录,只有带标记的目录才归本插件管理。 - 命令组(3 行):
/acp、/acp-cache、/agent-teams,只在 agent scope 注册内存影子;shadowPath为空是正常的,命令不写文件。
stale=true 表示源技能/宿主版本已变化、当前译文待刷新;missing=true 表示发现阶段没有捕获到该目标(例如项目未放行、运行时技能未注册)。这两类都会在 status 与页面 warnings 中显示,不会静默当作成功。
状态字段
| 字段 | 取值 / 含义 |
|---|---|
phase=booting |
插件刚开始启动,尚未完成捕获/翻译 |
phase=active |
中文覆盖已生效(技能与命令均按配置处理) |
phase=degraded |
部分目标回退词典或英文;看 warnings 与 events 原因 |
phase=stale |
指纹变化,旧译文待刷新 |
phase=restored |
已执行还原,当前英文原文生效 |
phase=failed |
捕获/翻译/影子/还原失败;插件保持 fail-closed,不会乱写 |
engine=llm |
当前译文由宿主 LLM 生成 |
engine=dictionary |
使用随包词典(离线可用) |
engine=source |
使用源技能原文(通常表示该目标本身已是中文,无需翻译) |
engine=none |
没有可用译文/未处理 |
engine=unknown |
宿主返回了未知引擎,按未翻译处理 |
自检(doctor)
页面“自检”调用 doctor,检查:state/路径可读写、cordis.patch.yml 与 profile 配置、LLM 可用性、trash.mjs 是否存在、global/preset/命令三层覆盖是否符合预期。只读,不改任何文件。
mandatory失败通常意味着插件无法安全继续(例如 trash 通道不可用、yaml 解析失败),应保持英文并排查;optional失败通常只影响某种引擎/项目(例如当前项目未放行、LLM 不可用但词典可用),会降级而不是崩溃。
故障排查
| 现象 | 排查 |
|---|---|
| 设置里看不到“Better Skill” | 最常见是没重启(v0.3.0+ 新客户端需重启扫描);确认 plugin_manager list_plugins 中 include:skill-desc-zh 为 enabled=true、fiberPhase=active;确认包版本 0.6.0 且 exports["./client"] 存在 |
| 页面显示“无法读取状态 / 设置页后端不可用” | 路由未注册或认证失败:确认已重启;用工具 skill_desc_zh(status) 对照插件是否正常;看宿主日志中 /plugins/skill-desc-zh/state 的 401/403/503;未重启时该路径返回 404 属正常 |
| 页面能开但数据一直 loading | 检查浏览器/宿主同源 fetch、cookie 与 CSP;GET /state 应返回 cache-control: no-store;必要时用工具 status 判断是 UI 还是 Host |
| 技能描述仍是英文 | 看 phase、每个目标的 engine 与 warnings;preset 需要项目根满足 .git + $HOME/allowlist;office 看 provider 是否 ready;目录快照可能需新会话 |
| 命令菜单仍是英文 | 确认重启后加载 v0.2.0+;status.commands.registered 是否 >0;global 视角本来就英文 |
| 点“还原”后英文没恢复 | 看页面 phase=restored 与 warnings;确认没有未标记同名目录冲突;必要时 doctor 看 trash 通道 |
| 重译失败 | LLM 不可用时自动回落词典;看 warnings/events;可改用离线词典或稍后重试 |
| Tab 切换后白屏/卡在错误页 | 确认加载的是 v0.6.0(单个 settings.section + 三个 Tab);旧版多入口页面需重启刷新;单个 Tab 崩溃应只影响该 Tab,切换后新 Tab 会重建错误边界 |
5.2 插件管理 Tab
入口:Better Skill → “插件管理”Tab(同一个 settings.section,id skill-desc-zh,order 16;不再单独占用左侧一级项)。
功能
| 能力 | 说明 |
|---|---|
| 第三方插件列表 | 读取当前 profile 的 pluginManager.listBundles(),过滤掉官方 bundle 与本插件;显示名称、版本、来源(npm / 本地 link:/file: / workspace / git / tarball)、启用状态、registry、检查时间 |
| 检查更新 | 对可更新的 npm 插件请求 registry 的 /<包名>/latest,5 分钟缓存,并发上限 6、单请求 8 秒超时;只判断“有没有新版”,不抓取发布时间 |
| 一键更新 | 二次确认后调用宿主 pluginManager.installBundle(name@latest);宿主返回 restart-required 时页面提示重启,pendingBuilds 时提示构建脚本待批准 |
| 只读边界 | 页面不启用/禁用/卸载插件(这些操作仍在原生“设置 → 插件”页);不写 profile 的任何配置或豁免白名单 |
依赖与 profile 解析
- 宿主服务:
pluginManager(listBundles/installBundle;版本与可用性从宿主读取,不可用时页面显示“需要重启加载 0.4.0+”,不会自行安装)。 - registry:优先使用
pluginManager提供的 registry;缺省回退https://registry.npmjs.org/。请求失败不阻塞其他插件,逐行显示“检查失败”。 - profile 解析优先级(
lib/plugin-admin.js:199-256):desktopProfiles.current(桌面端权威)→profileContext(CLI 启动器)→DSH_PROFILE_DIR→--profile参数 →web→desktop;只有含package.json的目录才算命中。 - 本插件的 profile 依赖是
link:~/dsh-plugins/skill-desc-zh,因此它自身显示“当前插件(link: 安装,不参与更新)”。
更新安全边界
- 只更新已安装的第三方 npm 插件:
official===true的官方 bundle、link:/file:/workspace/tarball 等非 npm 来源、以及本插件自身,全部标记为“不参与更新”。 - 不绕过供应链闸门:不写
pnpm-workspace.yaml的minimumReleaseAgeExclude,也不加--config.minimumReleaseAge=0;新版本仍在隔离期时,更新会失败并原样显示宿主错误。 - 构建脚本批准与兼容性闸门仍由宿主执行:更新只是调用宿主
installBundle,不自行执行 pnpm,也不代替用户批准构建脚本;遇到pending-builds时按宿主错误提示处理(构建脚本批准、启用/禁用、卸载统一到原生“设置 → 插件”页)。 - 不删除文件:更新只改 profile 依赖与 node_modules,源码目录不受影响;卸载/清理仍走原生插件页或
trash.mjs。
错误分类
页面把失败原因归一为以下类别(都显示为失败行/提示,不会把失败说成成功):
| code | 含义 / 用户动作 |
|---|---|
quarantine |
新版本仍在 24h 供应链隔离期;等隔离期结束或按 AGENTS.md 走人工审批,页面不提供绕过 |
pending-builds |
新版本含需要批准的构建脚本;到原生“设置 → 插件”页查看并批准,或放弃更新 |
incompatible |
宿主兼容性闸门拒绝(如 engines.dsh 不满足);不要强行安装 |
no-update |
已是最新版;无需操作 |
busy |
另一个更新/设置动作正在执行;稍后重试(同一插件并发更新由宿主与插件层单飞拦截) |
unavailable |
宿主 pluginManager 或设置页后端未加载;确认已重启并加载 v0.4.0+ |
另有 unknown-plugin(不在可管理列表)、not-updateable(非 npm 来源)、network(registry 请求失败)、install-failed / operation-error(宿主安装失败)。更新结果里的 restart-required 不是错误,页面会提示重启 DSH。
工具与 HTTP 接口
| 接口 | 用途 |
|---|---|
skill_desc_zh(action="plugins") |
只读:返回 { profile, registry, plugins, details, warnings };与页面读取同一 controller;不会安装/更新 |
GET /plugins/skill-desc-zh/plugins |
插件列表;?refresh=1 跳过 5 分钟列表缓存,强制重查 registry(仍受单请求 8 秒超时与并发 6 限制) |
POST /plugins/skill-desc-zh/plugins/action |
body { "action": "check" } 或 { "action": "update", "name": "<插件名>" };更新前会二次确认 |
所有设置页 HTTP 接口都复用宿主认证围栏(未认证 401/403、后端缺失 503)、JSON-only、body ≤64 KiB、cache-control: no-store;有动作在跑时返回 409,而不是排队重复安装。skill_desc_zh(action="plugins") 是只读工具,不会触发更新。
当前限制:更新动作未做真实安装验收(e2e 使用 fake
installBundle,只验证调用参数与错误分类);真实 Electron 进程、认证后的页面与一次实际更新需重启 DSH 后人工确认。官方 bundle 与link:本地插件永远不参与更新。
5.3 技能管理 Tab
入口:Better Skill → “技能管理”Tab(同一个 settings.section,id skill-desc-zh,order 16;不再单独占用左侧一级项)。
能做什么
| 能力 | 说明 |
|---|---|
| 列出技能 | 合并用户级 $HOME/.dsh/skills 与当前项目 <project>/.dsh/skills 的可写技能,并单列 plugin/preset/bundled/runtime 只读技能;source=user|project|all 可过滤,默认 all |
| 新建 | 只写 user 或(已放行的)project 根,生成 <name>/SKILL.md,校验 kebab-case、单行 description、body ≤200 KiB |
| 编辑 | 可改 description 与正文;保存时带 expectedFingerprint 做乐观锁,其他 frontmatter 字段与注释原样保留 |
| 禁用/启用 | 写入 state 的 skillAdmin.disabled[];只影响 agent 作用域:provider rank 40 覆盖同名技能,候选 modelInvocable:false,global 视角仍英文;可随时恢复 |
| 删除 | 只走 trash.mjs 移入废纸篓,不永久删除;记录删除元数据,可从页面“恢复” |
| 恢复 | 按删除记录把文件从废纸篓移回;目标已存在时返回 conflict,绝不覆盖;恢复时重新检查项目 allowlist |
| 回收站 | 列出最近删除(上限 200 条),支持恢复与查看原路径;不提供永久清空 |
所有写操作都只在用户级或已放行项目的 .dsh/skills 内;内置技能源文件与插件包永不修改。写盘前对已有文件做 --snapshot 留档,临时文件原子替换。
可写与只读边界
| 来源 | 可写 | 说明 |
|---|---|---|
user |
是 | $HOME/.dsh/skills |
project |
是 | <project>/.dsh/skills,仅 $HOME 下项目或 projectAllowlist 放行;必须满足项目根规则 |
plugin / preset / bundled / runtime |
否 | 只读展示;页面不提供编辑/禁用/删除,API 也拒绝写入 |
任意 ../、绝对路径、非 kebab 名称 |
否 | invalid,不会拼出根目录外的路径 |
只读技能仍会显示在“只读”折叠区,用于查看来源和中文描述;它们不参与可写统计,也不会混入 source=user 列表。真实运行中 browser-skill 属 runtime、office 属 bundled、四个 agent-preset 属 preset。
冲突、错误与安全
| 情况 | 返回/行为 |
|---|---|
| 文件在打开后被改过(指纹不符) | HTTP 409 / conflict,页面提示“重新加载”,不覆盖对方修改 |
| 目标已存在(新建/恢复) | conflict;恢复不会静默覆盖现有目录 |
| body 超过 256 KiB | HTTP 413 / too-large |
| 非法名称、scope、source、projectRoot | HTTP 400 / invalid 或 unsupported-source |
| 项目根不在 allowlist | unsupported-source,不读也不写 |
| 并发保存/动作 | 一个 skillBusy 单飞;第二个请求 409,不会同时改同一棵树 |
| trash.mjs 不可用/失败 | 失败关闭,绝不降级为 rm;页面显示错误 |
| 未认证 | 401/403;后端未加载 503;所有响应 cache-control: no-store |
正文首尾空白在保存时按设计归一化;description 使用 YAML 单行字符串,含换行会被拒绝。YAML 锚点/别名无法安全定点改写时返回 unsupported-frontmatter,原文件保持字节不变。
已知限制(重启前)
- 页面像素、真实认证、真实点击与真实文件写入尚未在运行中的 GUI 验收;本章的“通过”仅指源码、单测、真实模块探针与临时目录 e2e。
- 只管理文本技能;不处理二进制资源,不提供批量导入/导出。
- 禁用只在 agent 作用域生效,global 视角仍英文;这是宿主层级机制,不是页面 bug。
link:源码升级后必须重启 DSH 才会加载 v0.6.0 的 client/server 两半。- 仓库没有
.git,回滚依赖快照/备份而非git revert。 - 真实
~/.dsh的写操作未在本次验收中执行;首次使用请先在一个测试项目里试新建/删除/恢复。
Host API(v0.5.0)
| 接口 | 用途 |
|---|---|
skill_desc_zh(action="skills") |
只读列表:{ ok, projectRoot, skills, trash, counts, warnings };与页面同一载荷 |
GET /plugins/skill-desc-zh/skills?projectRoot=&source=&refresh=1 |
列表;source=user|project|all,refresh=1 跳过 2 秒缓存 |
GET /plugins/skill-desc-zh/skills/detail?scope=&name=&projectRoot= |
单个技能详情(frontmatter/body/fingerprint/mtime/size/resources) |
POST /plugins/skill-desc-zh/skills/action |
body { action, scope?, name?, description?, body?, expectedFingerprint?, id?, dryRun? };action 为 create/save/delete/restore/disable/enable |
业务失败仍返回 HTTP 200 + { ok:false, code, message }(如 conflict、invalid、unsupported-source、too-large、not-writable);协议/认证/忙状态用 400/401/403/409/413/503。成功保存返回 { ok:true, skill:{…}, changed:true },列表中的 fingerprint 形如 sha256:<64 hex>,由 description、body、relativePath 计算,不是整文件原始字节哈希。
5.4 重启后人工验收
以下项目在重启前只做了静态/单元/真实模块探针验证,页面像素、真实认证与点击副作用必须重启后由用户确认:
- 设置左侧只有一个“Better Skill”入口;进入后看到“描述中文化 / 插件管理 / 技能管理”三个 Tab,默认描述中文化,Tab 栏不重复、不缺失。
- 刷新/重开设置后恢复上次 Tab;键盘
←/→/Home/End可切换,焦点与aria-selected正确;窄窗 Tab 栏可横向滚动。 - 描述中文化 Tab 能读到真实状态(不是“无法读取状态”);应用/还原与工具
skill_desc_zh结果一致,中文/英文往返正确,页面不可见时停止轮询。 - 插件管理 Tab 能列出第三方插件并检查更新;遇到
pending-builds/隔离期/不兼容时提示正确;不把失败说成成功。 - 技能管理 Tab 能区分可写(user/project)与只读(plugin/preset/bundled/runtime),数量与来源正确;只读项无编辑/禁用/删除。
- 在测试项目新建/编辑/禁用/删除/恢复各走一遍;删除只进废纸篓,恢复不覆盖同名目标,禁用后 agent 目录不可调用。
- 并发保存/并发动作返回 409 且页面提示重载;非法名称/路径、超限 body、未认证分别得到 400/413/401/403,不出现“保存成功但文件没变”。
- 一个 Tab 渲染崩溃后切到其它 Tab 仍能正常渲染(
key=active重建错误边界);客户端加载失败时主界面不被拖垮。
文案/元数据已在 t19 修正:
confirm.restore不再写“4 个项目”,group.global改为“global 层 · provider 覆盖(office 系列 + browser-skill)”,命令组改为“命令 · agent 作用域影子(3 个斜杠命令)”,notices.scope改为“8 个全局技能(office ×7 + browser-skill)”;package.json同时提供./icon与./icon.svg两个导出说明符。真实页面文案仍以重启后的 client 加载结果为准。
5.5 回滚
设置页提供最直接的 L0 回滚:关闭开关或点“还原”。插件管理 Tab 的更新可用原生“设置 → 插件”页重装/降级;技能管理 Tab 的编辑/删除/禁用分别用“重新保存/回收站恢复/启用”回滚。更彻底的分级回滚(停用 bundle、卸载、清 state)见 §8;命令影子只在内存,回滚不需要删除文件。
- 停用 Better Skill:在原生“设置 → 插件”页禁用本插件 bundle;三个 Tab 一起消失,已安装的第三方插件与用户技能不受影响。
- 彻底移除本插件:先
skill_desc_zh(action="restore"),恢复所有技能删除项,再在原生插件页移除 bundle;源码目录不受影响,清理状态走trash.mjs。 - 回退文档/源码:本次文档修改前的快照在
~/.dsh/.file-backups/;仓库没有.git,不要执行不存在的git revert。
6. 影子机制与 rank 原理
6.1 为什么 office 用 provider、preset 用影子
宿主技能选择规则:先比层级,层级相同再比 rank,rank 小者胜。
- office 在 global 层,内置 provider
dsh-office的rank=600;本插件注册 global 层 providerrank=50→ 50 < 600,直接胜出,零文件写入。- 关键:
get()只替换description,content/resourceBase/whenToUse/path全部沿用捕获到的原始定义,所以office-render/office-recalculate/office-export-pdf正文末尾的## Installed LibreOffice Kit运行期附录不会丢。
- 关键:
- agent-preset 在 preset 层(自带
skill-filesystem,rank=300)。global 层再低的 rank 也跨不了层,所以只能写项目级<project>/.dsh/skills/<name>/(文件系统rank=100,与 preset 同层)→ 100 < 300,影子胜出。- 影子是整目录复制(含
references/、templates/),只把SKILL.md的description一行换成中文; - 目录内写
.skill-desc-zh.json标记(含源路径、sourceHash/shadowHash、资源清单、hostVersion),用于防误删和升级对账。
- 影子是整目录复制(含
browser-skill在 agent scope 的 runtime 层(rank 250),描述与正文硬编码在插件代码里;本插件发现阶段捕获原始定义,再经agent.ctx.get("skills")在同一个 agent 层注册skill-desc-zh-agent(rank 50 < 250)覆盖描述,正文仍原样透传。- 用户级
~/.dsh/skills(rank 400)本可覆盖 global 层,但会污染用户目录并外溢到其它 profile,本插件不写。
6.2 防自噬(bootstrap 顺序)
- provider 注册后先
ready=false,list()返回空,避免自己覆盖自己; - 在“原始 provider 仍生效”时用
skills.get()捕获 12 个原始定义; - 翻译、写影子、落 state;
- 最后
ready=true+invalidate(),技能目录切到中文。
6.3 项目根与生效范围
- 项目根 = 当前会话工作目录向上找第一个含
.git的目录(与宿主findProjectRoot一致); - 只有 项目在
$HOME下,或列在projectAllowlist时才会写影子; - 影子只在对应项目/工作区打开时生效;换一个 workspace,该工作区看不到这些中文(office 不受影响,因为 provider 是 global 的);
- 仓库默认把 e2e 工作区
/Applications/dsh放进 allowlist;普通用户的项目在$HOME下无需配置。
6.4 命令为什么只能注册在 agent scope
见 §4.2。核心约束是:全局同名 commands.register() 会直接抛错,只有调用方所在的 scope 层能安全插入同名影子;因此命令中文化与技能中文化一样,按 agent/项目生效,global 视角保持英文。
7. 升级前后自动流程
宿主没有“插件升级前”事件,插件用启动自愈实现等价语义:
宿主升级(或插件更新替换了内置 SKILL.md)
│
▼
下次启动 bootstrap 读取 state.hostVersion
│ 与当前 hostVersion 不同
▼
① 把已登记的项目影子全部走 trash.mjs 移入废纸篓(校验无残留)
② 清空 state.skills / state.cache,并 resetCommands / 清空命令层旧影子
③ 重新捕获 12 个技能原始定义 + 3 条命令定义
④ 重译(LLM 失败回落词典;再失败保留英文 + warning)
⑤ 重建 4 个影子目录、重新注册命令影子,写新 hostVersion
│
▼
事件:host-version-heal restored=N → startup → agent-created
实测事件(2026-10-20,模拟 0.2.0-rc.2 → 0.2.1-rc.2):
{"action":"host-version-heal","ok":true,"from":"0.2.0-rc.2","to":"0.2.1-rc.2","restored":4}
{"action":"startup","phase":"degraded","zhCount":7,"warnings":["检测到宿主版本变化…开始重新翻译"]}
{"action":"agent-created","phase":"active","zhCount":12,"shadows":[{"name":"agent-experience","status":"created"}, …]}
补充:
- 命令层同样自愈:hostVersion 变化会清空
state.commands与命令层旧影子,重新捕获命令定义后按 agent 重新注册;命令影子只在内存,不产生废纸篓记录。 - 指纹:
hostVersion + sha256(name\0description\0content\0resourceBase),配mtime/size快路径(仅文件捕获通道;skills.get()带运行时附录的必须重算)。 refresh:指纹变化 → 作废旧译文、重建影子;未变化 → 不动。restoreOnQuit: true:宿主退出时同步还原并记事件(默认false,即保留中文)。- 翻译失败绝不写半成品:
zh=null时该技能显示英文,phase降为degraded/failed,事件与status都能看到原因。
8. 废纸篓回滚与找回
8.1 分级回滚
| 级别 | 操作 | 效果 |
|---|---|---|
| L0 只回英文 | skill_desc_zh { "action": "restore" } |
影子进废纸篓,provider 下架,命令影子注销;插件保留,可再 apply |
| L1 临时停用 | plugin_manager set_plugin target="include:skill-desc-zh" enabled=false |
provider 注销、命令影子随 dispose 注销;注意 restoreOnQuit=false 时技能影子不会自动清,彻底回英文请先 L0 |
| L2 卸载 | 先 L0,再 plugin_manager remove_bundle target="dsh-skill-desc-zh" |
移除 profile 依赖/bundle/软链;源码目录不受影响 |
| L3 清状态 | node ~/.dsh/skills/safe-delete-trash/trash.mjs ~/.dsh/.skill-desc-zh |
state + events 进废纸篓(先确认 L0/L2 已完成) |
| L4 源码回退 | 本仓库没有 .git,不能 git revert;改动前把 lib/ 备份到仓库外,回滚时用 trash.mjs --snapshot 留档后覆盖 |
只影响源码,不影响已安装 profile |
移除
link:依赖前先完成 L0,并由用户确认移除范围;plugin_manager remove_bundle只改 profile 依赖/bundle,不会删除~/dsh-plugins/skill-desc-zh源码。若node_modules残留软链,交由包管理器处理或先与用户确认后用trash.mjs清理,禁止手删/rm。
8.2 找回被还原的英文影子 / 状态
- 所有移入废纸篓的目录名形如
agent-experience 3、cordis-plugin-development 4(macOS 自动加序号); - 用 Finder 打开“废纸篓”,把对应目录放回原处(
/Applications/dsh/.dsh/skills/)即可恢复; - 操作留痕:
~/.dsh/.trash-log.jsonl记录每次 rename 的源/目标/时间,可按时间定位; - 本插件从不永久删除;
trash.mjs拒绝时插件会失败关闭,绝不降级为rm。
8.3 手动清理残留(仅限带标记目录)
# 只删本插件创建的影子(每个目录内应有 .skill-desc-zh.json)
node ~/.dsh/skills/safe-delete-trash/trash.mjs /Applications/dsh/.dsh/skills/<name>
未标记目录、用户自建技能目录不要用本插件清理。
9. 故障排查
| 现象 | 排查 |
|---|---|
| 设置里没有“Better Skill” | 先确认已重启(v0.3.0+ client 需启动扫描);再看 list_plugins 的 fiberPhase=active 与包版本 0.6.0;完整排查见 §5.1 |
| 只有旧的多入口,没有三个 Tab | 运行进程仍是旧 client;确认已重启并加载 v0.6.0,list_plugins 的 include:skill-desc-zh 为 active;页面标题旁显示“需要重启加载”即说明旧进程未加载新 client |
| 技能管理 Tab 列表为空 / 只读区为空 | 确认项目根满足 .git+$HOME/allowlist;skill_desc_zh(action="skills", source="all") 对照;只读技能只在 source=all 出现;旧 client 可能没有只读分组 |
| 技能管理 Tab 列表为空 / 只读区为空 | 确认项目根满足 .git+$HOME/allowlist;skill_desc_zh(action="skills", source="all") 对照;只读技能只在 source=all 出现;旧 client 可能没有只读分组 |
| 技能管理保存返回 409 | 文件已被其他窗口改动;页面点“重新加载”取新指纹再改,不要强行覆盖;目标已存在的新建/恢复也会返回 conflict |
| 删除/恢复失败 | 确认 trash.mjs 可执行、目标在允许技能根内;错误码 trash-unavailable/unsupported-source/conflict 会原样显示,插件不会降级为永久删除 |
| 插件管理 Tab 显示“宿主 pluginManager 不可用” | 宿主未注册 pluginManager 服务或旧进程未加载 0.4.0+;重启后仍不可用则看 doctor/status 的 pluginAdmin 字段 |
| 检查更新全部失败 | 看 registry 是否可达、是否被代理拦截;单插件失败不影响其他行;GET /plugins/skill-desc-zh/plugins?refresh=1 可强制重查 |
| 更新按钮点不动 / 返回 409 | 另一个更新/设置动作仍在执行;等当前动作结束再试。页面和 HTTP 都有单飞保护,不会并发安装 |
| 设置页打不开 / 一直 loading | 多为路由未加载或认证失败:确认已重启;用 skill_desc_zh(status) 对照;重启前 /plugins/skill-desc-zh/state 返回 404 属正常 |
工具列表里没有 skill_desc_zh |
plugin_manager list_plugins 看 include:skill-desc-zh 是否 enabled=true / fiberPhase=active;新安装后新开一个会话,工具在下个模型步才可见 |
| 技能描述仍是英文 | status 看 phase 与每个技能 engine;office 看 provider 是否 ready(doctor);preset 看项目根是否含 .git、是否在 $HOME/projectAllowlist;目录快照可能需要新会话或刷新 GUI |
命令菜单仍是英文(/acp 等) |
确认已在重启后加载 v0.2.0;status.commands 看 enabled/targets/registered;doctor 看 command-layer.checked;global 视角本来就应英文;内置 6 条由 GUI 本地化,不归本插件 |
| 只有 4 个 preset 中文、7 个 office 英文(或反之) | 正常的分层现象:office 走 global provider(必须 bootstrap 完成),preset 走项目影子(必须在对应项目内);status 会写明每个技能的机制。若在插件 disable 窗口看过目录,快照会停留在当时状态,需新会话刷新 |
phase=degraded |
部分技能回退词典或英文:status 的 warnings、events.ndjson 的 startup 事件会给出原因(LLM 不可用/项目未放行/影子冲突) |
phase=failed |
看 ~/.dsh/.skill-desc-zh/events.ndjson 最后一条的 reason:常见为 trash.mjs 缺失/拒绝、yaml 解析失败、影子写入权限、state 损坏(state 损坏时插件只 fail-closed,不会乱写) |
| 已有同名目录但没被接管 | 目录缺少 .skill-desc-zh.json 标记时,插件只告警跳过,绝不覆盖/删除。确认是旧残留后,手动 trash.mjs 移走再 apply |
| 升级后中文没自动恢复 | 检查 events.ndjson 是否有 host-version-heal;确认 state.hostVersion 已更新;必要时 refresh 或 retranslate |
| 想强制换模型/重译 | 改 config.llm.provider/llm.model 后 retranslate;或在 doctor 中确认 LLM 可用 |
| 预检报 ❌ 隔离期 | 先看 ~/.dsh/profiles/desktop/pnpm-workspace.yaml 的 minimumReleaseAgeExclude 是否已含对应版本;不要自行忽略,按 AGENTS.md 向用户说明并取得确认。注意 audit 存在“不读该豁免列表”的已知误报 |
| 预检报“来源未验证” | 本地目录包无法做供应链校验,属预期提示;确认绝对路径内容可信后再安装 |
更多自检:
node ~/dsh-plugins/skill-desc-zh/scripts/verify.mjs --expect-installed --expect-applied
node ~/dsh-plugins/skill-desc-zh/scripts/preflight.mjs
verify.mjs 会检查:代码无永久删除调用、trash.js 通道、影子标记、profile 依赖/bundle、state 与 hostVersion 对账;无法从 Node 判定的项会明确打印 “ℹ️ 未验证”。
10. 已知限制
app.asar只读:内置技能源文件不能改;本插件只通过 provider(office)或项目影子(preset)覆盖描述,从不写宿主安装目录。- runtime 技能的覆盖依赖宿主 scope 语义:
browser-skill的描述硬编码在其插件代码里,由它自己在agent/created时用agent.ctx.get("skills").register()写进该 agent 的 scope 层(rank 250)。本插件用同样方式在该 agent 层注册skill-desc-zh-agent(rank 50)覆盖描述;这依赖宿主dsh-skill当前“最近层获胜、同层 rank 小者胜”的合并规则(已在 0.2.1-rc.2 实测)。宿主若改变 scope 语义,本项会退化为显示英文,status的agentLayer.checked会给出原因。 - 插件/宿主更新会替换内置
SKILL.md:旧译文与影子会随指纹变化失效,启动自愈会自动重译;不要手动编辑影子里的正文(下次 apply/refresh 会以源为准重建)。 - 项目级影子只在对应 workspace 生效:换项目打开时,该项目的 agent-preset 显示英文(除非它也放行并已 apply);office 是 global 的,不受影响。
- 必须满足项目根条件:agent-preset 影子只在“含
.git的项目根、且位于$HOME下或列入projectAllowlist”时创建;这是为了避免污染用户目录/其它 profile。 - LLM 不可用时:回落随包词典(15 条:技能 12 + 命令 3),再失败保持英文 + warning;不会写半成品,也不会阻塞宿主。
- 本地包预检无法验证供应链:来源/签名/发布时间不可判定,已在预检中如实标注。
restoreOnQuit默认关闭:退出宿主不会自动回英文;需要时显式restore。- 命令描述只在 agent scope 生效:global 视角始终英文;v0.2.0 代码需重启宿主后才加载;已打开的命令菜单可能因 GUI 软刷新短暂显示旧文案,重开即恢复。
- Better Skill 需要重启一次才出现:v0.3.0+ 的 client 半与 HTTP 路由只在宿主启动扫描 bundle 时登记;
link:源码热更新不会自动加载客户端。v0.6.0 的“单入口三 Tab”(描述中文化/插件管理/技能管理)与新增 HTTP 路由同理,重启后真实认证、键盘/焦点与点击仍需人工验收。 - HTTP 错误路径的两个缺陷已修复(t18/t21):动作抛异常后
busy会释放(500 后 GET/POST 不再 409);超时/迟到 reject 不再产生 unhandled rejection;插件更新的单飞改为在首个 await 前占位,避免并发重复安装。已加回归测试;真实 Electron 进程仍需重启后复验。 - 页面文案与 icon 元数据已修正(t19):还原确认不再写“4 个项目”,global 组改为“global 层 · provider 覆盖(office 系列 + browser-skill)”,命令组改为“命令 · agent 作用域影子”,
notices.scope写“8 个全局技能(office ×7 + browser-skill)”;package.json同时提供./icon与./icon.svg。真实页面渲染待重启确认。 - 插件管理 Tab 只覆盖 npm 第三方插件:官方
@deepseek-ai/*bundle、link:本地插件、workspace/git/tarball 来源都不参与更新;启用/禁用/卸载仍需原生“设置 → 插件”页。更新只调用宿主pluginManager,不会绕过隔离期或代替用户批准构建脚本;pending-builds会显示宿主错误,构建脚本批准在原生页完成。 - 更新未做真实安装验收:
e2e:plugin-admin用 fakeinstallBundle验证调用参数、失败分类、缓存与并发单飞,但没有真实拉取/安装一个新版本;真实更新、降级与重启后生效需用户重启后手动验收。插件列表中的最新版来自 registry/<pkg>/latest,5 分钟缓存,不显示发布时间。 - 技能管理只处理文本技能:不处理二进制资源,不提供批量导入/导出;只改 description/正文,frontmatter 的锚点/别名无法安全定点改写时返回
unsupported-frontmatter,原文件不变。 - 技能管理的真实写操作尚未在线验收:单测、真实模块探针与临时目录 e2e 已覆盖创建/保存/禁用/删除/恢复/冲突/只读拒绝,但本次没有写真实
~/.dsh或/Applications/dsh/.dsh;页面像素、真实认证与点击副作用需重启后人工确认。首次使用建议先在测试项目试新建/删除/恢复。 - 禁用只在 agent 作用域生效:global 技能列表仍显示英文;这是宿主层级/scope 机制,不是技能管理 Tab 的 bug。禁用 provider 的 rank 40 依赖宿主当前合并规则,宿主升级后需
doctor/回归复验。 - state v1/v2 → v3 自动迁移:首次写 state 前会对旧文件做快照;迁移只增加
skillAdmin.disabled/deletions,不改写技能翻译。仓库没有.git,回滚依赖~/.dsh/.file-backups/与trash.mjs,不要执行不存在的git revert。
11. 验收结论(2026-10-23,v0.6.0)
完整报告见 docs/verification.md:v0.1 A1–A12、v0.2.0 T1–T6 / A1–A10、v0.3.0 A1–A15、v0.4.0 A1–A12 与复验、v0.5.0 A1–A28 与两次复验、v0.6.0 A1–A12 与二次验收。当前结论是源码级 pass、ready-for-restart:Better Skill 单入口三 Tab、F1/F2 修复与 I1 基线裁定均已独立复核;运行中的旧进程尚未加载 13:20 后的源码。
| 项 | 结果 |
|---|---|
| 预检如实、退出码正确 | ✅(历史 v0.1 的 2 条 ❌ 为用户批准的 override;本轮无安装动作;本地目录来源仍标注未验证) |
应用内安装、fiberPhase=active |
✅(profile 为 link:~/dsh-plugins/skill-desc-zh;源码版本 0.6.0,运行进程待重启后加载;未安装/升级/重启) |
工具 skill_desc_zh 注册 |
✅;8 个 action(status/apply/restore/retranslate/refresh/doctor/plugins/skills)语义不变 |
| office 7 个中文(provider,正文透传) | ✅ 机制与调用验证;全新会话目录肉眼快照待用户确认 |
| runtime 1 个中文(agent 层 provider,rank 50 < 250) | ✅ 单元/机制验证;需重启后的新会话目录确认 |
| preset 4 个中文(影子 + 标记 + 资源完整) | ✅ |
| restore → 英文原文 / apply → 再次中文 | ✅(历史实测;影子进废纸篓,trash 日志可查) |
| 命令描述(v0.2.0) | ✅ 源码级:3 条命令 agent 中文、global 英文、执行委托原 handler;真实菜单/点击待重启后验收 |
| Better Skill 单入口(v0.6.0) | ✅ 源码级:唯一 settings.section id skill-desc-zh order 16;三 Tab(描述中文化/插件管理/技能管理);默认 translate;localStorage 记忆非法值回退;键盘/ARIA 完整 |
| F1 Tab 崩溃隔离 | ✅ 源码级:Boundary key={active};A 崩溃后切 B 会新建错误边界并正常渲染(独立探针 remounted_renders_child:true) |
| F2 aria-controls | ✅ 源码级:仅 active Tab 带 aria-controls=sdzh-panel-<active>;非激活为 null;panel aria-labelledby 正确 |
| 插件管理 Tab(v0.4.0 能力) | ✅ 源码级:列表/检查更新/更新预检与分类/单飞;B1/P1/P2/V1/C1 已修复并回归 |
| 技能管理 Tab(v0.5.0 能力) | ✅ 源码级:可写 user/project、只读 plugin/preset/bundled/runtime 分离;新建/编辑/禁用/删除/恢复/冲突/只读拒绝均已验证;真实页面与真实写盘待重启 |
| 技能/插件 HTTP 路由 | ✅ 单元/真实模块探针:认证 401/403、no-store、body 限额、并发 409;真实认证后 200 待重启 |
| state v3 与删除纪律 | ✅ v2→v3 自动迁移;唯一删除通道 lib/trash.js → trash.mjs;verify.mjs 扫描 49 文件无永久删除调用 |
| 单测 / e2e / 静态检查 | ✅ npm test 215/215;check:client 17/17、locale 298 keys;client 13/13;skills-e2e 32/32;e2e:plugin-admin 21/21;e2e:commands 10/10;e2e:agent-layer 7/7;node --check 49 文件 0 失败;verify.mjs 49 文件通过 |
| I1 基线裁定 | ✅ 接受替代证据(无 pre-t31 逐字节基线;未发现 lib/scripts 变更);docs/v0.6.0-sha256.txt 30/30 校验通过,作为后续基准 |
| 设置页真实 UI / 认证 / 点击 | ⏳ 待重启后人工确认(Better Skill 单入口、三个 Tab、焦点/键盘、页面真实写操作、可见性轮询、失败降级) |
| 真实插件更新 / 真实技能写盘 | ⏳ 确实未验证:plugin-admin e2e 用 fake installBundle;技能管理 e2e 用临时 HOME/项目,不碰真实 ~/.dsh 或 /Applications/dsh/.dsh |
待重启确认 / 已知边界:
- Better Skill 入口与三个 Tab 需重启验收:v0.6.0 客户端与 HTTP 路由要重启 DSH 后才加载;重启前旧进程的
/plugins/...返回 401(认证先于路由),不是页面可用。 - live 旧进程:旧进程仍持有旧版代码,
skill_desc_zh(action="plugins")可能仍报旧 lossless 错误;源码已修复,重启后才会消失。 - 7 个 office 在“全新会话技能目录”里的中文显示:历史快照可能停留在插件禁用窗口;state/provider/
status与skill("office-render")已证明覆盖生效,仍建议新开会话或刷新 GUI 技能页确认。 - v0.2.0 命令菜单与真实执行:源码级探针与 e2e 已证明 agent 中文、global/其它 agent 英文、执行委托原 handler;真实 GUI
/菜单与点击副作用待重启后确认。 - 动态
agent-teams-<profile>命令:模板逻辑已离线验证;当前 desktop 未配置profiles,没有真实动态别名可测。 - 本地包供应链 / profile lockfile 隔离期 / 仓库无
.git:来源、签名、发布时间无法判定;仓库不能git revert,源码回退依赖备份;见 §8 与 §10。 - v0.5.0 修复已复核(t29/t30):F1 幽灵资源项、F2 只读分组/统计、F3 只读译文、F4 禁用 provider definition、F5 restore allowlist、F6 同名 scope 均已修复;独立探针与回归测试通过,真实 Electron 环境仍需重启后复验。
- v0.4.0 / v0.6.0 修复:B1 lossless JSON、P1 更新单飞、P2 HTTP 并发单飞、V1
clipText删除纪律误报、C1 检查更新改走 POST;F1 Boundary key 重置、F2 aria-controls 仅激活、I1 sha 基线;独立探针与回归测试均通过。
当前安装状态:desktop profile 通过 link:~/dsh-plugins/skill-desc-zh 指向源码,包版本已是 0.6.0,enabled=true、fiberPhase=active;运行中的进程仍是重启前的旧代码,state 为 v2(12/12 技能中文、3 条命令中文,4 个影子在 /Applications/dsh/.dsh/skills/)。重启 DSH 后才会加载 Better Skill 单入口、三个 Tab、HTTP 路由与 v0.4.0–v0.6.0 修复,并把 state 迁移到 v3。
v0.6.0(Better Skill 单入口三 Tab)为源码级验收:215/215 单测 +
check:client17/17 + client 13/13 +skills-e2e32/32 +e2e:plugin-admin21/21 +e2e:commands10/10 +e2e:agent-layer7/7;独立复验确认 F1/F2,I1 接受替代证据。本轮未安装/升级/重启 profile,profile 仍为link:指向源码;真实页面、真实插件更新与真实技能写盘尚未验收。
12. 开发与测试
cd ~/dsh-plugins/skill-desc-zh
node --check lib/index.js && node --check client.js
npm test # node --test test/*.test.mjs,215 个用例
npm run e2e:agent-layer # agent 层覆盖机制端到端(用宿主真实 cordis/dsh-skill 模块)
npm run e2e:commands # 命令影子端到端(真实 CommandRuntime/createScope/execute)
npm run e2e:plugin-admin # 插件管理端到端(fake installBundle,不真实安装)
npm run e2e:skills # 技能管理端到端(临时 HOME/项目,不碰真实技能)
npm run check:client # 客户端 17 项静态检查(单入口三 Tab/active-only aria/Boundary key/locale)
node scripts/preflight.mjs # 安装预检(包装官方 audit.mjs)
node scripts/verify.mjs --expect-installed --expect-applied # 文件级验收
sha256sum -c docs/v0.6.0-sha256.txt # lib/scripts 基线自检(30 条)
代码结构:
lib/index.js 插件入口:配置、bootstrap、HTTP 路由接线、动作编排、事件
lib/http-api.js 设置页 HTTP 桥:/plugins/skill-desc-zh 的 state/doctor/action/plugins/skills,认证/限额/单飞
lib/plugin-admin.js 插件管理后台:profile 解析、第三方列表、registry 最新版、更新预检与分类、单飞
lib/skill-admin.js 技能管理后台:列表合并、只读过滤、创建/保存/删除/恢复/禁用/启用、统计
lib/skill-files.js 技能文件系统:扫描、安全路径、frontmatter 定点改写、原子写、trash 删除/恢复
lib/discover.js 枚举技能(skills.list/get + 文件系统)
lib/translate.js LLM 批量翻译 + 词典回落 + 校验
lib/dictionary.zh.json 15 条随包中文词典(技能 12 + 命令 3)
lib/provider.js global provider(rank 50,正文透传)
lib/agent-layer.js agent scope 层 provider(rank 50/禁用 rank 40,覆盖 runtime 技能如 browser-skill)
lib/shadow.js 项目影子创建/删除/对账(.skill-desc-zh.json)
lib/state.js 原子 state、history、互斥与留档、v1/v2→v3 迁移
lib/host-yaml.js 宿主 yaml 定点重写(只改 description)
lib/trash.js 唯一删除通道(spawn trash.mjs,失败关闭)
lib/events.js events.ndjson 审计日志
lib/tool.js skill_desc_zh 工具定义
lib/command-layer.js 命令描述影子(捕获/委托/指纹/模板/注册自检)
lib/paths.js 路径/项目根解析(生命周期编排在 lib/index.js)
client.js Better Skill 客户端(__ModuleLoader__ + React,单个 settings.section、三个 Tab、locale,15 行清单)
locale/zh.json / locale/en.json 各 298 keys,页面文案
icon.svg 设置页插件图标
scripts/check-client.mjs 客户端静态检查
scripts/preflight.mjs / scripts/verify.mjs 预检与验收
scripts/agent-layer-e2e.mjs 用宿主真实模块验证 agent 层覆盖(Electron-as-node)
scripts/commands-e2e.mjs 用宿主真实 CommandRuntime 验证命令影子(Electron-as-node)
scripts/plugin-admin-e2e.mjs 插件管理端到端(fake installBundle,不联网安装)
scripts/skills-e2e.mjs 技能管理端到端(临时 HOME/项目,不碰真实技能)
docs/v0.6.0-sha256.txt lib/scripts 30 条 sha256 基线(`sha256sum -c` 校验)
test/*.test.mjs 215 个用例(17 个文件),覆盖控制器/state/影子/翻译/命令/HTTP/客户端/插件管理/技能管理/Better Skill 三 Tab;默认不联网、不动真实技能
客户端侧的 client.js 不参与 Node 单测运行,需用 npm run check:client 做 manifest/入口/locale 静态检查;真实浏览器渲染与认证点击仍需重启后人工验收。
许可证:MIT。
No comments yet. Be the first to write one.