UNPKG

@codervisor/devlog-ai

Version:

AI Chat History Extractor & Docker-based Automation - TypeScript implementation for GitHub Copilot and other AI coding assistants with automated testing capabilities

172 lines (148 loc) 5.16 kB
/** * Chat Import Service * * Service for importing chat history from AI assistants and converting * them to the devlog system format. */ import type { ChatSession as CoreChatSession, ChatMessage as CoreChatMessage, ChatImportProgress, ChatSource, AgentType, StorageProvider, } from '@codervisor/devlog-core'; import { CopilotParser } from '../parsers/copilot/copilot-parser.js'; import type { WorkspaceData, ChatSession, Message } from '../models/index.js'; export interface ChatImportService { /** * Import chat history from GitHub Copilot */ importFromCopilot(): Promise<ChatImportProgress>; /** * Import chat history from a specific source */ importFromSource( source: ChatSource, config?: Record<string, unknown>, ): Promise<ChatImportProgress>; /** * Convert AI package chat data to core package format */ convertToCoreChatSessions(workspaceData: WorkspaceData): CoreChatSession[]; /** * Convert AI package messages to core package format */ convertToCoreMessages(sessions: ChatSession[]): CoreChatMessage[]; } export class DefaultChatImportService implements ChatImportService { private storageProvider: StorageProvider; constructor(storageProvider: StorageProvider) { this.storageProvider = storageProvider; } async importFromCopilot(): Promise<ChatImportProgress> { const importId = this.generateImportId(); const progress: ChatImportProgress = { importId, status: 'running', source: 'github-copilot', progress: { totalSessions: 0, processedSessions: 0, totalMessages: 0, processedMessages: 0, percentage: 0, }, startedAt: new Date().toISOString(), }; try { // Use CopilotParser to discover chat data const parser = new CopilotParser(); const workspaceData = await parser.discoverChatData(); progress.progress.totalSessions = workspaceData.chat_sessions.length; // Convert to core format const coreSessions = this.convertToCoreChatSessions(workspaceData); const coreMessages = this.convertToCoreMessages(workspaceData.chat_sessions); progress.progress.totalMessages = coreMessages.length; // Save to storage for (const session of coreSessions) { await this.storageProvider.saveChatSession(session); progress.progress.processedSessions++; } if (coreMessages.length > 0) { await this.storageProvider.saveChatMessages(coreMessages); progress.progress.processedMessages = coreMessages.length; } progress.progress.percentage = 100; progress.status = 'completed'; progress.completedAt = new Date().toISOString(); progress.results = { importedSessions: coreSessions.length, importedMessages: coreMessages.length, linkedSessions: 0, errors: 0, warnings: [], }; return progress; } catch (error: unknown) { progress.status = 'failed'; progress.completedAt = new Date().toISOString(); progress.error = { message: error instanceof Error ? error.message : 'Unknown error', details: { stack: error instanceof Error ? error.stack : undefined }, }; throw error; } } async importFromSource( source: ChatSource, config?: Record<string, unknown>, ): Promise<ChatImportProgress> { switch (source) { case 'github-copilot': return this.importFromCopilot(); default: throw new Error(`Unsupported chat source: ${source}`); } } convertToCoreChatSessions(workspaceData: WorkspaceData): CoreChatSession[] { return workspaceData.chat_sessions.map((session, index) => ({ id: session.session_id || `imported_${Date.now()}_${index}`, agent: session.agent as AgentType, timestamp: session.timestamp.toISOString(), workspace: session.workspace, workspacePath: session.workspace, title: `Chat Session ${session.session_id?.slice(0, 8) || index}`, status: 'imported' as const, messageCount: session.messages.length, duration: undefined, metadata: session.metadata, tags: [], importedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), linkedDevlogs: [], archived: false, })); } convertToCoreMessages(sessions: ChatSession[]): CoreChatMessage[] { const messages: CoreChatMessage[] = []; for (const session of sessions) { session.messages.forEach((message, index) => { messages.push({ id: message.id || `msg_${Date.now()}_${index}`, sessionId: session.session_id || `session_${Date.now()}`, role: message.role, content: message.content, timestamp: message.timestamp.toISOString(), sequence: index, metadata: message.metadata, searchContent: message.content.toLowerCase().replace(/[^\w\s]/g, ' '), }); }); } return messages; } private generateImportId(): string { return `import_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`; } }