插件与技能系统
三层扩展机制——插件生命周期管理(发现/加载/隔离)、技能系统(Bundled/目录/MCP)、依赖解析与安全验证
职责概述
解决的问题:用户想给 CC 加新能力,但不想写代码。同时,开发者想分发自己做的 CC 扩展。需要一个完整的插件生态:用户能发现、安装、管理插件;开发者能打包、发布、更新插件;CC 能安全地加载和运行插件。
应用场景:① 用户从市场安装一个"代码审查"插件,CC 就会自动在每次提交前做 review ② 团队在 .claude/commands/ 目录放几个 Markdown 文件,团队成员就能用 /review、/deploy 等自定义命令 ③ 插件开发者打包技能+MCP Server+Hooks 为一个完整的分发单元 ④ 文件变化时自动热加载更新后的插件。
一句话理解:就像 VS Code 的扩展系统——有市场、有安装、有配置、有热加载,用户和开发者各取所需。
架构设计
核心数据流
插件加载流程
技能发现流程
关键类型与接口
插件定义类型
// skills/bundledSkills.ts:15-41
export type BundledSkillDefinition = {
name: string
description: string
allowedTools?: string[]
argumentHint?: string
whenToUse?: string
model?: string
disableModelInvocation?: boolean
userInvocable?: boolean
defaultEnabled?: boolean
isEnabled?: () => boolean
// 钩子、上下文、Agent 定义等
hooks?: HooksSettings
context?: string
agent?: AgentDefinition
// 参考文件(提取到磁盘供模型读取)
files?: Record<string, string>
// 自定义提示生成
getPromptForCommand?: (args: string, toolUseContext: ToolUseContext)
=> Promise<ContentBlockParam[]>
}
内建插件定义
// plugins/builtinPlugins.ts — 内建插件注册表
const BUILTIN_PLUGINS: Map<string, BuiltinPluginDefinition> = new Map()
export function registerBuiltinPlugin(definition: BuiltinPluginDefinition): void {
BUILTIN_PLUGINS.set(definition.name, definition)
}
// 获取启用的内建插件,转换为 LoadedPlugin 对象
export function getBuiltinPlugins(): {
enabled: LoadedPlugin[]
disabled: LoadedPlugin[]
}
技能 Frontmatter 解析
// skills/loadSkillsDir.ts:185-265
export function parseSkillFrontmatterFields(
frontmatter: FrontmatterData,
markdownContent: string,
resolvedName: string,
descriptionFallbackLabel: 'Skill' | 'Custom command' = 'Skill',
): {
name: string
description: string
allowedTools: string[]
argumentHint?: string
whenToUse?: string
model?: string
// ... 更多字段
}
依赖解析结果
// utils/plugins/dependencyResolver.ts:58-67
export type ResolutionResult =
| { type: 'ok'; id: PluginId }
| { type: 'missing'; id: PluginId; requiredBy: PluginId }
| { type: 'cross-marketplace'; id: PluginId; requiredBy: PluginId }
// DFS 遍历传递依赖闭包
export async function resolveDependencyClosure(
rootId: PluginId,
lookup: (id: PluginId) => Promise<DependencyLookupResult | null>,
alreadyEnabled: ReadonlySet<PluginId>,
allowedCrossMarketplaces: string[],
): Promise<ResolutionResult[]>
设计模式与亮点
注册表模式
Bundled 技能通过全局注册表模式管理:BUNDLED_SKILLS Map(bundledSkills.ts:47)存储所有注册的技能定义。每个 bundled/xxx.ts 文件导出一个 register*Skill() 函数,在 initBundledSkills()(bundled/index.ts:24)中统一调用。这种模式允许条件注册(通过 feature() 门控)且保持注册逻辑的局部性。
特性门控加载
多个 bundled 技能受 feature() 门控:KAIROS 门控 dream/loop 技能、REVIEW_ARTIFACT 门控 hunter 技能、AGENT_TRIGGERS 门控 loop 技能、BUILDING_CLAUDE_APPS 门控 claudeApi 技能。运行时通过每个技能自己的 isEnabled 回调做细粒度控制,注册时用 feature() 做编译级排除。
版本化缓存隔离
pluginLoader.ts(L139-188)为每个插件维护版本化的缓存路径:cache/<marketplace>/<name>/<version>/。安装时先缓存到临时目录,校验成功后原子移动到最终路径。支持从 npm、Git(含子目录)、本地路径安装,并有 seed 目录预填充机制。
文件监控与热重载
skillChangeDetector.ts 使用 chokidar 监控所有技能目录(用户/项目/插件/动态),文件变更时触发防抖重载(300ms 窗口合并快速变更),通过 onDynamicSkillsLoaded 回调链通知消费者。这允许开发者在编辑技能文件后即时看到效果。
source: 'bundled'),目录技能按设置层级覆盖(project > user > local),插件技能通过 getPluginCommands() 合并。useMergedTools 最终通过 assembleToolPool() 合并所有来源。
开发者实践指南
创建新的 Bundled 技能
按照以下步骤创建一个新的 Bundled 技能:
- 在
skills/bundled/目录下创建新文件(如mySkill.ts) - 导出一个
registerMySkill()函数,内部调用registerBundledSkill() - 在
skills/bundled/index.ts的initBundledSkills()中导入并调用注册函数
// skills/bundled/mySkill.ts
import { registerBundledSkill } from '../bundledSkills.js'
export function registerMySkill() {
registerBundledSkill({
name: 'my-skill',
description: '我的自定义技能',
allowedTools: ['Read', 'Write'],
userInvocable: true,
getPromptForCommand: async (args) => [
{ type: 'text', text: `执行: ${args}` }
],
})
}
创建目录级技能
在 .claude/skills/ 目录下放置 Markdown 文件,使用 YAML frontmatter:
---
name: my-project-skill
description: 项目专用技能
allowed-tools: Read, Bash(grep *)
argument-hint: "<search-term>"
---
请搜索项目中的 $ARGUMENTS 并分析结果。
创建插件
插件目录结构:
my-plugin/
plugin.json # 清单文件
skills/ # 技能 Markdown 文件
agents/ # Agent 定义
hooks.json # Hook 配置
mcpServers/ # MCP 服务器配置
utils/plugins/validatePlugin.ts 中的 validateManifest() 在发布前校验插件。它会检查清单格式、路径穿越安全、frontmatter 语法等。
架构师决策指南
插件隔离模型
插件之间通过 ID 隔离(name@marketplace),依赖解析通过 DFS 遍历传递闭包。关键决策是"松耦合":每个插件的 MCP 服务器、Hooks、技能都是独立加载的,一个插件的失败不影响其他插件。verifyAndDemote()(dependencyResolver.ts:177)在加载时自动将缺少依赖的插件降级为 disabled,而非报错阻止加载。
刷新策略
插件状态变更通过 /reload-plugins 命令触发,而非自动刷新。useManagePlugins 检测到 needsRefresh 时只显示通知。这是因为自动刷新曾导致缓存一致性 bug——只清除了 loadAllPlugins 的 memoize 缓存,但下游的 commands/agents/hooks 加载器仍返回旧数据。
安全边界
插件系统有三层安全边界:
- 验证层(validatePlugin.ts)—— 检查路径穿越、manifest 格式、组件文件 frontmatter 语法
- 策略层(pluginPolicy.ts)—— 企业策略可限制允许的插件来源
- 运行时层(hooks.json 的 hooks)—— 插件提供的 hooks 在执行时受权限系统约束
Command 类型,但来源标记不同(source: 'bundled' vs 'plugin')。这使得权限系统需要区分处理——bundled 技能默认受信任,插件技能需要额外权限确认。
◈ 可视化处理拓扑图
插件与技能系统是一个多源发现、加载验证、统一注册的三阶段管线。插件从 session/marketplace/built-in 三个来源汇聚,经过 manifest 验证和路径安全检查后,将其 skills、hooks、MCP 配置注册到 Claude Code 运行时。所有技能最终统一为 Command 对象,模型调用时无感知来源差异。
loadSessionOnlyPlugins()
L2928 · 最高优先级
L1888 · 版本化缓存
builtinPlugins.ts:57
预装 seed 缓存
Phase 2 — 安装分发、Manifest 验证与加载
Phase 3 — 组件注册(Hooks / Skills / Commands)
hooks.json → HooksSettings
mergeHooksSettings()
.md → frontmatter → Command
source: bundled|plugin
dedupPluginMcpServers() 去重
内置: verify, simplify...
source: 'bundled'
.claude/skills/*.md
source: 'user'|'project'
加载的 .md 技能
source: 'plugin'
Phase 4 — 工具合并与权限应用
无需额外权限确认
额外权限检查流程
.claude/skills/*.md)、还是插件 skill(marketplace 安装包),最终都转化为同一个 Command 类型对象。source 字段仅用于权限分流——bundled 默认信任,plugin 需额外确认。版本化缓存的三级查找(主缓存 → seed → legacy)确保跨版本兼容和快速加载。安全验证层通过路径穿越检测和 frontmatter 语法校验防止恶意插件。⇉ 核心处理流程详解
插件与技能系统是将外部能力注入 Claude Code 的核心机制。插件(Plugin)是包含 skills、hooks 和 MCP 配置的完整包;技能(Skill/Command)是可被模型调用的 prompt 模板。以下是从插件发现到技能激活的完整处理链路:
loadAllPlugins()(pluginLoader.ts,通过 assemblePluginLoadResult() 在 L3155 编排)从三个来源收集插件:session 插件(--plugin-dir CLI 参数,loadSessionOnlyPlugins() L2928)、marketplace 插件(loadPluginsFromMarketplaces() L1888)、和 built-in 插件(getBuiltinPlugins() builtinPlugins.ts:57)。三个来源的结果通过 mergePluginSources()(pluginLoader.ts:3009)合并,session 优先级最高 — pluginLoader.ts:2928-3211loadPluginFromMarketplaceEntry()(pluginLoader.ts:2191)根据 source 类型分发安装:npm 通过 installFromNpm()(L492)、git 通过 gitClone()(L534)、github 通过 installFromGitHub()(L662)、git-subdir 通过 installFromGitSubdir()(L718)、local 通过 installFromLocal()(L856)。安装完成后通过 copyPluginToVersionedCache()(L365)缓存到版本化目录 — pluginLoader.ts:492-1098loadPluginManifest()(pluginLoader.ts:1147)从 plugin.json 解析清单,验证必要字段(name、version、description)。createPluginFromPath()(pluginLoader.ts:1348)是核心加载函数,它调用 validatePluginPaths()(L1265)检查所有组件文件是否存在,并通过 validatePlugin.ts 执行深度验证(路径穿越检测、frontmatter 语法校验)— pluginLoader.ts:1147-1770loadPluginHooks()(pluginLoader.ts:1224)从插件的 hooks.json 加载 hook 配置,通过 HooksSettingsSchema 验证格式。企业策略通过 pluginPolicy.ts 限制允许的插件来源。安全验证层(validatePlugin.ts)检查路径穿越(确保插件文件不会通过 ../ 访问插件目录外的文件)和 manifest 格式 — pluginLoader.ts:1224-1242, validatePlugin.tsregisterBundledSkill(),bundledSkills.ts:53,在启动时注册内置技能如 verify、simplify 等);目录 skills(loadSkillsFromSkillsDir(),loadSkillsDir.ts:407,从 .claude/skills/ 目录扫描 .md 文件);动态 skills(addSkillDirectories(),loadSkillsDir.ts:923,条件激活基于 paths frontmatter 的技能)。每个 .md 文件通过 frontmatter 解析为 PromptCommand 对象 — loadSkillsDir.ts:270-480parseSkillFrontmatterFields()(loadSkillsDir.ts:185)从 markdown frontmatter 提取技能元数据(name、description、allowed-tools、model 等)。createSkillCommand()(loadSkillsDir.ts:270)将解析结果构建为 Command 对象,包含 getPromptForCommand() 方法——该方法在调用时读取 .md 文件内容作为 prompt 注入,同时支持 Bundled Skill 的 extractBundledSkillFiles()(bundledSkills.ts:131)提取引用文件到磁盘 — loadSkillsDir.ts:185-401paths frontmatter 的技能是条件激活的(parseSkillPaths(),loadSkillsDir.ts:159)。activateConditionalSkillsForPaths()(loadSkillsDir.ts:997)在文件被访问时触发——从文件路径向上遍历到 cwd,发现匹配的 skill 目录后通过 addSkillDirectories() 动态加载。这实现了"打开 Python 文件时自动激活 Python 相关技能"的上下文感知行为 — loadSkillsDir.ts:997-1058useMergedTools()(hooks/useMergedTools.ts)将内置工具、MCP 工具和技能合并为统一的工具池。插件提供的 MCP 服务器通过 dedupPluginMcpServers() 去重。权限系统通过 source 字段区分 bundled(source: 'bundled')和 plugin(source: 'plugin')技能——bundled 技能默认受信任,插件技能需要额外权限确认 — hooks/useMergedTools.ts, builtinPlugins.ts:57-102.claude/skills/ 下的 markdown 文件)、还是插件 skill(从 marketplace 安装的包中的技能),最终都转化为同一个 Command 类型对象。这种统一抽象使得模型无需关心技能来源差异,调用路径完全一致:getPromptForCommand() → prompt 注入 → 模型执行。
★ 设计精华
1. 统一 Command 抽象与来源多态
Bundled skill、目录 skill、插件 skill 三者使用同一个 Command 类型,但 source 字段不同。这使得下游系统(工具合并、权限检查、UI 渲染)可以用统一接口处理所有技能,只在需要区分时检查 source。createSkillCommand()(loadSkillsDir.ts:270)是统一构建入口,无论来源如何,产出的对象都有相同的 getPromptForCommand() 方法签名。
export function createSkillCommand({
name, description, source, filePath, content,
frontmatter: { allowedTools, model, ... }
}): Command {
return {
type: 'prompt',
name,
source, // 'bundled' | 'user' | 'project' | 'plugin'
userFacingName: () => name,
getPromptForCommand: async (args, ctx) => {
// 读取 .md 文件内容,支持参数替换
// Bundled skill 额外提取引用文件
return contentBlocks
}
}
}
Command 接口并设置新的 source 值,无需修改任何下游代码。2. 版本化缓存的多级查找
插件安装后通过 getVersionedCachePath()(pluginLoader.ts:172)缓存到版本化目录(plugins/cache/v1/{pluginId}/{version}/)。加载时通过 resolvePluginPath()(pluginLoader.ts:266)按优先级查找:先查主缓存目录,再查 seed 目录(预装插件),最后查 legacy 非版本化缓存。这种三级查找确保了:(1) 版本升级时新旧缓存共存不冲突;(2) 新安装可以使用预装的 seed 缓存加速;(3) 老版本的非版本化缓存仍可被找到。
export async function resolvePluginPath(pluginId, version) {
// 1. 版本化主缓存
const versionedPath = getVersionedCachePath(pluginId, version)
if (await exists(versionedPath)) return versionedPath
// 2. Seed 缓存(预装)
const seedPath = await probeSeedCache(pluginId, version)
if (seedPath) return seedPath
// 3. Legacy 非版本化缓存
const legacyPath = getLegacyCachePath(pluginId)
if (await exists(legacyPath)) return legacyPath
}
3. 条件技能的文件路径触发
普通技能在启动时一次性加载,而条件技能(带 paths frontmatter 的技能)在运行时按需激活。activateConditionalSkillsForPaths()(loadSkillsDir.ts:997)接收当前被访问的文件路径列表,通过 discoverSkillDirsForPaths()(loadSkillsDir.ts:861)从文件路径向上遍历目录树,发现匹配的 .claude/skills/ 目录。这种"文件访问 → 目录遍历 → 技能激活"的延迟绑定实现了上下文感知:只有当用户实际操作相关文件时,对应的技能才被注入模型上下文。
4. 插件安全的三层防御
插件安全通过验证层、策略层和运行时层三重保障。验证层(validatePlugin.ts)检查路径穿越攻击——插件的组件文件路径(如 hooks.json、skill .md 文件)不能通过 ../ 引用插件目录外的文件。策略层(pluginPolicy.ts)允许企业管理员限制允许的插件来源。运行时层通过 source 字段区分信任等级——bundled 技能(source: 'bundled')跳过权限确认,插件技能(source: 'plugin')必须经过用户批准。
◈ Agent 实践借鉴 — 客服 Agent 插件/扩展设计
场景映射:客服 agent 在插件扩展上的真实问题
不同业务线的客服需要不同的技能,技能需要由业务方自己开发维护。典型痛点包括:
- 不同业务线不同技能:电商客服有"查物流"、"查订单"、"处理退款";金融客服有"查账单"、"处理挂失"、"利率计算";教育客服有"查课表"、"预约试听"、"退课"。硬编码所有技能不现实。
- 业务方想自己开发:物流部门最了解物流查询的细节(快递公司编码、轨迹节点含义),他们想自己开发"查物流"技能,不想每次都提需求给 AI 团队。
- 技能需要声明权限:"查订单"技能需要 CRM 读取权限和 ERP 读取权限;"处理退款"技能需要支付网关写入权限。不同技能的权限差异很大,不能一刀切。
- 上线新技能不能影响老技能:金融客服上线"利率计算"技能,不能导致电商客服的"查物流"技能出问题。技能之间必须隔离。
借鉴 CC + 客服改造
1. 技能清单(Skill Manifest)定义(借鉴 CC plugin.json)
CC 的 plugin.json 声明插件提供的 skills/agents/hooks。客服场景的技能清单声明 name、description、permissions、handler:
// 借鉴 CC 的 plugin.json → 客服场景的技能清单
interface SkillManifest {
name: string // 技能唯一标识,如 "query_logistics"
version: string // 版本号,如 "1.2.0"
description: string // 技能描述,如 "查询物流轨迹和预计到达时间"
businessLine: string // 所属业务线,如 "ecommerce" | "finance" | "education"
triggerPatterns: string[] // 触发模式,如 ["查物流", "物流查询", "快递到哪了"]
permissions: SkillPermission[] // 声明所需权限
inputSchema: { // 参数 schema
type: 'object',
properties: {
orderNo: { type: 'string', description: '订单号' },
expressCompany: { type: 'string', description: '快递公司(可选)' },
},
required: ['orderNo'],
}
handler: SkillHandler // 技能处理函数
}
interface SkillPermission {
system: string // 外部系统,如 "crm" | "erp" | "payment"
access: 'read' | 'write' // 权限级别
scope: string // 权限范围,如 "order:*" | "customer:phone"
}
// 示例:物流查询技能的清单
const queryLogisticsSkill: SkillManifest = {
name: 'query_logistics',
version: '1.2.0',
description: '查询物流轨迹和预计到达时间',
businessLine: 'ecommerce',
triggerPatterns: ['查物流', '物流查询', '快递到哪了', '我的包裹到哪了'],
permissions: [
{ system: 'erp', access: 'read', scope: 'order:logistics' },
{ system: 'logistics', access: 'read', scope: 'tracking:*' },
],
inputSchema: {
type: 'object',
properties: {
orderNo: { type: 'string', description: '订单号' },
},
required: ['orderNo'],
},
handler: async (params) => { /* ... */ },
}
2. 三层加载:内置 → 企业 → 第三方(借鉴 CC 三层扩展)
CC 的 Bundled → 目录 → 插件三层加载,对应客服场景的:内置技能 → 企业自定义技能 → 第三方技能:
// 借鉴 CC 的三层技能加载 → 客服场景的三层技能体系
type SkillSource = 'builtin' | 'enterprise' | 'third_party'
// 统一技能接口——借鉴 CC 的 Command 抽象
interface CustomerServiceSkill {
name: string
source: SkillSource // 仅用于权限分流
manifest: SkillManifest
execute(params: any, context: SkillContext): Promise<SkillResult>
}
// 三层加载——优先级从高到低,同名技能高优先级覆盖低优先级
async function loadAllSkills(): Promise<Map<string, CustomerServiceSkill>> {
const skillRegistry = new Map<string, CustomerServiceSkill>()
// Layer 1: 内置技能(最高信任,代码审计过)
// 查订单、查物流、退款处理等核心技能
const builtinSkills = await loadBuiltinSkills()
for (const skill of builtinSkills) {
skillRegistry.set(skill.name, { ...skill, source: 'builtin' })
}
// Layer 2: 企业自定义技能(业务方开发,内部分发)
// 从企业技能仓库加载,如 "利率计算"、"预约试听"
const enterpriseSkills = await loadEnterpriseSkills()
for (const skill of enterpriseSkills) {
skillRegistry.set(skill.name, { ...skill, source: 'enterprise' })
}
// Layer 3: 第三方技能(最低信任,需额外权限确认)
// 从第三方市场加载,如 "某 SaaS 工单系统集成"
const thirdPartySkills = await loadThirdPartySkills()
for (const skill of thirdPartySkills) {
// 同名不覆盖(内置/企业优先级更高)
if (!skillRegistry.has(skill.name)) {
skillRegistry.set(skill.name, { ...skill, source: 'third_party' })
}
}
return skillRegistry
}
// 按业务线过滤——电商客服只看到电商技能
function filterSkillsByBusinessLine(
registry: Map<string, CustomerServiceSkill>,
businessLine: string,
): CustomerServiceSkill[] {
return Array.from(registry.values()).filter(
skill => skill.manifest.businessLine === businessLine
|| skill.manifest.businessLine === 'common' // 通用技能
)
}
3. 权限声明 + 运行时检查(借鉴 CC source 字段权限分流)
CC 用 source 字段区分 bundled(默认信任)和 plugin(需权限确认)。客服场景按技能来源和权限声明做运行时检查:
// 借鉴 CC 的 source 字段权限分流 → 客服场景的权限检查
async function executeSkillWithPermissionCheck(
skill: CustomerServiceSkill,
params: any,
context: SkillContext,
): Promise<SkillResult> {
// 1. 按 source 信任级别分流
if (skill.source === 'third_party') {
// 第三方技能:必须确认用户授权
const approved = await confirmUserApproval(skill.manifest)
if (!approved) {
return { success: false, error: '用户未授权第三方技能执行' }
}
}
// builtin 和 enterprise 技能:跳过用户确认(已通过内部审计)
// 2. 运行时权限检查——技能声明了哪些权限,必须全部满足
for (const perm of skill.manifest.permissions) {
const hasPermission = await checkSystemPermission(
context.agentId,
perm.system,
perm.access,
perm.scope,
)
if (!hasPermission) {
return {
success: false,
error: `技能 ${skill.name} 需要 ${perm.system} 的 ${perm.access}(${perm.scope}) 权限,当前 agent 无此权限`,
}
}
}
// 3. 执行技能
try {
const result = await skill.execute(params, context)
return result
} catch (error) {
logError(`技能 ${skill.name} 执行失败`, error)
return { success: false, error: `技能执行异常: ${error.message}` }
}
}
4. 技能隔离:单个技能加载失败不影响其他(借鉴 CC 错误隔离)
// 借鉴 CC 的 errors 数组收集模式 → 技能加载隔离
interface SkillLoadResult {
loaded: CustomerServiceSkill[]
failed: SkillLoadError[]
}
interface SkillLoadError {
skillName: string
source: SkillSource
error: string
}
async function loadSkillsSafely(
manifests: SkillManifest[],
): Promise<SkillLoadResult> {
const loaded: CustomerServiceSkill[] = []
const failed: SkillLoadError[] = []
// 并行加载,每个技能独立 try-catch
await Promise.allSettled(
manifests.map(async (manifest) => {
try {
// 验证清单格式
validateManifest(manifest)
// 验证权限声明完整性
validatePermissions(manifest.permissions)
// 加载并注册技能
const skill = await compileSkill(manifest)
// 健康检查:确保依赖的外部系统可达
await healthCheckSkill(skill)
loaded.push(skill)
} catch (error) {
// 单个技能失败不影响其他技能
failed.push({
skillName: manifest.name,
source: manifest.businessLine as SkillSource,
error: error.message,
})
logWarning(`技能 ${manifest.name} 加载失败: ${error.message}`)
// 不 throw!继续加载其他技能
}
})
)
logInfo(`技能加载完成: ${loaded.length} 成功, ${failed.length} 失败`)
return { loaded, failed }
}
// 借鉴 CC 的 verifyAndDemote:依赖缺失时降级而非报错
function handleSkillDependencyFailure(
skill: CustomerServiceSkill,
missingSystems: string[],
): void {
logWarning(`技能 ${skill.name} 依赖的系统 ${missingSystems.join(', ')} 不可达,技能已降级为只读模式`)
// 不删除技能,而是降级为只读(可以查看配置但不能执行)
skill.execute = async () => ({
success: false,
error: `依赖系统不可用: ${missingSystems.join(', ')},请联系运维`,
})
}
落地清单
1. 统一技能接口——所有技能(内置/企业/第三方)实现同一个
CustomerServiceSkill 接口,agent 调用时不感知技能来源。2. 权限声明模型——技能必须在清单中声明所需权限(哪个系统、读/写、什么范围),运行时逐一检查。
3. 加载失败隔离——单个技能加载失败不阻塞其他技能,用
failed 数组收集错误。4. 三层优先级——同名技能高优先级覆盖低优先级(内置 > 企业 > 第三方),防止第三方技能覆盖核心功能。
1. 市场发现/版本化缓存——客服场景用内部分发(企业技能仓库),不需要 CC 的官方 Marketplace 和版本化缓存机制。
2. 技能文件监控热更新——客服场景技能变更走发布流程(测试 → 灰度 → 全量),不需要 CC 的 chokidar 文件监控实时热重载。
3. 条件技能激活——CC 根据文件路径匹配激活技能,客服场景按业务线过滤即可,不需要路径匹配。
4. 依赖解析 DFS 闭包——客服技能之间的依赖关系简单,不需要 CC 那么复杂的传递依赖解析。
代码索引
| 文件 | 行数 | 说明 |
|---|---|---|
hooks/useManagePlugins.ts | ~305 | React Hook:插件初始化加载、错误收集、通知 |
hooks/useMergedTools.ts | ~45 | React Hook:合并内置工具 + MCP 工具,应用过滤和去重 |
plugins/builtinPlugins.ts | ~159 | 内建插件注册表:注册、查询、启用/禁用、技能提取 |
plugins/bundled/index.ts | ~80 | Bundled 技能初始化入口:条件注册各技能 |
skills/bundledSkills.ts | ~221 | 技能注册表:registerBundledSkill、文件提取、路径校验 |
skills/loadSkillsDir.ts | ~1087 | 目录技能加载:frontmatter 解析、动态技能、条件激活 |
skills/mcpSkillBuilders.ts | ~45 | MCP 技能构建器注册表:桥接 MCP 资源到技能系统 |
utils/plugins/pluginLoader.ts | ~3303 | 插件加载核心:发现、缓存、安装、清单解析、合并 |
utils/plugins/validatePlugin.ts | ~904 | 插件验证:清单校验、路径穿越检测、组件文件验证 |
utils/plugins/dependencyResolver.ts | ~306 | 依赖解析:DFS 闭包遍历、跨市场检查、反向依赖 |
utils/plugins/refresh.ts | ~216 | 插件刷新:/reload-plugins 的实现,重载所有组件 |
utils/plugins/loadPluginHooks.ts | ~小型 | 加载插件的 hooks.json 配置 |
utils/plugins/installedPluginsManager.ts | ~中型 | 已安装插件管理:增删查改 |
utils/plugins/pluginPolicy.ts | ~小型 | 插件策略:企业允许/拒绝列表 |
utils/plugins/pluginVersioning.ts | ~中型 | 版本管理:版本比较、升级检查 |
utils/plugins/orphanedPluginFilter.ts | ~小型 | 孤儿插件过滤:清理无对应配置的缓存 |
utils/plugins/officialMarketplace.ts | ~中型 | 官方市场:市场发现、条目查询 |
utils/plugins/schemas.ts | ~中型 | 插件 Schema 定义:manifest、marketplace、PluginId |
utils/plugins/headlessPluginInstall.ts | ~中型 | 无头模式:非交互式插件安装 |
utils/plugins/performStartupChecks.tsx | ~中型 | 启动检查:版本兼容、弃用通知 |
utils/plugins/fetchTelemetry.ts | ~小型 | 插件遥测:安装/使用统计 |
utils/plugins/lspPluginIntegration.ts | ~中型 | LSP 集成:插件提供的 LSP 服务器 |
utils/skills/skillChangeDetector.ts | ~312 | 技能变更检测:chokidar 文件监控、防抖重载 |