有一次,我在团队里花了一整天调好的 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 字段。
如果要提交到官方市场,还可以加 homepage、repository、keywords 等字段,方便用户找到插件的来源。

把已有的 .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 的 name、description、version 都填好了。
插件在本地测试正常。
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 的朋友,让更多人少走弯路。
我是阿霄,我们下篇见。
夜雨聆风