Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第7章:技能系统

本章深入分析 Claude Code 的技能系统实现,包括 Skill 目录结构、加载机制、内置 Skills 详解和自定义 Skill 开发指南。

7.1 skills/ 目录结构

目录概览

src/skills/
├── bundled/                     # 内置 Skills 目录
│   ├── index.ts                 # 入口索引
│   ├── batch.ts                 # 批量处理 Skill
│   ├── claudeApi.ts             # Claude API Skill
│   ├── claudeApiContent.ts      # API 内容处理
│   ├── debug.ts                 # 调试 Skill
│   ├── keybindings.ts           # 键绑定 Skill
│   ├── loop.ts                  # 循环执行 Skill
│   ├── loremIpsum.ts            # 测试文本 Skill
│   ├── remember.ts              # 记忆 Skill
│   ├── simplify.ts              # 代码简化 Skill
│   ├── skillify.ts              # Skill 创建 Skill
│   ├── stuck.ts                 # 卡住处理 Skill
│   ├── updateConfig.ts          # 配置更新 Skill
│   ├── verify/                  # 验证 Skills 子目录
│   │   ├── index.ts
│   │   └── ...
│   └── scheduleRemoteAgents.ts  # 远程 Agent 调度
│
├── bundledSkills.ts             # 内置 Skill 注册机制
├── loadSkillsDir.ts             # Skill 目录加载逻辑
└── mcpSkillBuilders.ts          # MCP Skill 构建器

Skill 文件组织结构

graph TD
    subgraph "Skill 来源"
        BUNDLED[bundled/ 目录<br/>内置 Skills]
        USER[userSettings<br/>~/.claude/skills/]
        PROJECT[projectSettings<br/>.claude/skills/]
        PLUGIN[Plugin Skills<br/>外部插件]
        MCP[MCP Skills<br/>MCP Server 提供]
    end
    
    subgraph "加载器"
        LOADER[loadSkillsDir.ts]
        REGISTER[bundledSkills.ts]
        PLUGIN_LOADER[loadPluginCommands.ts]
        MCP_BUILDER[mcpSkillBuilders.ts]
    end
    
    subgraph "注册"
        REGISTRY[Command Registry]
        SKILL_TOOL[SkillTool]
    end
    
    BUNDLED --> REGISTER --> REGISTRY
    USER --> LOADER --> REGISTRY
    PROJECT --> LOADER --> REGISTRY
    PLUGIN --> PLUGIN_LOADER --> REGISTRY
    MCP --> MCP_BUILDER --> REGISTRY
    
    REGISTRY --> SKILL_TOOL

7.2 Skill 加载机制

Skill 文件格式(.md 文件)

Skill 文件采用 Markdown 格式,支持 YAML frontmatter 定义元数据:

---
name: my-skill
description: My custom skill for specific tasks
aliases: [my-skill-alias]
whenToUse: Use this skill when you need to perform X
argumentHint: [target_file]
allowedTools: [Read, Edit, Bash]
model: claude-sonnet-4-20250514
disableModelInvocation: false
userInvocable: true
---

# My Skill

This skill helps you perform specific tasks.

## Instructions

1. First, read the target file
2. Analyze the content
3. Make necessary modifications

## Example

When you run `/my-skill src/main.ts`:
- The skill will read the file
- Analyze its structure
- Suggest improvements

Frontmatter 字段说明

字段类型说明
namestringSkill 名称(唯一标识)
descriptionstring功能描述
aliasesstring[]别名列表
whenToUsestring使用场景说明
argumentHintstring参数提示
allowedToolsstring[]可用工具列表
modelstring指定使用的模型
disableModelInvocationboolean是否禁用模型调用
userInvocableboolean是否用户可调用
hooksHooksSettingsHook 配置

BundledSkill 注册机制

// 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[]>
}

const bundledSkills: Command[] = []

export function registerBundledSkill(definition: BundledSkillDefinition): void {
  const { files } = definition
  
  // 如果有附加文件,延迟提取到磁盘
  let skillRoot: string | undefined
  let getPromptForCommand = definition.getPromptForCommand
  
  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)
    }
  }
  
  const command: Command = {
    type: 'prompt',
    name: definition.name,
    description: definition.description,
    aliases: definition.aliases,
    hasUserSpecifiedDescription: true,
    allowedTools: definition.allowedTools ?? [],
    // ... 其他属性
    source: 'bundled',
    loadedFrom: 'bundled',
    getPromptForCommand,
  }
  bundledSkills.push(command)
}

export function getBundledSkills(): Command[] {
  return [...bundledSkills]
}

loadSkillsDir.ts 加载逻辑

// 从目录加载 Skills
export async function getSkillDirCommands(cwd: string): Promise<Command[]> {
  const skillsDirs = [
    // 用户配置目录
    join(getClaudeConfigHomeDir(), 'skills'),
    // 项目配置目录
    join(cwd, '.claude', 'skills'),
    // 其他配置目录
    ...getAdditionalDirectoriesForClaudeMd().map(d => join(d, 'skills')),
  ]
  
  const commands: Command[] = []
  
  for (const dir of skillsDirs) {
    if (!fs.existsSync(dir)) continue
    
    const files = await loadMarkdownFilesForSubdir(dir)
    for (const file of files) {
      const command = await parseSkillFile(file, dir)
      if (command) commands.push(command)
    }
  }
  
  return commands
}

// 解析 Skill 文件
async function parseSkillFile(file: MarkdownFile, baseDir: string): Promise<Command | null> {
  const { frontmatter, content } = parseFrontmatter(file.content)
  
  // 验证必要字段
  if (!frontmatter.name) return null
  
  return {
    type: 'prompt',
    name: frontmatter.name,
    description: coerceDescriptionToString(frontmatter.description) 
      ?? extractDescriptionFromMarkdown(content),
    aliases: frontmatter.aliases,
    whenToUse: frontmatter.whenToUse,
    allowedTools: frontmatter.allowedTools ?? [],
    model: frontmatter.model,
    contentLength: content.length,
    source: getSettingSource(baseDir),
    loadedFrom: 'skills',
    userInvocable: frontmatter.userInvocable ?? true,
    getPromptForCommand: async (args, context) => {
      // 参数替换
      const processedContent = substituteArguments(content, args)
      return [{ type: 'text', text: processedContent }]
    },
  }
}

Skill 加载流程图

sequenceDiagram
    participant Init as 启动初始化
    participant Bundled as bundledSkills.ts
    participant Loader as loadSkillsDir.ts
    participant Parser as Frontmatter Parser
    participant Registry as Command Registry
    
    Init->>Bundled: registerBundledSkill()
    Bundled->>Registry: 注册内置 Skills
    
    Init->>Loader: getSkillDirCommands(cwd)
    Loader->>Loader: 遍历 skills 目录
    Loader->>Parser: 解析 .md 文件
    
    Parser->>Parser: parseFrontmatter()
    Parser->>Parser: 验证字段
    Parser-->>Loader: Command 对象
    
    Loader-->>Registry: 注册 Skills
    
    Registry->>Registry: getCommands()
    Registry-->>Init: 合并所有 Skills

7.3 内置 Skills 详解

核心 Skills 分类

graph TD
    subgraph "开发辅助"
        INIT[init<br/>初始化项目]
        REVIEW[review<br/>代码审查]
        SECURITY[security-review<br/>安全审查]
        DEBUG[debug<br/>调试辅助]
        SIMPLIFY[simplify<br/>代码简化]
    end
    
    subgraph "配置管理"
        CONFIG[updateConfig<br/>配置更新]
        KEYBIND[keybindings-help<br/>键绑定]
        PERMISSION[fewer-permission-prompts<br/>权限优化]
    end
    
    subgraph "API 相关"
        API[claude-api<br/>API 开发]
        API_CONTENT[claudeApiContent<br/>API 内容]
    end
    
    subgraph "流程控制"
        LOOP[loop<br/>循环执行]
        BATCH[batch<br/>批量处理]
        STUCK[stuck<br/>卡住处理]
    end
    
    subgraph "记忆系统"
        REMEMBER[remember<br/>记忆存储]
        VERIFY[verify<br/>验证检查]
    end

