目录
  1. 1. 一、插件 vs 技能 — 概念区分
  2. 2. 二、技能系统核心 (skills/loadSkillsDir.ts, 1087行)
    1. 2.1. 2.1 技能是什么
    2. 2.2. 2.2 技能加载来源
    3. 2.3. 2.3 Frontmatter 完整字段定义
    4. 2.4. 2.4 Skill 到 Command 对象的转换
    5. 2.5. 2.5 变量替换系统
    6. 2.6. 2.6 去重机制
    7. 2.7. 2.7 路径匹配 (条件化技能)
  3. 3. 三、内置技能 (skills/bundled/, 17个文件)
    1. 3.1. 3.1 注册流程
    2. 3.2. 3.2 BundledSkillDefinition 结构
    3. 3.3. 3.3 附带文件提取机制
    4. 3.4. 3.4 典型内置技能实例: /stuck
  4. 4. 四、插件系统 (plugins/builtinPlugins.ts, 160行)
    1. 4.1. 4.1 插件注册
    2. 4.2. 4.2 插件启用/禁用逻辑
    3. 4.3. 4.3 Plugin vs Bundled Skill 的区别
  5. 5. 五、MCP技能桥接 (skills/mcpSkillBuilders.ts)
  6. 6. 六、完整的技能执行流
  7. 7. 七、插件 vs 技能 vs MCP — 架构边界
    1. 7.1. 7.1 三种扩展机制对比
    2. 7.2. 7.2 三种机制的分工边界
    3. 7.3. 7.3 技能的 RAG 本质与限制
  8. 8. 八、动态加载沙箱与安全隔离
    1. 8.1. 8.1 为什么需要沙箱?
    2. 8.2. 8.2 CC 的分层安全策略
    3. 8.3. 8.3 文件提取的安全写入
    4. 8.4. 8.4 MCP 技能的安全限制
  9. 9. 九、版本兼容与依赖管理
    1. 9.1. 9.1 语义化版本的强制执行
    2. 9.2. 9.2 CC 版本兼容性检测
    3. 9.3. 9.3 加载失败时的降级策略
  10. 10. 十、插件发现与市场模型
    1. 10.1. 10.1 三层插件来源
    2. 10.2. 10.2 插件发现流程
    3. 10.3. 10.3 与 VSCode Extension 系统的对比
  11. 11. 十一、插件生命周期与 Hook 系统集成
    1. 11.1. 11.1 完整的生命周期
    2. 11.2. 11.2 Hook 的注入时机
    3. 11.3. 11.3 内置技能完整清单
  12. 12. 涉及源文件
【Claude Code源码剖析】13-插件与技能系统

⚠️ 学习声明:本文档基于 Claude Code 2.1.88 源码分析整理,仅供个人学习研究使用,不做任何商业用途。

本质: 一套基于 Markdown frontmatter 的声明式扩展系统


一、插件 vs 技能 — 概念区分

这两个系统容易混淆,先厘清:

维度 Plugin (插件) Skill (技能)
定义 一个可启用/禁用的功能包 一个特定任务的prompt模板
粒度 包含多个skills + hooks + MCP servers 单个markdown文件
管理 /plugin UI 开关 /skills 目录放文件即生效
来源 {name}@builtin 或 marketplace 项目/.claude/skills/ 或用户/全局
源码 src/plugins/builtinPlugins.ts src/skills/loadSkillsDir.ts

简单说:Plugin是容器,Skill是内容。 一个Plugin可以包含多个Skills。


二、技能系统核心 (skills/loadSkillsDir.ts, 1087行)

2.1 技能是什么

一个Skill就是一个 带frontmatter的Markdown文件,放在约定的目录中:

<!-- 示例: .claude/skills/fix-tests.md -->
---
description: 自动修复失败的测试
allowed-tools:
- Bash
- Edit
- Read
when_to_use: 当用户要求修复测试或CI失败时
argument-hint: "[test file or pattern]"
---

当用户要求修复测试时,按以下步骤操作:
1. 运行 `npm test` 获取失败信息
2. 分析每个失败的测试
3. 修复代码或测试
4. 重新运行确认通过

