Files
ai-terminal-assistant/src/ui/terminal.ts
T
kurihada 1e0ecc2de7 feat: 添加会话持久化和 Todo 工具
会话持久化:
- 新增 SessionManager 和 SessionStorage,支持会话自动保存和恢复
- 会话数据存储在 ~/.local/share/ai-assist/,遵循 XDG 规范
- 支持对话历史、已发现工具、待办事项的持久化
- 启动时自动恢复同一工作目录的上次会话
- 支持会话归档和历史会话管理

Todo 工具:
- 新增 todoread 工具:读取当前会话的待办事项列表
- 新增 todowrite 工具:创建和更新待办事项
- 支持 pending/in_progress/completed 三种状态
- 待办事项随会话自动持久化

其他改进:
- ToolResult 类型新增可选的 metadata 字段
- Agent 支持会话管理器集成
- clearHistory 改为异步方法
2025-12-10 22:55:37 +08:00

154 lines
4.5 KiB
TypeScript

import * as readline from 'readline';
import chalk from 'chalk';
import type { Agent } from '../core/agent.js';
export class TerminalUI {
private agent: Agent;
private rl: readline.Interface;
private isClosed = false;
constructor(agent: Agent) {
this.agent = agent;
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: true,
});
// 监听关闭事件
this.rl.on('close', () => {
this.isClosed = true;
});
}
// 显示欢迎信息
private showWelcome(): void {
console.log(chalk.cyan('\n╔════════════════════════════════════════╗'));
console.log(chalk.cyan('║') + chalk.bold.white(' 🤖 AI Terminal Assistant ') + chalk.cyan('║'));
console.log(chalk.cyan('║') + chalk.gray(' Powered by DeepSeek / Claude ') + chalk.cyan('║'));
console.log(chalk.cyan('╚════════════════════════════════════════╝\n'));
console.log(chalk.gray('输入你的问题,或使用以下命令:'));
console.log(chalk.yellow(' /help') + chalk.gray(' - 显示帮助'));
console.log(chalk.yellow(' /clear') + chalk.gray(' - 清空对话历史'));
console.log(chalk.yellow(' /exit') + chalk.gray(' - 退出程序'));
console.log('');
}
// 处理特殊命令
private handleCommand(input: string): boolean {
const command = input.toLowerCase().trim();
switch (command) {
case '/help':
console.log(chalk.cyan('\n📖 帮助信息:'));
console.log(chalk.white(' 这是一个 AI 编程助手,可以帮你:'));
console.log(chalk.gray(' • 读写文件'));
console.log(chalk.gray(' • 执行 bash 命令'));
console.log(chalk.gray(' • 搜索代码'));
console.log(chalk.gray(' • 回答编程问题'));
console.log('');
return true;
case '/clear':
// clearHistory 现在是异步的
void this.agent.clearHistory();
console.log(chalk.green('✓ 对话历史已清空\n'));
return true;
case '/exit':
case '/quit':
console.log(chalk.cyan('\n👋 再见!\n'));
this.close();
process.exit(0);
default:
return false;
}
}
// 提问并获取用户输入
private prompt(): Promise<string> {
return new Promise((resolve, reject) => {
if (this.isClosed) {
reject(new Error('readline closed'));
return;
}
this.rl.question(chalk.green('You > '), (answer) => {
resolve(answer ?? '');
});
});
}
// 主循环
async start(): Promise<void> {
this.showWelcome();
while (!this.isClosed) {
try {
const input = await this.prompt();
// 跳过空输入
if (!input.trim()) {
continue;
}
// 处理命令
if (input.startsWith('/')) {
if (this.handleCommand(input)) {
continue;
}
}
// 发送给 AI
process.stdout.write(chalk.gray('思考中...'));
try {
let isFirstChunk = true;
await this.agent.chat(input, (text) => {
if (isFirstChunk) {
// 清除 "思考中..." 并显示 AI 前缀
process.stdout.write('\r' + ' '.repeat(20) + '\r');
process.stdout.write(chalk.blue('AI > '));
isFirstChunk = false;
}
// 处理工具调用的输出
if (text.startsWith('\n[调用工具:')) {
process.stdout.write(chalk.yellow(text));
} else if (text.startsWith('[结果:') || text.startsWith('[错误:')) {
process.stdout.write(chalk.gray(text));
} else {
process.stdout.write(text);
}
});
console.log('\n');
} catch (error) {
// 清除 "思考中..."
process.stdout.write('\r' + ' '.repeat(20) + '\r');
console.log(
chalk.red(
`❌ 错误: ${error instanceof Error ? error.message : String(error)}\n`
)
);
}
} catch {
// readline 关闭,退出循环
break;
}
}
console.log(chalk.cyan('\n👋 再见!\n'));
}
// 关闭
close(): void {
if (!this.isClosed) {
this.isClosed = true;
this.rl.close();
}
}
}