Skip to content

Latest commit

 

History

History
1045 lines (870 loc) · 36.3 KB

File metadata and controls

1045 lines (870 loc) · 36.3 KB

05 - 斜杠命令系统 (Slash Command System)

基于 Claude Code v2.1.88 源码分析 核心文件: src/types/command.ts, src/commands.ts, src/utils/processUserInput/


Architecture Diagram

Command System Architecture

1. 命令类型系统

Claude Code 的命令系统建立在一个精心设计的 TypeScript 类型体系之上。所有命令共享 CommandBase 基础类型,通过判别式联合 (discriminated union) 分为三种执行模式。

1.1 CommandBase -- 共享基础

type CommandBase = {
  availability?: CommandAvailability[]  // 认证/平台门控
  description: string                    // 用户可见描述
  hasUserSpecifiedDescription?: boolean  // 是否有用户自定义描述
  isEnabled?: () => boolean              // 动态启用/禁用 (默认 true)
  isHidden?: boolean                     // 从 typeahead/help 中隐藏 (默认 false)
  name: string                           // 唯一标识名
  aliases?: string[]                     // 别名列表
  isMcp?: boolean                        // 是否为 MCP 命令
  argumentHint?: string                  // 参数提示 (灰色显示)
  whenToUse?: string                     // 技能规范中的使用场景描述
  version?: string                       // 命令/技能版本
  disableModelInvocation?: boolean       // 禁止模型调用此命令
  userInvocable?: boolean                // 用户是否可直接 /name 调用
  loadedFrom?: 'commands_DEPRECATED' | 'skills' | 'plugin'
             | 'managed' | 'bundled' | 'mcp'  // 来源标记
  kind?: 'workflow'                      // 工作流命令标识
  immediate?: boolean                    // 立即执行,不等待队列停顿
  isSensitive?: boolean                  // 参数脱敏 (历史记录中显示 ***)
  userFacingName?: () => string          // 显示名 (可能与 name 不同)
}

1.2 三种命令类型

PromptCommand -- 提示词注入型

生成提示词内容注入到对话中,由模型执行实际操作。适用于需要模型推理的复杂任务。

type PromptCommand = {
  type: 'prompt'
  progressMessage: string           // 执行中的进度文本
  contentLength: number             // 内容长度 (用于 token 估算)
  argNames?: string[]               // 参数名列表
  allowedTools?: string[]           // 授予模型的额外工具权限
  model?: string                    // 指定模型别名
  source: SettingSource | 'builtin' | 'mcp' | 'plugin' | 'bundled'
  pluginInfo?: { pluginManifest: PluginManifest; repository: string }
  context?: 'inline' | 'fork'      // inline=内联展开, fork=子代理执行
  agent?: string                    // fork 模式下的代理类型
  effort?: EffortValue              // 推理深度
  paths?: string[]                  // glob 匹配模式,限定可见性
  hooks?: HooksSettings             // 技能专用钩子
  skillRoot?: string                // 技能资源根目录
  getPromptForCommand(
    args: string,
    context: ToolUseContext
  ): Promise<ContentBlockParam[]>   // 核心: 生成注入到对话的内容
}

关键特性:

  • context: 'fork' 模式下命令在独立子代理中运行,拥有独立 token 预算
  • allowedTools 可为技能临时授权额外工具 (如 /commit 授权 Bash(git add:*))
  • paths 实现延迟发现: 仅当模型操作匹配路径的文件后,技能才变为可见

LocalCommand -- 本地执行型

在 Node.js 进程中同步执行,返回文本结果。不渲染 UI 组件。

type LocalCommand = {
  type: 'local'
  supportsNonInteractive: boolean   // 是否支持 -p 模式
  load: () => Promise<LocalCommandModule>
}

type LocalCommandModule = {
  call: (args: string, context: LocalJSXCommandContext) => Promise<LocalCommandResult>
}

type LocalCommandResult =
  | { type: 'text'; value: string }                    // 文本输出
  | { type: 'compact'; compactionResult: CompactionResult; displayText?: string }  // 压缩操作
  | { type: 'skip' }                                   // 静默完成

LocalJSXCommand -- JSX 交互型

渲染 React/Ink 组件到终端,支持完整的交互式 UI (列表选择、表单输入等)。

type LocalJSXCommand = {
  type: 'local-jsx'
  load: () => Promise<LocalJSXCommandModule>
}

type LocalJSXCommandModule = {
  call: (
    onDone: LocalJSXCommandOnDone,
    context: ToolUseContext & LocalJSXCommandContext,
    args: string
  ) => Promise<React.ReactNode>
}

type LocalJSXCommandOnDone = (
  result?: string,
  options?: {
    display?: 'skip' | 'system' | 'user'  // 结果显示方式
    shouldQuery?: boolean                   // 完成后是否触发模型查询
    metaMessages?: string[]                 // 模型可见但用户隐藏的附加消息
    nextInput?: string                      // 链式命令输入
    submitNextInput?: boolean               // 自动提交下一条输入
  }
) => void

1.3 最终联合类型

type Command = CommandBase & (PromptCommand | LocalCommand | LocalJSXCommand)

1.4 辅助函数

// 解析用户可见名称,优先使用 userFacingName()
function getCommandName(cmd: CommandBase): string {
  return cmd.userFacingName?.() ?? cmd.name
}

// 判断命令是否启用,默认 true
function isCommandEnabled(cmd: CommandBase): boolean {
  return cmd.isEnabled?.() ?? true
}

2. CommandAvailability -- 认证/平台门控

2.1 可用性类型

type CommandAvailability =
  | 'claude-ai'   // claude.ai OAuth 订阅用户 (Pro/Max/Team/Enterprise)
  | 'console'     // Console API Key 用户 (直连 api.anthropic.com)

2.2 门控逻辑

availabilityisEnabled 是两个独立维度:

维度 含义 评估时机
availability 用 (认证/平台要求,静态) 每次 getCommands() 调用
isEnabled() 现在开不开 (功能标志/环境变量,动态) 每次 getCommands() 调用
function meetsAvailabilityRequirement(cmd: Command): boolean {
  if (!cmd.availability) return true  // 无声明 = 全平台可用
  for (const a of cmd.availability) {
    switch (a) {
      case 'claude-ai':
        if (isClaudeAISubscriber()) return true
        break
      case 'console':
        // 排除 3P (Bedrock/Vertex/Foundry) 和自定义 base URL 用户
        if (!isClaudeAISubscriber() && !isUsing3PServices()
            && isFirstPartyAnthropicBaseUrl())
          return true
        break
    }
  }
  return false
}

2.3 使用 availability 的命令

命令 availability 说明
/upgrade ['claude-ai'] 升级到 Max 计划
/usage ['claude-ai'] 查看计划用量
/voice ['claude-ai'] 语音模式
/chrome ['claude-ai'] Chrome 扩展设置
/desktop ['claude-ai'] 桌面应用跳转
/mobile 无 (全平台) 移动端 QR 码
/install-github-app ['claude-ai', 'console'] GitHub Actions 安装
/fast ['claude-ai', 'console'] 快速模式切换
/install-slack-app ['claude-ai'] Slack 应用安装
/web-setup ['claude-ai'] Web 远程环境设置

3. 命令注册与加载流程

3.1 多源命令组装

┌─────────────────────────────────────────────────┐
│             loadAllCommands(cwd)                 │
│        (memoized, 仅首次执行加载)                  │
│                                                  │
│  ┌──────────────┐  ┌──────────────┐             │
│  │ bundledSkills │  │builtinPlugin │             │
│  │ (内置技能)     │  │  Skills      │             │
│  └──────┬───────┘  └──────┬───────┘             │
│         │                  │                     │
│  ┌──────┴──────┐  ┌───────┴───────┐             │
│  │ skillDir    │  │ workflow      │             │
│  │ Commands    │  │ Commands      │             │
│  │(.claude/    │  │(WORKFLOW_     │             │
│  │  skills/)   │  │ SCRIPTS flag) │             │
│  └──────┬──────┘  └───────┬───────┘             │
│         │                  │                     │
│  ┌──────┴──────┐  ┌───────┴───────┐             │
│  │ plugin      │  │ plugin        │             │
│  │ Commands    │  │ Skills        │             │
│  └──────┬──────┘  └───────┬───────┘             │
│         │                  │                     │
│         └───────┬──────────┘                     │
│                 ▼                                │
│         ┌──────────────┐                         │
│         │  COMMANDS()  │  ◄── 内置命令列表         │
│         └──────────────┘                         │
└─────────────────────────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────────┐
│           getCommands(cwd)                       │
│     (每次调用都重新过滤)                            │
│                                                  │
│  1. loadAllCommands(cwd)  ← memoized             │
│  2. getDynamicSkills()    ← 文件操作中发现的技能     │
│  3. filter:                                      │
│     - meetsAvailabilityRequirement()             │
│     - isCommandEnabled()                         │
│  4. dedupe dynamic skills                        │
│  5. 返回最终命令列表                                │
└─────────────────────────────────────────────────┘

为什么不 memoize getCommands: 认证状态可以在会话中改变 (如用户执行 /login),所以 availability 和 isEnabled 检查必须每次重新评估。

3.2 COMMANDS() -- 内置命令列表

使用 lodash/memoize 延迟初始化,因为底层函数在模块加载时读取配置会报错:

const COMMANDS = memoize((): Command[] => [
  addDir, advisor, agents, branch, btw, chrome, clear, color,
  compact, config, copy, desktop, context, contextNonInteractive,
  cost, diff, doctor, effort, exit, fast, files, heapDump,
  help, ide, init, keybindings, installGitHubApp, installSlackApp,
  mcp, memory, mobile, model, outputStyle, remoteEnv, plugin,
  pr_comments, releaseNotes, reloadPlugins, rename, resume,
  session, skills, stats, status, statusline, stickers, tag,
  theme, feedback, review, ultrareview, rewind, securityReview,
  terminalSetup, upgrade, extraUsage, extraUsageNonInteractive,
  rateLimitOptions, usage, usageReport, vim,
  // Feature-gated commands (条件包含)
  ...(webCmd ? [webCmd] : []),
  ...(forkCmd ? [forkCmd] : []),
  ...(buddy ? [buddy] : []),
  ...(proactive ? [proactive] : []),
  ...(briefCommand ? [briefCommand] : []),
  ...(assistantCommand ? [assistantCommand] : []),
  ...(bridge ? [bridge] : []),
  ...(voiceCommand ? [voiceCommand] : []),
  thinkback, thinkbackPlay, permissions, plan, privacySettings,
  hooks, exportCommand, sandboxToggle,
  ...(!isUsing3PServices() ? [logout, login()] : []),  // 3P 用户不显示登录/登出
  passes, tasks,
  ...(workflowsCmd ? [workflowsCmd] : []),
  ...(torch ? [torch] : []),
  // Internal-only: 仅 Anthropic 员工可见
  ...(process.env.USER_TYPE === 'ant' && !process.env.IS_DEMO
      ? INTERNAL_ONLY_COMMANDS : []),
])

3.3 惰性加载模式

所有 locallocal-jsx 命令使用 load() 延迟导入:

// 命令注册只声明元数据,不加载实现
const compact = {
  type: 'local',
  name: 'compact',
  description: 'Clear conversation history...',
  load: () => import('./compact.js'),  // 用户调用时才加载
} satisfies Command

这种模式确保启动时只解析元数据,实际模块在首次调用时按需加载。


4. 命令分发流程

4.1 输入处理总览

用户输入 "/compact some args"
         │
         ▼
┌──────────────────────────────┐
│   processUserInput()         │
│   processUserInput.ts        │
│                              │
│   1. 解析输入类型 (string/   │
│      ContentBlockParam[])    │
│   2. 处理图片/粘贴内容       │
│   3. Bridge 安全检查         │
│   4. Ultraplan 关键词检测    │
│   5. 识别 "/" 前缀           │
│      → processSlashCommand() │
└──────────┬───────────────────┘
           │
           ▼
┌──────────────────────────────┐
│   processSlashCommand()      │
│   processSlashCommand.tsx    │
│                              │
│   1. parseSlashCommand()     │
│      → {commandName, args,   │
│         isMcp}               │
│   2. hasCommand() 检查       │
│   3. looksLikeCommand() 判断 │
│      (排除文件路径误识别)     │
│   4. getMessagesForSlash...()│
└──────────┬───────────────────┘
           │
           ▼
┌──────────────────────────────┐
│ getMessagesForSlashCommand() │
│                              │
│  1. getCommand() 解析命令    │
│  2. recordSkillUsage() 追踪  │
│  3. userInvocable 检查       │
│  4. switch (command.type):   │
│     ├─ 'local-jsx' ──────►  │
│     │   load() → mod.call() │
│     │   → setToolJSX(jsx)   │
│     │   → onDone() 回调     │
│     ├─ 'local' ──────────►  │
│     │   load() → mod.call() │
│     │   → LocalCommandResult│
│     └─ 'prompt' ─────────►  │
│         ├─ fork? ──► executeForkedSlashCommand()
│         │            (子代理运行)
│         └─ inline? ► getMessagesForPromptSlashCommand()
│                      (生成提示词注入对话)
└──────────────────────────────┘

4.2 斜杠命令解析

// src/utils/slashCommandParsing.ts
type ParsedSlashCommand = {
  commandName: string
  args: string
  isMcp: boolean
}

function parseSlashCommand(input: string): ParsedSlashCommand | null {
  // "/search foo bar" → { commandName: 'search', args: 'foo bar', isMcp: false }
  // "/mcp:tool (MCP) arg1" → { commandName: 'mcp:tool (MCP)', args: 'arg1', isMcp: true }
}

4.3 三种类型的分发细节

local-jsx 分发

case 'local-jsx': {
  return new Promise<SlashCommandResult>(resolve => {
    let doneWasCalled = false
    const onDone = (result?, options?) => {
      doneWasCalled = true
      // display: 'skip' → 无消息
      // display: 'system' → 命令输入/输出消息 (不发送到模型)
      // default → 用户消息 (发送到模型)
      resolve({ messages, shouldQuery: options?.shouldQuery ?? false, command })
    }
    command.load()
      .then(mod => mod.call(onDone, context, args))
      .then(jsx => {
        if (doneWasCalled) return  // onDone 在 call() 中已触发
        setToolJSX({
          jsx,
          shouldHidePromptInput: true,   // 隐藏输入框
          isLocalJSXCommand: true,       // 标记为 JSX 命令
          isImmediate: command.immediate  // 立即命令不等队列
        })
      })
  })
}

local 分发

case 'local': {
  const mod = await command.load()
  const result = await mod.call(args, context)
  switch (result.type) {
    case 'skip':    return { messages: [], shouldQuery: false }
    case 'compact': // 特殊处理: 重建消息列表
    case 'text':    return { messages: [userMsg, commandOutputMsg], shouldQuery: false }
  }
}

prompt 分发

case 'prompt': {
  if (command.context === 'fork') {
    return executeForkedSlashCommand(...)  // 子代理路径
  }
  return getMessagesForPromptSlashCommand(...)  // 内联路径
}

内联路径生成的消息结构:

  1. ProgressMessage -- 显示 "Loading /command..."
  2. 调用 command.getPromptForCommand(args, context) 获取内容
  3. 注册技能钩子 (registerSkillHooks)
  4. 添加技能调用记录 (addInvokedSkill)
  5. 构建 UserMessage 带命令标签和技能内容
  6. 设置 shouldQuery: true 触发模型响应

4.4 Forked 命令执行

context: 'fork' 的命令在子代理中运行:

┌─────────────────────────────────────────┐
│  executeForkedSlashCommand()            │
│                                         │
│  1. prepareForkedCommandContext()       │
│     - 获取技能内容                       │
│     - 创建隔离的 getAppState            │
│     - 选择代理定义                       │
│                                         │
│  2. Kairos 模式?                        │
│     ├─ 是: 后台 fire-and-forget         │
│     │   - 等待 MCP settle               │
│     │   - runAgent() 异步               │
│     │   - enqueuePendingNotification()  │
│     │   - 立即返回空消息                 │
│     └─ 否: 同步执行                     │
│         - runAgent() 带进度 UI          │
│         - 实时更新 setToolJSX()         │
│         - 返回结果文本                   │
└─────────────────────────────────────────┘

5. Remote 模式与 Bridge 安全

5.1 Remote Safe 命令

--remote 模式下仅允许不依赖本地文件系统/shell 的命令:

const REMOTE_SAFE_COMMANDS: Set<Command> = new Set([
  session, exit, clear, help, theme, color, vim,
  cost, usage, copy, btw, feedback, plan, keybindings,
  statusline, stickers, mobile,
])

