何时使用
用户要求安装/查询/卸载技能时触发。
核心规则
0. 命令快速参照
所有脚本操作都有专用 CLI 入口,不要读 install.mjs 源码找命令。完整列表在 references/quickref.md,关键命令(以下假设 CWD 为目标项目根目录,所有路径操作基于 CWD;自举安装时用 --target <path> 覆盖目标项目根目录):
node scripts/install.mjs copy-skill --source <p> --name <n> --scope <s> --target <project>
node scripts/install.mjs update-config --name <n> --scope <s> --target <project>
node scripts/install.mjs deploy-commands --source <p> --scope <s> [--force] --target <project>
node scripts/install.mjs deploy-plugins --source <p> [--force] --target <project>
node scripts/install.mjs clean-stale --scope <s> --target <project>
0a. 外来包风险提示
安装 zip 包或外来目录前,向用户提示:「此技能来自外部,安装后其脚本将以当前会话权限执行。请确认你信任该来源。」如需审查,可先安装 opencode-creator 技能,再要求 AI 审查后安装。
1. 检测结构
三种结构:Flat(SKILL.md 在根)、Single(一个子目录含 SKILL.md)、Multi(多个子目录各含 SKILL.md)。同时检测 agents/、commands/ 和 plugins/。
优先调用 testSkillSource(path) 一次性获取所有信息。
2. 技能名提取与修正
5 层回退:_meta.json slug → 解压/源目录名(裁剪版本/hash 后缀)→ zip 文件名 / 源目录父目录名 → frontmatter name: → H1 标题(# 标题)。提取后校验,必须符合 ^[a-z0-9]+(-[a-z0-9]+)*$。大写转小写,空格替换为连字符。若 SKILL.md 有 frontmatter 则 name 与最终目录名不一致时自动更新;若无 frontmatter 则不修改文件。
优先调用 getSkillName(sourceDir, zipName, frontmatterName) 提取名称。
3. 安装
安装流程分两种模式,根据前置检查结果选择。
同名检测(所有安装的前置)
本步骤在步骤 ② 读取 frontmatter 后立即执行,无论新装还是升级都要跑:
- 读取源包 SKILL.md frontmatter
name字段 - 扫描
.opencode/skills/下所有子目录的 SKILL.md frontmatter,查找name匹配 - 按结果分路:
- 未匹配 → 新安装,进入步骤 ③
- 匹配且目录名 = 源包 slug → 同目录,进入版本对比(§版本对比)
- 匹配但目录名 ≠ 源包 slug → slug 已变更,先询问清理旧目录再进入版本对比
slug 迁移询问:
📌 slug 已变更:self-improve → opencode-self-improve
是否清理旧目录 .opencode/skills/self-improve/?
[Yes] 先清理旧目录,再安装新版
[No] 保留旧目录,仅安装新版
- 用户选 Yes → 先删除旧目录并清理
opencode.json中的旧 key,再进入版本对比 - 用户选 No → 保留旧目录,直接进入版本对比
版本对比(仅升级/重装/降级)
匹配到已安装技能后执行:
- 读取已安装版
_meta.json的version字段 - 读取新版(临时目录/源目录)
_meta.json的version字段 - 展示版本对比:
已安装 v1.0.0 → 新版 v2.0.0 - 根据对比结果提供决策选项:
| 对比结果 | 展示标签 | 默认操作 | 可选操作 |
|---|---|---|---|
| 新版 > 已安装 | ⬆ 升级 |
升级 | 跳过/重装 |
| 新版 < 已安装 | ⬇ 降级 |
跳过(防误覆盖) | 降级/重装 |
| 新版 = 已安装 | 🔄 重装 |
重装 | 跳过 |
| 任意方缺 version | 📦 覆盖 |
覆盖(无法对比) | 跳过 |
无 _meta.json 或 version 字段时回退为简单「覆盖 / 跳过」选择。
优先调用 compareSkillVersion(v1, v2) 进行版本对比。
3a. zip 模式
| 步骤 | 操作 | 用户确认? | 命令参考 |
|---|---|---|---|
| ① | 调用 test-skill-source --path <zip> 自动解压到 .opencode/.tmp/ 并检测结构,无需手动操作。解压后结构检测结果直接返回 |
否 | test-skill-source --path <p> |
| ② | 检测结构 + 读取 frontmatter,展示摘要(技能名、版本、结构类型、有无 agent、有无命令、有无 scripts) | 是 — 信息确认 | test-skill-source 已含检测结果;get-skill-name / get-skill-version 辅助提取 |
| ③ | 询问安装位置(project / global),默认 project |
是 — 位置选择 | — |
| ④a | 同名检测:按 name 字段扫描已安装技能,不限于当前 slug。未匹配 → 新安装进步骤 ⑤;匹配且目录名≠slug → 先问清理旧目录再进 ④b;匹配且目录名=slug → 直接进 ④b |
是 — 若 slug 迁移则询问清理 | 扫描 .opencode/skills/*/SKILL.md frontmatter |
| ④b | 版本对比:读取已安装版 version,展示对比表,让用户选择升级/重装/降级/跳过 | 是 — 版本选择 | compareSkillVersion() |
| ⑤ | 执行源包完整性验证(§7a),不一致则拦截报错 | 否 | 见 §7a 表格 |
| ⑥ | 复制到对应路径 | 否 | copySkillToTarget() |
| ⑦ | 清理旧格式配置残留(skills 对象格式、无效 agents/commands 键)。默认 *:*:allow 已涵盖新技能权限,无需显式写入 allow |
否 | updateOpencodeConfig() |
| ⑧ | 若有 agents/,复制 .md 到 .opencode/agents/,已存在则询问覆盖 |
是 — 覆盖 agent? | fs.cpSync() |
| ⑨ | 若有 commands/,复制 .md 到对应命令目录(项目级:.opencode/commands/;全局级:~/.config/opencode/commands/),已存在则询问覆盖 |
是 — 覆盖命令? | deployCommandFiles() |
| ⑩ | 若有 plugins/,复制 .ts 到 .opencode/plugins/,已存在则询问覆盖 |
是 — 覆盖插件? | deployPluginFiles() |
| ⑪ | 必须询问是否清理源包(源码真相源目录永久保留,清理仅针对临时解压目录或下载的 zip 包) | 是 — 是否清理? | 清理顺序:.opencode/.tmp/ → 源 zip 包 → 源目录(仅非源码真相源的临时源) |
| ⑫ | 脚本依赖安装:检查技能目录 scripts/package.json,有则读取其中的 dependencies 列表,询问是否自动安装。选 Yes → 在 .opencode/ 下执行 npm install <pkg1> <pkg2>... --save,依赖装入 .opencode/node_modules/(opencode 自动创建的共享目录);选 No → 跳过 |
是 — 是否安装? | 读取 scripts/package.json 的 dependencies → cd .opencode && npm install |
确认点汇总: 步骤 ②③④a④b⑧⑨⑩⑪⑫ 共 9 次用户确认,不可跳过。其中 ④a 仅在检测到 slug 迁移时额外询问;④b 仅在匹配到已安装版本时出现,新安装跳过。⑫ 仅在技能含 scripts/package.json 时出现。 |
3b. 目录模式
与 zip 模式基本相同,区别:
- 跳过步骤 ①(无需解压)
- 检测直接在源目录上进行
- 步骤 ⑩ 源清理:目录模式下清理整条源目录路径(非 zip 包);非源码真相源的临时源才清理,永久源码跳过
| 步骤 | 与 zip 模式差异 |
|---|---|
| ① | 不存在,直接进入检测 |
| ② | 检测路径为源目录本身而非 .opencode/.tmp/ |
| ④a | 同名检测路径为源目录本体(而非解压目录) |
| ④b | 版本对比读取源目录 _meta.json |
| ⑤ | 源包一致性验证直接在源目录执行 |
| ⑩ | 清理源目录而非 zip 包 |
| ⑪ | 无差异,install 路径统一为 .opencode/ |
3c. 多技能包
解压后或源目录下有多个子目录各含 SKILL.md(Multi 结构),则:
- 列出所有技能(序号 + 名称 + 版本)
- 让用户选择安装哪些(可多选)
- 逐个按 §3a/§3b 流程处理,每个技能独立做覆盖检测
4. 配套 Agent、命令与脚本
安装目录/解压目录下有 agents/ 则复制 .md 到 .opencode/agents/。已存在时询问覆盖。zip 模式在步骤⑦执行,目录模式在步骤⑥后执行。
⚠️ 严禁向 opencode.json 写入 agents 字段。 代理通过 .opencode/agents/*.md 文件由插件系统自动发现,无需(且在某些版本中不得)在 opencode.json 中注册。向 JSON 写入 agents 条目会导致配置文件校验失败,opencode 无法启动。
⚠️ 技能权限默认已允许。 默认 agent 规则 *:*:allow 覆盖所有技能,无需显式写入 allow。仅在需要禁用某技能时写入 permissions 数组中的 deny 条目。skills 字段为路径数组(发现源),不是权限表。
安装目录/解压目录下有 scripts/ 则随技能目录整体复制(copySkillToTarget 递归复制全部内容),无需单独部署。路径引用使用 .opencode/skills/<name>/scripts/<file>。
安装目录/解压目录下有 commands/ 则复制 .md 到对应命令目录:
- 项目安装 → .opencode/commands/<文件名>.md
- 全局安装 → ~/.config/opencode/commands/<文件名>.md
已存在时询问覆盖。zip 模式在步骤⑧执行,目录模式在步骤⑦后执行。
更新时命令同步规则:升级/重装时(版本检测结果为升级或重装),在执行命令复制前先删除目标命令目录中所有与新版 commands/ 同名的 .md 文件,确保旧版残留被清理,再复制新版命令。若新版无 commands/ 目录,跳过命令部署,保留既有命令。
安装完成摘要:安装验证通过后,AI 从已安装技能的 SKILL.md 读取 ## 何时使用 和 commands/ 目录,生成简短的使用指引:
✅ <skill-name> v<version> 安装完成
如何触发:<从 何时使用 提取 2-3 条核心场景>
主要命令:<从 commands/ 提取命令列表,无则写「无(自动触发)」>
💡 重启 opencode 使新技能生效
5. 查询
批量扫描 *.zip 或预览单个 zip。检测结构、读取 frontmatter、展示结果后询问安装。参考 setup.md 和 scripts/install.mjs 中的操作函数。
6. 卸载
| 步骤 | 操作 | 用户确认? |
|---|---|---|
| ① | 检查项目路径 .opencode/skills/<name>/ 是否存在 |
否 |
| ② | 检查全局路径 ~/.config/opencode/skills/<name>/ 是否存在 |
否 |
| ③ | 展示检测到的安装位置,询问确认删除 | 是 |
| ④ | 删除对应目录 | 否 |
| ⑤ | 清理 opencode.json / opencode.jsonc 中对应条目 |
否 |
| ⑥ | 询问是否一并删除同名 agent(.opencode/agents/<name>.md) |
是 |
| ⑦ | 询问是否一并删除同名命令(.opencode/commands/<name>.md 或 ~/.config/opencode/commands/<name>.md) |
是 |
| ⑧ | 询问是否一并删除同名插件(.opencode/plugins/<name>.ts) |
是 |
注意: 无法卸载自身。若用户要求卸载 opencode-skill-installer,提示手动删除。
7. 验证
两个验证步骤均有专用 CLI 入口,避免内联 JS 的 PowerShell 转义问题:
# §7a 源包完整性验证
node scripts/install.mjs verify-integrity --path <sourceDir>
# §7b 安装后验证
node scripts/install.mjs verify-install --name <skillName> --scope <project|global>
输出 JSON { pass, checks: [{ name, pass, expected?, actual }] }。pass: false 表示检查未通过。
7a. 源包完整性验证(复制前执行)
在复制技能之前(步骤⑤前),验证源包内部一致性。发现不一致时拦截安装,列出差异让用户手动修复源包后再试:
| 检查项 | 说明 |
|---|---|
_meta.json version = SKILL.md frontmatter version |
逐字符比对 |
_meta.json slug = SKILL.md frontmatter slug |
对比 slug 字段 |
目录名 = _meta.json slug |
path.basename(sourcePath) === meta.slug |
输出示例:
{ "pass": false, "checks": [
{ "name": "version 一致", "pass": false, "actual": "❌ _meta.json v2.9.0 vs SKILL.md v2.10.0" },
{ "name": "目录名匹配 slug", "pass": false, "actual": "❌ 目录 \"my-old-name\" vs slug \"my-new-name\"" }
]}
7b. 安装后验证(复制后执行)
| 检查项 | 说明 |
|---|---|
| SKILL.md 存在 | 文件必须存在 |
| 命名合法 | getSkillName() 返回值匹配 ^[a-z0-9]+(-[a-z0-9]+)*$ |
| _meta.json 可读 | 存在则必须可 JSON 解析 |
| 技能目录完整 | SKILL.md + _meta.json 存在,文件结构完整 |
| 配套命令已部署 | 若有 commands/,全部已复制到命令目录 |
| 配套脚本已复制 | 若有 scripts/,目录非空 |
| 配套插件已部署 | 若有 plugins/,全部已复制到 .opencode/plugins/ |
| 源包一致性检查通过 | 参考 §7a 结果(复制前已校验,不再重复) |
7c. 安装失败分级恢复
安装过程中某一步骤失败时,按以下分级路径处理:
| 失败步骤 | 恢复行为 |
|---|---|
| 解压失败(zip 损坏/不完整) | 提示检查 zip 完整性 → 自动重试一次 → 仍失败则建议用户重新下载 |
| 结构检测失败 | 提示源路径中无合法技能包 → 列出检测到的文件内容辅助定位 → 询问用户是否手动指定 |
| 复制失败(权限/空间不足) | 检查目标磁盘空间 + 路径可写性 → 报告具体原因 → 建议更换安装位置 |
| 配置写失败(opencode.json 不可写) | 检查文件权限和目录存在性 → 输出手动配置指引让用户自行操作 |
Windows 特有问题: 压缩包内长路径(超过 260 字符)可能导致解压到 .opencode/.tmp/ 失败,此时需加 \\?\ 前缀或缩短源 zip 路径。
8. 自身安装/更新
安装或更新 opencode-skill-installer 自身时:
| 步骤 | 操作 |
|---|---|
| ① | 用户提供源路径($ARGUMENTS 或从对话中获取),指向新版安装器所在目录或 zip 包 |
| ② | 按 §3a/§3b 检测源结构(目录模式优先),验证为合法安装器包 |
| ③ | 询问安装位置(project / global),默认沿用原有安装位置 |
| ④ | 版本检测:展示已安装版 vs 新版版本,自动选择升级/重装 |
| ⑤ | 执行安装(§3a/§3b),跳过清理步骤(用户提供的源路径不应自动清理) |
| ⑤b | 自举安装(从 Factory 向目标项目安装安装器自身)时,CLI 命令加 --target <目标项目路径> 覆盖目标项目根目录,避免 CWD 混淆 |
卸载自身: 不受支持。用户需手动删除目录并清理配置。
前置检查
每次安装前,先依次完成以下 7 项检查,根据结果选择后续分支:
| 步骤 | 检查项 | 命令 |
|---|---|---|
| ① | 源路径是 zip 还是目录? | testSkillSource(path) 或 fs.existsSync() |
| ② | Flat 结构?(SKILL.md 在根) | testSkillSource(path) |
| ③ | Single/Multi 结构?(子目录含 SKILL.md) | testSkillSource(path) |
| ④ | 有配套 agents/? | testSkillSource(path) |
| ⑤ | 有配套 commands/? | testSkillSource(path) |
| ⑥ | 有配套 scripts/? | testSkillSource(path) |
| ⑦ | 有配套 plugins/? | testSkillSource(path) |
| ⑧ | 目标位置已存在同名技能? | 检查 .opencode/skills/<name>/ 和 ~/.config/opencode/skills/<name>/ |
安装前置确认
在执行前置检查之前,先展示安装确认行,让用户在开始前纠正理解偏差:
📌 安装前置确认:
- 源路径:[xxx.zip]
- 来源类型:[zip / 目录]
- 目标位置:[project / global]
- 操作类型:[安装 / 升级 / 降级 / 重装]
正确吗?
决策树:
源路径
├─ zip 文件 ──→ 解压到 .opencode/.tmp/ ──→ 检测结构 ──→ 安装(§3a)
└─ 目录 ──→ 直接检测结构 ──→ 安装(§3b,跳过解压)
数据存储
.opencode/skills/<name>/(项目)、~/.config/opencode/skills/<name>/(全局)、.opencode/agents/(agent)、.opencode/commands/(命令,项目级)、~/.config/opencode/commands/(命令,全局级)、.opencode/.tmp/(临时解压)。
脚本辅助
优先调用 scripts/install.mjs 中的函数完成机械操作(检测、命名提取、版本对比、复制、配置更新等),避免手动造命令。加载方式:
import { testSkillSource, getSkillName } from './scripts/install.mjs';
或通过 CLI 调用:
node scripts/install.mjs test-skill-source --path <path>
§7d. Config 条目清理
安装/验证后建议执行 config 清理,移除 permissions 数组中指向已不存在技能目录的残留条目:
node scripts/install.mjs clean-stale --scope project # 项目级
node scripts/install.mjs clean-stale --scope global # 全局级
流程:
1. 读取 opencode.json[c] 的 permissions 数组
2. 调用 getInstalledSkills(scope) 获取当前已安装的目录列表
3. 过滤 permissions 中 action === 'skill' 且 resource 不在已安装列表内的条目
4. 若有移除,写回文件并报告被清理的条目名
适用于:slug 重命名后旧名残留、手动删目录后配置未清理。
函数列表详见 references/quickref.md。AI 负责用户交互和决策,脚本执行具体操作。
权限/配置
| 权限 | 值 | 说明 |
|---|---|---|
skill |
allow |
读写 .opencode/skills/、.opencode/agents/、.opencode/commands/、.opencode/.tmp/ |
bash |
allow |
执行 Node.js 脚本和系统命令操作文件系统 |
edit |
allow |
创建/更新/删除技能目录和配置文件 |
read |
allow |
读取 zip 包和已安装技能,无需确认 |
external_directory |
ask |
全局安装/卸载时读取 ~/.config/opencode/ |
常见陷阱
| 陷阱 | 原因 | 修复 |
|---|---|---|
Flat 结构无 _meta.json |
只能从 zip 文件名推断名 | 确认时让用户验证 |
| 多技能 zip 未检测到 | 根目录无 SKILL.md 但子目录有 | 检查所有子目录 |
| 目录名含大写 | 要求全小写 | 自动转小写并更新 frontmatter |
| 名含空格 | 不允许空格 | 替换为连字符 |
| 技能被 deny | 权限配置 | 改为 allow |
| 文件损坏 | 下载不完整 | 跳过标记 |
| 无 SKILL.md | 非技能包 | 标记为非技能包 |
| agent 已存在 | 同名冲突 | 询问覆盖 |
| 技能已存在 | 覆盖检测缺失 | 安装前检查并询问 |
| 项目配置不存在 | 首次安装(根目录 opencode.json 和 .opencode/opencode.json 均缺失) |
自动创建 .opencode/opencode.json |
| 卸载自身 | 正在使用中 | 提示手动删除 |
| 目录已删但配置残留 | 手动删除后未清理 | 仅清理配置 |
| 源包未清理 | 安装后留下 zip/源目录 | 安装成功后自动删除源包 |
| SKILL.md 无 frontmatter | 从 ^name: 提取不到 |
回退到 H1 标题 ^# |
| 目录模式误用 zip 命令 | 源是目录却执行解压 | 走 §3b 跳过解压步骤 |
| 跳过用户确认点 | AI 批量执行多步未暂停 | 每步执行后等待用户确认 |
| 自身更新后未重启 | 配置已更新但旧会话仍生效 | 提示用户重启 opencode 会话 |
| 版本对比缺 version 字段 | 新旧任一方缺少 _meta.json 或 version |
回退为简单覆盖/跳过选择 |
| 降级操作误覆盖新技能 | 新版有 bug 想回退,却用旧包覆盖 | 降级默认操作为「跳过」,需用户主动确认降级 |
| 配套命令未部署 | 技能含 commands/ 但安装时未复制到命令目录 |
安装时检测 commands/ 并自动部署到对应位置:项目→.opencode/commands/,全局→~/.config/opencode/commands/ |
| 源目录与安装目录混淆 | 分不清源码真相源和运行时副本的区别 | 源码真相源(如项目的 Production/skills/<name>/)是永久目录,.opencode/skills/<name>/ 是运行时副本。清理仅针对临时目录和 zip 包,永久源码不清理 |
| _meta.json 与 SKILL.md 版本不一致 | 手动修改 _meta.json 后忘记同步 SKILL.md 的 frontmatter | §7a 源包完整性验证在复制前自动拦截,输出差异表供修复 |
| 向 opencode.json 写入 agents 条目 | AI 误以为代理需要在 JSON 中注册 | Update-OpencodeConfig 自动清理 agents 字段;SKILL.md §4 已明确禁止 |
| 安装后脚本依赖未装 | 技能包含 scripts/package.json 但安装时跳过依赖安装,运行时报错 |
安装时步骤 ⑪ 默认识别并询问;若用户跳过,安装完成提示中列出缺失依赖 |
技能改名(如 self-improve→opencode-self-improve),安装器按新 slug 创建目录但没删旧的 |
版本检测时按 name 字段匹配新旧技能,检测到目录名≠slug 时询问清理旧目录 |
|
| Config 中残留已卸载技能条目 | 手动删除技能目录后 opencode.json 中 key 未清理 | 运行 node scripts/install.mjs clean-stale --scope project 自动比对并移除 |
Config 中旧名条目残留(如 self-improve) |
slug 重命名后旧 key 未清除 | 同上,cleanStaleConfigEntries(scope) 自动清理 |
| 读源码找命令 | AI 不查 quickref.md 而去读 install.mjs 源码找 CLI 入口,浪费 tokens |
§0 已列出所有关键命令。用 references/quickref.md 速查,不读源码 |
| 在根目录新建 opencode.json 而非更新已有 .opencode/opencode.json | 安装时检测到无根目录 opencode.json 便自动创建,忽略了已存在的 .opencode/opencode.json | 步骤⑦ 先查已存在的 .opencode/opencode.json 或 opencode.json,均不存在才新建 .opencode/opencode.json |
| 外来包未提示风险直接安装 | 安装 zip 或外来目录时未提示风险,潜在恶意脚本获得 allow 权限 |
安装外来包前必须提示风险,由用户自行判断(见 §0a) |
相关技能
opencode-creator— 创建和审查技能设计opencode-skill-packager— 打包技能为 zip
反馈
在 opencode-skill-suite 提交 Issue 或 PR。