深度解析 Claude Code 源码(三):命令系统与 SKILL 调用机制

深度解析 Claude Code 源码(三):命令系统与 SKILL 调用机制blockquote 导语 经过上一篇对 上下文管理 的深度剖析 我们已经知道 Claude Code 是如何精打细算地处理超长对话的 接下来 我们将进入 Claude Code 极具扩展性的核心模块 命令系统与 SKILL 调用 了解这部分内容 你将掌握如何为 AI Agent 编写自定义技能 以及它是如何将自然语言转化为实际行动的 参考仓库 源码地址 blockquote

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



 
  
    
    

导语:经过上一篇对《上下文管理》的深度剖析,我们已经知道 Claude Code 是如何精打细算地处理超长对话的。接下来,我们将进入 Claude Code 极具扩展性的核心模块——命令系统与 SKILL 调用。了解这部分内容,你将掌握如何为 AI Agent 编写自定义技能,以及它是如何将自然语言转化为实际行动的。

参考仓库(源码地址):https://github.com/ChinaSiro/claude-code-sourcemap

历史文章:

深度解析 Claude Code 源码(一):整体架构与核心数据流


6.1 命令的三种类型

命令系统的核心定义在 types/command.tsCommand 是一个联合类型:
Command = CommandBase & (PromptCommand | LocalCommand | LocalJSXCommand)

类型 含义 执行方式 典型例子 prompt 生成提示词发给模型 getPromptForCommand() → 注入对话,触发 API /commit, /review, Skills local 本地执行,返回纯文本 load()call(){type: ‘text’, value} /compact, /cost local-jsx 本地执行,渲染 React UI load()call() → ReactNode, 渲染到终端 /help, /config, /mcp
6.2 命令注册机制

commands.ts 是命令注册中心,getCommands() 按以下顺序合并所有来源:

getCommands(cwd) ├── bundledSkills —— 内置 Skills (commit, simplify 等) ├── builtinPluginSkills —— 内置插件提供的 Skills ├── skillDirCommands —— 用户 .claude/skills/ 目录下的自定义 Skills ├── workflowCommands —— Workflow 脚本 ├── pluginCommands —— 第三方插件命令 ├── pluginSkills —— 第三方插件 Skills └── COMMANDS() —— 硬编码的 40+ 内置命令 

Feature Flag 门控: 很多命令通过 feature() 做编译期死代码消除:

const voiceCommand = feature(‘VOICE_MODE’) ? require(‘./commands/voice/index.js’).default : null 

VOICE_MODE 为 false 时,整个 voice 命令代码在构建时被移除。

6.3 命令执行链路

用户输入 /commit fix bug 后的完整路径:

用户输入 “/” ▼ processUserInput() ← processUserInput.ts │ 检测到 “/” 开头 ▼ processSlashCommand() ← processSlashCommand.tsx │ 1. parseSlashCommand() 解析命令名和参数 │ 2. hasCommand() 检查命令是否存在 │ 3. getCommand() 获取命令定义 ▼ getMessagesForSlashCommand() ← 核心分发逻辑 │ ├── case ‘local-jsx’: │ command.load() → mod.call(onDone, context, args) │ 返回 React 组件 → setToolJSX() 渲染到终端 │ 用户操作完成后 onDone() 回调 → resolve Promise │ ├── case ‘local’: │ command.load() → mod.call(args, context) │ 返回 {type: ‘text’, value} 或 {type: ‘compact’} │ 包装成 message 返回 │ └── case ‘prompt’:

 ├── context === 'fork'? → executeForkedSlashCommand() │ 启动子 Agent 执行,隔离上下文 └── 否则 → getMessagesForPromptSlashCommand() command.getPromptForCommand(args, context) → 生成提示词内容 → 包装成 UserMessage → shouldQuery: true (触发 API 调用) 

6.4 三个典型命令精读
1. /help - local-jsx 型 (最简单)
// commands/help/index.ts: const help = { type: ‘local-jsx’, name: ‘help’, description: ‘Show help and available commands’, load: () => import(‘./help.js’), // 懒加载 } satisfies Command

// commands/help/help.tsx: export const call: LocalJSXCommandCall = async (onDone, { options: { commands } }) => { return }

模式: load() 懒加载模块 → call() 返回 React 组件 → 渲染到终端 → 用户关闭时 onDone() 回调。

2. /compact - local 型 (中等复杂度)
// commands/compact/index.ts: const compact = { type: ‘local’, name: ‘compact’, isEnabled: () => !isEnvTruthy(process.env.DISABLE_COMPACT), supportsNonInteractive: true, load: () => import(‘./compact.js’), } satisfies Command 

call() 返回 {type: ‘compact’, compactionResult},这是特殊返回类型,processSlashCommand 对 compact 结果做特殊处理 (重建消息列表)。

3. /commit - prompt 型 (最有代表性)
// commands/commit.ts: const command = ] }, } 

关键设计:

  • allowedTools: 限制模型只能使用 git 相关命令。
  • executeShellCommandsInPrompt: 提示词中的 `git status` 会被预执行,结果内联。
  • shouldQuery: true: 提示词注入对话,触发一次 Claude API 调用。
6.5 命令系统设计总结
┌────────────────────────────────────────────────────────┐ │ Command 统一接口 │ │ name / description / isEnabled / load │ ├───────────────┬───────────────┬────────────────────────┤ │ local-jsx │ local │ prompt │ │ 渲染 UI │ 返回文本 │ 注入提示词 │ │ 不触发模型 │ 不触发模型 │ 触发模型调用 │ │ /help │ /compact │ /commit │ │ /config │ /cost │ /review │ │ /mcp │ /clear │ 所有 Skills │ └───────────────┴───────────────┴────────────────────────┘ 

核心设计点:

  • 懒加载: 所有命令都用 load: () => import(…) 延迟加载,启动时只注册元数据。
  • Skills 即命令: Skills 本质上就是 type: ‘prompt’ 的命令,模型通过 SkillTool 调用。
  • 可用性控制: availability 控制“谁能看到”,isEnabled 控制“是否开启”。
  • Bridge 安全: 远程控制 (手机/桌面端) 只能执行白名单命令,防止越权操作。

Skills 是 Claude Code 的可扩展能力单元。它复用 Command 接口的 type: ‘prompt’ 分支,但来源更多、调用方更广、配置更灵活。

7.1 Skill 与 Command 的根本区别
传统 Command (local / local-jsx) Skill (type: ‘prompt’) 本地执行,不调模型 生成提示词,注入对话,触发模型调用 硬编码在 TypeScript 里 可以是 Markdown 文件 只有用户能调用 ( /) 用户 ( /) 和模型 ( SkillTool) 都能调用 注册方式: import 进数组 注册方式: 5 种来源并行加载 无 allowedTools 可声明 allowedTools 临时授权工具 无 context: fork 可 fork 到子 Agent 隔离执行
7.2 Skill 的 5 种来源
getCommands() ──┬─ Bundled Skills ──────── 编译期内嵌

 │ (skills/bundled/) registerBundledSkill() 注册 │ ├─ Builtin Plugin Skills ─ 内置插件提供 │ (plugins/builtinPlugins.ts) │ ├─ Skill Dir Skills ────── 磁盘 Markdown 文件 │ 三级搜索路径: │ ├── ~/.claude/skills/ (用户级) │ ├── .claude/skills/ (项目级) │ └── managed/.claude/skills/ (企业策略级) │ ├─ Plugin Skills ───────── 第三方插件 │ 通过 plugin install 安装 │ └─ MCP Skills ──────────── MCP 服务器提供 存在 AppState.mcp.commands 中 

7.3 Skill 的完整生命周期 (以 /simplify 为例)
  1. 注册阶段 (启动时): registerBundledSkill() 将其包装成 Command 对象并推入 bundledSkills[] 数组。
  2. 用户调用路径 (/simplify): 解析命令 → getPromptForCommand → 返回提示词 → 包装成 UserMessage → 触发 Claude API 调用。
  3. 模型调用路径 (Claude 自主调用): SkillTool.call → 检查权限 → 判断 fork 模式 → 生成提示词 → 注入当前对话 → 返回 ToolResult。
7.4 磁盘 Skill: 加载与提示词生成

磁盘 Skill 支持目录格式(推荐):

.claude/skills/my-skill/ ├── SKILL.md ← 主文件 (文件名固定) ├── scripts/ ← 资源文件 └── templates/ 

SKILL.md Frontmatter 示例:

— description: “做什么的” allowed-tools:

  • Bash(git:*) when_to_use: “何时使用” context: fork paths: [“src/*/.ts”] —

Skill 提示词正文

你是一个…

提示词生成核心步骤:

  1. 拼上 baseDir 前缀:告诉模型 Skill 目录在哪,方便访问资源文件。
  2. 变量替换:精确匹配 \({CLAUDE_SKILL_DIR}\){CLAUDE_SESSION_ID}\(1、\)2 等。
  3. 执行内嵌 Shell 命令:提示词中的 `command` 会在发给模型前预执行,嵌入实时数据。
7.5 Skill 的三个特殊能力
  1. allowedTools (临时工具授权):执行期间,模型获得额外工具权限,结束后收回。
  2. context: ‘fork’ (子 Agent 隔离执行):启动子 Agent 隔离执行,不污染主对话上下文,只返回结果文本。
  3. paths (条件激活):只有当模型操作了匹配路径的文件时,该 Skill 才被激活。
7.6 动态 Skill 发现

模型对某个文件执行读写操作时,系统会向上遍历目录寻找 .claude/skills/,动态加载新 Skills 并通知缓存清除。

7.7 /skills 命令的展示范围

/skills 是一个 local-jsx 型管理命令,展示用户自己添加的 Skills。内置的 bundled Skills 不在此列表中。

loadedFrom 来源 显示 skills .claude/skills/ 目录 ✅ commands_DEPRECATED .claude/commands/plugin 第三方插件 ✅ mcp MCP 服务器 ✅ bundled 内置 Skills ❌
7.8 Skill 注册字段完整参考 (节选)
  • 身份与展示类: name, description, whenToUse, argumentHint
  • 模型交互类: getPromptForCommand, allowedTools, model, effort
  • 访问控制类: userInvocable, disableModelInvocation, isEnabled, paths
  • 执行模式类: context, agent, shell
  • 生命周期钩子类: hooks, files
7.9 Skills 系统架构总图
┌────────────────────────────────────────────────────────────┐ │ getCommands(cwd) │ │ 合并所有来源,过滤 availability + isEnabled, 去重 │ └─┬──────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ 用户输入 “/” │ │ SkillTool │ │ 系统提示词 │ │ │ │ (模型主动调用) │ │(告知模型有哪些Skill) │ ┌─▼────────────┐ │ └─┬────────────────┘ └──────────────────┘ │ │processSlash..│ │ │ │ └─┬────────────┘ │ │ │ │ │ │ │ ┌─▼────────────────────────────────────────┐ │ │ │ getPromptForCommand(args, context) │ │ │ │ │ │ │ │ ├── Bundled: 直接返回硬编码提示词 │ │ │ │ └── 磁盘: 读 SKILL.md -> 变量替换 │ │ │ │ -> 执行 `!command` 内嵌命令 │ │ │ │ │ │ │ │ 提示词注入对话 -> shouldQuery: true │ │ │ │ │ │ │ │ ▼ │ │ │ │ Claude API 调用 │ │ │ │ ▼ │ │ │ │ 模型根据提示词执行任务 │ │ └─┴────────────────────────────────────────┴─┘ 


以内置 Skill /simplify 为例,从用户按回车到终端渲染结果的完整追踪。

8.1 全景流程图
用户在终端输入 “/simplify” 并按回车 ▼ ① TextInput 组件 (Ink) onSubmit 回调 ▼ ② handlePromptSubmit() ← handlePromptSubmit.ts ▼ ③ executeUserInput() ← handlePromptSubmit.ts (防并发) ▼ ④ processUserInput() ← processUserInput.ts ▼ ⑤ processSlashCommand() ← processSlashCommand.tsx ▼ ⑥ getMessagesForPromptSlashCommand() (生成提示词) ▼ ⑦ onQuery() ← REPL.tsx (构建 systemPrompt) ▼ ⑧ query() → queryLoop() ← query.ts (调用 API) 终端渲染 Claude 的回复 

8.2 逐层详解 (核心代码)

⑥ getMessagesForPromptSlashCommand → 提示词生成 (核心)

async function getMessagesForPromptSlashCommand(command, args, context, …) ), // 消息1: 元数据 (用户可见)

createUserMessage({ content: result, isMeta: true }), // 消息2: 提示词正文 (模型可见) createAttachmentMessage({ type: 'command_permissions', allowedTools: ... }), // 权限声明 

] return { messages, shouldQuery: true, allowedTools: additionalAllowedTools, command } }

8.3 最终 API 请求结构

调用 API 时,/simplify 生成的消息在请求中大致如下:

,

] }

8.4 关键设计总结
设计点 实现 提示词注入 Skill 内容作为 isMeta: true 的 UserMessage,用户隐藏,模型可见 权限临时授权 allowedTools 写入 AppState,仅本 turn 有效 并发保护 QueryGuard 保证同一时刻只有一个 executeUserInput 在运行 可中断 每次执行创建新的 AbortController,用户按 ESC 触发 abort

一句话总结 Skill 的本质
提示词注入器 —— 把预写好的提示词作为用户消息注入对话,触发一次 Claude API 调用。Skill 本身不执行任何逻辑,所有工作都是模型读到提示词后自主完成的。


结语:至此,我们完整走通了 Skill 的端到端执行链路,揭开了 Claude Code 强大扩展能力的神秘面纱。

小讯
上一篇 2026-04-15 18:16
下一篇 2026-04-15 18:14

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/263970.html