打包层的出现让命令、代理、钩子、MCP 服务器、技能可以放进一个目录,通过清单(.claude-plugin/plugin.json)挂上名字与版本,再经由市场(marketplace.json)仓库统一分发。同一份配置可以被多台机器拉取,从而解决「每个项目都复制粘贴一份 .claude/ 目录」的复用难题;同时,「插件名:技能名」格式的命名空间机制,把同名的技能隔离开来,让不同插件里的 review 不再互相覆盖。
一、复用问题的根源是「分发粒度」不对
在 Claude Code 早期,扩展能力只能通过把 .md 文件复制到项目级 .claude/commands/、.claude/agents/ 或 .claude/hooks/hooks.json 来实现。这种做法在小团队单仓库里看似够用,一旦进入「同一份能力要在十几个仓库里都要用」的场景,就会出现三个具体问题:第一,每次新建仓库都要手动复制一份,新人入职往往漏配;第二,任何一处的修改不会自动同步到其他仓库,容易出现 N 份分叉;第三,钩子与 MCP 配置散落在各处,审计时找不全。
插件层的出现把「分发粒度」从「散落的文件」对齐到「一个被版本化的目录」。开发者把 commands/、agents/、skills/、hooks/、.mcp.json、.lsp.json 等组件放进同一个目录,再加一个 .claude-plugin/plugin.json 清单,就构成了一个可独立版本、可独立发布的插件。市场(marketplace.json)再把这些插件聚合成一个目录,任何机器用一行命令就能拉到完整配置。
下面这张表把「独立文件」与「插件打包」在分发效率上的差异列清楚:
| 维度 | 散落文件(.claude/) |
插件打包(.claude-plugin/) |
|---|---|---|
| 分发单位 | 单个 .md 或 hooks.json |
一个被清单标识的目录 |
| 复制方式 | 手动粘贴到每个仓库 | 一行「安装插件」命令 |
| 版本管理 | 人工同步,容易分叉 | 语义版本或提交 SHA 自动校验 |
| 多仓库一致性 | 弱,容易漂移 | 强,所有机器装同版本 |
| 跨团队共享 | 靠文档与口口相传 | 市场仓库统一分发 |
把这张表给运维与平台团队看一眼,几乎立刻能说清楚「为什么我们要把现有命令迁成插件」。
二、命名空间机制解决「同名技能」冲突
把技能放进插件后,调用语法从单纯的「技能名」变成「插件名:技能名」格式(例如 my-plugin:review)。这种命名空间不是装饰,是为了在多插件共存时避免相互覆盖。Claude Code 的设计与 npm、crates 这类包管理器的命名空间逻辑一致:每个插件的 name 字段不仅作为标识,也是其内部所有技能的命名空间前缀。
具体落地有三个要点:第一,plugin.json 中的 name 字段是命名空间根,必须用 kebab-case 风格,且全局唯一;第二,客户端在解析技能时会把「插件名:技能名」当作一个整体键,不同插件的同名技能因此可以共存;第三,用户在终端输入反斜杠触发菜单时,客户端会同时列出本地独立技能与命名空间内的「插件名:技能名」形式技能,帮助区分。
如果两个插件都想叫 tools,冲突时客户端会以「后安装的覆盖先安装的」或「明确报错」的方式提示。解决方法是修改其中一方的 name 字段,或把它放进不同市场(用户与项目作用域在解析顺序上也可能影响结果)。一个保守的命名习惯是「团队-功能」,例如 acme-deploy、acme-lint,这样既能避免重名,也能在调用 acme-deploy:preview 这种形式时一眼看清来源团队。
三、清单与目录布局的硬性规则
Claude Code 对插件目录布局做了硬性约束,违反就会被自动发现机制忽略。规则是:只有 plugin.json 必须放在 .claude-plugin/ 目录里,所有组件目录(commands/、agents/、skills/、hooks/、.mcp.json)都必须放在插件根目录,不能嵌套进 .claude-plugin/。这条规则看起来琐碎,但它是「按约定发现」机制的基石,客户端按固定路径扫描根目录,任何错位都会导致组件静默丢失。
下面是一个最小可用插件的目录示例:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ └── review.md
├── skills/
│ └── code-review/
│ └── SKILL.md
├── agents/
│ └── reviewer.md
├── hooks/
│ └── hooks.json
└── .mcp.json
清单 plugin.json 的最小版本只有 name 一个字段;version、description、author、homepage、repository、license 是推荐字段。其中 version 是「更新触发器」:不写 version 时,git 源会把每个 commit 当作新版本(commit SHA 即版本号);写明 version 后,用户只有在你主动 bump 时才收到更新,这对稳定生产环境非常关键。
四、市场仓库是分发枢纽
插件本身只是「目录」,真正解决「跨团队分发」的是市场(marketplace)。市场是一个 git 仓库,根目录放 .claude-plugin/marketplace.json,里面列出每个插件的 name、description、source。source 字段是核心,它可以是相对路径(./plugins/foo)、GitHub 仓库、Git URL、git-subdir、npm 包或 pip 包,这意味着市场可以聚合来自不同源、不同维护者的插件,而不必把所有东西都搬进同一个仓库。
安装流程被拆成两步:第一步是「把市场注册到本机」,把市场仓库的 git 地址加入本机列表;第二步是「从该市场安装指定插件」,从中拉取具体插件。这种「市场→插件」二级模型允许一个团队维护自己的市场,内部插件只对内可见;另一个市场由社区运营,覆盖公开插件;用户也可以同时注册多个市场,按需选用。
source 字段对跨仓库协作特别有用:插件 A 的代码在 org/repo-a,插件 B 在 org/repo-b,市场只负责引用,各自维护者按自己的节奏发版,不需要合并到一个大仓。下面是 marketplace.json 的一个最小示例:
{
"name": "team-tools",
"owner": { "name": "DevTools Team" },
"plugins": [
{
"name": "acme-deploy",
"source": "./plugins/acme-deploy",
"description": "团队部署技能合集"
}
]
}
五、把现有 .claude/ 迁到插件的最小步骤
很多团队在改造时倾向于「一步到位把所有文件搬过去」,但更稳的做法是渐进式迁移:先选一个低风险技能,放进插件骨架,本地用 claude --plugin-dir ./my-plugin 加载,确认无误后注册市场、再让一两个团队试用,最后才全量切换作用域。整套流程可以拆成五步,每一步都有明确验收点:
- 在临时目录搭骨架:创建
my-plugin/.claude-plugin/plugin.json与skills/,把目标技能原样拷过去; - 改写路径:把所有硬编码绝对路径换成
${CLAUDE_PLUGIN_ROOT}/...,这是插件安装后唯一能正确解析的相对根; - 本地加载验证:运行
claude --plugin-dir ./my-plugin,用「插件名:技能名」形式触发,确认行为与改造前一致; - 注册市场并邀请试用:把插件放进 git 仓库的
.claude-plugin/marketplace.json,同事运行「注册市场」与「安装插件」命令; - 切换作用域并清理:确认在「用户作用域」或「项目作用域」下都正常,再删除原
.claude/里的散落文件。
完成迁移后,后续任何修改都在插件仓库里发版,所有安装者下次启动就会同步新版本,不再有「谁还没改 hooks」的运营负担。
常见问题(FAQ)
Q1:技能放进插件后必须用「插件名:技能名」调用吗?
是的,这是协议层命名空间,本地无命名空间形式只对独立技能有效。
Q2:多个插件同名会怎样?
客户端会按安装顺序或明确报错提示冲突,需要修改其中一方的 name 字段。
Q3:市场仓库能否引用外部 git 仓库作为插件源?
可以,source 支持相对路径、GitHub、Git URL、git-subdir、npm 与 pip 等多种形式。