init Skill

// init Skill 定义
registerBundledSkill({
  name: 'init',
  description: 'Initialize a new CLAUDE.md file with codebase documentation',
  aliases: ['initialize'],
  whenToUse: 'Use when starting work on a new project to create documentation',
  argumentHint: '[project_path]',
  getPromptForCommand: async (args, context) => {
    return [
      { type: 'text', text: `
# Project Initialization

Analyze the codebase at ${args || 'current directory'} and create:

1. CLAUDE.md file with:
   - Project overview
   - Key files and their purposes
   - Build/test commands
   - Architecture summary

2. Recommended configurations:
   - .claude/settings.json if needed
   - MCP server recommendations

Use Glob to discover files, Read to analyze key files, and Write to create CLAUDE.md.
      ` }
    ]
  },
})

review Skill

registerBundledSkill({
  name: 'review',
  description: 'Review a pull request',
  aliases: ['pr-review'],
  whenToUse: 'Use when you need to review code changes in a PR',
  argumentHint: '[pr_url_or_number]',
  allowedTools: ['Bash', 'Read', 'Grep', 'Glob'],
  getPromptForCommand: async (args, context) => {
    // 解析 PR 信息
    const prInfo = await parsePRArg(args)
    
    return [
      { type: 'text', text: `
# Pull Request Review

Review PR ${prInfo.number}: ${prInfo.title}

## Tasks
1. Fetch the PR diff using \`gh pr diff ${prInfo.number}\`
2. Analyze the changes for:
   - Code quality issues
   - Potential bugs
   - Security concerns
   - Performance implications
3. Check if tests cover the changes
4. Provide actionable feedback

## Output Format
- Summary of changes
- Critical issues (if any)
- Suggestions for improvement
- Overall assessment
      ` }
    ]
  },
})

security-review Skill

registerBundledSkill({
  name: 'security-review',
  description: 'Complete a security review of pending changes',
  whenToUse: 'Use before merging sensitive changes or deploying to production',
  getPromptForCommand: async (args, context) => {
    return [
      { type: 'text', text: `
# Security Review

Perform a comprehensive security review of the current branch changes.

## Checklist
1. **Authentication & Authorization**
   - Check for proper auth checks
   - Verify permission boundaries

2. **Data Protection**
   - Sensitive data handling
   - Encryption requirements
   - Data validation

3. **Injection Prevention**
   - SQL injection
   - XSS vulnerabilities
   - Command injection

4. **API Security**
   - Input validation
   - Rate limiting
   - Error handling

5. **Dependency Security**
   - Known vulnerabilities
   - License compliance

Provide a detailed report with severity ratings and remediation steps.
      ` }
    ]
  },
})

claude-api Skill

registerBundledSkill({
  name: 'claude-api',
  description: 'Build, debug, and optimize Claude API / Anthropic SDK apps',
  whenToUse: 'TRIGGER when code imports anthropic/@anthropic-ai/sdk',
  allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Grep', 'Glob'],
  getPromptForCommand: async (args, context) => {
    return [
      { type: 'text', text: `
# Claude API Development

Assist with Claude API / Anthropic SDK development.

## Focus Areas
- Prompt caching optimization
- Model version migration (4.5 → 4.6 → 4.7)
- Tool use implementation
- Streaming response handling
- Batch processing
- Error handling and retries

## Best Practices
1. Always include prompt caching where applicable
2. Use proper error handling for API errors
3. Implement retry logic for transient failures
4. Track token usage for cost optimization

## Code Patterns
- Show proper SDK initialization
- Demonstrate tool definition patterns
- Provide streaming examples
      ` }
    ]
  },
})

