Skills 与 Plugins
可扩展的能力生态
Skills 通过 Frontmatter 声明 Hook,让 Markdown 文件具有编程能力
学习
概念讲解与核心设计分析
17.1 Skills 系统概览
Claude Code 的 skills/ 系统是一种可扩展的能力注入机制。Skills 本质上是结构化的 Markdown 文件,通过 Frontmatter 声明元数据和钩子,正文部分作为提示词注入到子对话中。
- loadSkillsDir.ts — 从
.claude/skills/目录加载用户自定义 Skills,支持嵌套目录和热更新 - bundledSkills.ts — 内置 Skills 注册表,包含 17+ 个预打包的 Skills
- SkillTool — Skills 执行工具,将 Skill 的提示词内容注入到子对话中执行,通过 skill 名称或完全限定名称(如 "plugin:skill-name")调用
- mcpSkillBuilders.ts — 将 MCP 服务器提供的 Prompts 转换为可调用的 Skills
17.2 内置 Skills 清单
Claude Code 预置了 17+ 个内置 Skills,涵盖开发工作流的各个方面:
- batch — 批量处理多个文件或任务
- claudeApi — Claude API 和 Anthropic SDK 集成指导
- claudeInChrome — Chrome 浏览器中使用 Claude 的专用模式
- debug — 调试辅助,提供系统状态诊断和日志分析
- keybindings — 快捷键配置和自定义
- loop — 循环执行:以固定间隔重复运行提示词或命令
- remember — 记忆管理:读写 CLAUDE.md 记忆文件
- scheduleRemoteAgents — 远程代理调度和管理
- simplify — 代码简化:检查变更代码的复用性、质量和效率
- skillify / skill-creator — Skill 创建工具:从模板创建新的自定义 Skill
- stuck — 卡住检测:当 AI 陷入循环时自动介入
- updateConfig — 通过 settings.json 配置 Claude Code 行为
- verify / verifyContent — 验证工具:确认代码变更的正确性和完整性
17.3 Frontmatter 钩子注册
Skills 可以在 Markdown Frontmatter 中声明 PreToolUse 和 PostToolUse 钩子,在工具调用前后自动触发:
- Frontmatter 使用 YAML 格式声明钩子匹配规则
- 支持按工具名称、文件模式等条件过滤
- 钩子可以修改工具参数、拦截执行或追加后处理逻辑
17.4 Plugin 系统
utils/plugins/ 目录包含 43+ 个文件,构成了完整的插件管理系统:
- pluginLoader.ts — 插件加载器,负责发现、加载和缓存插件
- installedPluginsManager.ts — 已安装插件管理器,处理版本化的插件生命周期
- managedPlugins.ts — 企业级托管插件,由组织管理员统一配置
- Plugin Marketplace — 插件市场集成,支持浏览、安装和自动更新
- blocklist — 插件黑名单机制,阻止已知有问题的插件版本
17.5 Slash 命令系统
commands/ 目录定义了 83+ 个 Slash 命令,按功能分类:
- Git 操作 — /commit, /pr, /review-pr 等版本控制快捷命令
- 会话管理 — /clear, /compact, /resume 等会话控制命令
- 调试工具 — /debug, /doctor, /bug-report 等诊断命令
- 配置管理 — /config, /theme, /model 等设置命令
- 项目工具 — /init, /search, /install 等项目操作命令
每个 Slash 命令可以直接执行逻辑,也可以触发对应的 Skill。当用户输入 "/skill-name" 时,系统会查找匹配的 Skill 并通过 SkillTool 执行。
架构
模块关系与设计决策
Skills / Plugins / Commands 架构图
用户入口层
Skills 引擎
Plugins 生态
commands/ — Slash 命令注册表
源码
共 3 个关键代码示例
// Skill 文件结构 (.claude/skills/my-skill.md)
// ---
// name: my-custom-skill
// description: 自定义代码审查 Skill
// hooks:
// PreToolUse:
// - tool: Write
// match: "**/*.ts"
// PostToolUse:
// - tool: Bash
// ---
// 你是一个代码审查专家。在写入 TypeScript 文件前...
interface SkillDefinition {
name: string;
description: string;
content: string; // Markdown 正文 = 提示词
hooks?: {
PreToolUse?: HookConfig[];
PostToolUse?: HookConfig[];
};
source: "bundled" | "user" | "mcp" | "plugin";
}
interface HookConfig {
tool: string; // 匹配的工具名
match?: string; // 文件模式(glob)
}
// loadSkillsDir.ts: 从磁盘加载用户 Skills
async function loadSkillsDir(
skillsPath: string
): Promise<SkillDefinition[]> {
const files = await glob(skillsPath + "/**/*.md");
const skills: SkillDefinition[] = [];
for (const file of files) {
const raw = await readFile(file, "utf-8");
const { frontmatter, body } = parseFrontmatter(raw);
skills.push({
name: frontmatter.name || basename(file, ".md"),
description: frontmatter.description || "",
content: body,
hooks: frontmatter.hooks,
source: "user"
});
}
return skills;
}
// bundledSkills.ts: 内置 Skills 注册表
const bundledSkills: SkillDefinition[] = [
{
name: "simplify",
description: "审查变更代码的质量和效率",
content: "检查代码复用性、质量和效率...",
source: "bundled"
},
{
name: "loop",
description: "以固定间隔循环执行命令",
content: "按照指定的时间间隔重复运行...",
source: "bundled"
}
// ... 15+ 更多内置 Skills
];// SkillTool: 执行 Skill 的核心工具
class SkillTool {
private registry: Map<string, SkillDefinition>;
async execute(input: {
skill: string;
args?: string;
}): Promise<SkillResult> {
// 按名称或完全限定名查找 Skill
const skill = this.findSkill(input.skill);
if (!skill) {
throw new Error(
"Skill not found: " + input.skill
);
}
// 将 Skill 内容注入子对话
const subConversation = createSubConversation({
systemPrompt: skill.content,
userMessage: input.args || "",
tools: getAvailableTools()
});
// 执行子对话
return await runSubConversation(subConversation);
}
private findSkill(name: string): SkillDefinition | undefined {
// 完全限定名: "plugin:skill-name"
if (name.includes(":")) {
return this.registry.get(name);
}
// 短名称匹配
return this.registry.get(name)
|| [...this.registry.values()].find(
s => s.name === name
);
}
}
// pluginLoader.ts: 插件加载与缓存
class PluginLoader {
private cache = new Map<string, Plugin>();
async loadPlugin(
pluginId: string
): Promise<Plugin> {
// 检查缓存
if (this.cache.has(pluginId)) {
return this.cache.get(pluginId)!;
}
// 从 marketplace 或本地加载
const manifest = await this.fetchManifest(pluginId);
// 黑名单检查
if (isBlocklisted(pluginId, manifest.version)) {
throw new Error(
"Plugin " + pluginId + " is blocklisted"
);
}
const plugin = await this.instantiate(manifest);
this.cache.set(pluginId, plugin);
return plugin;
}
}
// installedPluginsManager.ts: 版本化管理
class InstalledPluginsManager {
private plugins: Map<string, InstalledPlugin>;
async install(
pluginId: string,
version: string
): Promise<void> {
const plugin = await PluginLoader.loadPlugin(
pluginId
);
this.plugins.set(pluginId, {
...plugin,
installedVersion: version,
installedAt: new Date()
});
}
async checkUpdates(): Promise<UpdateInfo[]> {
const updates: UpdateInfo[] = [];
for (const [id, plugin] of this.plugins) {
const latest = await fetchLatestVersion(id);
if (latest !== plugin.installedVersion) {
updates.push({
pluginId: id,
current: plugin.installedVersion,
latest
});
}
}
return updates;
}
}// commands/ 目录: Slash 命令定义
interface SlashCommand {
name: string; // 命令名(不含 /)
description: string;
aliases?: string[]; // 别名
category: CommandCategory;
execute: (
args: string,
context: CommandContext
) => Promise<void>;
}
type CommandCategory =
| "git"
| "session"
| "debug"
| "config"
| "project";
// 示例: /commit 命令
const commitCommand: SlashCommand = {
name: "commit",
description: "分析变更并创建 Git 提交",
category: "git",
execute: async (args, ctx) => {
// 触发 commit Skill
await ctx.skillTool.execute({
skill: "commit",
args: args
});
}
};
// 命令路由器
class CommandRouter {
private commands = new Map<string, SlashCommand>();
register(cmd: SlashCommand): void {
this.commands.set(cmd.name, cmd);
// 注册别名
if (cmd.aliases) {
for (const alias of cmd.aliases) {
this.commands.set(alias, cmd);
}
}
}
async route(
input: string,
ctx: CommandContext
): Promise<boolean> {
if (!input.startsWith("/")) return false;
const parts = input.slice(1).split(" ");
const cmdName = parts[0];
const args = parts.slice(1).join(" ");
const cmd = this.commands.get(cmdName);
if (!cmd) {
// 尝试模糊匹配
const fuzzy = this.fuzzyMatch(cmdName);
if (fuzzy) {
await fuzzy.execute(args, ctx);
return true;
}
return false;
}
await cmd.execute(args, ctx);
return true;
}
private fuzzyMatch(
name: string
): SlashCommand | undefined {
const candidates = [...this.commands.entries()]
.filter(([k]) => k.startsWith(name));
return candidates.length === 1
? candidates[0][1]
: undefined;
}
listByCategory(
cat: CommandCategory
): SlashCommand[] {
return [...this.commands.values()]
.filter(c => c.category === cat);
}
}互动
步进式流程演示
Skills 与 Plugins 交互式演练
Step 1: 用户输入 Slash 命令
用户在 REPL 中输入 /simplify。CommandRouter 识别这是一个 Slash 命令,查找匹配的内置 Skill。
用户输入: /simplify
CommandRouter.route("/simplify")
→ 查找命令: "simplify"
→ 匹配到 bundled Skill: "simplify"
→ 触发 SkillTool.execute({ skill: "simplify" })
Step 2: Skill 内容注入
SkillTool 读取 simplify Skill 的 Markdown 内容,将其作为系统提示词注入子对话。子对话拥有完整的工具访问能力。
子对话创建:
systemPrompt: "检查变更代码的复用性、质量和效率。
找到问题后立即修复..."
tools: [Read, Write, Grep, Glob, Bash, LSP]
context: 当前 git diff 中的变更文件
Step 3: Frontmatter 钩子触发
如果 Skill 声明了 PreToolUse 钩子(如匹配 Write 工具 + *.ts 文件),则在每次工具调用前自动执行钩子逻辑。
Skill 钩子触发:
PreToolUse: Write "src/auth/login.ts"
→ 匹配 hooks.PreToolUse[0]: tool=Write, match="**/*.ts"
→ 执行前置检查: 代码风格验证
→ 通过 ✔ 继续执行 Write 工具
Step 4: Plugin 自动更新
InstalledPluginsManager 在启动时检查已安装插件的更新。发现新版本后提示用户,同时检查黑名单确保安全。
Plugin 更新检查:
my-linter-plugin: v1.2.0 → v1.3.0 (可更新)
code-formatter: v2.0.1 → v2.0.1 (最新)
unsafe-plugin: v0.9.0 → BLOCKLISTED (已拦截)
自动更新: my-linter-plugin → v1.3.0 ✔