Skip to main content
Glama
progress-tracker.ts34.3 kB
/** * Progress Tracking System * * Implements real-time progress tracking and reporting for projects, * epics, and individual tasks with completion time estimation. */ import { AtomicTask } from '../types/task.js'; import { AppError } from '../../../utils/errors.js'; import logger from '../../../logger.js'; /** * Simple Epic interface for progress tracking */ export interface Epic { id: string; title: string; createdAt: Date; completedAt?: Date; } /** * Simple Project interface for progress tracking */ export interface Project { id: string; name: string; createdAt: Date; completedAt?: Date; } /** * Progress calculation methods */ export type ProgressMethod = 'task_count' | 'estimated_hours' | 'weighted' | 'complexity'; /** * Task progress information */ export interface TaskProgress { taskId: string; status: string; progressPercentage: number; startedAt?: Date; completedAt?: Date; estimatedHours?: number; actualHours?: number; blockers: string[]; lastUpdated: Date; } /** * Epic progress information */ export interface EpicProgress { epicId: string; title: string; totalTasks: number; completedTasks: number; inProgressTasks: number; blockedTasks: number; progressPercentage: number; estimatedHours: number; actualHours: number; remainingHours: number; estimatedCompletionDate?: Date; startedAt?: Date; completedAt?: Date; tasks: TaskProgress[]; lastUpdated: Date; } /** * Project progress information */ export interface ProjectProgress { projectId: string; projectName: string; totalEpics: number; completedEpics: number; inProgressEpics: number; totalTasks: number; completedTasks: number; inProgressTasks: number; blockedTasks: number; progressPercentage: number; estimatedHours: number; actualHours: number; remainingHours: number; estimatedCompletionDate?: Date; startedAt?: Date; completedAt?: Date; epics: EpicProgress[]; lastUpdated: Date; } /** * Progress tracking configuration */ export interface ProgressConfig { method: ProgressMethod; updateIntervalMinutes: number; enableRealTimeUpdates: boolean; enableCompletionEstimation: boolean; enableDependencyTracking: boolean; enableCriticalPathMonitoring: boolean; enableScheduleDeviationAlerts: boolean; complexityWeights: Record<string, number>; statusWeights: Record<string, number>; deviationThresholdPercentage: number; criticalPathUpdateInterval: number; } /** * Progress event types */ export type ProgressEvent = | 'task_started' | 'task_progress_updated' | 'task_completed' | 'task_blocked' | 'task_failed' | 'task_dependency_resolved' | 'task_dependency_blocked' | 'epic_progress_updated' | 'epic_completed' | 'project_progress_updated' | 'project_completed' | 'milestone_reached' | 'critical_path_updated' | 'schedule_deviation_detected' | 'decomposition_started' | 'decomposition_progress' | 'decomposition_completed' | 'validation_started' | 'validation_completed' | 'research_triggered' | 'research_completed' | 'context_gathering_started' | 'context_gathering_completed' | 'dependency_detection_started' | 'dependency_detection_completed'; /** * Progress event data */ export interface ProgressEventData { event: ProgressEvent; projectId?: string; epicId?: string; taskId?: string; progressPercentage?: number; estimatedCompletion?: Date; timestamp: Date; metadata?: Record<string, unknown>; // Enhanced properties for dependency tracking dependencyId?: string; // Enhanced properties for schedule deviation deviationPercentage?: number; actualHours?: number; estimatedHours?: number; status?: string; // Enhanced properties for critical path monitoring criticalPathTasks?: Array<{ id: string; title: string; estimatedHours: number; status: string; }>; // Enhanced properties for vibe task manager components componentName?: string; stepName?: string; currentStep?: number; totalSteps?: number; message?: string; decompositionProgress?: { phase: 'research' | 'context_gathering' | 'decomposition' | 'validation' | 'dependency_detection'; progress: number; message: string; }; } /** * Progress Tracking System */ export class ProgressTracker { private static instance: ProgressTracker | null = null; private config: ProgressConfig; private progressCache = new Map<string, ProjectProgress>(); private updateTimer?: NodeJS.Timeout; private eventListeners = new Map<ProgressEvent, Array<(data: ProgressEventData) => void>>(); private constructor(config?: Partial<ProgressConfig>) { this.config = { method: 'weighted', updateIntervalMinutes: 5, enableRealTimeUpdates: true, enableCompletionEstimation: true, enableDependencyTracking: true, enableCriticalPathMonitoring: true, enableScheduleDeviationAlerts: true, complexityWeights: { 'simple': 1, 'medium': 2, 'complex': 3, 'critical': 4 }, statusWeights: { 'pending': 0, 'in_progress': 0.5, 'completed': 1, 'blocked': 0, 'failed': 0 }, deviationThresholdPercentage: 20, criticalPathUpdateInterval: 10, ...config }; if (this.config.enableRealTimeUpdates) { this.startProgressUpdates(); } logger.info({ config: this.config }, 'Progress tracker initialized'); } /** * Get singleton instance */ static getInstance(config?: Partial<ProgressConfig>): ProgressTracker { if (!ProgressTracker.instance) { ProgressTracker.instance = new ProgressTracker(config); } return ProgressTracker.instance; } /** * Calculate project progress */ async calculateProjectProgress(projectId: string): Promise<ProjectProgress> { try { // Placeholder implementation - in real implementation, this would fetch from storage const project: Project = { id: projectId, name: `Project ${projectId}`, createdAt: new Date() }; const epics: Epic[] = []; // Placeholder - would fetch from storage const epicProgresses: EpicProgress[] = []; let totalTasks = 0; let completedTasks = 0; let inProgressTasks = 0; let blockedTasks = 0; let totalEstimatedHours = 0; let totalActualHours = 0; // Calculate progress for each epic for (const epic of epics) { const epicProgress = await this.calculateEpicProgress(epic.id); epicProgresses.push(epicProgress); totalTasks += epicProgress.totalTasks; completedTasks += epicProgress.completedTasks; inProgressTasks += epicProgress.inProgressTasks; blockedTasks += epicProgress.blockedTasks; totalEstimatedHours += epicProgress.estimatedHours; totalActualHours += epicProgress.actualHours; } // Calculate overall progress percentage const progressPercentage = this.calculateProgressPercentage( epicProgresses.map(ep => ({ completed: ep.completedTasks, total: ep.totalTasks, estimatedHours: ep.estimatedHours, actualHours: ep.actualHours })) ); // Calculate completion estimates const remainingHours = Math.max(0, totalEstimatedHours - totalActualHours); const estimatedCompletionDate = this.config.enableCompletionEstimation ? this.estimateCompletionDate(remainingHours, inProgressTasks) : undefined; const projectProgress: ProjectProgress = { projectId, projectName: project.name, totalEpics: epics.length, completedEpics: epicProgresses.filter(ep => ep.progressPercentage >= 100).length, inProgressEpics: epicProgresses.filter(ep => ep.progressPercentage > 0 && ep.progressPercentage < 100).length, totalTasks, completedTasks, inProgressTasks, blockedTasks, progressPercentage, estimatedHours: totalEstimatedHours, actualHours: totalActualHours, remainingHours, estimatedCompletionDate, startedAt: project.createdAt, completedAt: progressPercentage >= 100 ? new Date() : undefined, epics: epicProgresses, lastUpdated: new Date() }; // Cache the result this.progressCache.set(projectId, projectProgress); // Emit progress event this.emitProgressEvent('project_progress_updated', { projectId, progressPercentage, estimatedCompletion: estimatedCompletionDate }); // Check for project completion if (progressPercentage >= 100 && !project.completedAt) { this.emitProgressEvent('project_completed', { projectId }); } logger.debug({ projectId, progressPercentage, totalTasks, completedTasks }, 'Project progress calculated'); return projectProgress; } catch (error) { logger.error({ err: error, projectId }, 'Failed to calculate project progress'); throw new AppError('Project progress calculation failed', { cause: error }); } } /** * Calculate epic progress */ async calculateEpicProgress(epicId: string): Promise<EpicProgress> { try { // Placeholder implementation - in real implementation, this would fetch from storage const epic: Epic = { id: epicId, title: `Epic ${epicId}`, createdAt: new Date() }; const tasks: AtomicTask[] = []; // Placeholder - would fetch from storage const taskProgresses: TaskProgress[] = []; let completedTasks = 0; let inProgressTasks = 0; let blockedTasks = 0; let totalEstimatedHours = 0; let totalActualHours = 0; // Calculate progress for each task for (const task of tasks) { const taskProgress = this.calculateTaskProgress(task); taskProgresses.push(taskProgress); if (taskProgress.status === 'completed') { completedTasks++; } else if (taskProgress.status === 'in_progress') { inProgressTasks++; } else if (taskProgress.blockers.length > 0) { blockedTasks++; } totalEstimatedHours += taskProgress.estimatedHours || 0; totalActualHours += taskProgress.actualHours || 0; } // Calculate overall progress percentage const progressPercentage = this.calculateProgressPercentage( taskProgresses.map(tp => ({ completed: tp.status === 'completed' ? 1 : 0, total: 1, estimatedHours: tp.estimatedHours || 0, actualHours: tp.actualHours || 0 })) ); // Calculate completion estimates const remainingHours = Math.max(0, totalEstimatedHours - totalActualHours); const estimatedCompletionDate = this.config.enableCompletionEstimation ? this.estimateCompletionDate(remainingHours, inProgressTasks) : undefined; const epicProgress: EpicProgress = { epicId, title: epic.title, totalTasks: tasks.length, completedTasks, inProgressTasks, blockedTasks, progressPercentage, estimatedHours: totalEstimatedHours, actualHours: totalActualHours, remainingHours, estimatedCompletionDate, startedAt: epic.createdAt, completedAt: progressPercentage >= 100 ? new Date() : undefined, tasks: taskProgresses, lastUpdated: new Date() }; // Emit progress event this.emitProgressEvent('epic_progress_updated', { epicId, progressPercentage, estimatedCompletion: estimatedCompletionDate }); // Check for epic completion if (progressPercentage >= 100 && !epic.completedAt) { this.emitProgressEvent('epic_completed', { epicId }); } return epicProgress; } catch (error) { logger.error({ err: error, epicId }, 'Failed to calculate epic progress'); throw new AppError('Epic progress calculation failed', { cause: error }); } } /** * Calculate task progress */ calculateTaskProgress(task: AtomicTask): TaskProgress { const now = new Date(); // Determine progress percentage based on status let progressPercentage = 0; if (task.status === 'completed') { progressPercentage = 100; } else if (task.status === 'in_progress') { progressPercentage = 50; // Could be enhanced with more granular tracking } // Check for blockers const blockers = task.dependencies?.filter(_dep => // In a real implementation, check if dependencies are blocking false // Placeholder ) || []; const taskProgress: TaskProgress = { taskId: task.id, status: task.status, progressPercentage, startedAt: task.startedAt, completedAt: task.completedAt, estimatedHours: task.estimatedHours, actualHours: task.actualHours, blockers, lastUpdated: now }; return taskProgress; } /** * Enhanced task status update with dependency tracking */ async updateTaskStatus( taskId: string, newStatus: string, progressPercentage?: number, actualHours?: number, dependencyUpdates?: { resolvedDependencies?: string[], blockedDependencies?: string[] } ): Promise<void> { try { logger.debug({ taskId, newStatus, progressPercentage, actualHours, dependencyUpdates }, 'Enhanced task status update requested'); // Emit appropriate status events switch (newStatus) { case 'in_progress': this.emitProgressEvent('task_started', { taskId }); break; case 'completed': this.emitProgressEvent('task_completed', { taskId, progressPercentage: 100 }); break; case 'blocked': this.emitProgressEvent('task_blocked', { taskId }); break; case 'failed': this.emitProgressEvent('task_failed', { taskId }); break; default: this.emitProgressEvent('task_progress_updated', { taskId, progressPercentage }); } // Handle dependency updates if enabled if (this.config.enableDependencyTracking && dependencyUpdates) { await this.handleDependencyUpdates(taskId, dependencyUpdates); } // Check for schedule deviations if enabled if (this.config.enableScheduleDeviationAlerts) { await this.checkScheduleDeviation(taskId, newStatus, actualHours); } // Invalidate cached progress for affected project this.progressCache.clear(); logger.debug({ taskId, newStatus }, 'Enhanced task status updated'); } catch (error) { logger.error({ err: error, taskId, newStatus }, 'Failed to update task status'); throw new AppError('Task status update failed', { cause: error }); } } /** * Update task progress (legacy method for backward compatibility) */ async updateTaskProgress( taskId: string, progressPercentage: number, actualHours?: number ): Promise<void> { await this.updateTaskStatus(taskId, 'in_progress', progressPercentage, actualHours); } /** * Get cached project progress */ getCachedProjectProgress(projectId: string): ProjectProgress | null { return this.progressCache.get(projectId) || null; } /** * Clear progress cache */ clearCache(projectId?: string): void { if (projectId) { this.progressCache.delete(projectId); } else { this.progressCache.clear(); } logger.debug({ projectId }, 'Progress cache cleared'); } /** * Add progress event listener */ addEventListener(event: ProgressEvent, listener: (data: ProgressEventData) => void): void { if (!this.eventListeners.has(event)) { this.eventListeners.set(event, []); } this.eventListeners.get(event)!.push(listener); } /** * Remove progress event listener */ removeEventListener(event: ProgressEvent, listener: (data: ProgressEventData) => void): void { const listeners = this.eventListeners.get(event); if (listeners) { const index = listeners.indexOf(listener); if (index > -1) { listeners.splice(index, 1); } } } /** * Calculate progress percentage using configured method */ private calculateProgressPercentage( items: Array<{ completed: number; total: number; estimatedHours?: number; actualHours?: number; }> ): number { if (items.length === 0) return 0; switch (this.config.method) { case 'task_count': { const totalTasks = items.reduce((sum, item) => sum + item.total, 0); const completedTasks = items.reduce((sum, item) => sum + item.completed, 0); return totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0; } case 'estimated_hours': { const totalHours = items.reduce((sum, item) => sum + (item.estimatedHours || 0), 0); const actualHours = items.reduce((sum, item) => sum + (item.actualHours || 0), 0); return totalHours > 0 ? Math.min((actualHours / totalHours) * 100, 100) : 0; } case 'weighted': { // Combine task count and hours with weights const taskProgress = this.calculateProgressPercentage(items.map(item => ({ completed: item.completed, total: item.total }))); const hourProgress = this.calculateProgressPercentage(items.map(item => ({ completed: item.actualHours || 0, total: item.estimatedHours || 0 }))); return (taskProgress * 0.6) + (hourProgress * 0.4); } default: return this.calculateProgressPercentage(items.map(item => ({ completed: item.completed, total: item.total }))); } } /** * Estimate completion date */ private estimateCompletionDate(remainingHours: number, activeTasks: number): Date | undefined { if (remainingHours <= 0) return new Date(); // Simple estimation: assume 8 hours per day per active task const hoursPerDay = Math.max(activeTasks * 8, 8); const daysRemaining = Math.ceil(remainingHours / hoursPerDay); const completionDate = new Date(); completionDate.setDate(completionDate.getDate() + daysRemaining); return completionDate; } /** * Start automatic progress updates */ private startProgressUpdates(): void { this.updateTimer = setInterval(() => { this.updateAllCachedProgress().catch(error => { logger.error({ err: error }, 'Error in automatic progress update'); }); }, this.config.updateIntervalMinutes * 60000); logger.debug({ intervalMinutes: this.config.updateIntervalMinutes }, 'Automatic progress updates started'); } /** * Update all cached progress */ private async updateAllCachedProgress(): Promise<void> { const projectIds = Array.from(this.progressCache.keys()); for (const projectId of projectIds) { try { await this.calculateProjectProgress(projectId); } catch (error) { logger.error({ err: error, projectId }, 'Failed to update cached progress'); } } } /** * Emit progress event */ emitProgressEvent(event: ProgressEvent, data: Partial<ProgressEventData>): void { const eventData: ProgressEventData = { event, timestamp: new Date(), ...data }; const listeners = this.eventListeners.get(event); if (listeners) { listeners.forEach(listener => { try { listener(eventData); } catch (error) { logger.error({ err: error, event }, 'Error in progress event listener'); } }); } } /** * Handle dependency updates and emit appropriate events */ private async handleDependencyUpdates( taskId: string, dependencyUpdates: { resolvedDependencies?: string[], blockedDependencies?: string[] } ): Promise<void> { try { if (dependencyUpdates.resolvedDependencies?.length) { for (const depId of dependencyUpdates.resolvedDependencies) { this.emitProgressEvent('task_dependency_resolved', { taskId, dependencyId: depId, timestamp: new Date() }); } logger.debug({ taskId, resolvedDependencies: dependencyUpdates.resolvedDependencies }, 'Task dependencies resolved'); } if (dependencyUpdates.blockedDependencies?.length) { for (const depId of dependencyUpdates.blockedDependencies) { this.emitProgressEvent('task_dependency_blocked', { taskId, dependencyId: depId, timestamp: new Date() }); } logger.warn({ taskId, blockedDependencies: dependencyUpdates.blockedDependencies }, 'Task dependencies blocked'); } } catch (error) { logger.error({ err: error, taskId }, 'Failed to handle dependency updates'); } } /** * Check for schedule deviations and emit alerts */ private async checkScheduleDeviation( taskId: string, status: string, actualHours?: number ): Promise<void> { try { // In a real implementation, this would: // 1. Fetch the task's estimated hours and scheduled completion // 2. Compare actual progress vs. expected progress // 3. Calculate deviation percentage // 4. Emit alerts if deviation exceeds threshold // Placeholder implementation if (actualHours && actualHours > 0) { // Simulate estimated hours (in real implementation, fetch from task) const estimatedHours = 8; // Placeholder const deviationPercentage = ((actualHours - estimatedHours) / estimatedHours) * 100; if (Math.abs(deviationPercentage) > this.config.deviationThresholdPercentage) { this.emitProgressEvent('schedule_deviation_detected', { taskId, deviationPercentage, actualHours, estimatedHours, status, timestamp: new Date() }); logger.warn({ taskId, deviationPercentage, actualHours, estimatedHours, threshold: this.config.deviationThresholdPercentage }, 'Schedule deviation detected'); } } } catch (error) { logger.error({ err: error, taskId }, 'Failed to check schedule deviation'); } } /** * Monitor critical path changes */ async monitorCriticalPath(projectId: string, tasks: AtomicTask[]): Promise<void> { try { if (!this.config.enableCriticalPathMonitoring) { return; } // In a real implementation, this would: // 1. Calculate the current critical path // 2. Compare with previous critical path // 3. Emit events if critical path has changed // 4. Update estimated project completion time // Placeholder implementation const criticalPathTasks = tasks .filter(task => task.priority === 'high' || task.dependencies.length > 0) .sort((a, b) => b.estimatedHours - a.estimatedHours) .slice(0, 5); // Top 5 critical tasks this.emitProgressEvent('critical_path_updated', { projectId, criticalPathTasks: criticalPathTasks.map(t => ({ id: t.id, title: t.title, estimatedHours: t.estimatedHours, status: t.status })), timestamp: new Date() }); logger.debug({ projectId, criticalPathTaskCount: criticalPathTasks.length }, 'Critical path monitoring updated'); } catch (error) { logger.error({ err: error, projectId }, 'Failed to monitor critical path'); } } /** * Get real-time task status summary */ async getTaskStatusSummary(projectId: string): Promise<{ total: number; pending: number; inProgress: number; completed: number; blocked: number; failed: number; progressPercentage: number; }> { try { // In a real implementation, this would fetch actual task data // Placeholder implementation const summary = { total: 10, pending: 2, inProgress: 3, completed: 4, blocked: 1, failed: 0, progressPercentage: 40 }; logger.debug({ projectId, summary }, 'Task status summary generated'); return summary; } catch (error) { logger.error({ err: error, projectId }, 'Failed to get task status summary'); throw new AppError('Task status summary generation failed', { cause: error }); } } /** * Track decomposition progress with detailed steps */ async trackDecompositionProgress( taskId: string, projectId: string, onProgress?: (progress: ProgressEventData) => void ): Promise<void> { const steps = [ { phase: 'research', message: 'Evaluating research needs and gathering insights', weight: 20 }, { phase: 'context_gathering', message: 'Collecting relevant codebase context', weight: 25 }, { phase: 'decomposition', message: 'Breaking down task into atomic components', weight: 30 }, { phase: 'validation', message: 'Validating task quality and atomicity', weight: 15 }, { phase: 'dependency_detection', message: 'Detecting intelligent dependencies', weight: 10 } ]; let currentProgress = 0; for (let i = 0; i < steps.length; i++) { const step = steps[i]; this.emitProgressEvent('decomposition_progress', { taskId, projectId, currentStep: i + 1, totalSteps: steps.length, progressPercentage: currentProgress, componentName: 'DecompositionService', stepName: step.phase, message: step.message, decompositionProgress: { phase: step.phase as 'research' | 'context_gathering' | 'decomposition' | 'validation' | 'dependency_detection', progress: currentProgress, message: step.message } }); if (onProgress) { onProgress({ event: 'decomposition_progress', taskId, projectId, currentStep: i + 1, totalSteps: steps.length, progressPercentage: currentProgress, timestamp: new Date(), decompositionProgress: { phase: step.phase as 'research' | 'context_gathering' | 'decomposition' | 'validation' | 'dependency_detection', progress: currentProgress, message: step.message } }); } // Simulate step completion time (in real implementation, this would be triggered by actual progress) await new Promise(resolve => setTimeout(resolve, 500)); currentProgress += step.weight; } this.emitProgressEvent('decomposition_completed', { taskId, projectId, progressPercentage: 100, componentName: 'DecompositionService', message: 'Task decomposition completed successfully' }); } /** * Track validation progress for atomic tasks */ async trackValidationProgress( taskIds: string[], projectId: string, onProgress?: (progress: ProgressEventData) => void ): Promise<void> { this.emitProgressEvent('validation_started', { projectId, message: `Starting validation for ${taskIds.length} tasks`, totalSteps: taskIds.length }); for (let i = 0; i < taskIds.length; i++) { const taskId = taskIds[i]; const progress = Math.round(((i + 1) / taskIds.length) * 100); this.emitProgressEvent('validation_started', { taskId, projectId, currentStep: i + 1, totalSteps: taskIds.length, progressPercentage: progress, componentName: 'AtomicTaskDetector', message: `Validating task ${i + 1} of ${taskIds.length}` }); if (onProgress) { onProgress({ event: 'validation_started', taskId, projectId, currentStep: i + 1, totalSteps: taskIds.length, progressPercentage: progress, timestamp: new Date(), componentName: 'AtomicTaskDetector', message: `Validating task ${i + 1} of ${taskIds.length}` }); } // Simulate validation time await new Promise(resolve => setTimeout(resolve, 200)); } this.emitProgressEvent('validation_completed', { projectId, progressPercentage: 100, componentName: 'AtomicTaskDetector', message: `Validation completed for all ${taskIds.length} tasks` }); } /** * Track research integration progress */ async trackResearchProgress( taskId: string, projectId: string, researchQueries: string[], onProgress?: (progress: ProgressEventData) => void ): Promise<void> { this.emitProgressEvent('research_triggered', { taskId, projectId, componentName: 'AutoResearchDetector', message: `Research triggered for complex task: ${researchQueries.length} queries`, totalSteps: researchQueries.length }); for (let i = 0; i < researchQueries.length; i++) { const progress = Math.round(((i + 1) / researchQueries.length) * 100); this.emitProgressEvent('decomposition_progress', { taskId, projectId, currentStep: i + 1, totalSteps: researchQueries.length, progressPercentage: progress, componentName: 'AutoResearchDetector', message: `Processing research query: ${researchQueries[i].substring(0, 50)}...` }); if (onProgress) { onProgress({ event: 'decomposition_progress', taskId, projectId, currentStep: i + 1, totalSteps: researchQueries.length, progressPercentage: progress, timestamp: new Date(), componentName: 'AutoResearchDetector', message: `Processing research query: ${researchQueries[i].substring(0, 50)}...` }); } await new Promise(resolve => setTimeout(resolve, 1000)); } this.emitProgressEvent('research_completed', { taskId, projectId, progressPercentage: 100, componentName: 'AutoResearchDetector', message: 'Research integration completed' }); } /** * Track context gathering progress */ async trackContextProgress( taskId: string, projectId: string, filesAnalyzed: number, totalFiles: number, onProgress?: (progress: ProgressEventData) => void ): Promise<void> { const progress = Math.round((filesAnalyzed / totalFiles) * 100); this.emitProgressEvent('context_gathering_started', { taskId, projectId, currentStep: filesAnalyzed, totalSteps: totalFiles, progressPercentage: progress, componentName: 'ContextEnrichmentService', message: `Analyzing file ${filesAnalyzed} of ${totalFiles}` }); if (onProgress) { onProgress({ event: 'context_gathering_started', taskId, projectId, currentStep: filesAnalyzed, totalSteps: totalFiles, progressPercentage: progress, timestamp: new Date(), componentName: 'ContextEnrichmentService', message: `Analyzing file ${filesAnalyzed} of ${totalFiles}` }); } if (filesAnalyzed >= totalFiles) { this.emitProgressEvent('context_gathering_completed', { taskId, projectId, progressPercentage: 100, componentName: 'ContextEnrichmentService', message: `Context gathering completed: ${totalFiles} files analyzed` }); } } /** * Track dependency detection progress */ async trackDependencyDetectionProgress( taskIds: string[], projectId: string, dependenciesDetected: number, onProgress?: (progress: ProgressEventData) => void ): Promise<void> { this.emitProgressEvent('dependency_detection_started', { projectId, componentName: 'OptimizedDependencyGraph', message: `Starting dependency detection for ${taskIds.length} tasks`, totalSteps: taskIds.length }); const progress = Math.round((dependenciesDetected / (taskIds.length * taskIds.length)) * 100); this.emitProgressEvent('dependency_detection_started', { projectId, progressPercentage: progress, componentName: 'OptimizedDependencyGraph', message: `Detected ${dependenciesDetected} dependencies so far` }); if (onProgress) { onProgress({ event: 'dependency_detection_started', projectId, progressPercentage: progress, timestamp: new Date(), componentName: 'OptimizedDependencyGraph', message: `Detected ${dependenciesDetected} dependencies so far` }); } } /** * Complete dependency detection tracking */ async completeDependencyDetectionProgress( projectId: string, finalDependencyCount: number, appliedDependencies: number ): Promise<void> { this.emitProgressEvent('dependency_detection_completed', { projectId, progressPercentage: 100, componentName: 'OptimizedDependencyGraph', message: `Dependency detection completed: ${finalDependencyCount} suggestions, ${appliedDependencies} applied` }); } /** * Get real-time progress for a specific component */ async getComponentProgress( _componentName: string, _projectId?: string ): Promise<{ isActive: boolean; currentStep?: number; totalSteps?: number; progressPercentage: number; message?: string; lastUpdate: Date; }> { // In a real implementation, this would track active operations // For now, return a placeholder implementation return { isActive: false, progressPercentage: 0, lastUpdate: new Date() }; } /** * Cleanup resources */ destroy(): void { if (this.updateTimer) { clearInterval(this.updateTimer); } this.progressCache.clear(); this.eventListeners.clear(); ProgressTracker.instance = null; logger.info('Progress tracker destroyed'); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server