Skill 触发机制

部分 Skill(如 claude-api)使用 whenToUse 字段定义触发条件。当代码匹配导入语句(如 import anthropic)时,Skill 会自动被建议使用。

7.4 自定义 Skill 开发

创建文件型 Skill

  1. 创建 .md 文件:
---
name: my-custom-skill
description: My custom skill description
aliases: [my-skill]
whenToUse: Use this when...
argumentHint: [arg1] [arg2]
allowedTools: [Read, Edit, Bash]
---

# My Custom Skill

Detailed instructions for the skill.

## Arguments

- `arg1`: First argument description
- `arg2`: Second argument description

## Steps

1. First step instruction
2. Second step instruction
3. ...

## Output

Expected output format.
  1. 放置到正确目录:
# 用户级 Skill
~/.claude/skills/my-custom-skill.md

# 项目级 Skill
.claude/skills/my-custom-skill.md

创建 Bundled Skill

// 在 bundled/index.ts 中注册
import { registerBundledSkill } from '../bundledSkills.js'

registerBundledSkill({
  name: 'my-bundled-skill',
  description: 'A bundled skill that ships with the CLI',
  whenToUse: 'Use when condition X is met',
  argumentHint: '[target]',
  allowedTools: ['Read', 'Write', 'Bash'],
  
  // 附加参考文件(可选)
  files: {
    'templates/default.md': '# Template content...',
    'schemas/config.json': '{"type": "object", ...}',
  },
  
  getPromptForCommand: async (args, context) => {
    const target = args || 'default'
    
    return [
      { type: 'text', text: `
# My Bundled Skill

Target: ${target}

## Instructions
Detailed instructions here...

## Available Resources
- templates/default.md (if needed)
- schemas/config.json (for validation)
      ` }
    ]
  },
})

Skill 开发最佳实践

flowchart TB
    subgraph "Skill 设计原则"
        P1[明确单一职责]
        P2[提供清晰指令]
        P3[限制工具范围]
        P4[使用 whenToUse]
        P5[处理参数错误]
    end
    
    subgraph "Skill 内容结构"
        S1[标题和概述]
        S2[参数说明]
        S3[执行步骤]
        S4[输出格式]
        S5[错误处理]
    end
    
    subgraph "测试验证"
        T1[测试边界情况]
        T2[验证工具权限]
        T3[检查输出格式]
    end
    
    P1 --> S1
    P2 --> S3
    P3 --> T2
    P4 --> S1
    P5 --> S5
    S1 --> T1
    S3 --> T3
    S4 --> T3

工具范围限制

使用 allowedTools 限制 Skill 可用的工具,避免不必要的权限请求。例如,只读 Skill 不应包含 EditWrite

Skill 调用流程

sequenceDiagram
    participant User as 用户
    participant REPL as REPL
    participant Parser as 命令解析
    participant SkillTool as SkillTool
    participant Skill as Skill Instance
    participant Model as Claude Model
    
    User->>REPL: /my-skill arg1 arg2
    REPL->>Parser: 解析斜杠命令
    Parser->>Parser: findCommand('my-skill')
    Parser-->>REPL: Command 对象
    
    REPL->>SkillTool: 调用 Skill
    SkillTool->>Skill: getPromptForCommand('arg1 arg2')
    Skill-->>SkillTool: ContentBlockParam[]
    
    SkillTool->>Model: 发送 Skill 内容
    Model->>Model: 执行 Skill 指令
    Model->>SkillTool: 工具调用请求
    
    SkillTool->>SkillTool: 检查 allowedTools
    SkillTool->>SkillTool: 执行工具
    SkillTool-->>Model: tool_result
    
    Model-->>SkillTool: 最终响应
    SkillTool-->>REPL: Skill 结果
    REPL->>User: 显示结果

下一章将分析 命令系统 的实现细节。