feat: 添加系统命令支持 (:clear)
- 新增系统命令模块 (core/system-commands) - 支持 :clear/:cls/:c 清空对话历史 - 命令注册表支持别名 - 可扩展的命令执行器 - Server 端支持 - 新增 /api/system-commands API - WebSocket 处理系统命令消息 - 会话清空 API 端点 - UI 端支持 - 新增 SystemCommandMenu 组件 - 输入 : 时显示命令建议菜单 - 键盘导航和选择 - 底部提示添加 : 快捷键
This commit is contained in:
@@ -643,3 +643,16 @@ export function submitUserInput(toolCallId: string, answer: string): boolean {
|
||||
const userInputWaiter = getUserInputWaiter();
|
||||
return userInputWaiter.submitInput(toolCallId, answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空 Agent 的对话历史(内存中)
|
||||
*
|
||||
* 用于系统命令 :clear
|
||||
*/
|
||||
export function clearAgentHistory(sessionId: string): void {
|
||||
const agent = agentCache.get(sessionId);
|
||||
if (agent) {
|
||||
agent.clearHistory();
|
||||
console.log(`[Agent] Cleared history for session ${sessionId}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ export {
|
||||
compressContext,
|
||||
// 用户输入响应
|
||||
submitUserInput,
|
||||
// 系统命令支持
|
||||
clearAgentHistory,
|
||||
// 类型导出
|
||||
type TokenUsage,
|
||||
type CompressionResult,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { cors } from 'hono/cors';
|
||||
import { logger } from 'hono/logger';
|
||||
import { createBunWebSocket } from 'hono/bun';
|
||||
|
||||
import { sessionsRouter, toolsRouter, configRouter, filesRouter, commandsRouter, mcpRouter, hooksRouter, agentsRouter, checkpointsRouter, providersRouter, servicesRouter, contextRouter, lspRouter } from './routes/index.js';
|
||||
import { sessionsRouter, toolsRouter, configRouter, filesRouter, commandsRouter, mcpRouter, hooksRouter, agentsRouter, checkpointsRouter, providersRouter, servicesRouter, contextRouter, lspRouter, systemCommandsRouter } from './routes/index.js';
|
||||
import {
|
||||
handleWebSocket,
|
||||
handleWebSocketMessage,
|
||||
@@ -90,6 +90,7 @@ api.route('/checkpoints', checkpointsRouter);
|
||||
api.route('/providers', providersRouter);
|
||||
api.route('/services', servicesRouter);
|
||||
api.route('/lsp', lspRouter);
|
||||
api.route('/system-commands', systemCommandsRouter);
|
||||
|
||||
// 上下文压缩相关(挂载到根路径,内部路由包含 /sessions/:id/context)
|
||||
api.route('/', contextRouter);
|
||||
|
||||
@@ -17,3 +17,4 @@ export { providersRouter } from './providers.js';
|
||||
export { servicesRouter } from './services.js';
|
||||
export { contextRouter } from './context.js';
|
||||
export { lspRouter } from './lsp.js';
|
||||
export { systemCommandsRouter } from './system-commands.js';
|
||||
|
||||
@@ -104,6 +104,47 @@ sessionsRouter.delete('/:id', async (c) => {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /sessions/:id/clear - 清空会话消息
|
||||
*
|
||||
* 删除会话的所有消息和相关数据,但保留会话本身
|
||||
*/
|
||||
sessionsRouter.post('/:id/clear', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
if (!sessionManager.exists(id)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Session not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// 删除会话的所有消息
|
||||
await MessageStorage.removeBySession(id);
|
||||
|
||||
// 重置会话状态
|
||||
sessionManager.updateStatus(id, 'idle');
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
message: 'Session messages cleared',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Sessions] Failed to clear messages:', error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to clear messages',
|
||||
},
|
||||
500
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/:id/messages - 获取会话消息
|
||||
*
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* System Commands API Routes
|
||||
*
|
||||
* 系统命令(: 前缀)相关的 REST API
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import { getSystemCommandRegistry } from '@ai-assistant/core';
|
||||
|
||||
export const systemCommandsRouter = new Hono();
|
||||
|
||||
/**
|
||||
* GET /system-commands - 列出所有系统命令
|
||||
*/
|
||||
systemCommandsRouter.get('/', (c) => {
|
||||
const registry = getSystemCommandRegistry();
|
||||
const commands = registry.list();
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
commands: commands.map((cmd) => ({
|
||||
name: cmd.name,
|
||||
description: cmd.description,
|
||||
aliases: cmd.aliases || [],
|
||||
})),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /system-commands/:name - 获取单个系统命令
|
||||
*/
|
||||
systemCommandsRouter.get('/:name', (c) => {
|
||||
const name = c.req.param('name');
|
||||
const registry = getSystemCommandRegistry();
|
||||
const command = registry.get(name);
|
||||
|
||||
if (!command) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'System command not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
aliases: command.aliases || [],
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -82,7 +82,7 @@ export type AgentModeType = 'build' | 'plan';
|
||||
|
||||
// 客户端发送的消息
|
||||
export interface ClientMessage {
|
||||
type: 'message' | 'cancel' | 'tool_response' | 'permission_response' | 'config_update' | 'mode_switch' | 'user_input_response';
|
||||
type: 'message' | 'cancel' | 'tool_response' | 'permission_response' | 'config_update' | 'mode_switch' | 'user_input_response' | 'system_command';
|
||||
sessionId: string;
|
||||
payload?: {
|
||||
content?: string;
|
||||
@@ -122,7 +122,9 @@ export interface ServerMessage {
|
||||
| 'subagent:end' // 子 Agent 执行结束
|
||||
| 'subagent:stream' // 子 Agent 流式输出
|
||||
| 'subagent:tool_start' // 子 Agent 工具调用开始
|
||||
| 'subagent:tool_end'; // 子 Agent 工具调用结束
|
||||
| 'subagent:tool_end' // 子 Agent 工具调用结束
|
||||
// 系统命令响应
|
||||
| 'system_command_result'; // 系统命令执行结果
|
||||
sessionId: string;
|
||||
payload?: unknown;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,18 @@
|
||||
|
||||
import type { WSContext } from 'hono/ws';
|
||||
import { getSessionManager } from './session/manager.js';
|
||||
import { processMessage, cancelProcessing, getOrCreateAgent, submitUserInput } from './agent/index.js';
|
||||
import { processMessage, cancelProcessing, getOrCreateAgent, submitUserInput, clearAgentHistory } from './agent/index.js';
|
||||
import { handlePermissionResponse, setSessionAutoApprove } from './permission/handler.js';
|
||||
import type { ClientMessage, ServerMessage } from './types.js';
|
||||
import {
|
||||
isSystemCommand,
|
||||
executeSystemCommand,
|
||||
initializeSystemCommands,
|
||||
MessageStorage,
|
||||
} from '@ai-assistant/core';
|
||||
|
||||
// 初始化系统命令
|
||||
initializeSystemCommands();
|
||||
|
||||
// 存储活跃的 WebSocket 连接
|
||||
const connections: Map<string, Set<WSContext>> = new Map();
|
||||
@@ -109,6 +118,12 @@ export async function handleWebSocketMessage(
|
||||
const agentMode = message.payload?.agentMode as 'build' | 'plan' | undefined;
|
||||
const autoApprove = message.payload?.autoApprove as boolean | undefined;
|
||||
|
||||
// 检测系统命令(: 前缀)
|
||||
if (isSystemCommand(content)) {
|
||||
await handleSystemCommand(sessionId, content);
|
||||
break;
|
||||
}
|
||||
|
||||
// 将 @filepath 转换为 ./filepath 格式(方便 AI 识别为文件路径)
|
||||
content = content.replace(/@([\w./-]+)/g, './$1');
|
||||
|
||||
@@ -281,3 +296,45 @@ export function getConnectionStats(): { sessions: number; connections: number }
|
||||
connections: totalConnections,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理系统命令(: 前缀)
|
||||
*/
|
||||
async function handleSystemCommand(sessionId: string, content: string): Promise<void> {
|
||||
const sessionManager = getSessionManager();
|
||||
|
||||
console.log(`[WS] System command: ${content}`);
|
||||
|
||||
// 执行系统命令
|
||||
const result = await executeSystemCommand(content, { sessionId });
|
||||
|
||||
// 处理特殊操作
|
||||
if (result.success && result.action) {
|
||||
switch (result.action.type) {
|
||||
case 'clear_messages': {
|
||||
// 清空存储的消息
|
||||
await MessageStorage.removeBySession(sessionId);
|
||||
|
||||
// 清空 Agent 内存中的对话历史
|
||||
clearAgentHistory(sessionId);
|
||||
|
||||
// 重置会话状态
|
||||
sessionManager.updateStatus(sessionId, 'idle');
|
||||
break;
|
||||
}
|
||||
// 可以在这里添加其他操作类型的处理
|
||||
}
|
||||
}
|
||||
|
||||
// 发送结果给客户端
|
||||
broadcastToSession(sessionId, {
|
||||
type: 'system_command_result',
|
||||
sessionId,
|
||||
payload: {
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
error: result.error,
|
||||
action: result.action,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user