你在项目里攒了一堆 slash command、几个好用的 subagent、一套 PreToolUse 校验脚本,想打包发给同事。仓库建好了,文件丢进去,然后卡住:
plugin.json放哪层?commands/和skills/是不是一回事?脚本里写死绝对路径,别人机器上的落点跟你一样吗?
anthropics/plugin-structure 回答的就是这类问题。它是 Anthropic 官方 plugin-dev 工具包里的 7 个 skill 之一,Smithery 上归类 Coding,安装量 5.6K(第三方目录站 2026 年 8 月同步数据),源码落在 anthropics/claude-code 仓库的 plugins/plugin-dev/skills/plugin-structure/。顺带说一句,你给的链接末尾那段 command-name 是文档里的示例占位符,真实 slug 是 plugin-structure。

它不是命令行工具,也不装任何依赖。整个产物是一个 SKILL.md,内容只有三块:目录约定、manifest 规范、路径规则。触发方式也很直白,用户嘴里冒出下面这类词,Claude Code 就把它读进上下文:
-
“create a plugin” -
“scaffold a plugin” -
“set up plugin.json” -
“use ${CLAUDE_PLUGIN_ROOT}”
装它本身没有门槛,一条命令进当前项目:
npx -y skills add anthropics/claude-code --skill plugin-structure
我一开始以为这只是份目录说明,没什么可拆的。把它的内容和当前官方 Create plugins 文档逐条对了一遍,发现事情有点意思:这份规范里相当一部分条目,已经和线上文档对不上了。它更像一个时间切片,不是活的真相源。
架构解析
它定下的骨架长这样。只有 .claude-plugin/plugin.json 是必须的,其余组件目录一律平铺在插件根,任何一层放错地方,自动发现就静默失效,连报错都不会给你。
plugin-name/
├── .claude-plugin/
│ └── plugin.json # Required
├── commands/ # 斜杠命令(.md)
├── agents/ # 子代理定义(.md)
├── skills/<name>/SKILL.md # 技能(目录 + SKILL.md)
├── hooks/hooks.json # 事件处理器
├── .mcp.json # MCP server 定义
└── scripts/ # 辅助脚本
最容易踩的坑,官方在文档里专门挂了 Warning。下面这四个目录都不能塞进 .claude-plugin/,那个目录只放 plugin.json:
-
commands/ -
agents/ -
skills/ -
hooks/
违反的后果不是报错,是静默失效。组件放错层,自动发现扫不到,你只会看到命令凭空消失却查不到任何日志,这个失败方式相当阴险。
五类组件的职责分工很清楚,混用会直接导致行为跑偏,因为它们的触发方根本不是同一类东西:
| 组件 | 触发方 | 位置 |
|---|---|---|
| skills | 模型自主判断 | skills/<name>/SKILL.md |
| commands | 用户敲 /name |
commands/<name>.md |
| agents | 模型或用户 @ 提及 | agents/<name>.md |
| hooks | 生命周期事件 | hooks/hooks.json |
| MCP | 插件启用即启动 | 根目录 .mcp.json |