2.2 技能加载来源

// loadSkillsDir.ts 中的 getSkillsPath():
function getSkillsPath(source: SettingSource | 'plugin', dir: 'skills' | 'commands'): string {
switch (source) {
case 'policySettings': return join(getManagedFilePath(), '.claude', dir) // 管理员级
case 'userSettings': return join(getClaudeConfigHomeDir(), dir) // ~/.claude/skills/
case 'projectSettings': return '.claude/skills' // 项目级
case 'plugin': return 'plugin' // 插件提供
}
}

加载优先级(高→低):

1. 管理员技能  /etc/claude-code/.claude/skills/   (IT管理员部署)
2. 用户技能 ~/.claude/skills/ (个人全局)
3. 项目技能 .claude/skills/ (项目级, 可git提交)
4. 内置技能 编译到binary中 (Anthropic提供)
5. MCP技能 MCP服务器注册 (远程工具扩展)

2.3 Frontmatter 完整字段定义

parseSkillFrontmatterFields() 提取的完整字段列表:

// loadSkillsDir.ts 原文返回类型:
{
displayName: string | undefined // 显示名 (name frontmatter)
description: string // 描述
hasUserSpecifiedDescription: boolean // 是否有用户指定的描述
allowedTools: string[] // 允许使用的工具列表
argumentHint: string | undefined // 参数提示
argumentNames: string[] // 命名参数列表
whenToUse: string | undefined // 模型自动调用条件
version: string | undefined // 版本号
model: string | undefined // 指定模型 (或 'inherit')
disableModelInvocation: boolean // 禁止模型自动调用
userInvocable: boolean // 用户是否可通过/命令调用
hooks: HooksSettings | undefined // 生命周期钩子
executionContext: 'fork' | undefined // 执行上下文 (fork=子agent)
agent: string | undefined // 指定agent类型
effort: EffortValue | undefined // 努力程度
shell: FrontmatterShell | undefined // shell配置
}

2.4 Skill 到 Command 对象的转换

createSkillCommand() 是关键的转换函数,它将解析出的frontmatter + markdown body转换为运行时 Command 对象:

// loadSkillsDir.ts 原文 (简化):
export function createSkillCommand({ ... }): Command {
return {
type: 'prompt',
name: skillName,
description,
allowedTools,
whenToUse,
// ...

async getPromptForCommand(args, toolUseContext) {
let finalContent = baseDir
? `Base directory for this skill: ${baseDir}\n\n${markdownContent}`
: markdownContent

// 1. 替换 $ARGUMENTS
finalContent = substituteArguments(finalContent, args, true, argumentNames)

// 2. 替换 ${CLAUDE_SKILL_DIR}
if (baseDir) {
const skillDir = process.platform === 'win32'
? baseDir.replace(/\\/g, '/') : baseDir
finalContent = finalContent.replace(/\$\{CLAUDE_SKILL_DIR\}/g, skillDir)
}

// 3. 替换 ${CLAUDE_SESSION_ID}
finalContent = finalContent.replace(
/\$\{CLAUDE_SESSION_ID\}/g, getSessionId()
)

// 4. 执行内联shell命令 (!`...`)
// 安全: MCP技能(远程不可信)不执行shell命令
if (loadedFrom !== 'mcp') {
finalContent = await executeShellCommandsInPrompt(finalContent, ...)
}

return [{ type: 'text', text: finalContent }]
},
}
}

2.5 变量替换系统

技能markdown中支持的变量:

变量 来源 用途
$ARGUMENTS 用户输入 /skill-name 这里是参数
$1, $2 命名参数 frontmatter中的 arguments 字段
${CLAUDE_SKILL_DIR} 技能文件目录 引用技能附带的脚本/配置文件
${CLAUDE_SESSION_ID} 当前session 日志/跟踪
!`command` shell执行 在prompt中内联执行shell命令(仅本地技能)

2.6 去重机制

// loadSkillsDir.ts 原文:
async function getFileIdentity(filePath: string): Promise<string | null> {
try {
return await realpath(filePath) // 解析符号链接到真实路径
} catch { return null }
}

为什么需要去重: 用户可能有 ~/.claude/skills/test.md./claude/skills/test.md 指向同一个文件(通过符号链接)。realpath() 解析到规范路径后去重。

2.7 路径匹配 (条件化技能)

// loadSkillsDir.ts 原文:
function parseSkillPaths(frontmatter: FrontmatterData): string[] | undefined {
if (!frontmatter.paths) return undefined
const patterns = splitPathInFrontmatter(frontmatter.paths)
.map(pattern => pattern.endsWith('/**') ? pattern.slice(0, -3) : pattern)
.filter(p => p.length > 0)
// ** (match-all) 等同于无路径限制
if (patterns.every(p => p === '**')) return undefined
return patterns
}

这使得技能可以声明”只在特定目录下生效”,例如:

paths: src/api/**

三、内置技能 (skills/bundled/, 17个文件)

3.1 注册流程

// skills/bundled/index.ts 原文:
export function initBundledSkills(): void {
registerUpdateConfigSkill() // 更新配置
registerKeybindingsSkill() // 快捷键帮助
registerVerifySkill() // 验证代码变更 (ant-only)
registerDebugSkill() // 调试
registerLoremIpsumSkill() // 生成测试数据
registerSkillifySkill() // 将操作转化为skill
registerRememberSkill() // 记忆
registerSimplifySkill() // 简化代码
registerBatchSkill() // 批处理
registerStuckSkill() // 诊断卡住的session (ant-only)

// 条件注册 (feature gate):
if (feature('KAIROS') || feature('KAIROS_DREAM'))
registerDreamSkill()
if (feature('AGENT_TRIGGERS'))
registerLoopSkill() // 循环执行
if (feature('BUILDING_CLAUDE_APPS'))
registerClaudeApiSkill() // Claude API使用技能
if (shouldAutoEnableClaudeInChrome())
registerClaudeInChromeSkill() // Chrome集成
}

3.2 BundledSkillDefinition 结构

// bundledSkills.ts 原文:
export type BundledSkillDefinition = {
name: string
description: string
aliases?: string[]
whenToUse?: string // 模型何时自动调用
argumentHint?: string
allowedTools?: string[]
model?: string
disableModelInvocation?: boolean
userInvocable?: boolean
isEnabled?: () => boolean // 运行时可见性检查
hooks?: HooksSettings
context?: 'inline' | 'fork' // fork = 在子agent中执行
agent?: string
files?: Record<string, string> // 附带的引用文件
getPromptForCommand: (args: string, context: ToolUseContext) => Promise<ContentBlockParam[]>
}

3.3 附带文件提取机制

某些内置技能需要引用额外文件(如脚本、配置):

// bundledSkills.ts 原文:
export function registerBundledSkill(definition: BundledSkillDefinition): void {
const { files } = definition

if (files && Object.keys(files).length > 0) {
skillRoot = getBundledSkillExtractDir(definition.name)
// 延迟提取: 首次调用时将文件写入磁盘
let extractionPromise: Promise<string | null> | undefined
const inner = definition.getPromptForCommand
getPromptForCommand = async (args, ctx) => {
extractionPromise ??= extractBundledSkillFiles(definition.name, files)
const extractedDir = await extractionPromise
const blocks = await inner(args, ctx)
if (extractedDir === null) return blocks
return prependBaseDir(blocks, extractedDir) // 添加 "Base directory: ..." 前缀
}
}
}

安全写入:

// bundledSkills.ts 原文:
// O_NOFOLLOW|O_EXCL 防止符号链接攻击
const SAFE_WRITE_FLAGS = process.platform === 'win32'
? 'wx'
: fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | O_NOFOLLOW

async function safeWriteFile(p: string, content: string): Promise<void> {
const fh = await open(p, SAFE_WRITE_FLAGS, 0o600) // 600 = owner-only
try { await fh.writeFile(content, 'utf8') }
finally { await fh.close() }
}

3.4 典型内置技能实例: /stuck

// skills/bundled/stuck.ts (ant-only, 完整prompt):
// 功能: 诊断本机上卡死/缓慢的Claude Code会话
//
// 调查步骤:
// 1. 列出所有Claude Code进程 (ps -axo pid=,pcpu=,rss=,...)
// 2. 检查可疑进程:
// - CPU ≥90% 持续 → 可能死循环
// - 状态 D (不可中断睡眠) → I/O挂起
// - 状态 T (停止) → 用户误按 Ctrl+Z
// - 状态 Z (僵尸) → 父进程未回收
// - RSS ≥4GB → 内存泄漏
// - 子进程卡死 (如 git)
// 3. 如发现问题 → 通过Slack MCP发送到 #claude-code-feedback
//
// 安全: 只诊断,不kill任何进程

四、插件系统 (plugins/builtinPlugins.ts, 160行)

4.1 插件注册

// builtinPlugins.ts 原文:
const BUILTIN_PLUGINS: Map<string, BuiltinPluginDefinition> = new Map()
export const BUILTIN_MARKETPLACE_NAME = 'builtin'

export function registerBuiltinPlugin(definition: BuiltinPluginDefinition): void {
BUILTIN_PLUGINS.set(definition.name, definition)
}

4.2 插件启用/禁用逻辑

// builtinPlugins.ts 原文:
export function getBuiltinPlugins(): { enabled: LoadedPlugin[]; disabled: LoadedPlugin[] } {
const settings = getSettings_DEPRECATED()
const enabled: LoadedPlugin[] = []
const disabled: LoadedPlugin[] = []

for (const [name, definition] of BUILTIN_PLUGINS) {
// 可用性检查 (某些插件可能依赖特定环境)
if (definition.isAvailable && !definition.isAvailable()) continue

const pluginId = `${name}@${BUILTIN_MARKETPLACE_NAME}`

// 优先级: 用户设置 > 插件默认值 > true
const userSetting = settings?.enabledPlugins?.[pluginId]
const isEnabled = userSetting !== undefined
? userSetting === true
: (definition.defaultEnabled ?? true)

const plugin: LoadedPlugin = {
name,
manifest: { name, description: definition.description, version: definition.version },
path: BUILTIN_MARKETPLACE_NAME, // 哨兵值,无文件路径
source: pluginId,
repository: pluginId,
enabled: isEnabled,
isBuiltin: true,
hooksConfig: definition.hooks,
mcpServers: definition.mcpServers,
}

if (isEnabled) enabled.push(plugin)
else disabled.push(plugin)
}
return { enabled, disabled }
}

4.3 Plugin vs Bundled Skill 的区别

// builtinPlugins.ts 原文注释:
/**
* Built-in plugins differ from bundled skills (src/skills/bundled/) in that:
* - They appear in the /plugin UI under a "Built-in" section
* - Users can enable/disable them (persisted to user settings)
* - They can provide multiple components (skills, hooks, MCP servers)
*/

