Plugin 的结构很朴素:一个目录,里面一份清单,几个组件目录。除了清单,别的都是可选。 真跑三份清单,两份过不了。 最典型的是缺 version:它能装能跑,但以后说不清「你装的是哪一版」。
上一节讲了为什么要有包,这一节看包长什么样。 这一节有真跑:真建了三份清单、真做了校验,其中一份声明了组件但目录不存在。
一、目录长什么样
.claude-plugin/plugin.json:清单,必须有。它决定这个包叫什么、里面有什么。 commands:斜杠命令,可选。 agents:子智能体定义,可选。
skills:技能,可选。 hooks:钩子脚本,可选。还有 .mcp.json 可以挂 MCP 服务器配置。 除了清单,别的都可以先不放。这个设计很实用:可以先只放一个技能,以后再补。
二、清单里那几个字段
name:包的名字,也是命名空间的来源(9.4 节要靠它)。 version:版本号。升级、回滚、排查「你装的是哪一版」全靠它。 description:一句话说明。人看这个决定装不装。
author:谁维护。出问题知道找谁,这一条在团队里比想象中重要。 前三个必填,第四个建议填。少一个必填,包就说不清楚自己是什么。
三、真跑:三份清单逐个校验
演示里三份:一份完整、一份缺 version、一份声明了两组组件但只建了一个目录。 结果只过一份。缺 version 那份最典型:它能装、能跑,但以后没法判断版本。 第三份的问题更隐蔽:装的时侯不报错,用的时候才发现某个命令不存在。
这类「能跑但不合格」的配置,是 Plugin 里最常见的问题——因为它不报错。
四、组件格式就是原来那些
commands 里是一个个 markdown,agents 里是一份份定义,skills 里是 SKILL.md。 包不改变它们的格式,只改变它们「放在哪儿、怎么被找到」。 所以做 Plugin 之前,应该先有能跑的单件——把它们拼起来才有意义。
反过来,先搭包再往里填内容,你会发现每个目录都在等一个还没想清楚的东西。
五、一个容易忽略的字段
清单里还能声明「组件列表」。一旦声明了,就有个新的失败模式:写了但目录不存在。 这个错误比「缺字段」更难查:装机不报错,用的时候才发现某个命令没有。 所以声明组件列表的时候,最好让校验器顺手检查目录是否存在。
这一条和 9.5 节那个「团队包检查清单」是一路的:把能自动查的都自动查。
六、动手 Demo:三份 plugin.json,两份过不了
Plugin 的物理结构很朴素:一个目录,里面一份清单,几个组件目录。 这里真建三份清单,逐份校验必填字段和组件目录。
for nm in (u"miss", u"nover", u"nocmd"):
d = json.load(open(p, encoding=u"utf-8"))
miss = [k for k in (u"name", u"version", u"description") if not d.get(k)]
# 声明了组件但目录不存在,也算问题
实跑输出:
--------------------------------------------------------------------
三份 plugin.json,两份过不了
--------------------------------------------------------------------
Plugin 的物理结构很朴素:一个目录,里面一份清单,几个组件目录。
清单位于 .claude-plugin/plugin.json,它决定「这个包叫什么、里面有什么」。
① 目录长什么样
--------------------------------------------------------------------
| 位置 | 放什么 | 必须有吗 |
|---|---|---|
| .claude-plugin/plugin.json | 清单 | 必须有 |
| commands/ | 斜杠命令 | 可选 |
| agents/ | 子智能体定义 | 可选 |
| skills/ | 技能 | 可选 |
| hooks/ | 钩子脚本 | 可选 |
| .mcp.json | MCP 服务器配置 | 可选 |
除了清单,别的都是可选。这个设计很实用:可以先只放一个技能,以后再补。
② 清单里那几个字段
--------------------------------------------------------------------
| 字段 | 干什么 | 必填吗 |
|---|---|:---:|
| name | 包的名字,也是命名空间的来源 | 必填 |
| version | 版本号,升级时靠它判断 | 必填 |
| description | 一句话说明,人看这个决定装不装 | 必填 |
| author | 谁维护,出问题找谁 | 建议 |
注意 version 不是「好看」用的:升级、回滚、排查「你装的是哪一版」全靠它。
③ 真跑:三份清单逐个校验
--------------------------------------------------------------------
| 这一份 | 缺什么 | 结果 |
|---|---|:---:|
| miss | — | 通过 |
| nover | version | 不通过 |
| nocmd | hooks 目录不在 | 不通过 |
三份里只有一份通过。缺 version 那份最典型:它能装、能跑,但以后没法判断「你装的是哪一版」。
这类「能跑但不合格」的配置,是 Plugin 里最常见的问题——因为它不报错。
④ 组件格式就是原来那些
--------------------------------------------------------------------
commands 里是一个个 markdown,agents 里是一份份定义,skills 里是 SKILL.md。
包不改变它们的格式,只改变它们「放在哪儿、怎么被找到」。
所以做 Plugin 之前,应该先有能跑的单件——把它们拼起来才有意义。
⑤ 一个容易忽略的字段
--------------------------------------------------------------------
清单里还能声明「组件列表」,一旦声明了,就有个新的失败模式:写了但目录不存在。
这个错误比「缺字段」更难查:装的时侯不报错,用的时候才发现某个命令没有。
! 清单字段与目录约定以真实实现为准;这里演示的是「一份清单加几个可选目录」这个形状。
①结构:一份清单在 .claude-plugin/plugin.json,其余 commands、agents、skills、hooks 都是可选目录。 除了清单都可以先不放。 ②清单四个字段:name(也是命名空间来源)、version(升级回滚排查全靠它)、 description(人看这个决定装不装)、author(出问题找谁)。 ③三份只过一份:一份缺 version(能装能跑,但说不清哪一版),一份声明了组件但目录不在(装机不报错, 用的时候才发现)。 这类「能跑但不合格」的配置是 Plugin 里最常见的问题——因为它不报错。
30 秒实验
- 给缺 version 那份补一个版本号,重跑一遍。它现在合格了吗?还差什么才算「能发布」?
- 把组件目录名从 commands 改成 cmd,重跑一遍。校验能发现吗?这说明目录名是约定还是可配的?
- 给清单加一个 author 字段,重跑一遍。它改变了哪一项检查的结果?「建议填」和「必填」的差别在哪?
常见误区
- 不写 version。能装能跑,但升级和回滚时说不清「之前是哪一版」。
- 声明了组件却不建目录。装机不报错,用的时候才发现命令不存在。
- 把配置写进包目录。卸载时可能一起被删掉。
- 先搭包再想内容。每个目录都在等一个还没想清楚的东西。
- 以为清单只是一份说明。它是「包是什么」的唯一声明来源。
自测三问(点开对答案)
1. Plugin 的目录结构里哪一项是必须的? —— .claude-plugin/plugin.json。其余 commands、agents、skills、hooks 都是可选。 2. 清单里哪三个字段必填? —— name、version、description。name 还是命名空间的来源。 3. 「声明了组件但目录不存在」为什么难查? —— 因为装机不报错,用的时候才发现某个命令不存在。结构清楚了,下一节讲装进来之后怎么管:装、升、停、卸。
- ☐ (本篇完,下一篇 9.3 安装与生命周期管理)
相关笔记
- Plugins 的定位:为什么要有包
- 安装与生命周期管理:装进来之后怎么管
- Claude Code 橙皮书 · 索引
- 🐎 Claude Code 实战 · 索引