giga-code
Version:
A personal AI CLI assistant powered by Grok for local development.
201 lines • 8.47 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConversationManager = void 0;
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
const os_1 = __importDefault(require("os"));
const api_keys_1 = require("./api-keys");
class ConversationManager {
constructor() {
this.conversationsDir = path_1.default.join(os_1.default.homedir(), '.giga', 'conversations');
}
static getInstance() {
if (!ConversationManager.instance) {
ConversationManager.instance = new ConversationManager();
}
return ConversationManager.instance;
}
async ensureConversationsDir() {
try {
await promises_1.default.mkdir(this.conversationsDir, { recursive: true });
}
catch (error) {
console.error('Failed to create conversations directory:', error);
}
}
generateConversationId() {
return `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
getConversationPath(id) {
return path_1.default.join(this.conversationsDir, `${id}.json`);
}
async generateTitle(messages) {
const apiKeys = (0, api_keys_1.loadApiKeys)();
const groqApiKey = apiKeys.groqApiKey || process.env.GROQ_API_KEY;
if (!groqApiKey) {
// Fallback to first user message if no API key
const firstUserMessage = messages.find(m => m.type === 'user');
return firstUserMessage?.content.slice(0, 50) + '...' || 'Untitled Conversation';
}
try {
// Get first few meaningful messages for context
const contextMessages = messages
.filter(m => m.type === 'user' || m.type === 'assistant')
.slice(0, 6)
.map(m => `${m.type}: ${m.content}`)
.join('\n');
const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${groqApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'llama-3.3-70b-versatile',
messages: [
{
role: 'system',
content: 'Generate a concise 3-6 word title for this coding conversation. Focus on the main task/topic. Examples: "React Component Bug Fix", "Database Schema Design", "API Integration Setup".'
},
{
role: 'user',
content: `Conversation:\n${contextMessages}\n\nTitle:`
}
],
max_tokens: 20,
temperature: 0.1
})
});
if (response.ok) {
const data = await response.json();
const title = data.choices?.[0]?.message?.content?.trim();
if (title && title.length > 0) {
return title.replace(/['"]/g, ''); // Remove quotes if any
}
}
}
catch (error) {
console.error('Failed to generate title:', error);
}
// Fallback to first user message
const firstUserMessage = messages.find(m => m.type === 'user');
return firstUserMessage?.content.slice(0, 50) + '...' || 'Untitled Conversation';
}
async saveConversation(messages, model, existingId) {
await this.ensureConversationsDir();
const id = existingId || this.generateConversationId();
const now = new Date();
// Generate title if this is a new conversation or if it doesn't have many messages yet
let title = 'New Conversation';
if (messages.length >= 2) {
title = await this.generateTitle(messages);
}
const conversation = {
id,
title,
messages,
createdAt: existingId ? (await this.loadConversation(id))?.createdAt || now : now,
updatedAt: now,
model,
messageCount: messages.filter(m => m.type === 'user' || m.type === 'assistant').length
};
const filePath = this.getConversationPath(id);
await promises_1.default.writeFile(filePath, JSON.stringify(conversation, null, 2));
return id;
}
async loadConversation(id) {
try {
const filePath = this.getConversationPath(id);
const data = await promises_1.default.readFile(filePath, 'utf-8');
const conversation = JSON.parse(data);
// Convert date strings back to Date objects
conversation.createdAt = new Date(conversation.createdAt);
conversation.updatedAt = new Date(conversation.updatedAt);
conversation.messages = conversation.messages.map((msg) => ({
...msg,
timestamp: new Date(msg.timestamp)
}));
return conversation;
}
catch (error) {
console.error(`Failed to load conversation ${id}:`, error);
return null;
}
}
async listConversations() {
await this.ensureConversationsDir();
try {
const files = await promises_1.default.readdir(this.conversationsDir);
const jsonFiles = files.filter(f => f.endsWith('.json'));
const summaries = [];
for (const file of jsonFiles) {
try {
const id = file.replace('.json', '');
const conversation = await this.loadConversation(id);
if (conversation) {
const firstUserMessage = conversation.messages.find(m => m.type === 'user');
const preview = firstUserMessage?.content.slice(0, 100) + '...' || 'No messages';
summaries.push({
id: conversation.id,
title: conversation.title,
createdAt: conversation.createdAt,
updatedAt: conversation.updatedAt,
model: conversation.model,
messageCount: conversation.messageCount,
preview
});
}
}
catch (error) {
console.error(`Failed to process conversation file ${file}:`, error);
}
}
// Sort by most recently updated first
return summaries.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
}
catch (error) {
console.error('Failed to list conversations:', error);
return [];
}
}
async getMostRecentConversation() {
try {
const summaries = await this.listConversations();
if (summaries.length === 0) {
return null;
}
// Get the most recent conversation (first in the sorted list)
const mostRecent = summaries[0];
return await this.loadConversation(mostRecent.id);
}
catch (error) {
console.error('Failed to get most recent conversation:', error);
return null;
}
}
async deleteConversation(id) {
try {
const filePath = this.getConversationPath(id);
await promises_1.default.unlink(filePath);
return true;
}
catch (error) {
console.error(`Failed to delete conversation ${id}:`, error);
return false;
}
}
async searchConversations(query) {
const allConversations = await this.listConversations();
if (!query.trim()) {
return allConversations;
}
const lowerQuery = query.toLowerCase();
return allConversations.filter(conv => conv.title.toLowerCase().includes(lowerQuery) ||
conv.preview.toLowerCase().includes(lowerQuery));
}
}
exports.ConversationManager = ConversationManager;
//# sourceMappingURL=conversation-manager.js.map