🐎 第9章 集腋成裘:Plugins 生态
📄 一句话概括

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 秒实验

  1. 给缺 version 那份补一个版本号,重跑一遍。它现在合格了吗?还差什么才算「能发布」?
  2. 把组件目录名从 commands 改成 cmd,重跑一遍。校验能发现吗?这说明目录名是约定还是可配的?
  3. 给清单加一个 author 字段,重跑一遍。它改变了哪一项检查的结果?「建议填」和「必填」的差别在哪?

常见误区

  1. 不写 version。能装能跑,但升级和回滚时说不清「之前是哪一版」。
  2. 声明了组件却不建目录。装机不报错,用的时候才发现命令不存在。
  3. 把配置写进包目录。卸载时可能一起被删掉。
  4. 先搭包再想内容。每个目录都在等一个还没想清楚的东西。
  5. 以为清单只是一份说明。它是「包是什么」的唯一声明来源。
自测三问(点开对答案) 1. Plugin 的目录结构里哪一项是必须的? —— .claude-plugin/plugin.json。其余 commands、agents、skills、hooks 都是可选。 2. 清单里哪三个字段必填? —— name、version、description。name 还是命名空间的来源。 3. 「声明了组件但目录不存在」为什么难查? —— 因为装机不报错,用的时候才发现某个命令不存在。

结构清楚了,下一节讲装进来之后怎么管:装、升、停、卸。

相关笔记