用户要求安装/查询/卸载技能时触发。
三种结构:Flat(SKILL.md 在根)、Single(一个子目录含 SKILL.md)、Multi(多个子目录各含 SKILL.md)。同时检测 agents/ 和 commands/。
优先调用 testSkillSource(path) 一次性获取所有信息。
5 层回退:_meta.json slug → 解压/源目录名(裁剪版本/hash 后缀)→ zip 文件名 / 源目录父目录名 → frontmatter name: → H1 标题(# 标题)。提取后校验,必须符合 ^[a-z0-9]+(-[a-z0-9]+)*$。大写转小写,空格替换为连字符。若 SKILL.md 有 frontmatter 则 name 与最终目录名不一致时自动更新;若无 frontmatter 则不修改文件。
优先调用 getSkillName(sourceDir, zipName, frontmatterName) 提取名称。
安装流程分两种模式,根据前置检查结果选择。
第 ⑦ 步覆盖检测时,若目标已存在同名技能,自动执行版本对比:
_meta.json 的 version 字段_meta.json 的 version 字段已安装 v1.0.0 → 新版 v2.0.0| 对比结果 | 展示标签 | 默认操作 | 可选操作 |
|---|---|---|---|
| --------- | --------- | --------- | --------- |
| 新版 > 已安装 | ⬆ 升级 | 升级 | 跳过/重装 |
| 新版 < 已安装 | ⬇ 降级 | 跳过(防误覆盖) | 降级/重装 |
| 新版 = 已安装 | 🔄 重装 | 重装 | 跳过 |
| 任意方缺 version | 📦 覆盖 | 覆盖(无法对比) | 跳过 |
无 _meta.json 或 version 字段时回退为简单「覆盖 / 跳过」选择。
优先调用 compareSkillVersion(v1, v2) 进行版本对比。
| 步骤 | 操作 | 用户确认? | 命令参考 |
|---|---|---|---|
| ------ | ------ | ----------- | --------- |
| ① | 解压 zip 到 .opencode/.tmp/ | 否 | 7z x / tar xf 或 testSkillSource(path) 自动检测 |
| ② | 检测结构 + 读取 frontmatter,展示摘要(技能名、版本、结构类型、有无 agent、有无命令、有无 scripts) | 是 — 信息确认 | getSkillName() / getSkillVersion() |
| ③ | 询问安装位置(project / global),默认 project | 是 — 位置选择 | — |
| ④ | 版本检测:目标已存在同名技能?存在则版本对比 + 决策 | 是 — 版本确认+操作选择 | compareSkillVersion() |
| ⑤ | 执行源包完整性验证(§7a),不一致则拦截报错 | 否 | 见 §7a 表格 |
| ⑥ | 复制到对应路径 | 否 | copySkillToTarget() |
| ⑦ | 更新 opencode.json(项目)或 opencode.jsonc(全局),设为 "allow";文件不存在则自动创建 | 否 | updateOpencodeConfig() |
| ⑧ | 若有 agents/,复制 .md 到 .opencode/agents/,已存在则询问覆盖 | 是 — 覆盖 agent? | fs.cpSync() |
| ⑨ | 若有 commands/,复制 .md 到对应命令目录(项目级:.opencode/commands/;全局级:~/.config/opencode/commands/),已存在则询问覆盖 | 是 — 覆盖命令? | deployCommandFiles() |
| ⑩ | 必须询问是否清理源包(源码真相源目录永久保留,清理仅针对临时解压目录或下载的 zip 包) | 是 — 是否清理? | 清理顺序:.opencode/.tmp/ → 源 zip 包 → 源目录(仅非源码真相源的临时源) |
确认点汇总: 步骤 ②③④⑧⑨⑩ 共 6 次用户确认,不可跳过。
与 zip 模式基本相同,区别:
| 步骤 | 与 zip 模式差异 |
|---|---|
| ------ | ---------------- |
| ① | 不存在,直接进入检测 |
| ② | 检测路径为源目录本身而非 .opencode/.tmp/ |
| ④ | 版本检测读取源目录 _meta.json(而非临时解压目录) |
| ⑤ | 源包一致性验证直接在源目录执行 |
| ⑩ | 清理源目录而非 zip 包 |
解压后或源目录下有多个子目录各含 SKILL.md(Multi 结构),则:
安装目录/解压目录下有 agents/ 则复制 .md 到 .opencode/agents/。已存在时询问覆盖。zip 模式在步骤⑦执行,目录模式在步骤⑥后执行。
⚠️ 严禁向 opencode.json 写入 agents 字段。 代理通过 .opencode/agents/*.md 文件由插件系统自动发现,无需(且在某些版本中不得)在 opencode.json 中注册。向 JSON 写入 agents 条目会导致配置文件校验失败,opencode 无法启动。
安装目录/解压目录下有 scripts/ 则随技能目录整体复制(copySkillToTarget 递归复制全部内容),无需单独部署。路径引用使用 .opencode/skills/。
安装目录/解压目录下有 commands/ 则复制 .md 到对应命令目录:
.opencode/commands/<文件名>.md~/.config/opencode/commands/<文件名>.md已存在时询问覆盖。zip 模式在步骤⑧执行,目录模式在步骤⑦后执行。
更新时命令同步规则:升级/重装时(版本检测结果为升级或重装),在执行命令复制前先删除目标命令目录中所有与新版 commands/ 同名的 .md 文件,确保旧版残留被清理,再复制新版命令。若新版无 commands/ 目录,跳过命令部署,保留既有命令。
安装完成提示:若技能包含 commands/ 且安装成功,提示用户该技能提供配套命令,建议使用对应信号词触发。对于 self-improve 等依赖命令注入规则到 AGENTS.md 的技能,建议用户运行配套命令以完成完整安装。
批量扫描 *.zip 或预览单个 zip。检测结构、读取 frontmatter、展示结果后询问安装。参考 setup.md 和 scripts/install.mjs 中的操作函数。
| 步骤 | 操作 | 用户确认? |
|---|---|---|
| ------ | ------ | ----------- |
| ① | 检查项目路径 .opencode/skills/ 是否存在 | 否 |
| ② | 检查全局路径 ~/.config/opencode/skills/ 是否存在 | 否 |
| ③ | 展示检测到的安装位置,询问确认删除 | 是 |
| ④ | 删除对应目录 | 否 |
| ⑤ | 清理 opencode.json / opencode.jsonc 中对应条目 | 否 |
| ⑥ | 询问是否一并删除同名校 agent(.opencode/agents/) | 是 |
| ⑦ | 询问是否一并删除同名命令(.opencode/commands/ 或 ~/.config/opencode/commands/) | 是 |
注意: 无法卸载自身。若用户要求卸载 opencode-skill-installer,提示手动删除。
两个验证步骤均有专用 CLI 入口,避免内联 JS 的 PowerShell 转义问题:
# §7a 源包完整性验证
node .opencode/skills/opencode-skill-installer/scripts/install.mjs verify-integrity --path <sourceDir>
# §7b 安装后验证
node .opencode/skills/opencode-skill-installer/scripts/install.mjs verify-install --name <skillName> --scope <project|global>
输出 JSON { pass, checks: [{ name, pass, expected?, actual }] }。pass: false 表示检查未通过。
在复制技能之前(步骤⑤前),验证源包内部一致性。发现不一致时拦截安装,列出差异让用户手动修复源包后再试:
| 检查项 | 说明 |
|---|---|
| -------- | ------ |
_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\"" }
]}
| 检查项 | 说明 |
|---|---|
| -------- | ------ |
| SKILL.md 存在 | 文件必须存在 |
| 命名合法 | getSkillName() 返回值匹配 ^[a-z0-9]+(-[a-z0-9]+)*$ |
| _meta.json 可读 | 存在则必须可 JSON 解析 |
| opencode.json 已配置 | skills. |
| 配套命令已部署 | 若有 commands/,全部已复制到命令目录 |
| 配套脚本已复制 | 若有 scripts/,目录非空 |
| 源包一致性检查通过 | 参考 §7a 结果(复制前已校验,不再重复) |
若发现配置为 "deny",手动改为 "allow"。
安装过程中某一步骤失败时,按以下分级路径处理:
| 失败步骤 | 恢复行为 |
|---|---|
| --------- | --------- |
| 解压失败(zip 损坏/不完整) | 提示检查 zip 完整性 → 自动重试一次 → 仍失败则建议用户重新下载 |
| 结构检测失败 | 提示源路径中无合法技能包 → 列出检测到的文件内容辅助定位 → 询问用户是否手动指定 |
| 复制失败(权限/空间不足) | 检查目标磁盘空间 + 路径可写性 → 报告具体原因 → 建议更换安装位置 |
| 配置写失败(opencode.json 不可写) | 检查文件权限和目录存在性 → 输出手动配置指引让用户自行操作 |
Windows 特有问题: 压缩包内长路径(超过 260 字符)可能导致解压到 .opencode/.tmp/ 失败,此时需加 \\?\ 前缀或缩短源 zip 路径。
安装或更新 opencode-skill-installer 自身时:
| 步骤 | 操作 |
|---|---|
| ------ | ------ |
| ① | 用户提供源路径($ARGUMENTS 或从对话中获取),指向新版安装器所在目录或 zip 包 |
| ② | 按 §3a/§3b 检测源结构(目录模式优先),验证为合法安装器包 |
| ③ | 询问安装位置(project / global),默认沿用原有安装位置 |
| ④ | 版本检测:展示已安装版 vs 新版版本,自动选择升级/重装 |
| ⑤ | 执行安装(§3a/§3b),跳过清理步骤(用户提供的源路径不应自动清理) |
| ⑥ | 安装完成后,提示用户重启 opencode 会话 使新配置生效 |
卸载自身: 不受支持。用户需手动删除目录并清理配置。
每次安装前,先依次完成以下 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) |
| ⑦ | 目标位置已存在同名技能? | 检查 .opencode/skills/ 和 ~/.config/opencode/skills/ |
在执行前置检查之前,先展示安装确认行,让用户在开始前纠正理解偏差:
📌 安装前置确认:
- 源路径:[xxx.zip]
- 来源类型:[zip / 目录]
- 目标位置:[project / global]
- 操作类型:[安装 / 升级 / 降级 / 重装]
正确吗?
决策树:
源路径
├─ zip 文件 ──→ 解压到 .opencode/.tmp/ ──→ 检测结构 ──→ 安装(§3a)
└─ 目录 ──→ 直接检测结构 ──→ 安装(§3b,跳过解压)
.opencode/skills/(项目)、~/.config/opencode/skills/(全局)、.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>
函数列表详见 references/quickref.md。AI 负责用户交互和决策,脚本执行具体操作。
| 权限 | 值 | 说明 |
|---|---|---|
| ------ | ---- | ------ |
skills.io | allow | 读写 .opencode/skills/、.opencode/agents/、.opencode/commands/、.opencode/.tmp/ |
bash | allow | 执行 Node.js 脚本和系统命令操作文件系统 |
write | allow | 创建/更新/删除技能目录和配置文件 |
read | allow | 读取 zip 包和已安装技能,无需确认 |
| 陷阱 | 原因 | 修复 |
|---|---|---|
| ------ | ------ | ------ |
Flat 结构无 _meta.json | 只能从 zip 文件名推断名 | 确认时让用户验证 |
| 多技能 zip 未检测到 | 根目录无 SKILL.md 但子目录有 | 检查所有子目录 |
| 目录名含大写 | 要求全小写 | 自动转小写并更新 frontmatter |
| 名含空格 | 不允许空格 | 替换为连字符 |
| 技能被 deny | 权限配置 | 改为 allow |
| 文件损坏 | 下载不完整 | 跳过标记 |
| 无 SKILL.md | 非技能包 | 标记为非技能包 |
| agent 已存在 | 同名冲突 | 询问覆盖 |
| 技能已存在 | 覆盖检测缺失 | 安装前检查并询问 |
| 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/)是永久目录,.opencode/skills/ 是运行时副本。清理仅针对临时目录和 zip 包,永久源码不清理 |
| _meta.json 与 SKILL.md 版本不一致 | 手动修改 _meta.json 后忘记同步 SKILL.md 的 frontmatter | §7a 源包完整性验证在复制前自动拦截,输出差异表供修复 |
| 向 opencode.json 写入 agents 条目 | AI 误以为代理需要在 JSON 中注册 | Update-OpencodeConfig 自动清理 agents 字段;SKILL.md §4 已明确禁止 |
opencode-creator — 创建和审查技能设计opencode-skill-packager — 打包技能为 zip在 opencode-skill-suite 提交 Issue 或 PR。
共 6 个版本