返回

Claude Code 源码分析 2:Commands 命令系统深度解析

深入分析 Claude Code 的 Commands 命令系统,包括 88+ 个命令的组织方式、三种命令类型(prompt/local/local-jsx)、延迟加载机制,以及如何扩展自定义命令。

本章概述

系列目录:Claude Code 源码分析系列索引

本章深入分析 Claude Code 的 Commands 命令系统,这是用户与 AI 交互的主要入口。Claude Code 支持 88+ 个内置命令,通过 /command-name 的方式调用。

你将学到

  • ✅ 三种命令类型的区别与应用场景
  • ✅ 命令系统的架构设计与延迟加载
  • ✅ 如何编写自定义命令
  • ✅ Prompt 命令的工作原理

命令系统概览

flowchart TB
    subgraph Commands["Commands 系统"]
        A[commands.ts] --> B[88+ 命令实现]
        B --> C1[PromptCommand]
        B --> C2[LocalCommand]
        B --> C3[LocalJSXCommand]
    end
    
    subgraph Registry["注册中心"]
        D[Commander.js] --> E[命令解析]
        E --> F[命令匹配]
        F --> G[命令执行]
    end
    
    subgraph Types["命令类型"]
        C1 --> H1[提示词模板]
        C2 --> H2[本地函数]
        C3 --> H3[React 组件]
    end
    
    A --> D

核心文件结构

src/
├── commands.ts              # 命令注册中心
├── commands/                # 88+ 命令实现
│   ├── init.ts             # /init 命令
│   ├── commit.ts           # /commit 命令
│   ├── skills/             # /skills 命令目录
│   ├── tasks/              # /tasks 命令目录
│   └── ...                 # 其他命令
└── types/command.ts        # 命令类型定义

命令类型系统

Claude Code 定义了三种命令类型,位于 src/types/command.ts

// types/command.ts:205-206
export type Command = CommandBase &
  (PromptCommand | LocalCommand | LocalJSXCommand)

1. PromptCommand - 提示词命令

特点:将用户输入转换为 AI 提示词

// types/command.ts:25-57
export type PromptCommand = {
  type: 'prompt'
  progressMessage: string
  contentLength: number
  argNames?: string[]
  allowedTools?: string[]  // 限制可用工具
  model?: string          // 指定模型
  source: SettingSource | 'builtin' | 'mcp' | 'plugin' | 'bundled'
  hooks?: HooksSettings   // 关联的 hooks
  context?: 'inline' | 'fork'  // 执行上下文
  agent?: string          // fork 模式下使用的代理类型
  effort?: EffortValue    // 工作量级别
  paths?: string[]        // 适用文件路径
  getPromptForCommand(
    args: string,
    context: ToolUseContext,
  ): Promise<ContentBlockParam[]>
}

典型示例/commit 命令

// commands/commit.ts:57-92
const command = {
  type: 'prompt',
  name: 'commit',
  description: 'Create a git commit',
  allowedTools: [
    'Bash(git add:*)',
    'Bash(git status:*)',
    'Bash(git commit:*)',
  ],
  contentLength: 0, // 动态内容
  progressMessage: 'creating commit',
  source: 'builtin',
  async getPromptForCommand(_args, context) {
    const promptContent = getPromptContent()
    const finalContent = await executeShellCommandsInPrompt(
      promptContent,
      context,
      '/commit',
    )
    return [{ type: 'text', text: finalContent }]
  },
} satisfies Command

2. LocalCommand - 本地命令

特点:执行本地 JavaScript/TypeScript 函数

// types/command.ts:62-78
export type LocalCommandCall = (
  args: string,
  context: LocalJSXCommandContext,
) => Promise<LocalCommandResult>

type LocalCommand = {
  type: 'local'
  supportsNonInteractive: boolean
  load: () => Promise<LocalCommandModule>  // 延迟加载
}

export type LocalCommandModule = {
  call: LocalCommandCall
}

执行流程

sequenceDiagram
    participant User as 用户
    participant CLI as Commander
    participant Loader as 延迟加载器
    participant Module as 命令模块
    
    User->>CLI: /command args
    CLI->>Loader: load()
    Loader->>Module: require('./command')
    Module-->>Loader: { call }
    Loader-->>CLI: LocalCommandModule
    CLI->>Module: call(args, context)
    Module-->>CLI: LocalCommandResult
    CLI-->>User: 显示结果

