Skills 是 Claude Code 最强大的扩展机制之一。它不是简单的”命令别名”,而是完整的 AI 行为定义:可以限制工具池、覆盖模型、注入 Hook、选择执行上下文(inline 或 fork)。更令人惊叹的是,Skills 支持条件激活——只有当你操作特定文件时才被发现。
导读:Skills 不只是命令 很多人第一次看到 Skills 时,会认为它就是”斜杠命令”的别名。但实际上,Skills 是一个完整的 AI 行为定义系统 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 --- name: code-review description: Expert code review with best practices allowed-tools: ['Read', 'Grep', 'WebSearch'] model: sonnet context: fork hooks: PreToolUse: - matcher: "Bash" hooks: - type: command command: "echo 'Review mode: read-only'" --- You are a code reviewer. Analyze the code for: - Security vulnerabilities- Performance issues- Code style and best practices- Documentation completenessProvide actionable feedback with specific line references.
这个 Skill 定义了:
工具限制 :只读工具(Read、Grep、WebSearch)
模型选择 :使用 Sonnet
执行上下文 :Fork 模式(独立子代理)
Hook 注入 :阻止 Bash 调用
一、Skills 系统架构 1.1 整体架构 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────────────────────────┐ │ Skills 系统 │ │ │ │ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │ │ │ Discovery │ │ Prompt │ │ SkillTool │ │ │ │ 发现 & 加载 │→│ 注入 & 呈现 │→│ 执行引擎 │ │ │ └─────────────┘ └──────────────┘ └────────────┘ │ │ ↑ ↓ │ │ ┌─────────────┐ ┌────────────┐ │ │ │ Activation │ │ Context │ │ │ │ 条件激活 │←────────────────────│ 上下文修改 │ │ │ └─────────────┘ └────────────┘ │ └─────────────────────────────────────────────────────┘
1.2 核心模块职责
模块
核心文件
职责
Discovery
loadSkillsDir.ts
从 6 种来源发现和加载 Skills
Prompt
prompt.ts + attachments.ts
将 Skill 列表注入 system-reminder
SkillTool
SkillTool.ts
验证、权限检查、执行 Skill
Activation
loadSkillsDir.ts
条件激活和动态发现
Context
forkedAgent.ts
上下文准备和修改
二、Skill 发现与加载 2.1 六种来源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────────────────────────┐ │ Skills 来源 │ ├─────────────────────────────────────────────────────┤ │ │ │ 1. Bundled Skills(内置) │ │ └─ src/skills/bundled/*.md │ │ │ │ 2. Built-in Plugin Skills(内置插件) │ │ └─ src/plugins/bundled/*/skills/*.md │ │ │ │ 3. Managed Skills(管理) │ │ └─ ${MANAGED_PATH}/.claude/skills/ │ │ │ │ 4. User Skills(用户全局) │ │ └─ ~/.claude/skills/ │ │ │ │ 5. Project Skills(项目级) │ │ └─ .claude/skills/ │ │ │ │ 6. Plugin Skills(插件) │ │ └─ ~/.claude/plugins/*/skills/ │ │ │ └─────────────────────────────────────────────────────┘
2.2 加载优先级 1 2 3 4 5 6 7 8 9 10 11 12 const loadAllCommands = memoize(async (cwd: string ): Promise <Command[]> => { return [ ...bundledSkills, ...builtinPluginSkills, ...skillDirCommands, ...workflowCommands, ...pluginCommands, ...pluginSkills, ...COMMANDS(), ] })
2.3 去重机制 1 2 3 4 5 6 7 8 9 const seenFileIds = new Map<string , SettingSource>()for (const entry of allSkillsWithPaths) { const fileId = await getFileIdentity(entry.filePath) const existingSource = seenFileIds.get(fileId) if (existingSource !== undefined ) continue seenFileIds.set(fileId, entry.skill.source) deduplicatedSkills.push(entry.skill) }
三、Frontmatter 解析 3.1 Frontmatter 字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type FrontmatterData = { 'allowed-tools' ?: string | string [] | null description?: string | null 'argument-hint' ?: string | null when_to_use?: string | null version?: string | null model?: string | null 'user-invocable' ?: string | null 'disable-model-invocation' ?: string | null hooks?: HooksSettings | null effort?: string | null context?: 'inline' | 'fork' | null agent?: string | null paths?: string | string [] | null shell?: string | null }
3.2 解析流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SKILL.md 文件 ↓ parseFrontmatter() ← frontmatterParser.ts ├─ 分离 YAML frontmatter 和 Markdown 内容 ├─ quoteProblematicValues() ← 处理特殊字符 └─ parseYaml() ← 解析 YAML ↓ parseSkillFrontmatterFields() ← loadSkillsDir.ts ├─ description 提取(frontmatter 或第一个 # 标题) ├─ parseUserSpecifiedModel() ← 模型别名解析 ├─ parseEffortValue() ← 力度级别解析 ├─ parseHooksFromFrontmatter() ← Hook 配置验证 └─ parseSlashCommandToolsFromFrontmatter() ← 工具列表解析 ↓ createSkillCommand() ← 生成 Command 对象
四、条件激活机制 4.1 工作原理 带 paths frontmatter 的 Skills 不会立即暴露给模型:
1 2 3 4 5 6 7 8 9 10 11 --- name: react-component-test description: Generate tests for React components paths: - "src/components/**/* .tsx" - "src/components/**/* .jsx"allowed-tools: ['Read', 'Write', 'Bash'] model: sonnet --- Generate comprehensive tests for this React component...
这个 Skill 只有在你操作 src/components/ 下的文件时才会被发现。
4.2 激活流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 启动时 ├─ 加载所有 Skills ├─ 有 paths 的 → conditionalSkills Map └─ 无 paths 的 → 立即可用 运行时(文件操作触发) ├─ activateConditionalSkillsForPaths(filePaths, cwd) │ ├─ 遍历 conditionalSkills Map │ ├─ 用 ignore 库匹配 paths 模式 │ │ └─ filePath 转为 cwd 相对路径后匹配 │ ├─ 匹配成功: │ │ ├─ 移入 dynamicSkills Map │ │ ├─ 从 conditionalSkills 删除 │ │ └─ 记录遥测 tengu_dynamic_skills_changed │ └─ 一旦激活,会话内持续有效 └─ 通知缓存失效 → skillsLoaded.emit()
4.3 动态发现 当操作深层目录文件时,系统自动发现新的 Skills:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 async function discoverSkillDirsForPaths ( filePaths: string [], cwd: string , ): Promise <string []> { for (const filePath of filePaths) { let currentDir = dirname(filePath) while (currentDir.startsWith(resolvedCwd + pathSep)) { const skillDir = join(currentDir, '.claude' , 'skills' ) if (!dynamicSkillDirs.has(skillDir)) { dynamicSkillDirs.add(skillDir) if (await exists(skillDir) && !await isGitignored(currentDir)) { newDirs.push(skillDir) } } currentDir = dirname(currentDir) } } return newDirs.sort((a, b ) => b.split(pathSep).length - a.split(pathSep).length) }
五、Skill 注入到对话 5.1 注入流程 Skills 通过 system-reminder 消息注入到对话中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function getSkillListingAttachments ( ): Promise <AttachmentMessage []> { const commands = await getSkillToolCommands(cwd) const budget = contextWindowTokens * 4 * 0.01 const formatted = formatCommandsWithinBudget(commands, budget) return [{ type : 'skill_listing' , content: formatted, skillCount: commands.length, }] }
5.2 预算控制 1 2 3 4 export const SKILL_BUDGET_CONTEXT_PERCENT = 0.01 export const DEFAULT_CHAR_BUDGET = 8 _000 export const MAX_LISTING_DESC_CHARS = 250
截断策略 :
1 2 3 4 5 6 7 8 9 10 formatCommandsWithinBudget(commands, budget) ├─ 计算总预算 ├─ 尝试全量描述 │ └─ 总字符 ≤ 预算 → 全部输出 │ ├─ 分区: Bundled(不截断) + 其余 │ ├─ Bundled Skills 始终保留完整描述 │ └─ 其余 Skills 平分剩余预算 │ └─ 截断描述 → maxDescLen 字符
6.1 执行流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 SkillTool.call({ skill, args }) │ ├─ 1. 标准化输入(去除前导 /) ├─ 2. 远程 Skill 检查(实验性) ├─ 3. 查找 Command 对象 ├─ 4. 记录使用频率 │ ├─ 5. 判断执行路径 │ ├─ command.context === 'fork' │ │ └─ → executeForkedSkill() │ │ │ └─ 默认 inline │ ├─ processPromptSlashCommand() │ │ ├─ getPromptForCommand(args, context) │ │ ├─ registerSkillHooks() │ │ ├─ addInvokedSkill() │ │ └─ 提取附件 → 创建消息 │ │ │ ├─ 提取 metadata: allowedTools, model, effort │ ├─ tagMessagesWithToolUseID() │ └─ 返回 { newMessages, contextModifier } │ └─ 6. contextModifier() 闭包 ├─ 更新 allowedTools ├─ 更新 model └─ 更新 effort
6.2 Inline vs Fork 扔回 Inline 返回 :1 2 3 4 5 6 7 8 9 10 11 { data: { success: true , commandName: 'commit' , allowedTools: ['Bash' , 'Read' ], model: 'sonnet' , status: 'inline' , }, newMessages: [...], contextModifier: (ctx ) => { ... }, }
Fork 返回 :1 2 3 4 5 6 7 8 9 10 { data: { success: true , commandName: 'verify' , status: 'forked' , agentId: 'agent_abc123' , result: '验证通过,所有测试已运行...' , }, }
七、Hook 集成 7.1 Hook 注册 Skills 可以通过 frontmatter 声明 Hook:
1 2 3 4 5 6 7 8 9 10 --- name: test-runner hooks: PostToolUse: - matcher: "Edit" hooks: - type: command command: "npm test" once: true ---
调用时自动注册为会话级 Hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function registerSkillHooks ( setAppState, sessionId, hooks, skillName, skillRoot ): void { for (const eventName of HOOK_EVENTS) { for (const matcher of hooks[eventName] || []) { for (const hook of matcher.hooks) { const onHookSuccess = hook.once ? () => removeSessionHook(setAppState, sessionId, eventName, hook) : undefined addSessionHook( setAppState, sessionId, eventName, matcher.matcher || '' , hook, onHookSuccess, skillRoot, ) } } } }
八、权限系统 8.1 检查流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 checkPermissions({ skill, args }, context) │ ├─ 1. Deny 规则检查(最高优先级) │ └─ getRuleByContentsForTool(context, SkillTool, 'deny') │ ├─ 精确匹配: "commit" === commandName │ └─ 前缀匹配: "review:*" → commandName.startsWith("review") │ ├─ 2. 远程 Skill 自动允许 │ ├─ 3. Allow 规则检查 │ ├─ 4. 安全属性自动允许 │ └─ skillHasOnlySafeProperties(command) │ └─ 无 hooks、无 allowedTools、无 fork │ └─ 5. 默认: 询问用户
8.2 安全属性白名单 如果 Skill 只包含以下属性,自动允许:
1 2 3 4 5 6 SAFE_SKILL_PROPERTIES = { type, name, description, contentLength, source, loadedFrom, progressMessage, userInvocable, disableModelInvocation, hasUserSpecifiedDescription, getPromptForCommand, userFacingName, ... }
九、关键源文件索引
文件
职责
src/tools/SkillTool/SkillTool.ts
SkillTool 定义、验证、权限、执行
src/tools/SkillTool/prompt.ts
工具提示词、Skill 列表格式化
src/skills/loadSkillsDir.ts
目录 Skill 发现、加载、去重、条件激活
src/skills/bundledSkills.ts
内置 Skill 注册系统
src/skills/bundled/index.ts
内置 Skills 初始化入口
src/commands.ts
命令聚合、排序、过滤
src/utils/forkedAgent.ts
Fork 上下文准备、结果提取
src/utils/hooks/registerSkillHooks.ts
Skill Hook 注册
十、总结 Claude Code 的 Skills 系统体现了几个核心设计原则:
声明式定义 :通过 Frontmatter 定义 AI 行为
条件激活 :基于文件路径的动态发现
双模式执行 :Inline(注入对话)和 Fork(独立子代理)
上下文修改 :动态调整工具池、模型、effort
Hook 集成 :将 Skill 行为扩展到工具生命周期
权限控制 :安全属性白名单自动允许
这个设计使得用户可以轻松定义新的 AI 行为,而不需要修改代码。
系列文章导航: