乐于分享
好东西不私藏

Claude Code 插件生态——站在巨人的肩膀上(下)

Claude Code 插件生态——站在巨人的肩膀上(下)

有一次,我在团队里花了一整天调好的 Skills 和 Hooks 配置,想着分享给同事用。

结果发现——除了把整个 .claude/ 目录压缩打包发过去,让对方手动解压到对应位置之外,竟然没有一个体面的分发方式。

那一刻我才意识到:配置做得再好,如果不方便分享,价值就只停留在自己手上。

上篇我们学了怎么用别人做好的插件,从发现市场到安装管理,整个消费者视角走了一遍。

这篇换个角度——如果你已经有一套调顺手的工作流,想把它分享给团队,或者发布到社区让更多人受益,怎么做?

💡

答案就是:把它打包成插件。

这篇文章就讲这件事:从零开始,把一个 .claude/ 目录下的独立配置,变成一个可以安装、可以分发的插件。

在开始之前,先说一件我觉得特别值得记住的事。

插件系统的核心设计逻辑,在几乎所有可扩展软件里都是一样的——一个清单文件描述"这是什么",一套标准目录结构承载"功能在哪里",然后通过标准接口让宿主加载。

npm 的 package.json、Chrome 扩展的 manifest.json、VS Code 的 package.json,以及 Claude Code 的 plugin.json,全是这个思路。

我第一次意识到这个规律的时候,还挺兴奋的。

因为它意味着——理解了这个通用逻辑,以后遇到任何新工具的插件系统,基本上都能快速找到切入点。


什么时候值得造插件

先说一个现实问题:不是所有配置都值得打包成插件。

说实话,我一开始也纠结过这个。

当时觉得什么都应该做成插件,显得"正规"。

后来发现完全没必要——如果只是你自己用,放在 .claude/ 目录下就够了,多此一举反而增加维护成本。

以下三种情况,才真的值得打包成插件:

你想分享给团队。

团队里每个人都需要同一套 Skills 和 Hooks,一人配好打包成插件,其他人一条命令装完,省去重复配置的时间。

你想发布给社区。

你做了一套很好用的工作流,想让更多人用到,插件就是标准的分发格式。

你需要跨多个项目复用。

你有好几个不同的项目,每个都需要同一套配置,与其每个项目手动复制,不如装一个插件,到处都能用。

官方有一句话说得很直白:先在 .claude/ 里快速迭代,准备好分享的时候再转换成插件。

我自己踩过的坑就是——上来就想着造插件,结果改来改去,后来才意识到应该先把配置跑通再说。

不用一开始就想着造插件,先把配置做好用,到了要分享的时候再来这一步。

这也是软件开发里"先本地原型、后标准化分发"的普遍实践——任何系统都一样,不要一上来就套框架。


插件长什么样

决定要造插件了,先搞清楚一个插件在文件系统里长什么样。

一个典型的插件目录结构是这样的:

my-plugin/
├── .claude-plugin/
│   └── plugin.json         ← 插件的唯一标识,只有这个放在这里
├── commands/               ← 自定义命令
│   └── review.md
├── agents/                 ← 子代理
│   └── code-reviewer.md
├── skills/                 ← 技能
│   └── SKILL.md
├── hooks/
│   └── hooks.json          ← 插件里的 Hooks 配置
├── bin/                    ← 可执行文件(自动加入 PATH)
├── .mcp.json               ← MCP 服务器配置
├── .lsp.json               ← LSP 语言服务器配置(代码智能插件用)
└── settings.json           ← 插件级别的配置

看这个结构,有一个贯穿始终的规律。

💡

.claude-plugin/ 目录里只放 plugin.json 这一个文件,其他所有组件都放在插件的根目录下。

这个设计和大多数插件系统一样——声明文件和实际代码分离。

plugin.json 是给机器读的元数据,告诉 Claude Code"这是一个插件、叫什么名字、有什么功能"。

其余目录才是真正承载功能的地方。

💡

这是新手最容易犯的错误:把 commands/agents/skills/ 这些目录放进了 .claude-plugin/ 里面。

这样的话,Claude Code 找不到这些组件,插件装上去什么效果都没有。

记住这个原则:.claude-plugin/ 只装清单,其他东西全在外面。

关于 bin/ 目录,值得单独说一句。

插件启用后,bin/ 目录下的可执行文件会自动加入 Bash 工具的 PATH。

这意味着你可以把自己写的小工具放进去,Hooks 脚本或者命令里直接调用,不需要写绝对路径。

关于 .lsp.json,上篇专门讲过 LSP 类插件。

如果你想为一门新语言添加代码智能支持,就需要在插件里提供这个文件,配置语言服务器的启动方式和参数。


plugin.json 怎么写

搞清楚了目录结构,接下来看最核心的文件。

plugin.json 是插件的身份证,也是 Claude Code 识别一个目录"是插件"的唯一标志。

一个完整的 plugin.json 长这样:

{
"name":"my-plugin",
"description":"我的第一个插件,包含代码审查工作流",
"version":"1.0.0",
"author":{
"name":"你的名字"
}
}

四个字段,逐个说一下。

name(必填)——插件的唯一标识符。

这个字段很关键,因为它同时决定了命令的命名空间。

上篇提到过,插件提供的命令都带前缀,格式是 /插件名:命令名

比如你的插件叫 my-plugin,里面有个命令文件叫 review.md,装完之后运行的就是 /my-plugin:review

description(强烈建议填)——在插件管理界面展示的描述,让别人知道这个插件是干什么的。

如果要提交到官方市场,这里写清楚很重要。

version(强烈建议填)——语义化版本号,格式是 `主版本.次版本.修订号`,比如 `1.0.0`。

这里有个机制值得理解。

💡

Claude Code 用版本号作为缓存的判断依据——版本号相同就认为插件没有更新,跳过下载。

所以改了代码但不改版本号,用户运行更新命令也不会拿到新代码。

(这个坑我踩过,排查了好一阵子才发现是版本号没改。)

据我观察,如果你不填版本号,Claude Code 会用 git commit SHA 作为版本。

每次提交的 SHA 必然不同,天然实现了"每个提交都算新版本"的效果。

但这样更新会非常频繁,不适合稳定发布的场景。

author(可选)——作者信息,格式是对象,包含 name 字段。

如果要提交到官方市场,还可以加 homepagerepositorykeywords 等字段,方便用户找到插件的来源。


把已有的 .claude/ 配置迁移成插件

好消息是:如果你已经在 .claude/ 目录下配置好了 Skills、Agents、Hooks,迁移成插件的工作量很小。

基本上就是把文件从 .claude/ 挪到插件目录,再加一个 plugin.json

我们对比一下独立配置和插件的目录结构差异。

独立配置(.claude/ 目录):

.claude/
├── commands/
│   └── review.md
├── agents/
│   └── code-reviewer.md
├── skills/
│   └── SKILL.md
└── settings.json           ← Hooks 配置在这里

迁移成插件之后:

my-plugin/
├── .claude-plugin/
│   └── plugin.json         ← 新增
├── commands/               ← 直接移过来
│   └── review.md
├── agents/                 ← 直接移过来
│   └── code-reviewer.md
├── skills/                 ← 直接移过来
│   └── SKILL.md
└── hooks/
    └── hooks.json          ← 注意:Hooks 的位置变了

主要改动只有两处。

第一:新增 .claude-plugin/plugin.json

这是让目录变成插件的关键,必须有。

第二:Hooks 的位置变了。

独立配置里,Hooks 是写在 .claude/settings.json 的 hooks 字段里。

插件里,Hooks 要单独放在 hooks/hooks.json 文件中。

格式一样,只是位置换了。

其他的——commands、agents、skills、.mcp.json——直接移过来就行,不需要改任何内容。

路径写法:必须用 ${CLAUDE_PLUGIN_ROOT}

迁移过来之后,有一个细节必须检查:Hooks 脚本里的路径写法

独立配置里,你可能写过这样的路径:

