@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
249 lines (216 loc) • 7.63 kB
text/typescript
/**
* Chat import service for importing chat history from various sources
*
* This service handles importing chat data through ChatHub (GitHub Copilot, etc.)
* into the devlog storage system with proper workspace mapping and linking.
*/
import type {
AgentType,
ChatDevlogLink,
ChatImportProgress,
ChatMessage,
ChatSession,
ChatSessionId,
ChatSource,
ChatStatus,
DevlogEntry,
StorageProvider,
} from '@codervisor/devlog-core';
// Define workspace info type instead of using any
interface WorkspaceInfo {
id: string;
name: string;
path?: string;
source: string;
firstSeen: string;
lastSeen: string;
sessionCount: number;
metadata: Record<string, unknown>;
}
export interface IChatHubService {
/**
* Ingest chat sessions from external clients
*/
ingestChatSessions(sessions: ChatSession[]): Promise<ChatImportProgress>;
/**
* Ingest chat messages from external clients
*/
ingestChatMessages(messages: ChatMessage[]): Promise<void>;
/**
* Process bulk chat data from external clients
*/
processBulkChatData(data: {
sessions: ChatSession[];
messages: ChatMessage[];
source: ChatSource;
workspaceInfo?: WorkspaceInfo;
}): Promise<ChatImportProgress>;
/**
* Get import progress by ID
*/
getImportProgress(importId: string): Promise<ChatImportProgress | null>;
/**
* Suggest links between chat sessions and devlog entries
*/
suggestChatDevlogLinks(
sessionId?: ChatSessionId,
minConfidence?: number,
): Promise<ChatDevlogLink[]>;
/**
* Auto-link chat sessions to devlog entries based on various heuristics
*/
autoLinkSessions(sessionIds: ChatSessionId[], threshold?: number): Promise<ChatDevlogLink[]>;
}
export class ChatHubService implements IChatHubService {
private storageProvider: StorageProvider;
private activeImports = new Map<string, ChatImportProgress>();
constructor(storageProvider: StorageProvider) {
this.storageProvider = storageProvider;
}
async ingestChatSessions(sessions: ChatSession[]): Promise<ChatImportProgress> {
const importId = this.generateImportId();
const progress: ChatImportProgress = {
importId,
status: 'running',
source: sessions[0]?.agent === 'GitHub Copilot' ? 'github-copilot' : 'manual',
progress: {
totalSessions: sessions.length,
processedSessions: 0,
totalMessages: 0,
processedMessages: 0,
percentage: 0,
},
startedAt: new Date().toISOString(),
};
this.activeImports.set(importId, progress);
try {
console.log(`[ChatHub] Ingesting ${sessions.length} chat sessions`);
for (const session of sessions) {
await this.storageProvider.saveChatSession(session);
progress.progress.processedSessions++;
progress.progress.percentage = Math.round(
(progress.progress.processedSessions / progress.progress.totalSessions) * 100,
);
}
progress.status = 'completed';
progress.completedAt = new Date().toISOString();
progress.results = {
importedSessions: sessions.length,
importedMessages: 0,
linkedSessions: 0,
errors: 0,
warnings: [],
};
console.log(`[ChatHub] Successfully ingested ${sessions.length} sessions`);
return progress;
} catch (error: unknown) {
console.error('[ChatHub] Error ingesting sessions:', error);
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 ingestChatMessages(messages: ChatMessage[]): Promise<void> {
try {
console.log(`[ChatHub] Ingesting ${messages.length} chat messages`);
await this.storageProvider.saveChatMessages(messages);
console.log(`[ChatHub] Successfully ingested ${messages.length} messages`);
} catch (error: unknown) {
console.error('[ChatHub] Error ingesting messages:', error);
throw error;
}
}
async processBulkChatData(data: {
sessions: ChatSession[];
messages: ChatMessage[];
source: ChatSource;
workspaceInfo?: WorkspaceInfo;
}): Promise<ChatImportProgress> {
const importId = this.generateImportId();
const progress: ChatImportProgress = {
importId,
status: 'running',
source: data.source,
progress: {
totalSessions: data.sessions.length,
processedSessions: 0,
totalMessages: data.messages.length,
processedMessages: 0,
percentage: 0,
},
startedAt: new Date().toISOString(),
};
this.activeImports.set(importId, progress);
try {
console.log(
`[ChatHub] Processing bulk data: ${data.sessions.length} sessions, ${data.messages.length} messages from ${data.source}`,
);
// Process workspace info if provided
if (data.workspaceInfo) {
await this.storageProvider.saveChatWorkspace(data.workspaceInfo);
}
// Ingest sessions
for (const session of data.sessions) {
await this.storageProvider.saveChatSession(session);
progress.progress.processedSessions++;
}
// Ingest messages
if (data.messages.length > 0) {
await this.storageProvider.saveChatMessages(data.messages);
progress.progress.processedMessages = data.messages.length;
}
// Update final progress
progress.progress.percentage = 100;
progress.status = 'completed';
progress.completedAt = new Date().toISOString();
progress.results = {
importedSessions: data.sessions.length,
importedMessages: data.messages.length,
linkedSessions: 0, // TODO: Implement auto-linking
errors: 0,
warnings: [],
};
console.log(`[ChatHub] Successfully processed bulk data from ${data.source}`);
return progress;
} catch (error: unknown) {
console.error('[ChatHub] Error processing bulk data:', error);
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 getImportProgress(importId: string): Promise<ChatImportProgress | null> {
return this.activeImports.get(importId) || null;
}
async suggestChatDevlogLinks(
sessionId?: ChatSessionId,
minConfidence = 0.5,
): Promise<ChatDevlogLink[]> {
// Simplified implementation - can be enhanced later
console.log(
`[ChatHub] Suggesting links for session ${sessionId || 'all'} with min confidence ${minConfidence}`,
);
// TODO: Implement sophisticated chat-devlog linking logic
// For now, return empty array - this will be enhanced with proper analysis
return [];
}
async autoLinkSessions(sessionIds: ChatSessionId[], threshold = 0.8): Promise<ChatDevlogLink[]> {
// Simplified implementation - can be enhanced later
console.log(`[ChatHub] Auto-linking ${sessionIds.length} sessions with threshold ${threshold}`);
// TODO: Implement sophisticated auto-linking logic
// For now, return empty array - this will be enhanced with proper analysis
return [];
}
// Helper methods
private generateImportId(): string {
return `chathub_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}
}