Skip to content

Latest commit

 

History

History
303 lines (228 loc) · 9.83 KB

File metadata and controls

303 lines (228 loc) · 9.83 KB

第7章:命令系统——斜杠的魔法

斜杠命令是什么?

在 Claude Code 的对话中,你可以输入以 / 开头的特殊命令:

/commit          ← 创建 Git 提交
/compact         ← 压缩对话历史
/review          ← 代码审查
/theme dark      ← 切换到深色主题
/help            ← 显示帮助信息

这些命令不会被发送给 AI,而是直接由程序处理。它们就像游戏里的"作弊码"——一种快速触发特定功能的方式。

命令的注册中心

所有斜杠命令在 commands.ts(约 25,000 行)中注册。每个命令都有统一的结构:

type Command = {
  name: string          // 命令名,如 "commit"
  help: string          // 帮助文本
  aliases?: string[]    // 别名,如 ["ci"] 是 "commit" 的别名
  priority?: number     // 在命令列表中的排序
  isVisible?: boolean   // 是否在帮助中显示

  handler(input: string, context: CommandContext): void
  // 处理函数:接收用户输入的参数,执行命令

  shouldHighlightInCommandPalette?: (context: CommandContext) => boolean
  // 是否在命令面板中高亮(推荐给用户)
}

看到了吗?每个命令都遵循同一个"模板"。这就是接口的力量——只要你按照模板来写,你的命令就能无缝融入系统。

命令的分类

Claude Code 有 50 多个斜杠命令,我们可以分成几大类:

对话控制类

命令 作用 示例
/compact 压缩对话历史,释放 token 空间 /compact
/context 查看当前的 token 使用情况 /context
/clear 清空当前对话 /clear
/resume 恢复之前的会话 /resume

开发工具类

命令 作用 示例
/commit 创建 Git 提交 /commit
/review 代码审查 /review PR_URL
/diff 查看代码差异 /diff
/doctor 诊断环境问题 /doctor

设置类

命令 作用 示例
/config 编辑配置 /config
/theme 切换主题 /theme light
/vim 切换 Vim 模式 /vim

系统类

命令 作用 示例
/login 登录 /login
/logout 登出 /logout
/cost 查看费用 /cost
/help 帮助 /help

命令的解析流程

当你输入 /commit -m "修复 bug" 时,发生了什么?

用户输入: "/commit -m 修复 bug"
    │
    ▼
1. 检测到以 "/" 开头
   → 这是一个斜杠命令,不是普通消息
    │
    ▼
2. 提取命令名: "commit"
   提取参数: "-m 修复 bug"
    │
    ▼
3. 在命令注册表中查找 "commit"
   → 找到了!CommitCommand
    │
    ▼
4. 检查别名
   如果输入是 "/ci",也会匹配到 CommitCommand
   (因为 "ci" 是它的别名)
    │
    ▼
5. 调用 handler
   CommitCommand.handler("-m 修复 bug", context)
    │
    ▼
6. handler 执行具体逻辑
   → 查看 git 状态
   → 生成提交信息
   → 执行 git commit

深入一个命令:/compact

让我们详细看看 /compact 命令的实现,因为它揭示了一个有趣的问题——上下文窗口的限制

问题:AI 的"短期记忆"

AI 模型有一个叫"上下文窗口"的限制——它一次能"看到"的文本量是有限的。就像你的工作台空间有限,放太多东西就放不下了。

当你和 Claude 对话很久之后,消息越来越多,token 数(可以理解为"文字数")越来越大。最终会接近上下文窗口的上限。

解决方案:压缩

/compact 命令的做法很聪明——它让 AI 总结之前的对话:

// 简化的 /compact 实现逻辑
async function compactHandler(input, context) {
  const messages = context.getMessages()

  // 1. 找到可以压缩的旧消息
  const oldMessages = messages.slice(0, -5)  // 保留最近 5 条
  const recentMessages = messages.slice(-5)

  // 2. 让 AI 总结旧消息
  const summary = await claude.summarize(oldMessages)
  // 例如:"用户在开发一个 React 应用,已经完成了登录页面,
  //        正在处理数据获取的 bug。"

  // 3. 用总结替换旧消息
  context.setMessages([
    { role: "system", content: `之前的对话摘要:${summary}` },
    ...recentMessages,
  ])

  // 4. 通知用户
  console.log(`已压缩 ${oldMessages.length} 条消息,释放了 ${savedTokens} tokens`)
}

这就像做读书笔记:你不需要记住书的每个字,只需要记住关键点。AI 把长长的对话"浓缩"成一段摘要,腾出空间来继续对话。

