feat: 添加系统命令支持 (:clear)

- 新增系统命令模块 (core/system-commands)
  - 支持 :clear/:cls/:c 清空对话历史
  - 命令注册表支持别名
  - 可扩展的命令执行器

- Server 端支持
  - 新增 /api/system-commands API
  - WebSocket 处理系统命令消息
  - 会话清空 API 端点

- UI 端支持
  - 新增 SystemCommandMenu 组件
  - 输入 : 时显示命令建议菜单
  - 键盘导航和选择
  - 底部提示添加 : 快捷键
This commit is contained in:
2025-12-17 19:25:42 +08:00
parent 4fc6b61134
commit e0444a966f
21 changed files with 1109 additions and 9 deletions
+19
View File
@@ -150,6 +150,25 @@ export type {
CommandOperationResult,
} from './commands/index.js';
// System Commands (: 前缀)
export {
getSystemCommandRegistry,
resetSystemCommandRegistry,
SystemCommandExecutor,
isSystemCommand,
parseSystemCommand,
executeSystemCommand,
initializeSystemCommands,
builtinSystemCommands,
} from './system-commands/index.js';
export type {
SystemCommand,
SystemCommandContext,
SystemCommandResult,
SystemCommandAction,
SystemCommandInput,
} from './system-commands/index.js';
// Checkpoint
export {
CheckpointManager,
@@ -0,0 +1,22 @@
/**
* :clear 系统命令
*
* 清空当前会话的历史记录
*/
import type { SystemCommand } from '../types.js';
export const clearCommand: SystemCommand = {
name: 'clear',
description: '清空当前会话的历史记录',
aliases: ['cls', 'c'],
execute: async (_context) => {
// 实际的清空操作由 Server 层处理
// 这里只返回操作指令
return {
success: true,
message: '会话已清空',
action: { type: 'clear_messages' },
};
},
};
@@ -0,0 +1,15 @@
/**
* 内置系统命令
*/
import type { SystemCommand } from '../types.js';
import { clearCommand } from './clear.js';
/**
* 所有内置系统命令
*/
export const builtinSystemCommands: SystemCommand[] = [
clearCommand,
];
export { clearCommand };
@@ -0,0 +1,135 @@
/**
* System Command 执行器
*
* 解析和执行系统命令
*/
import type {
SystemCommandContext,
SystemCommandInput,
SystemCommandResult,
} from './types.js';
import { getSystemCommandRegistry } from './registry.js';
/**
* System Command 执行器
*/
export class SystemCommandExecutor {
/**
* 检查输入是否为系统命令
*/
static isSystemCommand(input: string): boolean {
return input.trim().startsWith(':');
}
/**
* 解析系统命令输入
*/
static parseInput(input: string): SystemCommandInput | null {
const trimmed = input.trim();
if (!trimmed.startsWith(':')) {
return null;
}
// 移除 : 前缀
const content = trimmed.slice(1);
// 分割命令和参数
const spaceIndex = content.indexOf(' ');
if (spaceIndex === -1) {
return {
command: content.toLowerCase(),
arguments: '',
args: [],
};
}
const command = content.slice(0, spaceIndex).toLowerCase();
const argsString = content.slice(spaceIndex + 1).trim();
return {
command,
arguments: argsString,
args: argsString ? argsString.split(/\s+/) : [],
};
}
/**
* 执行系统命令
*/
static async execute(
input: string,
context: Partial<SystemCommandContext> = {}
): Promise<SystemCommandResult> {
const parsed = this.parseInput(input);
if (!parsed) {
return {
success: false,
error: '无效的系统命令格式',
};
}
const registry = getSystemCommandRegistry();
const command = registry.get(parsed.command);
if (!command) {
// 列出可用命令作为提示
const available = registry
.list()
.map((c) => `:${c.name}`)
.join(', ');
return {
success: false,
error: `未知的系统命令: :${parsed.command}`,
message: available ? `可用的系统命令: ${available}` : undefined,
};
}
try {
const result = await command.execute({
arguments: parsed.arguments,
args: parsed.args,
sessionId: context.sessionId,
workdir: context.workdir,
});
return result;
} catch (error) {
return {
success: false,
error:
error instanceof Error ? error.message : '系统命令执行失败',
};
}
}
}
/**
* 便捷函数:检查是否为系统命令
*/
export function isSystemCommand(input: string): boolean {
return SystemCommandExecutor.isSystemCommand(input);
}
/**
* 便捷函数:解析系统命令
*/
export function parseSystemCommand(
input: string
): SystemCommandInput | null {
return SystemCommandExecutor.parseInput(input);
}
/**
* 便捷函数:执行系统命令
*/
export async function executeSystemCommand(
input: string,
context?: Partial<SystemCommandContext>
): Promise<SystemCommandResult> {
return SystemCommandExecutor.execute(input, context);
}
@@ -0,0 +1,46 @@
/**
* System Commands 模块
*
* 提供系统命令(: 前缀)的所有功能导出
*/
// 类型
export type {
SystemCommand,
SystemCommandContext,
SystemCommandResult,
SystemCommandAction,
SystemCommandInput,
} from './types.js';
// 注册表
export {
SystemCommandRegistry,
getSystemCommandRegistry,
resetSystemCommandRegistry,
} from './registry.js';
// 执行器
export {
SystemCommandExecutor,
isSystemCommand,
parseSystemCommand,
executeSystemCommand,
} from './executor.js';
// 内置命令
export { builtinSystemCommands, clearCommand } from './builtin/index.js';
// 初始化函数
import { getSystemCommandRegistry } from './registry.js';
import { builtinSystemCommands } from './builtin/index.js';
/**
* 初始化系统命令注册表
*
* 注册所有内置系统命令
*/
export function initializeSystemCommands(): void {
const registry = getSystemCommandRegistry();
registry.registerAll(builtinSystemCommands);
}
@@ -0,0 +1,99 @@
/**
* System Command 注册表
*
* 管理所有系统命令的注册和查找
*/
import type { SystemCommand } from './types.js';
/**
* System Command 注册表
*/
export class SystemCommandRegistry {
private commands = new Map<string, SystemCommand>();
private aliases = new Map<string, string>();
/**
* 注册系统命令
*/
register(command: SystemCommand): void {
this.commands.set(command.name, command);
// 注册别名
if (command.aliases) {
for (const alias of command.aliases) {
this.aliases.set(alias, command.name);
}
}
}
/**
* 批量注册系统命令
*/
registerAll(commands: SystemCommand[]): void {
for (const command of commands) {
this.register(command);
}
}
/**
* 获取系统命令
*/
get(name: string): SystemCommand | undefined {
// 先尝试直接查找
const command = this.commands.get(name);
if (command) {
return command;
}
// 再尝试别名查找
const aliasTarget = this.aliases.get(name);
if (aliasTarget) {
return this.commands.get(aliasTarget);
}
return undefined;
}
/**
* 检查命令是否存在
*/
has(name: string): boolean {
return this.commands.has(name) || this.aliases.has(name);
}
/**
* 获取所有命令
*/
list(): SystemCommand[] {
return Array.from(this.commands.values());
}
/**
* 清空注册表
*/
clear(): void {
this.commands.clear();
this.aliases.clear();
}
}
// 全局单例
let globalRegistry: SystemCommandRegistry | null = null;
/**
* 获取全局 System Command 注册表
*/
export function getSystemCommandRegistry(): SystemCommandRegistry {
if (!globalRegistry) {
globalRegistry = new SystemCommandRegistry();
}
return globalRegistry;
}
/**
* 重置全局注册表(用于测试)
*/
export function resetSystemCommandRegistry(): void {
globalRegistry = null;
}
@@ -0,0 +1,74 @@
/**
* System Command 系统类型定义
*
* System Command 是以 : 开头的系统操作命令。
* 与普通 Command 不同,System Command 不调用 Agent
* 而是直接执行系统操作(如清空会话、切换模型等)。
*/
/**
* System Command 定义
*/
export interface SystemCommand {
/** 命令名称(不含 : 前缀) */
name: string;
/** 命令描述 */
description: string;
/** 命令别名 */
aliases?: string[];
/** 执行函数 */
execute: (context: SystemCommandContext) => Promise<SystemCommandResult>;
}
/**
* System Command 执行上下文
*/
export interface SystemCommandContext {
/** 原始参数字符串 */
arguments: string;
/** 解析后的参数数组 */
args: string[];
/** 当前会话 ID */
sessionId?: string;
/** 当前工作目录 */
workdir?: string;
}
/**
* System Command 执行结果
*/
export interface SystemCommandResult {
/** 是否成功 */
success: boolean;
/** 返回消息(显示给用户) */
message?: string;
/** 错误信息 */
error?: string;
/** 附加数据 */
data?: Record<string, unknown>;
/** 特殊操作指令 */
action?: SystemCommandAction;
}
/**
* System Command 操作指令
*
* 用于通知前端执行特定操作
*/
export type SystemCommandAction =
| { type: 'clear_messages' }
| { type: 'new_session'; sessionId?: string }
| { type: 'switch_model'; model: string }
| { type: 'reload' };
/**
* System Command 输入解析结果
*/
export interface SystemCommandInput {
/** 命令名称(不含 : 前缀) */
command: string;
/** 原始参数字符串 */
arguments: string;
/** 解析后的参数数组 */
args: string[];
}