DeepDojo.AI发布 2026-11-13更新 2026-11-13English
能回滚的 Claude Code 插件升级流程
团队插件用了一段时间,总会迎来升级:新版本修了 bug、加了功能,大家照常更新。这次的新版本在启动时,顺手把插件的本地缓存迁移成了新的存储格式。
没过多久,它在几位同事那里大面积报错。你赶紧把插件退回旧版,以为工作流就此复原,结果旧版一读到新格式的缓存文件,照样因为解析失败而崩溃。
问题出在“代码版本”和“持久数据”被当成了一件事。插件代码可以随版本回退,运行中产生的缓存、索引和状态却留在原地,已经变成了新格式。插件分发出去之后,还有几类东西要单独管:
- 敏感配置:令牌标记为敏感后存进了安全存储,后续脚本却可能把它打印进日志;
- 运行数据:写进安装目录的缓存,每次升级都可能被整个替换掉;
- 升级回滚:代码能退回旧版,数据未必能。
办法是把这几条线分开设计:敏感值从保存到使用全程追一遍;运行数据写进专门的持久目录;升级前记下旧版本、备份数据,走一遍常用调用,确认能回滚再向团队推广。
01 敏感配置:存好之后,还要看怎么用
插件可以在清单中声明用户配置。敏感项标记为 sensitive: true,按 Claude Code 的插件参考,输入时会被遮盖,值存进安全存储,而不是写进 settings.json。
{
"name": "team-notify",
"version": "1.0.0",
"userConfig": {
"channel": {
"type": "string",
"title": "通知频道",
"description": "默认通知频道"
},
"token": {
"type": "string",
"title": "通知服务凭据",
"description": "通知服务 token",
"sensitive": true
}
}
}配置值分开管理:
| 内容 | 处理方式 |
|---|---|
| 频道、API endpoint、检查开关、团队名、默认环境 | 普通 userConfig |
| 私有 token 等秘密 | 敏感 userConfig、环境变量或系统凭据存储;不写进模板、脚本和 .mcp.json |
标记完敏感项,还要沿着值往后查:它被哪个脚本读取,脚本会不会把它打印出来?官方说明,Hook 进程会以环境变量的形式拿到每一项用户配置。专门的存储解决的是保存问题,挡不住后续使用时写进日志。逐项找出泄露会造成影响的内容,再核对当前版本的保存方式和实际调用。
02 运行数据写进 CLAUDE_PLUGIN_DATA
插件可以用 ${CLAUDE_PLUGIN_DATA} 保存自己的运行数据,适合放缓存、本地索引、插件状态、最近一次运行记录,以及依赖安装目录。示例 Hook 配置:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/log-command.js\" --data-dir \"${CLAUDE_PLUGIN_DATA}\""
}
]
}
]
}
}写插件脚本时,最容易犯的一个错误,是随手在源码同级目录新建一个 cache/ 或 index.db。接下来会发生什么?插件发布新版本、用户升级时,原来的安装目录会被新文件整个替换。你为用户建立好的本地搜索索引、会话状态或缓存文件随之消失,用户下一次启动又得从头把整个工程扫一遍。
所以要分清两个内置变量的职责:
CLAUDE_PLUGIN_ROOT只用来存放随版本分发的静态代码、预置模板和只读脚本,不往里面写运行时数据。- 需要跨版本保留的本地缓存、构建索引或者用户偏好,写进
CLAUDE_PLUGIN_DATA指向的持久目录。
按 Claude Code 的插件清单参考,这个目录位于 ~/.claude/plugins/data/<id>/,首次引用时创建,插件更新时保留。把运行数据与静态源码分开存放,插件经历多次版本迭代后,用户的体验才不会三天两头被打断。
卸载就是另一回事了。官方说明,插件从最后一个安装位置卸载时,默认会删除这个数据目录。哪些数据能重建、哪些需要留下,先分清,再看卸载选项并备份。
03 发布前,按实际加载路径检查
发布前先从本地路径加载插件,验证真实安装效果:
claude --plugin-dir ./my-plugin
claude --plugin-dir ./my-plugin --plugin-dir ./another-plugin按实际加载路径检查:
| 检查层 | 要看到的结果 |
|---|---|
| 清单和入口 | plugin.json 合法,命令列得出、调得动 |
| 代理与方法 | agents 能发现,skills 按需触发 |
| 自动化与服务 | hooks 时机正确,MCP 连得上,LSP 不阻塞启动 |
| 材料与配置 | 模板路径正确,脚本可执行,userConfig 读得到 |
| 敏感内容 | 已标记,不进源码、日志或版本库 |
再运行一次静态校验:
claude plugin validate ./my-plugin官方把 claude plugin validate 作为清单的权威检查,它会核对各组件路径是否存在、是否越出插件目录。校验通过后,再选一条低风险命令走完实际流程。它读得到模板吗?需要的 Hook 真触发了吗?外部服务连不上时,结果会清楚说明失败吗?静态校验查配置,实际调用再验证组件之间是否接得上。
04 升级:代码和数据是两条线
升级按这个顺序准备:
记旧版本与 DATA 路径,确认旧版本可取得
→ 保存旧数据,或说明哪些可以重建
→ 升级并运行常用、带参数的调用
→ 比较字段、格式、参数解析与行为
→ 不兼容就先修,尚未解决不向团队推广按下升级之前,顺着最坏的情况多推演一步:万一新版本大面积报错,只把插件代码退回旧版,整个工作流就能复原吗?回到开头的例子,很可能不行。新版本的初始化脚本已经把 CLAUDE_PLUGIN_DATA 里的缓存库迁移成新结构,代码降回去,旧版逻辑读到新格式的文件,照样崩溃。
所以插件的升级预案,要把“代码版本”和“持久数据”当成两条独立的线来看待:
- 代码这条线:版本清晰打标,确保随时能取到上一个稳定版本的安装包;
- 数据这条线:升级前对持久数据目录做快照备份,或者在设计之初就让新版能读旧格式、旧版能识别并跳过新格式。
只记录版本号还不够。升级前确认数据能够回滚,再向团队推广大版本。
05 外部插件按未知代码对待
插件聚合了自动执行、外部连接和写入,沿整条组件链检查:
- 来源与说明:可信来源、依赖和安装说明、版本记录。
- 执行范围:自动 Hook、代理工具、敏感 MCP,以及系统或生产写入。
- 配置:敏感
userConfig标记与真实使用过程。 - 恢复:可禁用、可卸载,并有回滚办法。
在完成工具审查之前,任何外部引入的插件包,都应视作一组拥有本地执行权力的未知代码。把文件装成一个包,不会自动为这些操作建立独立沙箱。插件里的子代理确实被限制了部分字段(hooks、mcpServers、permissionMode 会被忽略),防止插件代理在安装后隐式获得过高控制权;但运行环境、工具权限和外部账号仍需各自收紧。
06 插件问题按层排查
遇到问题时,按现象分层检查。
插件无法安装,检查:
- Claude Code 版本是否支持该插件能力。
plugin.json是否是合法 JSON。- 插件目录结构是否正确。
- 当前用户是否有读取权限。
- 本地路径是否正确。
组件没有加载,检查:
plugin.json中声明的路径是否存在。- commands 是否放在正确目录。
- agents frontmatter 是否有效。
- skills 是否包含
SKILL.md。 - hooks 配置事件名是否正确。
安装后命令不可用,用 /plugin 确认插件已安装并启用,必要时执行 /reload-plugins。它会重新读取清单、commands、agents、skills、hooks 和 MCP/LSP 配置,便于在当前会话中验证修改。
MCP 连接失败,检查:
- MCP server 是否安装。
- 环境变量是否存在。
- 凭据是否有效。
- endpoint 是否可达。
- 插件内
.mcp.json是否路径正确。
Hook 执行问题,检查:
- 脚本是否有执行权限。
- Hook 事件名是否正确。
matcher是否匹配。- 脚本是否能读取 stdin JSON。
- 错误是否写到 stderr。
插件何时打包、目录和版本号怎么定,见 Claude Code 配置什么时候该做成插件。命令和模板同样要把真实凭据留在文件之外,有副作用的命令怎样设边界,见Claude Code 只读命令为什么还能改文件。
代码可以随版本号来回切换,数据只会往前走。升级预案里给数据留一条退路,降级才真的能把工作流带回原来的样子。