命名约定贯穿始终,所有目录和文件一律 kebab-case。skills 用“目录加 SKILL.md”而不是扁平文件,这不是形式主义,skills/api-testing/ 的目录名直接决定了调用名,改名等于改 API。连带一个反常识的细节:只发一个 skill 的插件可以把 SKILL.md 直接放插件根,省掉 skills/ 这一层。
manifest 这一层,规范只强制一个 name 字段,要求 kebab-case 且全局唯一。剩下这一堆全是推荐项,功能各不相同:
-
version:语义化版本,只有 bump 了用户才会收到更新 -
description:装在插件管理器里显示的那句话 -
author/homepage/repository/license:归属与出处 -
keywords:marketplace 检索归类用
它同时还允许在 manifest 里重定义组件路径,规则是“补充”不是“替换”:
{
"name": "plugin-name",
"commands": "./custom-commands",
"agents": ["./agents", "./specialized-agents"],
"hooks": "./config/hooks.json",
"mcpServers": "./.mcp.json"
}
默认目录和自定义路径会同时生效,两边都有的组件全都会被加载。这个行为符合直觉,但真踩上去会让人排查半天,尤其当你以为自定义路径会覆盖默认值的时候。
工作流分析
组件是怎么被找到的?规范给了一条六步链路,全部在插件启用时跑完,改完之后跑 /reload-plugins 就能生效,不用重启会话:
-
读 .claude-plugin/plugin.json -
扫 commands/下的.md文件 -
扫 agents/下的.md文件 -
扫 skills/下含SKILL.md的子目录 -
加载 hooks/hooks.json -
加载根目录 .mcp.json
真正让这套东西可移植的是 ${CLAUDE_PLUGIN_ROOT}。同一个插件,装在不同人机器上落点完全不同,因为路径取决于安装方式、操作系统、个人偏好这一堆变量。marketplace 装是一种路径,本地目录装又是另一种,npm 分发的还会再变一次。所以规范要求把插件内部路径统统抽象成这个环境变量,作者必须放弃对安装位置的知情权。
# hook 脚本里这样引用
bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate.sh
# 这三样全部禁止
bash /Users/name/plugins/my-plugin/hooks/scripts/validate.sh
bash ./hooks/scripts/validate.sh
bash ~/plugins/my-plugin/hooks/scripts/validate.sh
hooks 是这个体系里最能玩出花的部分,一共支持 9 种事件,按时机可以分成四组:
-
PreToolUse/PostToolUse:工具调用前后 -
SessionStart/SessionEnd:会话起止 -
UserPromptSubmit/PreCompact:提示词提交与上下文压缩 -
Stop/SubagentStop/Notification:结束与通知
配置里的 matcher 用正则匹配工具名,命中才执行:
{
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate.sh",
"timeout": 30
}]
}]
}
一次文件写入的完整链路里,hook 的介入时机长下面这样。中间那一步脚本执行是分水岭,退出码决定了这次调用是放行还是被拦下来,整套拦截机制的代价就压在这个数字上:

skills 的加载逻辑和 hooks 完全相反。它不在启动时把正文全量读进上下文,而是走渐进披露:启动时只读每个 SKILL.md 的 description,命中了才把正文拉进来。这也是为什么 description 的写法比正文更致命,写砸了等于这个 skill 从不存在。
使用场景
适不适合用它,判断标准很朴素:你要不要把这套配置交给别人。从零搭一个插件时,目录怎么分层、manifest 写哪些字段,它能一次性给全,省掉翻文档的时间,也省掉踩完坑才发现约定早写在那里的懊恼。
把现有 .claude/ 配置迁成可分发的插件,官方给的迁移路径直白到有点粗暴,cp -r .claude/commands my-plugin/ 一行就完事,hooks 从 settings.json 里原样拷出来即可。团队里要分发、要版本化,那更是插件的主场,marketplace 就是为这件事建的。
不适合的场景同样清楚。如果你只是自己用两三个 skill,standalone 的 .claude/skills/ 更快,不用管 manifest,不用管命名空间,改完文件实时生效。Anthropic 的态度也很明确:先在项目里快速迭代,等要分享了再转成插件,别一上来就上工程化。
它最大的局限在定位本身,这是份规范快照,不是活文档。SKILL.md 结尾写着“详见 references/ 和 examples/ 目录”,但那些配套文件不在 skills.sh 的抓取范围内,你装完 skill 手里只有一份主文档,示例和参考资料得自己去仓库翻。
从工具包内部的热度分布也能看出点东西。plugin-dev 的 7 个 skill 安装量咬得很紧,最高的 agent-development 和最低的 plugin-structure 之间差不到 8%,说明开发者要的从来不是其中一个,是整包:

所以真正划算的用法是直接装 plugin-dev 插件,跑 /plugin-dev:create-plugin 走那套 8 阶段引导流程,把 12 个示例和 6 个校验脚本一起拿到手,而不是单独拎出 plugin-structure 来读。claude.com 上给整个工具包标了 6.7 万次安装,比任何单个 skill 高出一个数量级,这个对比本身就把话说完了。
洞察与反思
把 SKILL.md 和当前官方 Create plugins 文档逐条比对,能抓到三处实打实的漂移。每一处都不是措辞差异,是足以让你写出过时配置的那种,而且是照着做才发现不对的那种。
| 条目 | plugin-structure SKILL.md | 当前官方文档 |
|---|---|---|
commands/ |
一等组件,与 skills 并列 | 标注“Use skills/ for new plugins” |
plugin.json |
Required: Plugin manifest | optional if components use default locations |
| 目录清单 | 7 项 | 11 项,新增 .lsp.json、monitors/、bin/、settings.json |
最要命的在 commands/。SKILL.md 把它当成和 skills/ 平级的推荐做法,当前文档却已把它归为遗留格式,明确要求新插件用 skills/。背后的原因也不复杂:两者在 Claude Code 里早就合并了,skills/<name>/SKILL.md 同样能生成 /name,还额外支持模型自动触发,commands 只是扁平文件的旧形态。
manifest 的必填性也变了。当前文档的原话是“optional if components use default locations”,只要组件全在默认目录,连 plugin.json 都可以省。这一刀把插件的入门门槛又砍低了一档,很符合 Anthropic 一贯的收敛方向,从 skill 到插件一直在做减法。
目录清单还膨胀了一圈,多出来的这四样在 SKILL.md 里一个字都没提:
-
.lsp.json:接语言服务器,给 Claude 实时代码智能 -
monitors/monitors.json:挂后台日志监听,有事件主动通知 -
bin/:塞进 Bash 工具的 PATH,随插件启用生效 -
settings.json:下发默认配置
它们不是边角料。尤其是 settings.json 里的 agent 字段,能直接把插件的某个 subagent 设成主线程,等于让插件接管 Claude Code 的默认行为。
工具包里真正值钱的其实另有其物,是那 6 个生产级校验脚本。validate-hook-schema.sh 这类东西能在你提交之前就把 hooks 配置的语法错误挑出来,比人眼可靠。再配上 /plugin-dev:create-plugin 那条从 discovery 一路走到 documentation 的 8 阶段引导,整条链路比照着规范手搓目录稳当得多。
不过公平地说,这份规范最精彩的部分和这些漂移无关,是 ${CLAUDE_PLUGIN_ROOT} 这个设计。它解决的不是“路径写起来方便”,而是“插件作者必须放弃对安装位置的知情权”。正是这个放弃,换来了整个生态的可分发性,插件体系能立住靠的就是这一条。
所以结论就一句:把它当心智模型读,把 plugins-reference 当真相源查。前者帮你建立目录直觉,后者保证你不写出过时的配置。真要动手,直接上 claude plugin validate ./your-plugin,官方提交 marketplace 前跑的就是这条命令,过不了它连提交入口都进不去。
资源地址
总结
plugin-structure 讲的东西不复杂,但它把“插件该长什么样”从口口相传变成了可执行的约定。目录分层、组件平铺、路径环境变量化,这三条撑起了整个 Claude Code 插件生态的可组合性,也让别人写的插件你一眼就能看懂结构。
它不适合当实时参考。三处版本漂移说明文档维护存在滞后,你要真照着 SKILL.md 搭新插件,会在 commands 和 manifest 上做出过时选择。把它当入门心智模型,配合官方 plugins-reference 一起用,才是性价比最高的姿势。
最后留个判断:当插件数量继续涨,靠目录约定做自动发现还够不够?Anthropic 已经在往 marketplace、依赖管理、命名空间这些方向加东西,插件体系正在从“文件夹约定”长成一套真正的包管理系统,这个演化过程比这份静态规范值得盯得多。