5.2 Bridge Safe 命令

通过 Remote Control (手机/Web 客户端) 发送的命令需要额外安全检查:

function isBridgeSafeCommand(cmd: Command): boolean {
  if (cmd.type === 'local-jsx') return false   // 渲染 Ink UI,阻止
  if (cmd.type === 'prompt') return true        // 展开为文本,安全
  return BRIDGE_SAFE_COMMANDS.has(cmd)          // local 型需要显式允许
}

const BRIDGE_SAFE_COMMANDS: Set<Command> = new Set([
  compact, clear, cost, summary, releaseNotes, files,
])

原则:

  • prompt 类型天然安全 (只生成文本)
  • local-jsx 类型全部阻止 (需要终端渲染)
  • local 类型需要显式列入白名单

6. Feature-Gated 命令

6.1 Bun Bundle Feature Flags

使用 bun:bundlefeature() 实现编译时死代码消除:

import { feature } from 'bun:bundle'

const proactive = feature('PROACTIVE') || feature('KAIROS')
  ? require('./commands/proactive.js').default : null

const voiceCommand = feature('VOICE_MODE')
  ? require('./commands/voice/index.js').default : null
Feature Flag 命令 说明
PROACTIVE / KAIROS /proactive 主动建议模式
KAIROS / KAIROS_BRIEF /brief 简报模式
KAIROS /assistant 助手模式
BRIDGE_MODE /bridge Bridge 连接管理
DAEMON + BRIDGE_MODE /remote-control-server 远程控制服务器
VOICE_MODE /voice 语音模式
HISTORY_SNIP /force-snip 强制历史剪切
WORKFLOW_SCRIPTS /workflows 工作流管理
CCR_REMOTE_SETUP /web-setup Web 远程环境设置
EXPERIMENTAL_SKILL_SEARCH (清除索引缓存) 技能搜索实验功能
KAIROS_GITHUB_WEBHOOKS /subscribe-pr PR Webhook 订阅
ULTRAPLAN /ultraplan 超级计划 (CCR 远端执行)
TORCH /torch Torch 调试工具
UDS_INBOX /peers 对等通信
FORK_SUBAGENT /fork 子代理分叉
BUDDY /buddy Buddy 协作模式
MCP_SKILLS (MCP 技能过滤) MCP 技能发现
COORDINATOR_MODE (协调器分发逻辑) 工作者协调

6.2 Internal-Only 命令

INTERNAL_ONLY_COMMANDS 仅在 USER_TYPE === 'ant' 且非 demo 模式下注入:

const INTERNAL_ONLY_COMMANDS = [
  backfillSessions,  // 会话回填
  breakCache,        // 缓存清除
  bughunter,         // Bug 猎手
  commit,            // Git 提交
  commitPushPr,      // 提交+推送+PR
  ctx_viz,           // 上下文可视化
  goodClaude,        // (已 stub)
  issue,             // 创建 Issue
  initVerifiers,     // 初始化验证器
  forceSnip?,        // 强制历史剪切
  mockLimits,        // 模拟限流
  bridgeKick,        // Bridge 踢出
  version,           // 版本信息
  ultraplan?,        // 超级计划
  subscribePr?,      // PR 订阅
  resetLimits,       // 重置限流
  resetLimitsNonInteractive,
  onboarding,        // 引导流程
  share,             // 分享会话
  summary,           // 会话摘要
  teleport,          // 远程传送
  antTrace,          // 追踪调试
  perfIssue,         // 性能 Issue
  env,               // 环境变量查看
  oauthRefresh,      // OAuth 刷新
  debugToolCall,     // 工具调用调试
  agentsPlatform,    // 代理平台
  autofixPr,         // 自动修复 PR
]

6.3 Agents Platform 条件加载

const agentsPlatform =
  process.env.USER_TYPE === 'ant'
    ? require('./commands/agents-platform/index.js').default
    : null

此命令使用运行时环境变量检查而非 feature(),因为它需要在非 Bun 构建环境中也能正确排除。


7. 命令完整清单

7.1 通用命令 (所有用户)

会话管理

