夜雨聆风学习资料网

ARTICLE · 1173541

Claude Code 插件也会'依赖地狱'?我用版本范围加打包,把这事理清了

Claude Code 插件也会'依赖地狱'?我用版本范围加打包,把这事理清了

上周三早上,群里有人 @ 我:装了你那个部署插件,怎么 Claude 里啥都没出来?

我自己一试,好好的。再一问,他昨天点了下"更新全部插件",然后就废了。

折腾半天才反应过来——我那个插件依赖另一个插件里的 MCP 服务器。那个被依赖的插件升级时改了个工具名,我的插件还在按老名字调,于是整个加载失败。

说白了,就是插件的依赖地狱。这事前几年 npm 圈天天上演,现在轮到 Claude Code 了。

我翻了下官方新出的依赖文档,把该踩的、该配的捋了一遍,这篇一次讲清。不管你是写插件的,还是只想给团队装一套顺手的插件,都用得上。

先搞懂"依赖"是个啥

简单说:A 插件用到了 B 插件的 MCP 服务器或 skill,那 B 就是 A 的依赖。

Claude Code 默认怎么处理?——不加约束的话,每次更新就跟着 B 的最新版走。

这就埋了个雷。B 的作者哪天顺手把某个 MCP 工具改了个名,所有依赖 A 的人一更新,A 当场就瘫,而且是对所有人生效,你连报错都来不及看。

一句话记住:不锁版本的依赖,等于把插件的命门交到别人手里。

怎么给依赖上把锁

写插件的人,在 .claude-plugin/plugin.json 里加个 dependencies 数组就行。看看这个例子:

{"name":"deploy-kit","version":"3.1.0","dependencies":["audit-logger",{"name":"secrets-vault","version":"~2.1.0"}]}

注意到区别没:

  • "audit-logger" 是裸字符串,意思就是"跟着人家市场走,随时更新";
  • { "name": "secrets-vault", "version": "~2.1.0" } 用了对象写法,加了版本范围。

有了 ~2.1.0 这个约束,你的用户就只会一直收 2.1.x 的小补丁,永远不会被偷偷推到 2.2。等哪天你自己测过新版本了,再发布一版插件、把范围放宽——升级时机拿回自己手里。

版本范围写法和 npm 那套一样,常用的就这几个:

写法
意思
~2.1.0
只跟 2.1.x 的补丁
^2.0
2.x 里随便升,不进 3.0
>=1.4
1.4 及以上
=2.1.0
死死钉在 2.1.0

还有个坑:范围默认不匹配预发布版,比如 2.0.0-beta.1 是选不中的。真要测 beta,得写成 ^2.0.0-0 这种带后缀的。

更香的是"插件套餐"

锁版本解决的是"别崩",但还有个更实用的问题:团队里每个人要装的插件,能不能一次装齐?

可以。你发布一个只有 name 和 dependencies 的空壳插件,装上它,等于把依赖全装了:

{"name":"backend-standard","version":"1.0.0","description":"后端工程师的标准插件集","dependencies":["secrets-vault","deploy-kit",{"name":"db-migrate","version":"^3.0"},"oncall-runbook"]}

平台组在内部市场发一个 backend-standard,工程师一句话搞定:

claude plugin install backend-standard

以后再想加个插件,不用挨个通知——发一版新的 backend-standard,多挂个依赖就行。开了市场自动更新的,下次跟着更新;没开的,跑一句 claude plugin update backend-standard,再在会话里 /reload-plugins 补上新依赖。

这招我特别推荐给团队负责人:比起在群公告里喊十遍"记得装 XXX",一个套餐插件干净又体面。

跨市场依赖:默认是关着的

插件市场之间有道墙。默认情况下,你的插件不允许依赖另一个市场里的插件——防止某个市场偷偷给你装没审过的来源。

想开这道口子,得在根市场(也就是用户正在装的插件所在的那个市场)的 .claude-plugin/marketplace.json 里加白名单:

{"name":"your-marketplace","allowCrossMarketplaceDependenciesOn":["your-shared-marketplace"],"plugins":[{"name":"deploy-kit","dependencies":[{"name":"audit-logger","marketplace":"your-shared-marketplace"}]}]}

注意:白名单只认根市场那一份。写在别的市场里没用。另外,如果用户已经自己装过并启用了那个跨市场插件,这道墙自动放行,不用改配置。

如果没配白名单还硬要跨市场,安装会直接失败,报错里会点名让你去设 allowCrossMarketplaceDependenciesOn。

写插件时,本地怎么一起测

你正在开发插件 A,同时还在改依赖 B,总不能每次都装到市场里试吧?

直接俩一起加载就行:

claude --plugin-dir ./my-dependency --plugin-dir ./my-plugin

本地这份会直接满足依赖声明,不用真的去市场装。而且本地副本不校验版本范围(因为本地压根不需要 version)。

一个提醒:只要这个本地依赖被禁用或你没带 --plugin-dir,你的插件就会加载失败。报错里出现 <名字>@inline 的,就是那个 --plugin-dir 加载的副本。

如果你是被依赖的那一方

你的插件被别人加了版本约束,那就得打 tag,否则约束没法解析。命名规则是 <插件名>--v<版本号>,跟 plugin.json 里的 version 对上。

Claude Code 提供了现成命令,标签名它自己拼:

claude plugin tag --push

它会先校验插件、检查 plugin.json 和市场条目版本是否一致、确认工作区干净、以及 tag 有没有重名,通过了才打。想先看看结果就加 --dry-run。

几个容易翻车的点

踩了一圈,下面这几条最容易被忽略:

  • 非 git 来源的约束只管"检查",不管"拉哪个版本"。npm、archive、command 这几种源,约束是在插件加载时才校验的,装上不匹配就直接禁用;记住给这类依赖的 plugin.json 写上 version,不然它满足不了任何约束。
  • 多个插件约束同一个依赖时,取都满足的最高版本。如果两个范围没交集(比如一个要 ~2.1,一个要 ~3.0),装第二个会直接失败。
  • 想知道到底解析到了哪个版本,跑 claude plugin list。tag 解析出来的会带一截 12 位提交号,像 2.1.0-8713c5b11005。
  • 卸载最后一个约束方之后,依赖会恢复"跟着市场走"的状态,下次更新就放飞了。
  • 旧依赖别忘了清。claude plugin prune 能把没插件再需要的自动依赖删掉。

最后

我现在的做法很简单:凡是我自己写的插件,依赖一律写版本范围。多写一行,换来的是别人更新时我不用半夜爬起来救火。

你要是只管用插件、不写插件,那更该记住"插件套餐"那招——给团队一个 xxx-standard,一劳永逸。

顺带说一句,依赖这玩意儿本质是信任问题。你依赖谁,就是把一部分控制权交出去。所以我的原则是:能自己一行代码解决的,就别拉一个插件进来。

相关学习资料