命令的条件注册

不是所有命令在所有情况下都可用。有些命令需要特定条件才会出现:

export function getAllCommands(): Command[] {
  return [
    // 基础命令,总是可用
    HelpCommand,
    CompactCommand,
    ClearCommand,

    // 需要 Git 环境
    ...(isGitRepo() ? [CommitCommand, DiffCommand] : []),

    // 需要特定功能开关
    ...(feature('VOICE_MODE') ? [VoiceCommand] : []),

    // 需要登录
    ...(isAuthenticated() ? [ShareCommand] : []),
  ]
}

这就像一个游戏:你需要解锁特定成就才能使用某些技能。没有 Git 仓库就没有 /commit,没有登录就没有 /share

命令面板

当你输入 / 但还没输入完命令名时,Claude Code 会显示一个命令面板——列出所有可用命令供你选择:

┌─ Commands ────────────────────────────┐
│  /commit    Create a git commit        │
│  /compact   Compress conversation      │
│  /config    Edit settings              │
│  /context   View token usage           │
│  /diff      View code changes          │
│  /doctor    Diagnose issues            │
│  /help      Show help                  │
│  /review    Review code changes        │
│  /theme     Switch theme               │
└────────────────────────────────────────┘

随着你继续输入,列表会实时过滤:

输入: /co

┌─ Commands ────────────────────────────┐
│  /commit    Create a git commit        │  ← 匹配 "co"
│  /compact   Compress conversation      │  ← 匹配 "co"
│  /config    Edit settings              │  ← 匹配 "co"
│  /context   View token usage           │  ← 匹配 "co"
│  /cost      View usage costs           │  ← 匹配 "co"
└────────────────────────────────────────┘

这种实时过滤是怎么实现的?

function filterCommands(input: string, commands: Command[]): Command[] {
  const query = input.toLowerCase()
  return commands
    .filter(cmd => cmd.name.toLowerCase().startsWith(query))
    .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
}

就是简单的字符串匹配 + 排序。有时候简单的方案就是最好的方案。

自己写一个命令

理解了命令系统后,让我们想象一下怎么写一个新命令。假设我们要写一个 /weather 命令,显示天气信息:

const WeatherCommand: Command = {
  name: "weather",
  help: "显示当前天气",
  aliases: ["w"],

  async handler(input, context) {
    const city = input || "Beijing"

    // 调用天气 API
    const weather = await fetchWeather(city)

    // 显示结果
    context.displayMessage({
      role: "system",
      content: `${city} 的天气:${weather.temperature}°C, ${weather.description}`,
    })
  },
}

然后只需要在 getAllCommands() 里加一行:

export function getAllCommands(): Command[] {
  return [
    // ... 其他命令
    WeatherCommand,  // 新增
  ]
}

就这么简单!这就是好的架构设计的力量——添加新功能只需要两步:实现接口 + 注册。不需要修改其他任何代码。

命令 vs 工具

你可能会困惑:命令和工具有什么区别?

命令 工具
触发者 用户输入 /xxx AI 决定使用
执行者 程序直接执行 AI 请求后程序执行
权限 不需要权限检查 需要权限检查
示例 /commit, /theme Bash, FileRead

简单来说:命令是用户的快捷操作,工具是 AI 的能力扩展。

本章小结

  • 斜杠命令是以 / 开头的特殊命令,由程序直接处理
  • 每个命令遵循统一的接口(name、help、handler 等)
  • 命令可以条件注册——根据环境决定哪些命令可用
  • /compact 命令通过让 AI 总结旧对话来释放 token 空间
  • 命令面板提供实时过滤和搜索
  • 好的架构让添加新命令只需两步:实现 + 注册
  • 命令由用户触发,工具由 AI 触发——这是核心区别

设计启示:接口即契约

命令系统最值得学习的设计思想是接口即契约

Command 接口定义了"一个命令应该是什么样的",这就是一份"契约"。只要你遵守这份契约(提供 name、help、handler),你的代码就能融入系统。

这个原则在现实世界中也很常见:

  • USB 接口是一份契约:任何遵守 USB 协议的设备都能连接电脑
  • 法律格式是一份契约:任何按格式填写的合同都能被法律认可
  • 考试大纲是一份契约:只要覆盖大纲内容,任何教材都能用

当你将来设计系统时,先定义好接口(契约),再实现具体功能。这样其他人(或者你未来的自己)就知道怎么扩展你的系统。

下一章,我们将进入对话引擎篇,深入了解消息系统的工作原理。


本书由 everettjf 使用 Claude Code 分析泄露源码编写 | 保留出处即可自由转载