五、MCP技能桥接 (skills/mcpSkillBuilders.ts)

MCP服务器可以通过 prompts/list 端点暴露prompt模板,Claude Code 将它们转换为 Skill:

MCP服务器声明:
prompts/list → [{ name: "analyze-code", description: "...", arguments: [...] }]

Claude Code转换:
MCP prompt → createSkillCommand({
skillName: "mcp-server-name/analyze-code",
loadedFrom: 'mcp',
source: 'mcp',
// 注意: MCP技能不执行内联shell命令 (安全)
})

六、完整的技能执行流

用户输入: /fix-tests src/api/
或模型判断 whenToUse 匹配自动调用


┌─────────────────────────┐
│ 查找 Command by name │
│ (skills + bundled + │
│ managed + mcp) │
└──────────┬──────────────┘

┌──────────┴──────────────┐
│ getPromptForCommand() │
│ 1. substituteArguments │ ← $ARGUMENTS = "src/api/"
│ 2. ${CLAUDE_SKILL_DIR} │ ← 替换为技能文件目录
│ 3. ${CLAUDE_SESSION_ID} │ ← 替换为session ID
│ 4. executeShellCommands │ ← 执行 !`...` 内联命令
│ 5. return ContentBlock │
└──────────┬──────────────┘

┌──────────┴──────────────┐
│ context == 'fork'? │
│ yes → runForkedAgent() │ ← 在子agent中执行
│ no → 直接注入主循环 │ ← 作为user message
└─────────────────────────┘

