Claude Code 源码解析
返回目录
扩展与集成17

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 中声明 PreToolUsePostToolUse 钩子,在工具调用前后自动触发:

  • 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 架构图

用户入口层

Slash 命令 /xxx
83+ 个命令
SkillTool 调用
AI 自动匹配

Skills 引擎

bundledSkills
17+ 内置 Skills
loadSkillsDir
.claude/skills/ 自定义
mcpSkillBuilders
MCP Prompts 转 Skill
Frontmatter 钩子: PreToolUse / PostToolUse

Plugins 生态

pluginLoader
加载 & 缓存
installedMgr
版本管理
managedPlugins
企业托管
marketplace
市场 & 更新

commands/ — Slash 命令注册表

/commit
/pr
/review-pr
/clear
/compact
/debug
/doctor
/config
...80+

源码

3 个关键代码示例

01
Skill 加载与 Frontmatter 解析
TypeScript
// 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
];
02
SkillTool 执行与 Plugin 加载
TypeScript
// 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;
  }
}
03
Slash 命令注册与路由
TypeScript
// 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 ✔

相关源文件

skills/plugins/commands/