{
"PostToolUse":[{
"command":"./scripts/check.sh"
}]
}

这种相对路径在独立配置里能用,但在插件里会报错。

原因不是 Claude Code 的特殊设计,而是所有插件系统的普遍约束。

通过 marketplace 安装的插件,Claude Code 会把整个插件目录复制到本地缓存(~/.claude/plugins/cache/ 下),然后从缓存目录运行,而不是在你的开发目录里原地运行。

这就是相对路径失效的根本原因。

你写的 ./scripts/check.sh 在开发时能用,但安装后实际执行的位置已经是缓存目录,相对路径指向的地方根本没有这个文件。

任何把插件从开发目录复制到另一个位置再运行的系统,都有这个问题。

理解了这一点,以后遇到类似系统会自然想到检查路径依赖。

💡

插件里凡是引用插件自身文件的路径,必须用 ${CLAUDE_PLUGIN_ROOT} 这个变量。

它指向插件被安装到缓存后的绝对路径,永远指向当前版本实际运行的位置。

改完之后是这样:

{
"command":"${CLAUDE_PLUGIN_ROOT}/scripts/check.sh"
}

另外还有一个变量 ${CLAUDE_PLUGIN_DATA},指向跨版本持久化的数据目录。

如果你的插件需要存储在插件更新之后仍然保留的数据(比如 node_modules、虚拟环境、缓存),放在这里——它不随版本更新而变化。


本地测试

插件写好之后,不用先发布再测试,可以直接在本地加载运行。

加载本地插件:

claude --plugin-dir ./my-plugin

这条命令启动 Claude Code 时,会直接加载 ./my-plugin 这个目录作为插件,不需要安装。

你可以同时加载多个:

claude --plugin-dir ./plugin-a --plugin-dir ./plugin-b

在不重启的情况下更新插件:

修改了插件文件之后,不用每次都关掉重开,在 Claude Code 里运行:

/reload-plugins

大多数改动都能立即生效。

因为 hooks、skills、agents 这些组件,本质上是 Claude Code 读取文件并注册的,/reload-plugins 触发一次重新扫描就能更新注册信息。

不过 LSP 配置是例外。

LSP 服务器是一个独立的长期运行进程,Claude Code 在启动时以子进程方式启动它。

/reload-plugins 无法在不断开通信连接的情况下热替换这个进程。

改了 LSP 配置需要完整重启 Claude Code 才能生效。

本地插件和市场插件的优先级:

如果本地插件和已安装的市场插件同名,本地版本优先。

这对开发调试很方便——你可以边改边测,不会被已安装的旧版本干扰。


分享给团队 vs 发布到市场

插件开发好了,根据你的目标,有两条完全不同的路。

路线一:自建 marketplace(适合团队内部)

这是大多数企业和团队的实际场景,也是最实用的路径。

步骤:

在 GitHub(或 GitLab、自托管 Git)建一个仓库,把你的插件目录推进去。

在仓库根目录创建 .claude-plugin/marketplace.json,内容大致如下:

{
"name":"my-team-marketplace",
"owner":{
"name":"your-org"
},
"plugins":[
{
"name":"my-plugin",
"source":"./my-plugin"
}
]
}

团队成员把这个仓库作为 marketplace 来源添加进来:

/plugin marketplace add your-org/marketplace-repo

添加之后,这个 marketplace 就出现在 /plugin 的 Marketplaces 标签页里。

团队成员可以在 Discover 里浏览并安装你发布的插件。

如果是团队统一使用,推荐用 Project scope 安装。

什么是 Project scope?简单说就是——插件配置写在项目的 .claude/settings.json 里,而不是用户个人的全局配置里。

这样做的好处是,配置跟着项目走,提交到 git 后,团队其他人拉取代码就能自动提示安装。

配置长这样:

{
"enabledPlugins":{
"my-plugin@my-team-marketplace":true
}
}
💡

一个人配好,全团队同步。

路线二:提交官方市场(适合公开发布)

如果你想让更多人用到你的插件,可以提交到 Anthropic 官方市场,通过审核后就会出现在所有人的 Discover 页面里。

官方提供了两个提交入口:

个人用户市场:claude.ai/settings/plugins/submit

企业/开发者市场:platform.claude.com/plugins/submit

提交之前确认几件事:plugin.json 的 namedescriptionversion 都填好了。

插件在本地测试正常。

Hooks 脚本里的路径都用了 ${CLAUDE_PLUGIN_ROOT}


进阶:两个特殊配置

这两个配置属于进阶用法,一般的工作流插件用不到。

但如果你想做更定制化的东西,了解一下有好处。

把插件的 Agent 设为主线程

插件里可以带一个 settings.json,目前支持一个特殊用途:把插件内的某个 Agent 设为 Claude Code 的主线程

{
"agent":"security-reviewer"
}

这里写的是 agent 的名字(不含路径和扩展名),对应 agents/security-reviewer.md 这个文件。

设置之后,Claude Code 启动时会默认以这个 Agent 的身份运行,改变了整个交互的默认行为。

这个功能适合做"角色专属版本"的插件。

比如专门为某个技术栈定制的 Claude Code,装上就换了一套默认人格和工作方式。

不过说实话,这个功能我目前还没找到特别好的使用场景,感觉更适合做高度定制化的内部工具。

(另外 settings.json 还支持一个 subagentStatusLine 字段,用来定制子代理的状态栏显示,感兴趣的可以查官方文档。)

插件里的依赖管理

如果你的插件里有 Hooks 脚本依赖 Node.js 包或者 Python 库,插件本身不应该把 node_modules 或者虚拟环境包进去。

那会让插件体积很大,也不便于维护。

正确的做法是:只提交 package.json 和 package-lock.json(或者 requirements.txt)。

然后写一个 SessionStart Hook,在 Claude Code 启动时自动安装依赖到 ${CLAUDE_PLUGIN_DATA} 目录里:

{
"SessionStart":[{
"command":"cd ${CLAUDE_PLUGIN_DATA} && npm install --prefix . ${CLAUDE_PLUGIN_ROOT}"
}]
}

依赖安装一次就持久保存在 ${CLAUDE_PLUGIN_DATA} 里。

插件更新时不需要重新安装(除非 package.json 有变化)。

这个做法同样是分发场景的通用实践。

npm、pip、Homebrew 安装的都是"依赖声明",工具自己去拉依赖内容,不把整个 node_modules 传来传去。


小结

两篇合起来,把插件生态从头到尾走了一遍。

上篇是消费者视角:发现好用的插件、安装、管理。

下篇是生产者视角:把自己的配置打包成插件、本地测试、分享给团队或发布到市场。

💡

插件不是什么神秘的东西,它就是 Skills、Agents、Hooks、MCP 的打包单元,加上一个 plugin.json 作为身份证。

你已经会配这四样东西,造插件只是多了一步打包。

记住几个关键点。

.claude-plugin/ 里只放 plugin.json,其他组件都在插件根目录。

Hooks 脚本里的路径用 ${CLAUDE_PLUGIN_ROOT}——因为插件安装后在缓存目录运行,相对路径会失效。

先在 .claude/ 里把配置做好用,要分享的时候再来打包。

团队分发用自建 marketplace + Project scope,公开发布用官方市场提交流程。

说实话,插件系统这件事,最难的不是技术,而是判断"什么时候该做"

先把配置做好用,分享的需求自然会出现。

到那时候再来翻这一篇,对照着做就行。


下一篇,我们进入一个全新的话题:Claude Code 的记忆系统

你有没有遇到过这种情况:每次开新会话,都得重新告诉 Claude 你的项目背景、你的工作习惯、你希望它用什么风格回答……

记忆系统就是解决这个问题的。

它能让 Claude 真正记住你——不只是记住项目,而是记住你这个人,你的偏好,你之前给过的反馈。

如果你觉得这两篇插件生态的内容对你有帮助,点个赞、转发给同样在用 Claude Code 的朋友,让更多人少走弯路。

我是阿霄,我们下篇见。