七、插件 vs 技能 vs MCP — 架构边界

7.1 三种扩展机制对比

维度 插件 (Plugin) 技能 (Skills) MCP
扩展内容 新工具 + 新命令 + Hooks + MCP 新知识/指令(Prompt 模板) 新工具(外部服务进程)
实现方式 TypeScript 函数(编译进 binary) Markdown 文件(运行时加载) 外部进程(任意语言)
加载时机 CC 启动时(同步) CC 启动时(异步扫描目录) Session 开始时(spawn 子进程)
复杂度 高(需写代码 + 编译) 低(纯文本,即写即用) 中(需部署服务进程)
隔离性 进程内(共享事件循环) 进程内(prompt 注入) 进程外(stdio/socket 隔离)
用户可控 可禁用(/plugin UI) 删除文件即移除 断开连接即移除
适用场景 深度集成(自定义工具逻辑) 项目知识/规范/工作流 第三方 API 集成
安全风险 高(可执行任意 TypeScript) 低(只影响 Prompt) 中(进程级隔离但有工具权限)

7.2 三种机制的分工边界

                 ┌──────────────────────────────────┐
│ Plugin(容器) │
│ ┌────────────┐ ┌────────────┐ │
│ │ Skills │ │ MCP │ │
│ │ (prompt) │ │ Servers │ │
│ └────────────┘ └────────────┘ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Hooks │ │ Custom │ │
│ │ (lifecyle)│ │ Tools │ │
│ └────────────┘ └────────────┘ │
└──────────────────────────────────┘

选择策略:
需要新知识/规范 → Skill(Markdown,最简单)
需要新外部工具 → MCP Server(进程隔离,最安全)
需要深度集成/生命周期控制 → Plugin(最强大,但需要编译)
三者可组合:Plugin 内包含 Skills + MCP + Hooks

7.3 技能的 RAG 本质与限制

技能(Skills)本质上是一种简化的 RAG(Retrieval-Augmented Generation),但其设计哲学与传统 RAG 有本质区别:

维度 传统 RAG Skill 系统
检索时机 查询时(动态) 启动时(静态预加载)
检索方式 Embedding + 向量相似度 目录扫描 + Frontmatter 匹配
选择性 Top-K 相关文档 whenToUse 字段条件匹配
延迟 100-500ms(嵌入计算) 0ms(已预加载到 System Prompt)
精确度 召回率-精确率权衡 精确匹配(用户主动调用或 whenToUse 触发)

Skill 的预加载模式避免了 RAG 的检索不确定性,但代价是增加了 System Prompt 长度。这是一个用 Token 换确定性的经典权衡。


八、动态加载沙箱与安全隔离

8.1 为什么需要沙箱?

Plugin 系统面临的核心安全挑战:第三方 TypeScript 代码可以直接访问文件系统、网络、环境变量

Plugin 的能力(进程内,无沙箱):
✅ 读取用户文件(可能泄露 ~/.ssh/id_rsa)
✅ 发起网络请求(可能外传数据)
✅ 执行 shell 命令(通过 child_process)
✅ 修改环境变量(影响其他 Plugin)
❌ 不能:操作系统级破坏(受限于 Node.js 权限)

8.2 CC 的分层安全策略

Layer 1 — 来源可信度分级
Built-in Plugin(Anthropic 签名) → 完全信任
Marketplace Plugin(社区审核) → 有限信任
Local Plugin(用户手写) → 完全信任(用户自己写的)
MCP Server(外部进程) → 进程隔离 + 工具权限控制