命令 别名 类型 说明
/clear local 清除对话历史
/compact local 压缩对话历史,保留摘要
/resume /continue local-jsx 恢复之前的对话
/rename local-jsx 重命名当前对话
/branch /fork local-jsx 在当前位置创建对话分支
/rewind /checkpoint local 回退代码和/或对话到先前状态
/export local-jsx 导出对话到文件或剪贴板
/tag local-jsx 为会话添加可搜索标签

模型与推理

命令 类型 说明
/model local-jsx 切换 AI 模型
/effort local-jsx 设置推理深度
/plan local-jsx 启用/查看计划模式
/advisor local 配置 Advisor 双模型审查

配置与设置

命令 别名 类型 说明
/config /settings local-jsx 打开配置面板
/memory local-jsx 编辑 CLAUDE.md 记忆文件
/permissions /allowed-tools local-jsx 管理工具权限规则
/privacy-settings local-jsx 查看/更新隐私设置
/hooks local-jsx 查看钩子配置
/keybindings local 打开快捷键配置文件
/sandbox local-jsx 切换沙箱模式

代码审查与 Git

命令 类型 说明
/review prompt 审查 Pull Request (本地 gh 执行)
/ultrareview local-jsx 深度 Bug 搜索 (CCR 远端,10-20 分钟)
/security-review prompt 安全审查当前分支变更
/diff local-jsx 查看未提交更改和每轮差异
/pr-comments prompt 获取 GitHub PR 评论

上下文与文件

命令 类型 说明
/context local-jsx / local 可视化当前上下文使用量 (彩色网格)
/files local 列出当前上下文中的所有文件
/add-dir local-jsx 添加新的工作目录
/cost local 显示会话费用
/status local-jsx 显示会话状态
/stats local-jsx 显示 Claude Code 使用统计

MCP 与插件

命令 类型 说明
/mcp local-jsx 管理 MCP 服务器 (immediate=true)
/skills local-jsx 列出可用技能
/agents local-jsx 管理代理配置
/plugin local-jsx 管理插件
/reload-plugins local 激活挂起的插件更改

UI 与终端

命令 别名 类型 说明
/help local-jsx 显示帮助和可用命令
/theme local-jsx 更改主题
/color local-jsx 更改代理颜色
/vim local 切换 Vim/Normal 编辑模式
/exit /quit local-jsx 退出 REPL
/copy local-jsx 复制最后一条消息
/stickers local 订购 Claude Code 贴纸
/statusline prompt 状态栏开关

诊断与工具

命令 别名 类型 说明
/doctor local-jsx 诊断和验证安装设置
/terminal-setup local-jsx 终端设置
/ide local-jsx 管理 IDE 集成
/heapdump local JS 堆转储到 ~/Desktop

反馈与信息

命令 别名 类型 说明
/feedback /bug local-jsx 提交反馈/Bug 报告
/btw local-jsx 快速备注
/release-notes local 查看更新日志

项目初始化

命令 类型 说明
/init prompt 生成 CLAUDE.md 和项目配置
/output-style local-jsx 已废弃: 使用 /config

账户与认证

命令 类型 条件
/login local-jsx 非 3P 服务用户
/logout local-jsx 非 3P 服务用户

其他

命令 别名 类型 说明
/passes local-jsx Pass 管理
/tasks /bashes local-jsx 后台任务管理
/insights prompt 生成使用分析报告 (懒加载,113KB)
/think-back local-jsx 2025 年度回顾
/thinkback-play local 播放回顾动画
/remote-env local-jsx 配置远程环境默认值
/rate-limit-options local-jsx 速率限制选项

7.2 平台限定命令

命令 availability 类型 说明
/upgrade claude-ai local-jsx 升级到 Max 计划
/usage claude-ai local-jsx 查看计划用量
/extra-usage (动态) local-jsx / local 超额用量配置
/fast claude-ai, console local-jsx 快速模式切换
/chrome claude-ai local-jsx Chrome Beta 设置
/desktop claude-ai local-jsx 桌面应用跳转
/mobile 全平台 local-jsx 移动端 QR 码
/voice claude-ai local 语音模式
/install-github-app claude-ai, console local-jsx GitHub Actions
/install-slack-app claude-ai local Slack 应用
/web-setup claude-ai local-jsx Web 远程环境
/session /remote local-jsx 远程会话 (remote 模式)

