Appearance
第15章 Skill 与插件系统
开篇引言
在前面的章节中,我们深入分析了 Claude Code 的工具系统、权限模型和多 Agent 协作架构。这些机制构成了系统的核心骨骼,但真正赋予 Claude Code 生命力的,是其高度可扩展的 Skill 与插件系统。
想象一个开发者的日常场景:团队内部有一套定制化的代码审查规范,希望 Claude Code 在每次代码变更后自动执行检查;或者需要为特定框架编写一套部署流程,让模型在用户说出 /deploy 时自动执行一系列预定义操作。传统的工具系统虽然强大,但每添加一种新能力都需要修改核心代码。这就引出了一个根本性的架构问题:如何在不修改系统核心的前提下,让用户和社区自由扩展 Claude Code 的能力?
Claude Code 的回答是构建了三层递进的扩展体系:Skill(技能) 提供了声明式的能力描述机制,通过 Markdown 文件即可定义新技能;Plugin(插件) 在 Skill 之上增加了组件化封装,支持技能、Hooks、MCP 服务器的捆绑分发;Hooks(钩子) 则深入到工具执行的生命周期中,允许在关键节点插入自定义逻辑。三者协同工作,配合统一的 Slash 命令系统,形成了一个既灵活又安全的扩展架构。
本章将从源码层面深入剖析这套扩展体系的设计与实现,揭示其背后的架构决策和工程智慧。
本章要点
- Skill 系统的三种来源:文件系统 Skill(
.claude/skills/目录下的 Markdown 文件)、Bundled Skill(编译到 CLI 二进制中的内置技能)、MCP Skill(通过 MCP 协议远程加载的技能),以及它们如何统一转换为Command对象 - BundledSkillDefinition 类型:内置技能的声明式定义,包括名称、描述、触发条件、允许的工具列表、执行上下文等关键字段的设计考量
- SkillTool 的完整生命周期:从输入验证、权限检查、命令查找,到 inline/fork 两种执行模式的分流机制
- Plugin 系统的分层架构:BuiltinPlugin(内置插件)与 Marketplace Plugin(市场插件)的双轨机制,以及
LoadedPlugin类型如何统一表示两者 - Hooks 的四种类型:command(Shell 命令)、prompt(LLM 提示)、agent(Agent 验证器)、http(HTTP 回调),以及它们在 PreToolUse/PostToolUse 等事件节点上的精密执行逻辑
- Slash 命令系统的统一注册架构:80+ 命令的分类管理、优先级机制和动态加载策略
- 三个子系统的协作关系:Skill 定义能力,Plugin 封装和分发能力,Hooks 在执行时拦截和增强能力
15.1 Skill 系统
Claude Code 的扩展体系由 Skill、Plugin 和 Hooks 三层构成,它们通过统一的 Slash 命令系统对外暴露。下图展示了三个子系统之间的关系:
15.1.1 Skill 的本质:声明式能力描述
在 Claude Code 的架构中,Skill 本质上是一种声明式的能力描述。每个 Skill 最终都被转换为一个 Command 对象,其中最重要的是 getPromptForCommand 方法——它返回一段 prompt 内容,指导模型如何完成特定任务。这种设计意味着,添加新能力不需要编写任何 TypeScript 代码,只需创建一个 Markdown 文件并用 frontmatter 声明元数据即可。
Command 类型定义在 src/types/command.ts 中,是一个联合类型:
typescript
// 文件: src/types/command.ts
export type PromptCommand = {
type: 'prompt'
progressMessage: string
contentLength: number
argNames?: string[]
allowedTools?: string[]
model?: string
source: SettingSource | 'builtin' | 'mcp' | 'plugin' | 'bundled'
hooks?: HooksSettings
skillRoot?: string
context?: 'inline' | 'fork'
agent?: string
effort?: EffortValue
paths?: string[]
getPromptForCommand(
args: string,
context: ToolUseContext,
): Promise<ContentBlockParam[]>
}
export type Command = CommandBase &
(PromptCommand | LocalCommand | LocalJSXCommand)这里的 source 字段清晰地标记了命令的来源:builtin 表示硬编码的内部命令(如 /help、/clear),bundled 表示编译进二进制的内置 Skill,plugin 表示来自插件,mcp 表示来自 MCP 服务器。这个字段在后续的权限检查、遥测上报、prompt 截断等环节都起到了关键的分流作用。
15.1.2 skills/ 目录结构
Skill 系统的源码组织在 src/skills/ 目录下:
src/skills/
bundledSkills.ts -- Bundled Skill 注册表与类型定义
loadSkillsDir.ts -- 文件系统 Skill 加载器
mcpSkillBuilders.ts -- MCP Skill 构建器注册表
bundled/
index.ts -- 所有 Bundled Skill 的初始化入口
simplify.ts -- /simplify 代码审查技能
updateConfig.ts -- /update-config 配置管理技能
keybindings.ts -- /keybindings 快捷键管理技能
verify.ts -- /verify 验证技能
claudeApi.ts -- /claude-api API 开发技能
batch.ts -- /batch 批量处理技能
loop.ts -- /loop 循环执行技能
remember.ts -- /remember 记忆技能
stuck.ts -- /stuck 卡住恢复技能
debug.ts -- /debug 调试技能
... 更多内置技能这个目录结构体现了清晰的职责分离:bundledSkills.ts 负责类型定义和注册机制,loadSkillsDir.ts 负责从磁盘加载用户自定义 Skill,bundled/ 目录存放所有编译到二进制中的内置 Skill 实现。
15.1.3 BundledSkillDefinition 类型
BundledSkillDefinition 是定义内置 Skill 的核心类型,位于 src/skills/bundledSkills.ts:
typescript
// 文件: src/skills/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'
agent?: string
files?: Record<string, string>
getPromptForCommand: (
args: string,
context: ToolUseContext,
) => Promise<ContentBlockParam[]>
}每个字段都承载着精确的设计意图:
whenToUse:详细描述 Skill 的适用场景,在 SkillTool 的 prompt 中呈现给模型,帮助模型判断何时应自动调用该 SkillallowedTools:声明该 Skill 执行时需要的工具白名单,SkillTool 会通过contextModifier临时将这些工具添加到权限系统中context:'inline'表示 Skill 内容直接展开到当前对话中,'fork'表示在独立的 sub-agent 中执行,隔离上下文和 token 预算files:附加的参考文件,在首次调用时惰性提取到磁盘,模型可以通过 Read/Grep 工具按需访问disableModelInvocation:设为true时,模型无法通过 SkillTool 自动调用该 Skill,仅允许用户通过/前缀手动触发
files 字段的设计尤其值得关注。由于 Bundled Skill 编译进二进制文件中,没有磁盘上的文件目录可供模型读取。通过 files 字段,Skill 可以声明一组参考文件,系统会在首次调用时将它们提取到 getBundledSkillsRoot() 下的安全目录中: