引子
经过上文的讨论,现在我们有了:
- 持久注入的
CLAUDE.md - 按需加载的Skill(技能)
- 隔离执行的SubAgent(子智能体)
- 确定性控制的Hook(钩子)
- 扩展工具边界的MCP(Model Context Protocol,模型上下文协议)
这些定制工具越攒越多,却散落在各个项目的.claude/目录和settings.json里,共享给同事靠手动拷贝,既没有版本,也没法批量更新。
那么,攒下的定制件,怎样打包、分发、更新?
这就是Plugin(插件),一个把定制件打包分发的容器,所要解决的问题。
原理
插件
插件是什么
插件不引入任何新能力。它是一个分发容器:把已有的Skills、Agents、Hook、MCP配置打包成一个可安装、可版本化的单元,再经Marketplace(插件市场)分发给团队或社区。
目录该怎么摆
一个插件就是一个遵循固定布局的目录。
包括plugin.json清单在内,各部分都是可选的,有什么放什么。
例如:
1 | my-plugin/ |
注意:
- 只有
plugin.json放在.claude-plugin/子目录里。 skills/、agents/、hooks/等组件目录以及.mcp.json必须放在插件根目录,不能放进.claude-plugin/内。
组件之间靠什么不打架
靠命名空间不打架。plugin.json中的name不只是展示名,还决定命名空间。
这个我们在《3.Skill》讨论过,插件里的Skill以/plugin-name:skill的形式调用。
比如插件quality-review里名为review的Skill,调用名是/quality-review:review。
通过这种机制,让多个插件、以及插件与本地配置之间的同名组件互不冲突。
市场
市场是什么
插件解决打包,市场解决"去哪找、怎么装"。
一个市场本质上是一份插件索引:仓库或目录的.claude-plugin/marketplace.json文件,列出若干插件条目及其来源。
用户何时拿到更新
ClaudeCode把版本号当缓存键,执行/plugin update(或自动更新触发)时比较本地版本号和远端版本号是否最新。
远端版本号按如下顺序解析:
plugin.json里的version- 市场条目里的
version(marketplace.json的plugins数组中描述该插件的那一项,见后文「市场配置」) - 插件git来源的commit SHA(
github、url等git托管来源)
由此有两种发布策略:
- 设
version(写在plugin.json)
锁定版本,用户只有在我们手动改了这个字段后才更新。"发了新代码却忘了改版本号"是"更新不生效"最常见的原因。适合有稳定发布节奏的插件。 - 两处都不设
version
回落到commit SHA,每次提交即新版本,用户总能拿到最新提交。适合开发中、快速迭代的插件。
什么情况下需要打包?
只服务当前项目的配置,放在.claude/里随代码提交就够了。只有需要团队共享、跨项目重用或社区分发时,才值得打包成插件。
插件形态与独立.claude/形态一一对应,同一份组件只是换个位置摆放。
| 组件 | 独立形态 | 插件形态 | 机制参考 |
|---|---|---|---|
| Skill | .claude/skills/<name>/SKILL.md,调用名/<skill> |
skills/<name>/SKILL.md,调用名/<plugin>:<skill> |
《3.Skill》 |
| Sub-Agent | .claude/agents/*.md |
agents/*.md,hooks/mcpServers/permissionMode三字段不支持,写了会被忽略;需要这些能力时,改用独立.claude/agents/形态 |
《4.SubAgent》 |
| Hook | settings.json的hooks字段 |
hooks/hooks.json,脚本路径用${CLAUDE_PLUGIN_ROOT}引用 |
《5.Hook》 |
| MCP服务器 | .mcp.json |
插件根的.mcp.json,命令路径用${CLAUDE_PLUGIN_ROOT}引用 |
《6.MCP》 |
示例
需求
我们为团队维护一个代码质量插件quality-review,并通过团队市场分发,让队友在项目里自动获得它。
代码质量插件
示例代码(目录结构):
1 | quality-review/ |
示例代码(.claude-plugin/plugin.json):
1 | { |
示例代码(skills/review/SKILL.md):
1 | --- |
用--plugin-dir本地调试,改完文件重新启动即可测试,省去反复安装卸载的循环。示例代码:
1 | claude --plugin-dir ./quality-review |
会话里以命名空间全名调用:
1 | /quality-review:review src/ |
市场配置
团队市场是一个GitHub仓库a-corp/claude-plugins,quality-review以相对路径放在仓库内,另一个插件deploy-tool引用外部仓库。
示例代码(.claude-plugin/marketplace.json):
1 | { |
团队成员添加市场并安装:
1 | /plugin marketplace add a-corp/claude-plugins |
注意:install时@后面写的是市场清单里的name(team-plugins),不是仓库地址。
团队自动安装
要让团队成员在某个项目里自动获得指定插件,而不要人人手动执行安装命令。
方法是:在项目的.claude/settings.json里声明extraKnownMarketplaces(市场从哪来)与enabledPlugins(启用哪些插件)即可。
示例代码:
1 | { |
这份文件随项目代码提交,同事在该项目里使用ClaudeCode时即可获得同一套插件,插件的变更也随代码评审流程可追溯。
实践
plugin.json字段
plugin.json是插件的清单。
该文件本身是可选的,只有需要声明元数据时才必须提供。
| 字段 | 必需 | 说明 |
|---|---|---|
name |
是 | 插件标识,也是组件的命名空间前缀 |
description |
否 | 插件管理器中展示的描述,建议提供 |
version |
否 | 版本号;省略时以git commit SHA作为版本,见前文「用户何时拿到更新」 |
author |
否 | 作者信息,对象形式{name, email},email可省略 |
homepage |
否 | 文档或主页URL |
repository |
否 | 仓库URL |
license |
否 | SPDX许可证标识,如MIT、Apache-2.0 |
会话内命令(/plugin)
插件在会话内用/plugin命令族管理。不带参数的/plugin打开交互管理器,含Discover、Installed、Marketplaces、Errors四个标签页。
| 命令 | 作用 |
|---|---|
/plugin install <name>@<marketplace> |
安装插件,交互式选择作用范围 |
/plugin uninstall <name>@<marketplace> |
卸载插件 |
/plugin enable <name>@<marketplace> |
启用插件 |
/plugin disable <name>@<marketplace> |
禁用插件 |
/plugin list [--enabled\|--disabled] |
列出已安装插件 |
/plugin details <name>@<marketplace> |
查看插件详情 |
/plugin validate [path] |
校验plugin.json或marketplace.json语法 |
/plugin marketplace add <source> |
添加市场 |
/plugin marketplace list |
列出已配置的市场 |
/plugin marketplace remove <name> |
移除市场 |
/plugin marketplace update [name] |
更新指定市场或全部市场 |
/reload-plugins |
重新加载活动插件,无需重启会话 |
终端命令(claude plugin)
脚本或自动化场景下可用claude plugin前缀的终端命令,安装时以--scope显式指定作用范围:
| 命令 | 作用 |
|---|---|
claude plugin init <name> |
创建插件骨架目录(默认位于~/.claude/skills/下) |
claude plugin install <name>@<marketplace> --scope <user\|project\|local> |
在指定作用范围安装插件 |
claude plugin marketplace add <source> [--scope] [--sparse] |
添加市场 |
claude plugin validate . |
校验当前目录 |
Marketplace
marketplace.json字段
| 字段 | 必需 | 说明 |
|---|---|---|
name |
是 | 市场标识,kebab-case,安装时@<marketplace>引用的就是它 |
owner |
是 | 维护者信息,对象形式{name, email},email可省略 |
plugins |
是 | 插件条目数组 |
description |
否 | 市场描述 |
version |
否 | 市场清单自身的版本 |
metadata.pluginRoot |
否 | 相对路径的基准目录,如"./plugins" |
renames |
否 | {"旧名": "新名"}映射,用于插件重命名或移除,见后文「重命名与迁移」 |
插件条目与来源
plugins数组中每个条目的字段:
| 字段 | 说明 |
|---|---|
name |
插件标识,必需 |
source |
插件来源,必需;字符串形式的相对路径,或对象形式的远程来源 |
displayName |
UI显示名,缺省时回退用name |
version |
该条目声明的插件版本 |
strict |
默认true:plugin.json是权威,市场条目仅作补充;设为false时市场条目全权控制,与plugin.json冲突会导致加载失败 |
relevance |
推荐信号,需管理员设置允许才生效 |
defaultEnabled |
安装后是否默认启用,默认true |
source支持五种来源类型:
| 类型 | 写法 | 可选锁定 |
|---|---|---|
| 相对路径 | "./plugins/quality-review" |
— |
| GitHub | {"source": "github", "repo": "owner/repo"} |
ref、sha |
| git URL | {"source": "url", "url": "https://gitlab.com/team/plugin.git"} |
ref、sha |
| git子目录 | {"source": "git-subdir", "url": "owner/repo", "path": "tools/plugin"} |
ref、sha |
| npm | {"source": "npm", "package": "@org/plugin"} |
version、registry |
重命名与迁移
市场维护者要重命名或下架插件时,在marketplace.json里声明renames映射(键是插件旧名):值为新名表示重命名,值为null表示该插件已下架。
已安装用户的配置会自动迁移(把对旧名的引用改写或清理掉)。
注意:如果插件的远程源本身换了位置,用户仍需手动重新执行一次/plugin install。
插件环境变量
插件相关的环境变量如下。其中私有仓库插件的自动更新,依赖GITHUB_TOKEN/GITLAB_TOKEN/BITBUCKET_TOKEN等完成认证。
| 环境变量 | 用途 |
|---|---|
CLAUDE_CODE_PLUGIN_CACHE_DIR |
自定义插件缓存目录 |
CLAUDE_CODE_PLUGIN_SEED_DIR |
容器构建时预装插件的目录 |
CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS |
插件git操作超时,毫秒,默认120000 |
CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE |
git拉取失败时保留已有缓存,适合离线环境 |
GITHUB_TOKEN / GITLAB_TOKEN / BITBUCKET_TOKEN |
私有仓库插件自动更新时的认证 |