7.3 Bundled 技能 (内置技能)

这些是以技能格式注册的内置命令,存放在 src/skills/bundled/:

技能 说明
batch 批量操作
claude-api Claude API 使用指导
claude-in-chrome Chrome 扩展指导
debug 调试辅助
keybindings 快捷键配置 (技能版)
loop 循环执行
lorem-ipsum 占位文本
remember 记忆管理
schedule 远程代理调度
simplify 代码简化
skillify 技能创建
stuck 卡住时的帮助
update-config 配置更新
verify 验证检查

8. 示例命令实现

8.1 PromptCommand 示例: /review

最简单的 prompt 命令 -- 生成提示词让模型执行 PR 审查:

const review: Command = {
  type: 'prompt',
  name: 'review',
  description: 'Review a pull request',
  progressMessage: 'reviewing pull request',
  contentLength: 0,       // 动态内容,长度不确定
  source: 'builtin',
  async getPromptForCommand(args): Promise<ContentBlockParam[]> {
    return [{ type: 'text', text: `
      You are an expert code reviewer...
      1. If no PR number, run gh pr list
      2. If PR number provided, run gh pr view + gh pr diff
      3. Analyze and review...
      PR number: ${args}
    ` }]
  },
}

8.2 PromptCommand 高级示例: /commit

allowedTools 和 shell 命令预执行的 prompt 命令:

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()  // 包含 !`git status` 等 shell 模板
    // executeShellCommandsInPrompt 会展开 !`...` 为实际输出
    const finalContent = await executeShellCommandsInPrompt(promptContent, ...)
    return [{ type: 'text', text: finalContent }]
  },
} satisfies Command

8.3 LocalCommand 示例: /compact

返回结构化结果的本地命令:

// index.ts -- 仅元数据
const compact = {
  type: 'local',
  name: 'compact',
  description: 'Clear conversation history but keep a summary in context...',
  isEnabled: () => !isEnvTruthy(process.env.DISABLE_COMPACT),
  supportsNonInteractive: true,
  argumentHint: '<optional custom summarization instructions>',
  load: () => import('./compact.js'),
} satisfies Command

// compact.ts -- 实际实现
export const call: LocalCommandCall = async (args, context) => {
  const compactionResult = await performCompaction(...)
  return {
    type: 'compact',  // 特殊结果类型,触发消息重建
    compactionResult,
    displayText: 'Context compacted successfully',
  }
}

8.4 LocalJSXCommand 示例: /config

渲染交互式配置面板:

// index.ts
const config = {
  aliases: ['settings'],
  type: 'local-jsx',
  name: 'config',
  description: 'Open config panel',
  load: () => import('./config.js'),
} satisfies Command

// config.ts (简化)
export const call: LocalJSXCommandCall = async (onDone, context, args) => {
  // 返回 React/Ink JSX 组件
  return <ConfigPanel
    onDone={(result) => onDone(result, { display: 'system' })}
    context={context}
  />
}

8.5 条件启用示例: /session

根据运行模式动态启用/隐藏:

const session = {
  type: 'local-jsx',
  name: 'session',
  aliases: ['remote'],
  description: 'Show remote session URL and QR code',
  isEnabled: () => getIsRemoteMode(),      // 仅 remote 模式启用
  get isHidden() {
    return !getIsRemoteMode()              // 非 remote 模式从列表中隐藏
  },
  load: () => import('./session.js'),
} satisfies Command

8.6 懒加载 shim 示例: /insights

对超大模块 (113KB) 使用代理模式延迟加载:

const usageReport: Command = {
  type: 'prompt',
  name: 'insights',
  description: 'Generate a report analyzing your Claude Code sessions',
  contentLength: 0,
  progressMessage: 'analyzing your sessions',
  source: 'builtin',
  async getPromptForCommand(args, context) {
    // 首次调用时才加载 113KB 模块
    const real = (await import('./commands/insights.js')).default
    if (real.type !== 'prompt') throw new Error('unreachable')
    return real.getPromptForCommand(args, context)
  },
}

8.7 双模态命令: /extra-usage

同一功能根据交互/非交互模式注册为不同类型:

export const extraUsage = {
  type: 'local-jsx',
  name: 'extra-usage',
  description: 'Configure extra usage...',
  isEnabled: () => isExtraUsageAllowed() && !getIsNonInteractiveSession(),
  load: () => import('./extra-usage.js'),
} satisfies Command

export const extraUsageNonInteractive = {
  type: 'local',
  name: 'extra-usage',            // 同名! getCommands 时只有一个生效
  supportsNonInteractive: true,
  description: 'Configure extra usage...',
  isEnabled: () => isExtraUsageAllowed() && getIsNonInteractiveSession(),
  get isHidden() { return !getIsNonInteractiveSession() },
  load: () => import('./extra-usage-noninteractive.js'),
} satisfies Command

9. 技能命令过滤

9.1 SkillTool 可用命令

模型可通过 SkillTool 调用的命令:

const getSkillToolCommands = memoize(async (cwd: string): Promise<Command[]> => {
  const allCommands = await getCommands(cwd)
  return allCommands.filter(cmd =>
    cmd.type === 'prompt' &&
    !cmd.disableModelInvocation &&
    cmd.source !== 'builtin' &&
    (cmd.loadedFrom === 'bundled' ||
     cmd.loadedFrom === 'skills' ||
     cmd.loadedFrom === 'commands_DEPRECATED' ||
     cmd.hasUserSpecifiedDescription ||
     cmd.whenToUse)
  )
})

9.2 用户可调用的技能

const getSlashCommandToolSkills = memoize(async (cwd: string): Promise<Command[]> => {
  const allCommands = await getCommands(cwd)
  return allCommands.filter(cmd =>
    cmd.type === 'prompt' &&
    cmd.source !== 'builtin' &&
    (cmd.hasUserSpecifiedDescription || cmd.whenToUse) &&
    (cmd.loadedFrom === 'skills' ||
     cmd.loadedFrom === 'plugin' ||
     cmd.loadedFrom === 'bundled' ||
     cmd.disableModelInvocation)  // 模型不可调用 = 仅用户可调用
  )
})

10. 缓存管理

命令系统使用多层缓存,需要在特定时机清除:

// 仅清除命令 memoization (动态技能添加时)
function clearCommandMemoizationCaches(): void {
  loadAllCommands.cache?.clear?.()
  getSkillToolCommands.cache?.clear?.()
  getSlashCommandToolSkills.cache?.clear?.()
  clearSkillIndexCache?.()  // 外层索引也要清
}

// 完全清除 (插件/技能文件变更时)
function clearCommandsCache(): void {
  clearCommandMemoizationCaches()
  clearPluginCommandCache()
  clearPluginSkillsCache()
  clearSkillCaches()
}

11. 命令发现与匹配

11.1 查找算法

function findCommand(commandName: string, commands: Command[]): Command | undefined {
  return commands.find(_ =>
    _.name === commandName ||
    getCommandName(_) === commandName ||  // userFacingName
    _.aliases?.includes(commandName)
  )
}

11.2 有效命令名检测

// 区分命令名和文件路径
function looksLikeCommand(commandName: string): boolean {
  return !/[^a-zA-Z0-9:\-_]/.test(commandName)
}

输入 /var/log/syslog 不会被误识别为命令,因为包含 / 等非法字符。额外检查文件系统 (stat) 确认不是文件路径。

11.3 描述格式化

用户界面和模型提示使用不同的描述格式:

function formatDescriptionWithSource(cmd: Command): string {
  // workflow → "(workflow)" 后缀
  // plugin → "(pluginName)" 前缀 或 "(plugin)" 后缀
  // bundled → "(bundled)" 后缀
  // user/project/enterprise → "(Personal)" 等来源标注
  // builtin/mcp → 原始描述
}

12. 设计总结

设计决策 原因
三类型判别联合 清晰分离 UI 渲染/文本处理/模型交互三种执行模式
load() 懒加载 启动时只解析元数据,减少初始化开销
availability + isEnabled 双维度 分离静态平台约束和动态功能开关
memoize + 每次过滤 平衡加载开销和认证状态实时性
Bridge/Remote 白名单 精确控制跨平台命令安全边界
context: 'fork' 隔离长时间运行的技能,避免阻塞主对话
Feature flags 编译时消除 外部构建中完全移除内部功能代码
immediate 标记 关键命令 (如 /mcp) 绕过队列等待,立即执行