3. LocalJSXCommand - React 命令

特点:渲染 React 组件到终端 UI

// types/command.ts:131-152
export type LocalJSXCommandCall = (
  onDone: LocalJSXCommandOnDone,
  context: ToolUseContext & LocalJSXCommandContext,
  args: string,
) => Promise<React.ReactNode>

type LocalJSXCommand = {
  type: 'local-jsx'
  load: () => Promise<LocalJSXCommandModule>  // 延迟加载
}

export type LocalJSXCommandModule = {
  call: LocalJSXCommandCall
}

应用场景:需要复杂交互的命令,如配置向导、多步骤表单。

命令基础属性

所有命令共享的基础属性(CommandBase):

// types/command.ts:175-203
export type CommandBase = {
  availability?: CommandAvailability[]  // 可用性限制
  description: string                   // 命令描述
  hasUserSpecifiedDescription?: boolean // 用户自定义描述
  isEnabled?: () => boolean            // 是否启用
  isHidden?: boolean                   // 是否隐藏
  name: string                         // 命令名
  aliases?: string[]                   // 别名
  isMcp?: boolean                      // 是否 MCP 命令
  argumentHint?: string                // 参数提示
  whenToUse?: string                   // 使用场景说明
  version?: string                     // 版本
  disableModelInvocation?: boolean     // 禁用模型调用
  userInvocable?: boolean              // 用户可调用
  loadedFrom?: 'commands_DEPRECATED' | 'skills' | 'plugin' | 'managed' | 'bundled' | 'mcp'
  kind?: 'workflow'                    // 工作流类型
  immediate?: boolean                  // 立即执行
  isSensitive?: boolean                // 敏感命令(参数脱敏)
  userFacingName?: () => string        // 显示名称
}

命令注册机制

集中式注册

所有命令在 commands.ts 中导入并导出:

// commands.ts
import init from './commands/init.js'
import commit from './commands/commit.js'
import skills from './commands/skills/index.js'
// ... 88+ 个导入

export function getCommands() {
  return {
    init,
    commit,
    skills,
    // ...
  }
}

Commander.js 集成

使用 Commander.js 处理 CLI 参数解析:

// main.tsx 中的命令注册
import { Command as CommanderCommand } from '@commander-js/extra-typings';

const program = new CommanderCommand()
  .name('claude')
  .description('Claude Code CLI')
  .version(version);

// 注册所有命令
const commands = getCommands()
for (const [name, command] of Object.entries(commands)) {
  program.addCommand(createCommanderCommand(name, command))
}

延迟加载机制

为什么需要延迟加载?

Claude Code 有 88+ 个命令,如果全部在启动时加载:

  1. 启动慢 - 需要解析和执行所有命令模块
  2. 内存占用高 - 加载不必要的依赖
  3. 循环依赖风险 - 模块间可能相互依赖

延迟加载实现

// 延迟加载模式
const myCommand = {
  type: 'local',
  name: 'heavy-command',
  load: () => import('./heavy-command/index.js'),
  // 只有在执行时才调用 load()
}

对比直接导入 vs 延迟加载

// ❌ 直接导入 - 启动时加载
import heavyCommand from './commands/heavy/index.js'

// ✅ 延迟加载 - 使用时加载
const heavyCommand = {
  type: 'local',
  load: () => import('./commands/heavy/index.js'),
}

命令可用性控制

基于认证的可用性

// types/command.ts:169-174
export type CommandAvailability =
  | 'claude-ai'    // claude.ai OAuth 订阅者
  | 'console'      // Console API key 用户

// 使用示例
const command = {
  name: 'premium-feature',
  availability: ['claude-ai'],  // 仅付费用户可用
  // ...
}

动态启用控制

const command = {
  name: 'experimental',
  isEnabled: () => feature('EXPERIMENTAL_FLAG'),
  // ...
}

实战:编写自定义命令

示例 1:简单的 Prompt 命令

// commands/my-command.ts
import type { Command } from '../commands.js'

const command: Command = {
  type: 'prompt',
  name: 'my-command',
  description: 'My custom command',
  progressMessage: 'processing',
  source: 'builtin',
  contentLength: 100,
  async getPromptForCommand(args, context) {
    return [
      {
        type: 'text',
        text: `Please help me with: ${args}`
      }
    ]
  }
}

export default command

示例 2:Local 命令

