UNPKG

@webdevtoday/grok-cli

Version:

A sophisticated CLI tool for interacting with xAI Grok 4, featuring conversation history, file reference, custom commands, memory system, and genetic development workflows

251 lines 9.35 kB
"use strict"; /** * Conversation history management for Grok CLI * Handles conversation persistence, loading, and continuation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HistoryManager = void 0; const fs_extra_1 = require("fs-extra"); const path_1 = require("path"); const os_1 = require("os"); /** * History manager for conversation persistence */ class HistoryManager { constructor() { this.conversations = new Map(); this.historyDir = (0, path_1.join)((0, os_1.homedir)(), '.grok-cli', 'history'); this.historyIndex = (0, path_1.join)(this.historyDir, 'conversations.json'); } /** * Initialize history directory and load conversation index */ async initialize() { await (0, fs_extra_1.ensureDir)(this.historyDir); await this.loadConversationIndex(); } /** * Save a conversation session to history */ async saveConversation(session, messages) { if (messages.length === 0) return; await this.initialize(); const conversationFile = (0, path_1.join)(this.historyDir, `${session.id}.json`); const now = new Date(); const historySession = { session, messages, metadata: { totalMessages: messages.length, startTime: session.startTime, lastUpdateTime: now, cwd: session.cwd, }, }; await (0, fs_extra_1.writeFile)(conversationFile, JSON.stringify(historySession, null, 2)); // Update conversation index const firstMessage = messages[0]?.content || ''; const lastMessage = messages[messages.length - 1]?.content || ''; const conversationHistory = { id: session.id, startTime: session.startTime, lastUpdateTime: now, cwd: session.cwd, messageCount: messages.length, firstMessage: firstMessage.slice(0, 100), lastMessage: lastMessage.slice(0, 100), filePath: conversationFile, }; this.conversations.set(session.id, conversationHistory); await this.saveConversationIndex(); } /** * Load a conversation from history */ async loadConversation(sessionId) { await this.initialize(); const conversation = this.conversations.get(sessionId); if (!conversation || !(await (0, fs_extra_1.pathExists)(conversation.filePath))) { return null; } try { const content = await (0, fs_extra_1.readFile)(conversation.filePath, 'utf-8'); const historySession = JSON.parse(content); // Convert date strings back to Date objects historySession.session.startTime = new Date(historySession.session.startTime); historySession.metadata.startTime = new Date(historySession.metadata.startTime); historySession.metadata.lastUpdateTime = new Date(historySession.metadata.lastUpdateTime); historySession.messages.forEach(message => { message.timestamp = new Date(message.timestamp); if (message.toolCalls) { message.toolCalls.forEach(toolCall => { toolCall.timestamp = new Date(toolCall.timestamp); }); } }); return historySession; } catch (error) { console.error(`Failed to load conversation ${sessionId}:`, error); return null; } } /** * Get the most recent conversation */ async getLastConversation() { await this.initialize(); if (this.conversations.size === 0) { return null; } const conversations = Array.from(this.conversations.values()); conversations.sort((a, b) => b.lastUpdateTime.getTime() - a.lastUpdateTime.getTime()); return conversations[0] || null; } /** * List recent conversations */ async getRecentConversations(limit = 10) { await this.initialize(); const conversations = Array.from(this.conversations.values()); conversations.sort((a, b) => b.lastUpdateTime.getTime() - a.lastUpdateTime.getTime()); return conversations.slice(0, limit); } /** * Search conversations by content */ async searchConversations(query, limit = 10) { await this.initialize(); const queryLower = query.toLowerCase(); const matchingConversations = Array.from(this.conversations.values()).filter(conv => { return conv.firstMessage.toLowerCase().includes(queryLower) || conv.lastMessage.toLowerCase().includes(queryLower); }); matchingConversations.sort((a, b) => b.lastUpdateTime.getTime() - a.lastUpdateTime.getTime()); return matchingConversations.slice(0, limit); } /** * Delete a conversation from history */ async deleteConversation(sessionId) { await this.initialize(); const conversation = this.conversations.get(sessionId); if (!conversation) { return false; } try { if (await (0, fs_extra_1.pathExists)(conversation.filePath)) { await require('fs-extra').remove(conversation.filePath); } this.conversations.delete(sessionId); await this.saveConversationIndex(); return true; } catch (error) { console.error(`Failed to delete conversation ${sessionId}:`, error); return false; } } /** * Get conversation statistics */ async getStatistics() { await this.initialize(); const conversations = Array.from(this.conversations.values()); if (conversations.length === 0) { return { totalConversations: 0, totalMessages: 0, oldestConversation: null, newestConversation: null, avgMessagesPerConversation: 0, }; } const totalMessages = conversations.reduce((sum, conv) => sum + conv.messageCount, 0); const dates = conversations.map(conv => conv.startTime); const oldestConversation = new Date(Math.min(...dates.map(d => d.getTime()))); const newestConversation = new Date(Math.max(...dates.map(d => d.getTime()))); return { totalConversations: conversations.length, totalMessages, oldestConversation, newestConversation, avgMessagesPerConversation: Math.round(totalMessages / conversations.length), }; } /** * Clean up old conversations */ async cleanupOldConversations(maxAge = 30) { await this.initialize(); const maxAgeMs = maxAge * 24 * 60 * 60 * 1000; // Convert days to milliseconds const cutoffDate = new Date(Date.now() - maxAgeMs); const toDelete = Array.from(this.conversations.values()).filter(conv => conv.lastUpdateTime < cutoffDate); let deletedCount = 0; for (const conversation of toDelete) { if (await this.deleteConversation(conversation.id)) { deletedCount++; } } return deletedCount; } /** * Export conversation history */ async exportConversations(outputPath) { await this.initialize(); const exportData = { exportDate: new Date(), conversations: Array.from(this.conversations.values()), totalConversations: this.conversations.size, }; await (0, fs_extra_1.ensureDir)((0, path_1.dirname)(outputPath)); await (0, fs_extra_1.writeFile)(outputPath, JSON.stringify(exportData, null, 2)); } /** * Load conversation index from disk */ async loadConversationIndex() { if (!(await (0, fs_extra_1.pathExists)(this.historyIndex))) { return; } try { const content = await (0, fs_extra_1.readFile)(this.historyIndex, 'utf-8'); const data = JSON.parse(content); this.conversations.clear(); for (const conv of data.conversations || []) { // Convert date strings back to Date objects conv.startTime = new Date(conv.startTime); conv.lastUpdateTime = new Date(conv.lastUpdateTime); this.conversations.set(conv.id, conv); } } catch (error) { console.error('Failed to load conversation index:', error); } } /** * Save conversation index to disk */ async saveConversationIndex() { const data = { lastUpdated: new Date(), conversations: Array.from(this.conversations.values()), }; try { await (0, fs_extra_1.writeFile)(this.historyIndex, JSON.stringify(data, null, 2)); } catch (error) { console.error('Failed to save conversation index:', error); } } /** * Get history directory path */ getHistoryDir() { return this.historyDir; } } exports.HistoryManager = HistoryManager; //# sourceMappingURL=history.js.map