"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ThreadManager = void 0; const database_1 = require("../database"); const git_integration_1 = require("../git/git-integration"); const embedding_service_1 = require("./embedding-service"); const uuid_1 = require("uuid"); const date_fns_1 = require("date-fns"); const locale_1 = require("date-fns/locale"); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); class ThreadManager { dbManager; threadsDAO; messagesDAO; fileChangesDAO; gitIntegration; embeddingService; claudeContextPath = path_1.default.join(process.cwd(), '.claude', '.threads', 'current-context.md'); constructor(dbManager) { this.dbManager = dbManager; this.embeddingService = new embedding_service_1.XenovaEmbeddingService(); this.threadsDAO = new database_1.ThreadsDAO(dbManager); this.messagesDAO = new database_1.MessagesDAO(dbManager, this.embeddingService); this.fileChangesDAO = new database_1.FileChangesDAO(dbManager); this.gitIntegration = new git_integration_1.GitIntegration(); fs_extra_1.default.ensureDirSync(path_1.default.dirname(this.claudeContextPath)); // Ensure directory exists on startup } get messagesDAOInstance() { return this.messagesDAO; } async updateClaudeMd(thread, messages) { // 格式化历史消息 const historyText = messages.map(msg => { const time = new Date(msg.timestamp).toLocaleString('zh-CN'); const roleText = msg.role === 'user' ? '用户' : '助手'; return `### [${time}] ${roleText}\n\n${msg.content}\n`; }).join('\n---\n\n'); const content = `# 📋 当前线程上下文\n\n**⚠️ 重要:上下文隔离规则**\n\n您当前在独立的对话线程中工作。请**严格遵守**以下规则:\n\n1. **只参考本文档中的历史对话**\n2. **忽略本线程之外的所有内容**\n3. **不要引用或提及其他线程的信息**\n\n---\n\n## 线程信息\n\n- 📋 标题:${thread.title}\n- 📝 描述:${thread.description || '无'}\n- 🆔 ID:${thread.id}\n- 🌿 Git 分支:${thread.gitBranch || '无'}\n- 🏷️ 标签:${thread.metadata.tags?.join(', ') || '无'}\n- 📊 消息数:${thread.messageCount}\n\n---\n\n## 历史对话\n\n${historyText || '暂无历史对话'}\n\n---\n\n**再次强调:请只参考上述对话内容进行回复,忽略本线程之外的所有历史记录。**\n`; // 写入文件 await fs_extra_1.default.writeFile(this.claudeContextPath, content, 'utf-8'); } async createThread(input) { const { title, description, tags, switchTo = true } = input; // 1. 生成 UUID const threadId = (0, uuid_1.v4)(); // 2. 准备 Git 分支名(但不立即创建,延迟到切换时) let gitBranch; if (await this.gitIntegration.isGitRepo()) { gitBranch = `thread/${threadId.substring(0, 8)}`; // 注意:分支将在首次切换到此 thread 时创建,避免阻塞 } // 3. 创建 Thread 记录 // 先创建为非激活状态,如果是 switchTo=true,后面会统一调用 setActive 处理互斥 let newThread = this.threadsDAO.create({ id: threadId, sessionId: threadId, // 相同 title, description, gitBranch, isActive: false, metadata: { filesChanged: 0, linesAdded: 0, linesDeleted: 0, tags: tags || [] } }); // 4. 处理切换逻辑 (默认为 true) if (switchTo) { this.threadsDAO.setActive(threadId); newThread.isActive = true; // 更新内存中的对象状态 // 更新 CLAUDE.md 上下文 await this.updateClaudeMd(newThread, []); } // 5. 生成启动命令 const launchCommand = `claude --session-id ${threadId}`; // 6. 返回结果(快速返回,不阻塞) return { thread: newThread, message: this.formatCreateMessage(newThread), launchCommand }; } formatCreateMessage(thread) { const shortId = thread.id.substring(0, 8); const gitBranchInfo = thread.gitBranch ? `- **Git 分支**: \`${thread.gitBranch}\`` : ''; return `### ✨ 新线程已创建 - **标题**: ${thread.title} - **ID**: \`${shortId}\` ${gitBranchInfo} **🚀 启动独立会话**: \`claude --session-id ${thread.id}\` 或: \`clt ${shortId}\` `; } formatCombinedSwitchMessage(thread, messages) { const shortId = thread.id.substring(0, 8); // Filter out commands and format context messages const contextMessages = messages.filter(msg => !msg.content.trim().startsWith('/')); const displayLimit = 5; // Reduced limit const recentMessages = contextMessages .slice(0, displayLimit) .reverse() .map(msg => { const time = (0, date_fns_1.formatDistanceToNow)(msg.timestamp, { locale: locale_1.zhCN }); const preview = msg.content.substring(0, 60).replace(/\n/g, ' '); const icon = msg.role === 'user' ? '👤' : '🤖'; return `- ${time} ${icon} ${preview}${msg.content.length > 60 ? '...' : ''}`; }) .join('\n'); const contextSection = contextMessages.length > 0 ? `**💬 最近消息**:\n${recentMessages}\n` : ''; const gitBranchInfo = thread.gitBranch ? `- **Git**: \`${thread.gitBranch}\`` : ''; return `### 🔄 已切换到线程 - **标题**: ${thread.title} - **ID**: \`${shortId}\` ${gitBranchInfo} - **统计**: ${messages.length} 消息 | ${thread.metadata.filesChanged} 文件变更 ${contextSection} **⚠️ 完全隔离上下文**: 推荐重启: \`exit\` 后运行 \`claude --session-id ${thread.id}\` `; } async getThread(id, includeMessages = false, includeFileChanges = false, messageLimit = 50) { const thread = this.threadsDAO.findById(id); if (!thread) { return { thread: null }; } let messages; if (includeMessages) { messages = this.messagesDAO.findByThreadId(id, { limit: messageLimit }); } let fileChanges; if (includeFileChanges) { fileChanges = this.fileChangesDAO.findByThreadId(id); } return { thread, messages, fileChanges }; } async listThreads(input) { const { threads, total } = this.threadsDAO.findAll(input); const currentActive = this.threadsDAO.getActive(); // Format the message for display const message = this.formatListThreadsMessage(threads, total, currentActive?.id); return { threads, total, currentThreadId: currentActive?.id, message }; } formatListThreadsMessage(threads, total, currentThreadId) { if (threads.length === 0) { return `📋 **线程列表**: 暂无线程。使用 \`/thread create