// commands/my-local-command/index.ts
import type { LocalCommandModule } from '../../types/command.js'

export const call: LocalCommandModule['call'] = async (args, context) => {
  // 执行本地逻辑
  const result = await doSomething(args)
  
  return {
    type: 'text',
    value: result
  }
}

示例 3:LocalJSX 命令

// commands/my-jsx-command/index.tsx
import React from 'react'
import { Box, Text } from 'ink'
import type { LocalJSXCommandModule } from '../../types/command.js'

export const call: LocalJSXCommandModule['call'] = async (onDone, context, args) => {
  const [count, setCount] = React.useState(0)
  
  return (
    <Box flexDirection="column">
      <Text>Count: {count}</Text>
      <Text>Press Enter to continue...</Text>
    </Box>
  )
}

命令执行上下文

LocalJSXCommandContext

// types/command.ts:80-98
export type LocalJSXCommandContext = ToolUseContext & {
  canUseTool?: CanUseToolFn
  setMessages: (updater: (prev: Message[]) => Message[]) => void
  options: {
    dynamicMcpConfig?: Record<string, ScopedMcpServerConfig>
    ideInstallationStatus: IDEExtensionInstallationStatus | null
    theme: ThemeName
  }
  onChangeAPIKey: () => void
  onChangeDynamicMcpConfig?: (config: Record<string, ScopedMcpServerConfig>) => void
  onInstallIDEExtension?: (ide: IdeType) => void
  resume?: (sessionId: UUID, log: LogOption, entrypoint: ResumeEntrypoint) => Promise<void>
}

onDone 回调

// types/command.ts:117-126
export type LocalJSXCommandOnDone = (
  result?: string,
  options?: {
    display?: CommandResultDisplay  // 'skip' | 'system' | 'user'
    shouldQuery?: boolean          // 是否发送给模型
    metaMessages?: string[]        // 元消息
    nextInput?: string            // 下一个输入
    submitNextInput?: boolean     // 是否自动提交
  },
) => void

命令分类

内置命令分类

类别 命令示例 说明
Git 操作 /commit, /branch 版本控制相关
会话管理 /clear, /compact, /resume 会话生命周期
配置 /config, /theme, /settings 用户配置
开发工具 /review, /test, /debug 开发辅助
代理 /agents, /team 子代理管理
技能 /skills 技能系统
插件 /plugin, /mcp 扩展管理
诊断 /doctor, /cost 系统诊断

最佳实践

1. 命令设计原则

// ✅ 好的命令设计
const goodCommand = {
  name: 'clear',
  description: 'Clear the conversation', // 清晰简洁
  progressMessage: 'clearing',           // 进行时状态
  isEnabled: () => hasActiveSession(),   // 条件启用
  // ...
}

// ❌ 避免
const badCommand = {
  name: 'do-something-vague',
  description: 'This command does something', // 描述不清
  // 缺少 progressMessage
  // ...
}

2. 延迟加载优先

// ✅ 对于复杂命令,使用延迟加载
const heavyCommand = {
  type: 'local',
  load: () => import('./heavy/index.js'), // 动态导入
}

// ❌ 避免直接导入重型依赖
import { heavyDependency } from 'heavy-lib' // 启动时加载

3. 工具权限控制

// ✅ 限制命令可用工具
const safeCommand = {
  type: 'prompt',
  allowedTools: [
    'Bash(git add:*)',      // 只允许特定 git 命令
    'Bash(git status:*)',
  ],
  // ...
}

小结

本章深入分析了 Claude Code 的命令系统:

关键概念

概念 说明 应用场景
PromptCommand 生成提示词给 AI 需要 AI 处理的任务
LocalCommand 执行本地函数 纯本地操作
LocalJSXCommand 渲染 React UI 交互式命令
延迟加载 使用时才加载 减少启动时间
可用性控制 基于认证/标志 功能灰度发布

架构优势

  1. 类型安全 - TypeScript 类型定义完整
  2. 可扩展 - 三种命令类型覆盖各种场景
  3. 高性能 - 延迟加载减少启动时间
  4. 灵活 - 支持条件启用和权限控制

学习收获

  • 🎯 命令模式实践 - 88+ 命令的统一管理
  • 🔄 延迟加载策略 - 大型应用的启动优化
  • 🎨 类型设计 - TypeScript 高级类型应用
  • 🛡️ 权限控制 - 基于认证的可用性管理

系列导航

参考资源