Layer 2 — 权限最小化
Skill: allowedTools 字段限制可调用的工具
Plugin: onCreate/onDestroy hook 中声明的资源访问
MCP: toolPermissions 白名单控制

Layer 3 — 运行时监控
FileRead hook 可注入文件访问审计
Bash hook 可拦截危险命令
Stop hook 记录 Plugin 的副作用

8.3 文件提取的安全写入

内置技能需要将附属文件写入磁盘时,使用 O_NOFOLLOW | O_EXCL 标志防止符号链接攻击:

// 防止恶意技能通过符号链接写入敏感文件
// 例:攻击者创建 link: extract_dir/config → /etc/passwd
const SAFE_WRITE_FLAGS =
O_WRONLY | // 只写
O_CREAT | // 创建新文件
O_EXCL | // 文件已存在则失败(防覆盖)
O_NOFOLLOW; // 不跟随符号链接(防跳转)

// 还有文件权限 0o600:owner-only read+write
async function safeWriteFile(p: string, content: string): Promise<void> {
const fh = await open(p, SAFE_WRITE_FLAGS, 0o600);
try { await fh.writeFile(content, 'utf8'); }
finally { await fh.close(); }
}

8.4 MCP 技能的安全限制

从 MCP 服务器加载的 Skill 不执行内联 shell 命令

// loadSkillsDir.ts — 区分可信来源
if (loadedFrom !== 'mcp') {
// 本地技能:可以执行 !`command` 内联 shell
finalContent = await executeShellCommandsInPrompt(finalContent, ...)
}
// MCP 技能:跳过 shell 执行,防止远程服务器注入恶意命令

这个设计防止了以下攻击场景:

攻击场景:
恶意 MCP 服务器声明了 prompt template:
"删除所有临时文件: !`rm -rf /tmp/*`"

如果执行这个内联命令 → 任意代码执行
CC 通过 loadedFrom === 'mcp' 检查拦截

九、版本兼容与依赖管理

9.1 语义化版本的强制执行

CC 的 Plugin 系统要求版本号(通过 manifest version 字段),但不强制使用 semver——这让社区 Plugin 的兼容性管理变得困难:

// Plugin Manifest 中的版本字段
type PluginManifest = {
name: string;
version: string; // "1.2.3" — 建议 semver,但不强制
claudeCodeVersion?: string; // ">=2.1.0" — 依赖的 CC 版本
dependencies?: Record<string, string>; // 其他 Plugin 依赖
};

9.2 CC 版本兼容性检测

// 检测 Plugin 是否兼容当前 CC 版本
function checkPluginCompatibility(
manifest: PluginManifest,
ccVersion: string
): CompatibilityResult {
if (!manifest.claudeCodeVersion) {
return { compatible: true, warning: 'No CC version requirement specified' };
}

// 使用 semver 范围匹配
if (!semver.satisfies(ccVersion, manifest.claudeCodeVersion)) {
return {
compatible: false,
reason: `Plugin requires CC ${manifest.claudeCodeVersion}, current: ${ccVersion}`
};
}

return { compatible: true };
}

9.3 加载失败时的降级策略

Plugin 加载失败的分级处理:

致命失败(阻止启动):
└─ 核心 Plugin(如 tool permissions plugin)失败

降级运行(警告,继续):
├─ 社区 Plugin 不兼容 → 跳过该 Plugin
├─ Plugin LSP server 启动失败 → 跳过 LSP 功能
└─ Hooks 注册失败 → 跳过该 hook

静默忽略(不打扰用户):
└─ 可选依赖缺失
// 实际实现中的容错加载
async function loadPluginsWithFallback(plugins: Plugin[]): Promise<LoadedPlugin[]> {
const results: LoadedPlugin[] = [];

for (const plugin of plugins) {
try {
const loaded = await loadPlugin(plugin);
results.push(loaded);
} catch (err) {
if (plugin.isBuiltin) {
// 内置 Plugin 失败 → 致命错误
throw new Error(`Built-in plugin ${plugin.name} failed to load: ${err.message}`);
} else {
// 社区 Plugin 失败 → 记录警告,继续启动
console.warn(`Plugin ${plugin.name} skipped: ${err.message}`);
}
}
}

return results;
}

