# 第三阶段 · 模块三 · 第一节:斜杠命令与命令解析
斜杠命令(/help, /compact)是如何解析的?命令处理流程是什么?内置命令有哪些?
Claude Code 全局架构
┌─────────────────────────────────────────────────────────────────────┐
│ 查询引擎层 → 工具/服务层 → 命令系统 ← 本节 │
│ │
│ 用户输入 │
│ ├── 普通文本 ──> query() ──> 模型处理 │
│ └── 斜杠命令(/help) ──> 命令解析器 ──> 命令处理器 │
└─────────────────────────────────────────────────────────────────────┘
源码位置:`src/commands.ts`(754行)
// 命令导入
import help from './commands/help/index.js'
import clear from './commands/clear/index.js'
import compact from './commands/compact/index.js'
import config from './commands/config/index.js'
import diff from './commands/diff/index.js'
import cost from './commands/cost/index.js'
import session from './commands/session/index.js'
import status from './commands/status/index.js'
import resume from './commands/resume/index.js'
// ... 更多命令
| 命令 | 作用 | 示例 |
| `/help` | 显示帮助 | `/help` |
| `/compact` | 压缩上下文 | `/compact` |
| `/clear` | 清空会话 | `/clear` |
| `/model` | 切换模型 | `/model claude-opus-4` |
| `/context` | 查看上下文 | `/context` |
| `/cost` | 查看消耗 | `/cost` |
| `/session` | 会话管理 | `/session list` |
| `/resume` | 恢复会话 | `/resume |
源码位置:`src/commands.ts`
interface Command {
// 命令名称
name: string;
// 命令描述
description: string;
// 处理命令
async getPromptForCommand(
args: string, // 命令参数
context: CommandContext, // 执行上下文
): Promise<CommandResult>;
}
interface CommandContext {
messages: Message[]; // 对话历史
env: Record<string, string>; // 环境变量
cwd: string; // 当前目录
// ...
}
问 1:命令和工具有什么区别?
| 方面 | 工具 | 命令 |
| 调用方式 | 模型决定 | 用户主动触发 |
| 执行位置 | query() 循环内 | 命令解析器 |
| 返回结果 | tool_result | CommandResult |
| 示例 | Read, Write | /help, /compact |
问 2:命令的执行流程是什么?
// 用户输入
const input = "/help compact";
// Step 1: 解析命令
const { command, args } = parseSlashCommand(input);
// command = "help", args = "compact"
// Step 2: 查找命令处理器
const handler = getCommandHandler("help");
// handler = helpCommand
// Step 3: 执行命令
const result = await handler.getPromptForCommand(args, context);
// result = { content: "Available commands: ..." }
// Step 4: 返回结果给用户
displayResult(result);
问 3:命令是如何注册的?
// 命令注册表
const commands: Map<string, Command> = new Map();
// 注册内置命令
commands.set('help', helpCommand);
commands.set('compact', compactCommand);
commands.set('clear', clearCommand);
// ...
// 支持动态注册
function registerCommand(command: Command) {
commands.set(command.name, command);
}
问 4:如何处理未知命令?
function handleCommand(input: string): CommandResult | null {
const { command, args } = parseSlashCommand(input);
const handler = commands.get(command);
if (!handler) {
return {
type: 'error',
content: `Unknown command: /${command}. Type /help for available commands.`,
};
}
return handler.getPromptForCommand(args, context);
}
问 5:命令可以组合吗?
// 支持管道(类似 shell)
"/help | grep compact"
// 解析
const parts = input.split('|');
// parts = ["/help", "grep compact"]
// 执行第一个命令
const result1 = await executeCommand("/help");
// result1 = "Available commands: /help, /compact, ..."
// 将结果传给下一个命令
const result2 = await executeCommand("grep compact", result1);
// result2 = "/compact - Compact the conversation context"
// 斜杠命令格式
/command [args]
// 示例
/help
/compact
/model claude-opus-4
/context 50
function parseSlashCommand(input: string): { command: string; args: string } | null {
// 必须以 / 开头
if (!input.startsWith('/')) {
return null;
}
// 分割命令和参数
const parts = input.slice(1).split(/\s+/);
// "/help compact" -> ["help", "compact"]
const command = parts[0] || '';
const args = parts.slice(1).join(' ');
return { command, args };
}
问 1:为什么用 / 作为命令前缀?
// 1. 不会和普通文本冲突
// 用户输入 "/help" 不太可能是要模型处理 "/help" 这个词
// 2. 键盘容易输入
// / 键在键盘上方的右手边
// 3. 和 shell/bash 的习惯一致
// ls, cd, grep 等命令和 /help 不同
问 2:参数如何解析?
// 简单空格分隔
"/model claude-opus-4" -> command="model", args="claude-opus-4"
// 带引号的参数
"/context "50 lines"" -> args='"50 lines"'
// 选项风格
"/compact --aggressive" -> args="--aggressive"
问 3:命令别名如何处理?
// 别名映射
const aliases: Record<string, string> = {
'h': 'help',
'?': 'help',
'c': 'compact',
'cl': 'clear',
};
// 解析后转换
function resolveCommand(name: string): string {
return aliases[name] || name;
}
// 使用
const { command, args } = parseSlashCommand(input);
const resolved = resolveCommand(command);
问 4:如何防止命令注入?
// 问题:用户可能输入
// "/help; rm -rf /"
// 解决方案:只允许特定的命令
const allowedCommands = new Set(['help', 'compact', 'clear', ...]);
function handleCommand(input: string): CommandResult | null {
const { command } = parseSlashCommand(input);
if (!allowedCommands.has(command)) {
return { type: 'error', content: 'Command not allowed' };
}
// 安全执行
}
问 5:命令可以嵌套吗?
// 不支持嵌套
// "/help /compact" 会被解析为
// command = "help", args = "/compact"
// 因为解析器遇到第一个空格就停止
// 每个命令都实现这个方法
interface Command {
name: string;
description: string;
async getPromptForCommand(
args: string,
context: CommandContext,
): Promise<CommandResult>;
}
源码位置:`src/commands/help/index.ts`
// /help 命令实现
const helpCommand: Command = {
name: 'help',
description: 'Show available commands',
async getPromptForCommand(args: string, context: CommandContext) {
// 如果没有参数,显示所有命令
if (!args) {
return {
type: 'text',
content: formatCommandsList(allCommands),
};
}
// 搜索匹配的命令
const matches = searchCommands(args, allCommands);
return {
type: 'text',
content: formatCommandsList(matches),
};
},
};
问 1:命令返回什么?
interface CommandResult {
type: 'text' | 'error' | 'stop';
content: string;
// 可选:是否替换用户输入
replaceInput?: string;
// 可选:是否清空上下文
clearContext?: boolean;
}
问 2:命令可以修改上下文吗?
// /clear 命令清空上下文
const clearCommand: Command = {
name: 'clear',
description: 'Clear the conversation',
async getPromptForCommand(args, context) {
return {
type: 'text',
content: 'Context cleared.',
clearContext: true, // 标记清空
};
},
};
问 3:命令可以中断循环吗?
// /exit 命令中断
const exitCommand: Command = {
name: 'exit',
description: 'Exit Claude Code',
async getPromptForCommand(args, context) {
return {
type: 'stop', // 标记停止
content: 'Goodbye!',
};
},
};
用户输入
│
├── 普通文本 ──> query()
│
└── /命令 ──> parseSlashCommand()
│
▼
命令解析器
│
├── /help ──> helpCommand.getPromptForCommand()
├── /compact ──> compactCommand.getPromptForCommand()
└── ...
// 命令处理器形成责任链
class CommandChain {
private handlers: CommandHandler[] = [];
addHandler(handler: CommandHandler) {
this.handlers.push(handler);
}
async handle(input: string): Promise<CommandResult | null> {
for (const handler of this.handlers) {
if (handler.canHandle(input)) {
return await handler.handle(input);
}
}
return null; // 无法处理
}
}
问题:什么时候用命令,什么时候用工具?
答案:
| 场景 | 方案 | 原因 |
| 用户主动操作 | 命令 | 需要用户明确意图 |
| 模型决策操作 | 工具 | 模型根据上下文决定 |
| 修改会话状态 | 命令 | /clear 等 |
| 获取外部信息 | 工具 | WebSearch 等 |
问题:如何实现类似 shell 的命令历史(↑↓ 键)?
答案:
class CommandHistory {
private history: string[] = [];
private position: number = 0;
add(command: string) {
this.history.push(command);
this.position = this.history.length;
}
up(): string | null {
if (this.position > 0) {
this.position--;
return this.history[this.position];
}
return null;
}
down(): string | null {
if (this.position < this.history.length - 1) {
this.position++;
return this.history[this.position];
}
return null;
}
}
问题:如何实现 Tab 补全?
答案:
function getCompletions(partial: string): string[] {
// 匹配以 partial 开头的命令
const matches = allCommands.filter(cmd =>
cmd.name.startsWith(partial)
);
return matches.map(cmd => `/${cmd.name}`);
}
// 使用
// 用户输入 /he + Tab
// 补全为 /help
| 文件 | 核心内容 |
| `src/commands.ts` | 命令系统入口 |
| `src/commands/help/` | /help 命令实现 |
| `src/commands/compact/` | /compact 命令实现 |
下一节我们将深入 内置命令详解:
- /help, /compact, /clear 的源码分析
- 命令的权限控制
- 命令的输出格式化
*- 第一轮:□ 事实准确性*
*- 第二轮:□ 深度与洞见*
*- 第三轮:□ 可读性与价值*