Claude Code 插件体系里,技能的真实命令名是 /插件名:技能名,命名空间由插件清单中的 name 字段决定,目录或 frontmatter name 只能决定最后一段;这套前缀机制专门用来防止不同插件之间同命令互相覆盖。下面从源头、规则到工程取舍说清楚。
一、命名空间到底由谁决定
插件装载时,Claude Code 会读取 my-plugin/.claude-plugin/plugin.json 里的 name,把它作为技能命令的固定前缀。name 是必填字段,缺失则回退到插件目录名,但仍然带前缀。
| 组件路径 | frontmatter name |
实际命令 |
|---|---|---|
my-plugin/skills/review/SKILL.md |
无 | /my-plugin:review |
my-plugin/skills/review/SKILL.md |
fancy |
/my-plugin:fancy |
my-plugin/SKILL.md(根目录文件) |
review |
/my-plugin:review |
my-plugin/SKILL.md(根目录文件) |
无 | /my-plugin:my-plugin(回退) |
前两行说明子目录场景下,name 只替换最后一段,插件前缀不动;后两行说明没有子目录可取名时,name 充当完整尾段。多个插件各自取一个 review 子技能、命令互不冲突的原因,就是这个固定前缀。
二、个人、项目、插件、企业四级作用域
技能不止在插件里,还散落在四个加载层,命名空间只是其中一层的防冲突机制。完整优先级从高到低为:企业 > 个人 > 项目 > 插件同名技能,但插件的命名空间隔离让”插件层”和”上三层”几乎不会正面对撞。
- 企业层:通过托管 settings 统一下发,作用域是组织内全员;
- 个人层:
~/.claude/skills/<name>/SKILL.md,跟随当前用户; - 项目层:
.claude/skills/<name>/SKILL.md,跟随仓库; - 插件层:
my-plugin/skills/<name>/SKILL.md,跟随插件启用状态。
四个作用域里,命令默认不带前缀,仅插件层强制 /插件:技能 双段写法。其余三层如果目录重名,遵循”企业 > 个人 > 项目”的覆盖顺序,但同名资源仍可能误覆盖——这是设计上有意保留的简洁。
三、为什么需要这种命名空间
共享经济里,”谁拥有这个名字”和”这个名字在系统里只指向一个东西”,是两件不同的事。多插件并行启用时,若没有前缀,谁的 review 胜出取决于安装顺序,谁后装谁覆盖——这跟 npm 同名包、Chrome 同名扩展的老问题如出一辙。
工程上真正想守住的边界有三条:
- 隔离:插件 A 和插件 B 都能自带
review、commit、fix-lint而不冲突; - 可追溯:用户在命令历史里看到
/security:vulnerability-scan,一眼知道是哪个安全插件提供的; - 可分发:插件作者不需要抢名字,只需要把插件名起好,技能名随便取。
社区上不止一个 Claude Code 生态项目把”插件技能必须带命名空间”写成强约束,本质就是让命名权从”全局命令名”降级为”插件局部名”,把命名权让渡给插件清单。
四、命名规则的工程细节
把命名空间用稳,前缀、尾段、文件布局三件事要同时盯住。
4.1 前缀部分
- 来源:
plugin.json里的name字段; - 长度无硬上限,但建议短而稳定,不要带空格或下划线;
- 同一市场下两个插件不能同名,否则后注册的会覆盖前者的命令前缀。
4.2 尾段部分
- 来源优先级:frontmatter
name> 子目录名 > 文件名(根目录SKILL.md场景); - 字符约束:小写字母、数字、连字符,单段最长 64 字符;
- 旧版本 v2.1.216 之前曾用
name整段替换命令,新版本已经把插件前缀补回来。
4.3 文件布局的影响
子目录是否存在,会改变 name 究竟替换哪一段:
my-plugin/
├── .claude-plugin/plugin.json # name: "my-plugin",决定前缀
├── skills/
│ ├── review/SKILL.md # 默认 → /my-plugin:review
│ └── review/fancy/SKILL.md # 嵌套 → /my-plugin:review:fancy
└── SKILL.md # 根文件,name: review → /my-plugin:review
嵌套目录在冲突时会展开成多段路径,例如 apps/web/.claude/skills/deploy/SKILL.md 解析为 /apps/web:deploy。插件技能不具备这种”相对工作目录”自动展开能力,命名空间就是它的稳定锚点。
五、典型落地步骤
把现有散装技能迁移进插件并享受命名空间保护,标准动作是这几步:
- 在项目根创建
my-plugin/.claude-plugin/plugin.json,写入name、description、version; - 把原
.claude/skills/下的每个技能目录整体移到my-plugin/skills/; - 对需要重命名的技能,在
SKILL.md的 YAML frontmatter 加上name: 想要的尾段; - 用
claude --plugin-dir ./my-plugin本地试跑,确认/my-plugin:*命令出现; - 在
~/.claude/plugins/cache之外调试时,每次改动后跑/reload-plugins实时生效。
{
"name": "my-plugin",
"description": "团队内部代码审查与部署技能包",
"version": "1.0.0",
"author": { "name": "Platform Team" }
}
执行后,my-plugin/skills/review/SKILL.md 默认就能用 /my-plugin:review 调用,无需在命令里指定绝对路径。
六、常见踩坑
- 把所有功能目录塞进
.claude-plugin/子目录:.claude-plugin/只能放清单,技能、命令、hooks、.mcp.json、.lsp.json必须放在插件根; - 改了技能但忘改
plugin.json里的version:插件缓存按版本号生效,不升版本用户拿不到更新; - 插件名用了大写或下划线:插件清单
name允许但不利于跨平台展示,建议一律小写连字符; - 把命名空间当成”内部别名”:用户看到的命令就是
/插件:技能,没有进一步缩写机制,别试图靠文档传播”等价于/xxx“的说法。
到这一步,命名空间的来源、四级作用域、命令解析优先级就完整跑通了;接下来真要发布到市场,记得在清单里填对 name、每次发版 bump 版本号,技能名冲突的烦恼就能从源头掐掉。
常见问题(FAQ)
Q1:插件名和技能名都改了,但用户命令没变?
插件按 plugin.json 里的 version 缓存,版本号没变就不会重新加载;升一个 patch 版本再分发即可。
Q2:能用 frontmatter name 去掉插件前缀吗?
v2.1.216 之前可以,现在已经恢复前缀;非交互场景下同名会保留 /插件:技能 形式。
Q3:两个插件取了一样的 name 会怎样?
后注册的插件会覆盖前者的命令前缀;市场侧一般会做唯一性校验,但本地测试时仍可能撞名。