十、插件发现与市场模型

10.1 三层插件来源

1. 内置 Plugin(Built-in)
来源:编译进 CC binary(src/plugins/builtinPlugins.ts)
标识:{name}@builtin
可禁用但不能卸载

2. 本地 Plugin(Local)
来源:项目 .claude/plugins/ 目录
开发模式:直接引用 TypeScript 源码
作用域:仅当前项目

3. Marketplace Plugin(社区)
来源:远程 Registry(npm/GitHub)
安装:claude plugin install {name}
缓存:~/.claude/plugins/cache/

10.2 插件发现流程

启动时:
├─ 1. 扫描 builtin registry → 获取内置 Plugin 列表
├─ 2. 读取 user settings → 获取已安装的社区 Plugin
├─ 3. 扫描 project .claude/plugins/ → 获取本地 Plugin
├─ 4. 合并 Plugin 列表(后加载覆盖先加载的配置)
└─ 5. 每个 Plugin 调用 getPluginLspServers() 等方法收集能力

10.3 与 VSCode Extension 系统的对比

维度 CC Plugin VSCode Extension
扩展点 Tools, Commands, Skills, Hooks, MCP, LSP Commands, Views, Languages, Debug Adapters
UI 贡献 无(终端 UI) WebView, TreeView, StatusBar 等
激活事件 启动时全量加载 onLanguage:typescript 等懒激活
隔离 进程内(无隔离) 进程内 + Extension Host 分离
分发 无正式 Marketplace 内置 Marketplace
代码签名 可选(Verified Publishers)
自动更新 默认开启

CC Plugin 系统相比 VSCode 的最大差距在于懒激活隔离性——所有 Plugin 在启动时同步加载,VSCode 可以延迟到需要时才激活特定 Extension。对 CC 这种追求快速启动的工具,这是需要未来优化的方向。


十一、插件生命周期与 Hook 系统集成

11.1 完整的生命周期

Plugin 生命周期:

[注册] → registerBuiltinPlugin(definition)

[加载] → loadPlugin(pluginId)
├─ 读取 manifest
├─ 检查版本兼容性
├─ 执行 onInit() hook(如定义)
└─ 注册 tools/commands/skills/mcp/hooks

[运行] → Plugin 提供的工具和命令生效

[卸载] → user disables plugin
├─ 注销 tools/commands
├─ 断开 MCP 连接
├─ 执行 onShutdown() hook
└─ 清理临时文件

11.2 Hook 的注入时机

Plugin 通过 Hooks 可以介入 Agent 循环的关键节点:

PostToolUse → 工具执行后,结果返回前
└─ 用途:审计文件修改、注入 LSP 诊断

Stop → Agent 决定停止时
└─ 用途:检查是否有未完成的工作

PreToolUse → 工具执行前
└─ 用途:阻止危险操作

UserPromptSubmit → 用户输入提交时
└─ 用途:预处理用户输入

Notification → 系统通知时
└─ 用途:转发通知到外部系统(Slack等)

11.3 内置技能完整清单

技能 文件 作用 触发条件
stuck bundled/stuck.ts 诊断卡死会话 Agent 检测到循环
security-policy bundled/security.ts 注入安全约束 始终加载
debug bundled/debug.ts 调试辅助 用户调用 /debug
verify bundled/verify.ts 验证代码变更 ant-only
dream bundled/dream.ts 自动思考模式 KAIROS feature gate
loop bundled/loop.ts 循环执行 AGENT_TRIGGERS gate
remember bundled/remember.ts 记忆管理 始终加载
skillify bundled/skillify.ts 转化为 skill 用户调用 /skillify
batch bundled/batch.ts 批处理 用户调用 /batch

涉及源文件

  • builtinPlugins.ts
  • bundledSkills.ts
  • loadSkillsDir.ts
  • src/plugins/
  • src/skills/
  • src/skills/bundled/
打赏
  • 微信
  • 支付宝

评论