插件是一个可安装、可启用并可组合多种扩展能力的目录。它可以同时提供 Commands、Agents、Skills、Hooks、Output Styles、MCP Server、LSP Server 和受限的默认设置。
范围说明:本文描述当前源码中的加载器、Marketplace 和安装状态模型。具体可用插件取决于构建内容、Marketplace、组织策略和当前项目设置。
| 概念 | 作用 | 是否独立安装单元 |
|---|---|---|
| Plugin | 扩展能力的容器和版本边界 | 是 |
| Skill | 带说明、资源和按需加载规则的能力包 | 可以单独存在,也可由 Plugin 提供 |
| Marketplace | 描述 Plugin 名称、版本、来源和元数据的目录 | 不是运行时能力本身 |
Plugin 与 Skill 不是同义词。一个 Plugin 可以没有 Skill,也可以包含多个 Skills;同一个 Plugin 还可以同时注册 Hook、Agent 和 MCP Server。Skills 的独立加载流程见 Skills 机制。
加载器支持约定目录,也支持在 Manifest 中追加自定义相对路径:
example-plugin/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ └── inspect.md
├── agents/
│ └── reviewer.md
├── skills/
│ └── refactor/
│ └── SKILL.md
├── hooks/
│ └── hooks.json
├── output-styles/
│ └── concise.md
├── .mcp.json
└── .lsp.json
这里是结构示例,不表示仓库必须存在这些文件。
Manifest 的规范位置是 .claude-plugin/plugin.json。Manifest 缺失时,loadPluginManifest() 会创建最小内存 Manifest,因此约定目录仍可被发现。Manifest 中的组件路径必须是以 ./ 开头、位于 Plugin 根目录内的相对路径;加载时还会校验真实路径,避免通过符号链接或 .. 越界。
一个精简示例:
{
"name": "example-plugin",
"version": "1.2.0",
"description": "Repository review helpers",
"commands": ["./extra/inspect.md"],
"agents": ["./extra/reviewer.md"],
"skills": ["./extra/refactor"],
"hooks": "./config/hooks.json",
"mcpServers": {
"helper": {
"type": "stdio",
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/server.mjs"]
}
}
}示例名称和文件路径仅用于说明 Schema。
PluginManifestSchema 支持名称、版本、描述、作者、主页、仓库、许可证、关键词和依赖。依赖项可写完整的 plugin@marketplace,也可写裸名称;裸名称相对当前 Plugin 所属 Marketplace 解析。
| 字段 | 支持形式 |
|---|---|
commands |
单个 Markdown/Skill 路径、路径数组,或带内联内容/元数据的名称映射 |
agents |
Markdown 路径或数组 |
skills |
Skill 目录路径或数组 |
outputStyles |
文件或目录路径,也可为数组 |
hooks |
JSON 路径、内联 Hook 配置或混合数组 |
mcpServers |
JSON 路径、MCPB 路径/URL、内联映射或混合数组 |
lspServers |
JSON 路径、内联映射或混合数组 |
channels |
把 Plugin MCP Server 声明为消息 Channel,并定义配置项 |
settings |
合并到设置级联的默认值;加载时只保留允许字段 |
userConfig |
启用时向用户收集的 Plugin 配置 |
Manifest 顶层未知字段会被剥离,以兼容未来或供应方扩展;严格的嵌套配置仍会因字段或类型错误而校验失败。开发者校验入口会提供更明确的未知字段反馈。
userConfig 支持字符串、数字、布尔、目录和文件等类型。字段名必须是可用作标识符的格式。非敏感值写入设置中的 pluginConfigs[pluginId].options,敏感值写入系统安全存储或凭据文件。
加载组件时可使用:
${CLAUDE_PLUGIN_ROOT}
${CLAUDE_PLUGIN_DATA}
${user_config.KEY}
Hook 还会收到相应的 CLAUDE_PLUGIN_OPTION_<KEY> 环境变量。敏感配置不会注入 Skill/Agent 文本,但可用于需要运行时秘密的 MCP、LSP 和 Hook 配置。
pluginLoader.ts 明确区分两类发现来源:
- 设置中以
plugin@marketplace标识的 Marketplace Plugin。 --plugin-dir或 SDK Plugin 选项注入的当前会话目录。
此外,构建内可注册 name@builtin Plugin。Builtin 可以包含 Skills、Hooks 和 MCP Server,并按运行环境决定是否可见以及默认是否启用。
整体生命周期为:
flowchart TD
A["设置、Builtin 或 --plugin-dir"] --> B["解析 Plugin ID 和作用域"]
B --> C["解析 Marketplace 与安装记录"]
C --> D["定位版本化缓存或会话目录"]
D --> E["读取并校验 Manifest"]
E --> F["校验依赖、策略和启用状态"]
F --> G["解析约定目录与自定义路径"]
G --> H["加载 Commands / Agents / Skills / Hooks"]
G --> I["提取 MCP / LSP / Settings"]
H --> J["加入当前应用能力集合"]
I --> J
加载器收集错误而不是在第一个坏 Plugin 处终止全部加载。最终结果分为 enabled、disabled 和 errors,各组件加载器只消费已启用项。
Marketplace Plugin 的稳定标识为:
<plugin-name>@<marketplace-name>
Commands、Agents 和 Output Styles 会使用 Plugin 名称建立命名空间,例如:
example-plugin:inspect
example-plugin:reviewer
嵌套目录会继续追加命名空间片段。Plugin MCP Server 使用 plugin:<plugin>:<server> 作为 Server 名,再由 MCP 层生成 mcp__...__... Tool 名称。
加载器会检测重复 Plugin 名、同一 Plugin 内重复文件路径以及 MCP 连接签名重复。对于 MCP,手动配置优先于 Plugin,多个 Plugin 之间按加载顺序保留第一个。
- 直接 URL 指向
marketplace.json。 - GitHub
owner/repo,可指定 Ref、Manifest 路径和 Sparse Checkout 路径。 - 任意 Git URL。
- npm 包。
- 本地文件或本地目录。
- 设置内联的 Marketplace 定义。
hostPattern 和 pathPattern 用于组织策略匹配,不是普通 Marketplace 内容来源。
Marketplace 状态分成三层:
| 层 | 代表什么 | 典型位置 |
|---|---|---|
| 声明意图 | 用户或项目希望使用哪些 Marketplace | 设置中的 extraKnownMarketplaces |
| 本地缓存 | Marketplace Manifest 或克隆仓库 | Plugin 目录下的 marketplaces/ |
| 已知状态 | 来源、缓存位置和更新时间 | known_marketplaces.json |
marketplaceManager.ts 会协调这三层,并支持离线读取缓存。Plugin 安装内容放入版本化 Cache,安装元数据则独立记录在 installed_plugins.json。
组织策略可以:
- 明确阻止某些 Marketplace。
- 只允许严格已知的来源。
- 按 Host 或本地路径模式放行来源。
- 阻止特定 Plugin 的安装或启用。
受保护的官方 Marketplace 名称还会校验来源组织和 Unicode 同形字符,避免第三方来源冒用保留名称。
可持久化作用域定义在 PluginScopeSchema:
| 作用域 | 可见范围 | 设置来源 |
|---|---|---|
user |
所有项目 | 用户设置 |
project |
当前项目,适合共享 | 项目设置 |
local |
当前项目,仅本地覆盖 | 本地设置 |
managed |
组织管理 | 托管设置;普通安装命令不写入 |
--plugin-dir 属于额外的 flag 会话作用域,不写入安装记录。
installed_plugins.json 当前使用 V2 结构:一个 Plugin ID 可以同时拥有多条不同 Scope、项目路径和版本的安装记录。project 与 local 记录只有在 projectPath 匹配当前原始工作目录时才生效;user 和 managed 全局生效。
安装状态与启用状态有意分离:
installed_plugins.json记录版本、安装路径、时间和 Git SHA。- 各级设置中的
enabledPlugins表示当前项目是否启用。
因此,“已经下载到磁盘”和“当前会话已加载”是两个不同结论。
pluginOperations.ts 提供主要操作:
- 解析 Plugin ID 和 Marketplace。
- 检查 Marketplace 与组织策略。
- 获取来源并计算版本。
- 写入版本化 Cache。
- 记录对应 Scope 的安装条目。
- 更新该 Scope 的
enabledPlugins意图。
启用状态以设置为先。local: false 可以覆盖共享项目中的 true,让用户只在自己环境禁用某个 Plugin。启用时还会检查组织阻止策略和依赖。
更新采用非原地方式:获取新来源,计算新版本并写入新的版本化目录,然后更新磁盘安装记录。当前运行会话保留启动时快照,因此更新结果通常需要重启才能应用;旧版本在没有安装记录引用后才进入清理流程。
卸载只移除指定 Scope 的设置与安装记录。同一 Plugin 仍被其他 Scope 引用时,缓存内容继续保留;最后一个 Scope 被移除后才清理配置和孤立版本。卸载依赖项时会提示仍依赖它的 Plugin,但不会把所有相关项静默级联删除。
| 组件 | 加载与命名 | 运行时入口 |
|---|---|---|
| Commands | Markdown/Skill 转为命令,名称带 Plugin 前缀 | loadPluginCommands.ts |
| Agents | Frontmatter 转为 Agent Definition,名称带 Plugin 前缀 | loadPluginAgents.ts |
| Skills | 作为 Plugin 来源的 Skill/Command 按需加载 | loadPluginCommands.ts |
| Hooks | 转为带 pluginRoot、pluginName、pluginId 的 Hook Matcher |
loadPluginHooks.ts |
| Output Styles | 文件转为带 Plugin 前缀的输出样式 | loadPluginOutputStyles.ts |
| MCP | 提取、变量替换、命名空间化后进入 MCP 配置合并 | mcpPluginIntegration.ts |
| LSP | 校验 Server 配置并加入语言服务管理 | lspPluginIntegration.ts |
| Settings | 过滤到允许键后加入设置级联 | pluginLoader.ts |
Plugin Agent 中部分高控制级 Frontmatter 会被忽略,因为第三方 Plugin 不应取得与用户项目内 Agent 文件相同的控制范围。具体字段以 loadPluginAgents.ts 的过滤逻辑为准。
dependencyResolver.ts 在加载时检查依赖:
- 依赖未启用或未找到时,依赖方会被降级到 Disabled,并记录结构化错误。
- Bare Dependency 相对当前 Marketplace 解析。
- 禁用或卸载被依赖项时,操作层可列出反向依赖。
错误类型覆盖 Manifest、路径、网络、Marketplace、MCP/LSP 配置、依赖、策略和缓存缺失等场景。当前部分调用点仍使用通用错误,类型文件明确记录了后续细化计划。
单个组件失败不必然破坏其他 Plugin。例如一个 MCP 配置错误会作为 Plugin Error 汇总,其他正常 Plugin 仍可继续加载。
Plugin Loader、Commands、Agents、Hooks 和 Output Styles 都有独立缓存。改变设置或执行 Reload 时需要按依赖顺序清理并重新加载,不能只删除一个内存 Map。
当前行为要点:
- 远程设置变化可触发 Plugin Hook 热重载。
- 已禁用或卸载的 Hook 会立即从注册表裁剪。
- 新启用 Plugin 的完整组件通常等待显式 Reload 或下一次启动。
- MCP 管理器在 Plugin Reload 后会移除过期 Server 并连接新增 Server。
- 后台更新只改变磁盘安装记录,不改写当前会话启动快照。
| 现象 | 优先检查 |
|---|---|
| Plugin 找不到 | 是否使用完整 plugin@marketplace、Marketplace 是否已声明和缓存 |
| 已安装但未加载 | enabledPlugins、当前 Scope、项目路径和依赖状态 |
| Manifest 报错 | 字段类型、以 ./ 开头的路径、嵌套严格字段 |
| Command/Agent 重名 | Plugin 命名空间、嵌套目录和重复文件路径 |
| MCP Server 消失 | 配置变量、用户配置、签名去重和 MCP 策略 |
| 修改后未生效 | 当前会话缓存;执行 Plugin Reload 或重启 |
| 只在某项目失效 | project/local 的 projectPath 与设置覆盖关系 |
| Marketplace 被忽略 | Blocklist、Strict Known Marketplace、保留名称来源校验 |
src/utils/plugins/pluginLoader.ts:发现、Manifest、缓存和总加载流程。src/utils/plugins/schemas.ts:Manifest、Marketplace、来源、Scope 与安装记录 Schema。src/utils/plugins/marketplaceManager.ts:Marketplace 声明、缓存和同步。src/utils/plugins/installedPluginsManager.ts:安装状态、V2 迁移和 Scope 过滤。src/services/plugins/pluginOperations.ts:安装、更新、启用、禁用和卸载。src/utils/plugins/dependencyResolver.ts:依赖校验与降级。src/types/plugin.ts:运行时 Plugin 与错误类型。src/utils/plugins/mcpPluginIntegration.ts:Plugin MCP Server 集成。