import { Database } from '../database/Database'; import { OptimizationRecommendation } from '../models'; import { TokenTracker } from './TokenTracker'; import { SessionManager } from './SessionManager'; export class WorkflowOptimizer { private db: Database; private tokenTracker: TokenTracker; private sessionManager: SessionManager; constructor(db: Database, tokenTracker: TokenTracker, sessionManager: SessionManager) { this.db = db; this.tokenTracker = tokenTracker; this.sessionManager = sessionManager; } async analyzeWorkflowEfficiency(projectId?: string): Promise<{ score: number; recommendations: OptimizationRecommendation[]; insights: string[]; }> { const recommendations: OptimizationRecommendation[] = []; const insights: string[] = []; let score = 100; // Analyze session patterns const sessionAnalysis = await this.analyzeSessionPatterns(projectId); score -= sessionAnalysis.penalties; recommendations.push(...sessionAnalysis.recommendations); insights.push(...sessionAnalysis.insights); // Analyze token usage const tokenAnalysis = await this.analyzeTokenUsage(); score -= tokenAnalysis.penalties; recommendations.push(...tokenAnalysis.recommendations); insights.push(...tokenAnalysis.insights); // Analyze scheduling efficiency const schedulingAnalysis = await this.analyzeSchedulingEfficiency(); score -= schedulingAnalysis.penalties; recommendations.push(...schedulingAnalysis.recommendations); insights.push(...schedulingAnalysis.insights); return { score: Math.max(0, Math.min(100, score)), recommendations: this.prioritizeRecommendations(recommendations), insights }; } async suggestOptimalSession(taskDescription: string, complexity: 'simple' | 'medium' | 'complex'): Promise<{ modelType: 'opus' | 'sonnet'; estimatedTokens: number; batchingSuggestions: string[]; preparationTips: string[]; }> { const modelType = this.recommendModel(taskDescription, complexity); const estimatedTokens = this.estimateTokenUsage(taskDescription, complexity, modelType); return { modelType, estimatedTokens, batchingSuggestions: this.generateBatchingSuggestions(taskDescription), preparationTips: this.generatePreparationTips(complexity) }; } async generateOptimalSchedule(upcomingTasks: Array<{ description: string; complexity: 'simple' | 'medium' | 'complex'; estimatedDuration: number; // minutes priority: 'high' | 'medium' | 'low'; }>): Promise<{ schedule: Array<{ task: string; recommendedTime: Date; modelType: 'opus' | 'sonnet'; grouping: string; }>; totalEstimatedTokens: number; optimizationNotes: string[]; }> { const schedule = []; let totalTokens = 0; const optimizationNotes = []; // Group tasks by complexity and type const groupedTasks = this.groupTasksByType(upcomingTasks); // Get current window status const currentWindow = await this.sessionManager.getCurrentWindow(); const nextWindow = await this.sessionManager.getNextAvailableWindow(); let currentTime = currentWindow ? new Date() : (nextWindow || new Date()); for (const [groupName, tasks] of Object.entries(groupedTasks)) { for (const task of tasks) { const suggestion = await this.suggestOptimalSession(task.description, task.complexity); schedule.push({ task: task.description, recommendedTime: new Date(currentTime), modelType: suggestion.modelType, grouping: groupName }); totalTokens += suggestion.estimatedTokens; currentTime = new Date(currentTime.getTime() + task.estimatedDuration * 60 * 1000); } } // Add optimization notes if (totalTokens > 400000) { optimizationNotes.push('High token usage expected. Consider splitting across multiple 5-hour windows'); } const opusTasks = upcomingTasks.filter(t => t.complexity === 'complex').length; if (opusTasks > 3) { optimizationNotes.push('Multiple complex tasks detected. Consider using overlap hack for extended sessions'); } return { schedule, totalEstimatedTokens: totalTokens, optimizationNotes }; } async detectWorkflowAnomalies(): Promise<{ anomalies: Array<{ type: 'token_spike' | 'session_fragmentation' | 'model_misuse' | 'scheduling_conflict'; severity: 'low' | 'medium' | 'high'; description: string; recommendation: string; }>; trends: Array<{ metric: string; trend: 'improving' | 'declining' | 'stable'; change: number; }>; }> { const anomalies = []; const trends = []; // Detect token spikes const recentUsage = await this.tokenTracker.getUsageStats(7); const avgDailyTokens = recentUsage.reduce((sum, day) => sum + day.totalTokens, 0) / 7; if (avgDailyTokens > 100000) { anomalies.push({ type: 'token_spike', severity: 'high', description: `Daily token usage (${avgDailyTokens.toLocaleString()}) is unusually high`, recommendation: 'Review recent sessions for inefficiencies and consider using /compact more frequently' }); } // Detect session fragmentation const recentSessions = await this.sessionManager.getSessionHistory(20); const shortSessions = recentSessions.filter(s => { const duration = s.endTime ? (s.endTime.getTime() - s.startTime.getTime()) / (1000 * 60) : 0; return duration < 10; }).length; if (shortSessions > recentSessions.length * 0.5) { anomalies.push({ type: 'session_fragmentation', severity: 'medium', description: `${shortSessions} out of ${recentSessions.length} recent sessions were shorter than 10 minutes`, recommendation: 'Consider batching similar tasks into longer, more focused sessions' }); } // Detect model misuse const opusHeavyDays = recentUsage.filter(day => day.opusSessions > day.sonnetSessions).length; if (opusHeavyDays > recentUsage.length * 0.6) { anomalies.push({ type: 'model_misuse', severity: 'medium', description: 'Opus is being used more frequently than Sonnet', recommendation: 'Reserve Opus for complex tasks and use Sonnet for routine implementation work' }); } // Calculate trends if (recentUsage.length >= 2) { const recentAvg = recentUsage.slice(0, 3).reduce((sum, day) => sum + day.optimizationScore, 0) / 3; const olderAvg = recentUsage.slice(3, 6).reduce((sum, day) => sum + day.optimizationScore, 0) / Math.min(3, recentUsage.length - 3); if (recentAvg > olderAvg + 5) { trends.push({ metric: 'optimization_score', trend: 'improving', change: recentAvg - olderAvg }); } else if (recentAvg < olderAvg - 5) { trends.push({ metric: 'optimization_score', trend: 'declining', change: olderAvg - recentAvg }); } else { trends.push({ metric: 'optimization_score', trend: 'stable', change: 0 }); } } return { anomalies, trends }; } private async analyzeSessionPatterns(projectId?: string): Promise<{ penalties: number; recommendations: OptimizationRecommendation[]; insights: string[]; }> { const penalties = 0; const recommendations: OptimizationRecommendation[] = []; const insights: string[] = []; const sessions = projectId ? await this.db.all('SELECT * FROM sessions WHERE projectId = ? ORDER BY startTime DESC LIMIT 10', [projectId]) : await this.sessionManager.getSessionHistory(10); // Analyze session length distribution const sessionLengths = sessions.map(session => { const endTime = (session as { endTime?: string | Date }).endTime || new Date(); const endTimeDate = typeof endTime === 'string' ? new Date(endTime) : endTime; return (endTimeDate.getTime() - new Date((session as { startTime: string }).startTime).getTime()) / (1000 * 60); // minutes }); const avgLength = sessionLengths.reduce((sum, length) => sum + length, 0) / sessionLengths.length; if (avgLength < 15) { recommendations.push({ type: 'workflow', priority: 'medium', message: 'Average session length is quite short. Consider batching related tasks.' }); } insights.push(`Average session duration: ${avgLength.toFixed(1)} minutes`); return { penalties, recommendations, insights }; } private async analyzeTokenUsage(): Promise<{ penalties: number; recommendations: OptimizationRecommendation[]; insights: string[]; }> { const penalties = 0; const recommendations: OptimizationRecommendation[] = []; const insights: string[] = []; const usageStats = await this.tokenTracker.getUsageStats(7); usageStats.reduce((sum, day) => sum + day.totalTokens, 0); const avgTokensPerSession = usageStats.reduce((sum, day) => sum + (day.totalTokens / Math.max(1, day.sessionsCount)), 0) / usageStats.length; if (avgTokensPerSession > 50000) { recommendations.push({ type: 'token', priority: 'high', message: 'High token usage per session detected. Consider using /compact more frequently.', potentialSavings: avgTokensPerSession * 0.3 }); } insights.push(`Average tokens per session: ${avgTokensPerSession.toLocaleString()}`); return { penalties, recommendations, insights }; } private async analyzeSchedulingEfficiency(): Promise<{ penalties: number; recommendations: OptimizationRecommendation[]; insights: string[]; }> { const penalties = 0; const recommendations: OptimizationRecommendation[] = []; const insights: string[] = []; const currentWindow = await this.sessionManager.getCurrentWindow(); if (!currentWindow) { recommendations.push({ type: 'scheduling', priority: 'medium', message: 'No active 5-hour window. Consider using the overlap hack to prepare for intensive sessions.' }); } insights.push(currentWindow ? 'Currently within an active 5-hour window' : 'No active window detected'); return { penalties, recommendations, insights }; } private recommendModel(taskDescription: string, complexity: 'simple' | 'medium' | 'complex'): 'opus' | 'sonnet' { if (complexity === 'complex') return 'opus'; if (complexity === 'simple') return 'sonnet'; // For medium complexity, analyze task description const complexKeywords = ['architecture', 'design', 'refactor', 'optimize', 'debug complex', 'implement system']; const hasComplexKeywords = complexKeywords.some(keyword => taskDescription.toLowerCase().includes(keyword) ); return hasComplexKeywords ? 'opus' : 'sonnet'; } private estimateTokenUsage(taskDescription: string, complexity: 'simple' | 'medium' | 'complex', modelType: 'opus' | 'sonnet'): number { const baseTokens = { simple: { sonnet: 5000, opus: 8000 }, medium: { sonnet: 15000, opus: 25000 }, complex: { sonnet: 30000, opus: 50000 } }; return baseTokens[complexity][modelType]; } private generateBatchingSuggestions(taskDescription: string): string[] { const suggestions = [ 'Prepare all related files and context before starting', 'Group similar implementation tasks together', 'Plan testing and linting as part of the same session' ]; if (taskDescription.includes('test')) { suggestions.push('Include test writing in the same session as implementation'); } if (taskDescription.includes('refactor')) { suggestions.push('Batch related refactoring tasks to maintain context'); } return suggestions; } private generatePreparationTips(complexity: 'simple' | 'medium' | 'complex'): string[] { const tips = { simple: [ 'Have specific files ready to reference', 'Prepare clear, concise instructions' ], medium: [ 'Review relevant documentation beforehand', 'Prepare test cases or expected outcomes', 'Consider edge cases in advance' ], complex: [ 'Create a detailed step-by-step plan', 'Prepare comprehensive project context', 'Have fallback approaches ready', 'Schedule adequate time for implementation' ] }; return tips[complexity]; } private groupTasksByType(tasks: Array<{ description: string; complexity: 'simple' | 'medium' | 'complex'; estimatedDuration: number; priority: 'high' | 'medium' | 'low'; }>): Record> { return tasks.reduce((groups, task) => { const category = this.categorizeTask(task); if (!groups[category]) groups[category] = []; groups[category].push(task); return groups; }, {} as Record>); } private categorizeTask(task: { description: string; complexity: 'simple' | 'medium' | 'complex'; estimatedDuration: number; priority: 'high' | 'medium' | 'low'; }): string { if (task.complexity === 'complex') return 'Complex Architecture'; if (task.description.includes('test')) return 'Testing'; if (task.description.includes('fix') || task.description.includes('bug')) return 'Bug Fixes'; if (task.description.includes('feature')) return 'Feature Development'; return 'General Tasks'; } private prioritizeRecommendations(recommendations: OptimizationRecommendation[]): OptimizationRecommendation[] { return recommendations.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; return priorityOrder[b.priority] - priorityOrder[a.priority]; }); } }