zai-mcp-server
Version:
🚀 REVOLUTIONARY AI-to-AI Collaboration Platform v6.1! NEW: Advanced Debugging Tools with Screenshot Analysis, Console Error Parsing, Automated Fix Generation, 5 Specialized Debugging Agents, Visual UI Analysis, JavaScript Error Intelligence, CSS/HTML Fix
1,296 lines (1,032 loc) • 47.2 kB
JavaScript
/**
* Predictive Task Management System
* AI-powered system to predict task failures, optimize resource allocation, provide accurate timelines, and assess risks
*/
import { EventEmitter } from 'events';
import crypto from 'crypto';
import fs from 'fs/promises';
import path from 'path';
export class PredictiveTaskManagement extends EventEmitter {
constructor(options = {}) {
super();
this.predictiveDir = options.predictiveDir || './predictive';
this.maxHistoryEntries = options.maxHistoryEntries || 1000;
this.predictionAccuracyThreshold = options.predictionAccuracyThreshold || 0.75;
this.riskAssessmentInterval = options.riskAssessmentInterval || 60000; // 1 minute
this.taskHistory = [];
this.activeProjects = new Map();
this.predictionModels = new Map();
this.resourceMetrics = new Map();
this.riskFactors = new Map();
this.timelineEstimates = new Map();
this.failurePredictions = new Map();
// Prediction model weights
this.modelWeights = {
complexity: 0.25,
teamExperience: 0.20,
resourceAvailability: 0.15,
historicalSuccess: 0.15,
dependencies: 0.10,
timeConstraints: 0.10,
externalFactors: 0.05
};
console.log('🔮 Predictive Task Management System initialized');
// Initialize prediction models immediately (synchronous)
this.initializePredictionModels();
// Initialize directories and load historical data (async)
this.initializePredictiveSystem();
this.startRiskAssessment();
}
async initializePredictiveSystem() {
try {
await fs.mkdir(this.predictiveDir, { recursive: true });
await this.loadHistoricalData();
console.log(`📁 Predictive directory initialized: ${this.predictiveDir}`);
} catch (error) {
console.warn('⚠️ Failed to initialize predictive system:', error.message);
}
}
async loadHistoricalData() {
try {
const historyFile = path.join(this.predictiveDir, 'task-history.json');
const data = await fs.readFile(historyFile, 'utf8');
this.taskHistory = JSON.parse(data);
console.log(`📥 Loaded ${this.taskHistory.length} historical task entries`);
} catch (error) {
console.log('📝 No existing task history found, starting fresh');
}
}
async saveHistoricalData() {
try {
const historyFile = path.join(this.predictiveDir, 'task-history.json');
await fs.writeFile(historyFile, JSON.stringify(this.taskHistory, null, 2));
console.log('💾 Task history saved');
} catch (error) {
console.warn('⚠️ Failed to save task history:', error.message);
}
}
initializePredictionModels() {
// Initialize different prediction models
this.predictionModels.set('failure_prediction', {
name: 'Task Failure Prediction',
accuracy: 0.82,
features: ['complexity', 'team_experience', 'dependencies', 'timeline_pressure'],
lastTrained: Date.now(),
predictions: 0
});
this.predictionModels.set('timeline_estimation', {
name: 'Timeline Estimation',
accuracy: 0.78,
features: ['task_type', 'complexity', 'team_size', 'historical_velocity'],
lastTrained: Date.now(),
predictions: 0
});
this.predictionModels.set('resource_optimization', {
name: 'Resource Optimization',
accuracy: 0.85,
features: ['workload_distribution', 'skill_matching', 'availability', 'priority'],
lastTrained: Date.now(),
predictions: 0
});
this.predictionModels.set('risk_assessment', {
name: 'Risk Assessment',
accuracy: 0.80,
features: ['external_dependencies', 'technology_maturity', 'team_stability', 'scope_clarity'],
lastTrained: Date.now(),
predictions: 0
});
console.log(`🤖 Initialized ${this.predictionModels.size} prediction models`);
}
async createProject(projectData) {
const projectId = crypto.randomBytes(8).toString('hex');
const project = {
id: projectId,
name: projectData.name,
description: projectData.description,
createdAt: Date.now(),
lastUpdated: Date.now(),
status: 'planning',
tasks: [],
team: projectData.team || [],
timeline: {
startDate: projectData.startDate || Date.now(),
estimatedEndDate: null,
actualEndDate: null,
milestones: []
},
resources: {
allocated: projectData.resources || {},
utilized: {},
efficiency: 0
},
risks: [],
predictions: {
failureRisk: 0,
timelineAccuracy: 0,
resourceOptimization: 0,
overallHealth: 0
},
metrics: {
velocity: 0,
burndownRate: 0,
qualityScore: 0,
teamSatisfaction: 0
}
};
this.activeProjects.set(projectId, project);
// Generate initial predictions
await this.generateProjectPredictions(projectId);
console.log(`🔮 Created predictive project: ${projectData.name} (${projectId})`);
this.emit('project:created', {
projectId,
project,
predictions: project.predictions
});
return projectId;
}
async addTaskToProject(projectId, taskData) {
const project = this.activeProjects.get(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
const taskId = crypto.randomBytes(8).toString('hex');
const task = {
id: taskId,
projectId: projectId,
title: taskData.title,
description: taskData.description,
type: taskData.type || 'development',
complexity: taskData.complexity || 'medium',
priority: taskData.priority || 'medium',
assignee: taskData.assignee,
dependencies: taskData.dependencies || [],
estimatedHours: taskData.estimatedHours,
actualHours: 0,
status: 'todo',
createdAt: Date.now(),
startedAt: null,
completedAt: null,
tags: taskData.tags || [],
risks: [],
predictions: {
failureRisk: 0,
timelineAccuracy: 0,
completionProbability: 0,
resourceRequirement: 0
}
};
project.tasks.push(task);
project.lastUpdated = Date.now();
// Generate task-specific predictions
await this.generateTaskPredictions(projectId, taskId);
// Update project-level predictions
await this.generateProjectPredictions(projectId);
console.log(`📋 Added task to project ${projectId}: ${taskData.title}`);
this.emit('task:added', {
projectId,
taskId,
task,
predictions: task.predictions
});
return taskId;
}
async generateTaskPredictions(projectId, taskId) {
const project = this.activeProjects.get(projectId);
if (!project) return;
const task = project.tasks.find(t => t.id === taskId);
if (!task) return;
// Predict failure risk
task.predictions.failureRisk = await this.predictTaskFailureRisk(task, project);
// Predict timeline accuracy
task.predictions.timelineAccuracy = await this.predictTimelineAccuracy(task, project);
// Predict completion probability
task.predictions.completionProbability = await this.predictCompletionProbability(task, project);
// Predict resource requirements
task.predictions.resourceRequirement = await this.predictResourceRequirement(task, project);
// Store prediction in cache
this.failurePredictions.set(taskId, {
taskId,
projectId,
predictions: task.predictions,
timestamp: Date.now(),
confidence: this.calculatePredictionConfidence(task, project)
});
console.log(`🔮 Generated predictions for task ${taskId}: failure risk ${(task.predictions.failureRisk * 100).toFixed(1)}%`);
}
async predictTaskFailureRisk(task, project) {
let riskScore = 0;
// Complexity factor
const complexityWeights = { low: 0.1, medium: 0.3, high: 0.6, critical: 0.9 };
riskScore += (complexityWeights[task.complexity] || 0.3) * this.modelWeights.complexity;
// Team experience factor
const teamExperience = this.calculateTeamExperience(project.team, task.type);
riskScore += (1 - teamExperience) * this.modelWeights.teamExperience;
// Dependencies factor
const dependencyRisk = this.calculateDependencyRisk(task, project);
riskScore += dependencyRisk * this.modelWeights.dependencies;
// Historical success rate
const historicalSuccess = this.getHistoricalSuccessRate(task.type, task.complexity);
riskScore += (1 - historicalSuccess) * this.modelWeights.historicalSuccess;
// Time constraints factor
const timeConstraints = this.calculateTimeConstraints(task, project);
riskScore += timeConstraints * this.modelWeights.timeConstraints;
// Resource availability factor
const resourceAvailability = this.calculateResourceAvailability(task, project);
riskScore += (1 - resourceAvailability) * this.modelWeights.resourceAvailability;
return Math.min(riskScore, 1.0);
}
async predictTimelineAccuracy(task, project) {
let accuracyScore = 0.8; // Base accuracy
// Adjust based on historical data
const historicalAccuracy = this.getHistoricalTimelineAccuracy(task.type, task.complexity);
accuracyScore *= historicalAccuracy;
// Adjust based on team velocity
const teamVelocity = this.calculateTeamVelocity(project);
accuracyScore *= teamVelocity;
// Adjust based on scope clarity
const scopeClarity = this.calculateScopeClarity(task);
accuracyScore *= scopeClarity;
return Math.min(accuracyScore, 1.0);
}
async predictCompletionProbability(task, project) {
const failureRisk = task.predictions.failureRisk || 0;
const timelineAccuracy = task.predictions.timelineAccuracy || 0.8;
// Base completion probability
let completionProb = 0.85;
// Adjust for failure risk
completionProb *= (1 - failureRisk);
// Adjust for timeline accuracy
completionProb *= timelineAccuracy;
// Adjust for team capacity
const teamCapacity = this.calculateTeamCapacity(project);
completionProb *= teamCapacity;
return Math.min(completionProb, 1.0);
}
async predictResourceRequirement(task, project) {
let resourceMultiplier = 1.0;
// Complexity adjustment
const complexityMultipliers = { low: 0.8, medium: 1.0, high: 1.5, critical: 2.0 };
resourceMultiplier *= complexityMultipliers[task.complexity] || 1.0;
// Team experience adjustment
const teamExperience = this.calculateTeamExperience(project.team, task.type);
resourceMultiplier *= (2 - teamExperience); // Less experience = more resources needed
// Dependencies adjustment
const dependencyCount = task.dependencies.length;
resourceMultiplier *= (1 + dependencyCount * 0.1);
// Historical adjustment
const historicalMultiplier = this.getHistoricalResourceMultiplier(task.type, task.complexity);
resourceMultiplier *= historicalMultiplier;
return Math.min(resourceMultiplier, 3.0); // Cap at 3x
}
calculateTeamExperience(team, taskType) {
if (!team || team.length === 0) return 0.5;
const experienceScores = team.map(member => {
// Simulate experience calculation
const baseExperience = member.experience || 0.7;
const typeExperience = member.skills?.[taskType] || 0.5;
return (baseExperience + typeExperience) / 2;
});
return experienceScores.reduce((sum, score) => sum + score, 0) / experienceScores.length;
}
calculateDependencyRisk(task, project) {
if (!task.dependencies || task.dependencies.length === 0) return 0;
let riskScore = 0;
for (const depId of task.dependencies) {
const depTask = project.tasks.find(t => t.id === depId);
if (!depTask) {
riskScore += 0.3; // External dependency risk
} else if (depTask.status !== 'completed') {
riskScore += 0.2; // Internal dependency risk
}
}
return Math.min(riskScore, 1.0);
}
getHistoricalSuccessRate(taskType, complexity) {
const relevantHistory = this.taskHistory.filter(h =>
h.type === taskType && h.complexity === complexity
);
if (relevantHistory.length === 0) return 0.8; // Default success rate
const successfulTasks = relevantHistory.filter(h => h.status === 'completed').length;
return successfulTasks / relevantHistory.length;
}
calculateTimeConstraints(task, project) {
if (!task.estimatedHours || !project.timeline.estimatedEndDate) return 0.3;
const remainingTime = project.timeline.estimatedEndDate - Date.now();
const requiredTime = task.estimatedHours * 60 * 60 * 1000; // Convert to milliseconds
if (remainingTime <= 0) return 1.0; // Past deadline
if (requiredTime > remainingTime) return 0.8; // Tight timeline
if (requiredTime > remainingTime * 0.8) return 0.5; // Moderate pressure
return 0.2; // Comfortable timeline
}
calculateResourceAvailability(task, project) {
if (!task.assignee) return 0.7; // No assignee
// Simulate resource availability calculation
const assigneeWorkload = this.calculateAssigneeWorkload(task.assignee, project);
return Math.max(0.1, 1 - assigneeWorkload);
}
calculateAssigneeWorkload(assignee, project) {
const assigneeTasks = project.tasks.filter(t =>
t.assignee === assignee &&
['todo', 'in_progress'].includes(t.status)
);
const totalHours = assigneeTasks.reduce((sum, t) => sum + (t.estimatedHours || 8), 0);
const weeklyCapacity = 40; // 40 hours per week
return Math.min(totalHours / weeklyCapacity, 1.0);
}
getHistoricalTimelineAccuracy(taskType, complexity) {
const relevantHistory = this.taskHistory.filter(h =>
h.type === taskType &&
h.complexity === complexity &&
h.estimatedHours &&
h.actualHours
);
if (relevantHistory.length === 0) return 0.8;
const accuracyScores = relevantHistory.map(h => {
const accuracy = Math.min(h.estimatedHours / h.actualHours, h.actualHours / h.estimatedHours);
return Math.max(accuracy, 0.1);
});
return accuracyScores.reduce((sum, score) => sum + score, 0) / accuracyScores.length;
}
calculateTeamVelocity(project) {
const completedTasks = project.tasks.filter(t => t.status === 'completed');
if (completedTasks.length === 0) return 0.8;
const velocityScores = completedTasks.map(task => {
if (!task.estimatedHours || !task.actualHours) return 0.8;
return Math.min(task.estimatedHours / task.actualHours, 1.0);
});
return velocityScores.reduce((sum, score) => sum + score, 0) / velocityScores.length;
}
calculateScopeClarity(task) {
let clarityScore = 0.8;
// Check description quality
if (!task.description || task.description.length < 50) {
clarityScore -= 0.2;
}
// Check if requirements are defined
if (!task.estimatedHours) {
clarityScore -= 0.1;
}
// Check if acceptance criteria exist
if (!task.acceptanceCriteria || task.acceptanceCriteria.length === 0) {
clarityScore -= 0.1;
}
return Math.max(clarityScore, 0.3);
}
calculateTeamCapacity(project) {
if (!project.team || project.team.length === 0) return 0.5;
const capacityScores = project.team.map(member => {
const workload = this.calculateAssigneeWorkload(member.id, project);
return Math.max(0.1, 1 - workload);
});
return capacityScores.reduce((sum, score) => sum + score, 0) / capacityScores.length;
}
getHistoricalResourceMultiplier(taskType, complexity) {
const relevantHistory = this.taskHistory.filter(h =>
h.type === taskType &&
h.complexity === complexity &&
h.estimatedHours &&
h.actualHours
);
if (relevantHistory.length === 0) return 1.0;
const multipliers = relevantHistory.map(h => h.actualHours / h.estimatedHours);
return multipliers.reduce((sum, mult) => sum + mult, 0) / multipliers.length;
}
calculatePredictionConfidence(task, project) {
let confidence = 0.8;
// Adjust based on historical data availability
const historicalDataPoints = this.taskHistory.filter(h =>
h.type === task.type && h.complexity === task.complexity
).length;
if (historicalDataPoints > 50) confidence += 0.1;
else if (historicalDataPoints < 10) confidence -= 0.2;
// Adjust based on team experience
const teamExperience = this.calculateTeamExperience(project.team, task.type);
confidence += (teamExperience - 0.5) * 0.2;
// Adjust based on scope clarity
const scopeClarity = this.calculateScopeClarity(task);
confidence += (scopeClarity - 0.5) * 0.2;
return Math.max(0.3, Math.min(confidence, 0.95));
}
async generateProjectPredictions(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) return;
// Calculate overall project health
project.predictions.overallHealth = await this.calculateProjectHealth(project);
// Calculate failure risk
project.predictions.failureRisk = await this.calculateProjectFailureRisk(project);
// Calculate timeline accuracy
project.predictions.timelineAccuracy = await this.calculateProjectTimelineAccuracy(project);
// Calculate resource optimization
project.predictions.resourceOptimization = await this.calculateResourceOptimization(project);
// Update timeline estimates
await this.updateTimelineEstimates(projectId);
console.log(`🔮 Updated project predictions for ${projectId}: health ${(project.predictions.overallHealth * 100).toFixed(1)}%`);
this.emit('project:predictions_updated', {
projectId,
predictions: project.predictions
});
}
async calculateProjectHealth(project) {
let healthScore = 0.8;
// Task completion rate
const completedTasks = project.tasks.filter(t => t.status === 'completed').length;
const totalTasks = project.tasks.length;
const completionRate = totalTasks > 0 ? completedTasks / totalTasks : 0;
healthScore *= (0.5 + completionRate * 0.5);
// Average task health
const taskHealthScores = project.tasks.map(task => {
const failureRisk = task.predictions?.failureRisk || 0.3;
return 1 - failureRisk;
});
if (taskHealthScores.length > 0) {
const avgTaskHealth = taskHealthScores.reduce((sum, score) => sum + score, 0) / taskHealthScores.length;
healthScore *= avgTaskHealth;
}
// Timeline adherence
if (project.timeline.estimatedEndDate) {
const timeRemaining = project.timeline.estimatedEndDate - Date.now();
const totalDuration = project.timeline.estimatedEndDate - project.timeline.startDate;
const timeElapsed = Date.now() - project.timeline.startDate;
if (timeRemaining > 0 && totalDuration > 0) {
const expectedProgress = timeElapsed / totalDuration;
const actualProgress = completionRate;
const timelineAdherence = Math.min(actualProgress / expectedProgress, 1.0);
healthScore *= (0.7 + timelineAdherence * 0.3);
}
}
return Math.max(0.1, Math.min(healthScore, 1.0));
}
async calculateProjectFailureRisk(project) {
if (project.tasks.length === 0) return 0.3;
// Average task failure risk
const taskRisks = project.tasks.map(task => task.predictions?.failureRisk || 0.3);
const avgTaskRisk = taskRisks.reduce((sum, risk) => sum + risk, 0) / taskRisks.length;
// Project-specific risk factors
let projectRisk = avgTaskRisk;
// Team stability risk
const teamStability = this.calculateTeamStability(project);
projectRisk += (1 - teamStability) * 0.2;
// Scope creep risk
const scopeCreepRisk = this.calculateScopeCreepRisk(project);
projectRisk += scopeCreepRisk * 0.15;
// External dependency risk
const externalDepRisk = this.calculateExternalDependencyRisk(project);
projectRisk += externalDepRisk * 0.1;
return Math.min(projectRisk, 1.0);
}
async calculateProjectTimelineAccuracy(project) {
if (project.tasks.length === 0) return 0.8;
// Average task timeline accuracy
const taskAccuracies = project.tasks.map(task => task.predictions?.timelineAccuracy || 0.8);
const avgTaskAccuracy = taskAccuracies.reduce((sum, acc) => sum + acc, 0) / taskAccuracies.length;
// Project-specific factors
let projectAccuracy = avgTaskAccuracy;
// Team velocity consistency
const velocityConsistency = this.calculateVelocityConsistency(project);
projectAccuracy *= velocityConsistency;
// Scope clarity
const overallScopeClarity = this.calculateOverallScopeClarity(project);
projectAccuracy *= overallScopeClarity;
return Math.max(0.3, Math.min(projectAccuracy, 1.0));
}
async calculateResourceOptimization(project) {
let optimizationScore = 0.8;
// Workload distribution
const workloadDistribution = this.calculateWorkloadDistribution(project);
optimizationScore *= workloadDistribution;
// Skill-task matching
const skillMatching = this.calculateSkillMatching(project);
optimizationScore *= skillMatching;
// Resource utilization efficiency
const utilizationEfficiency = this.calculateUtilizationEfficiency(project);
optimizationScore *= utilizationEfficiency;
return Math.max(0.3, Math.min(optimizationScore, 1.0));
}
calculateTeamStability(project) {
// Simulate team stability calculation
if (!project.team || project.team.length === 0) return 0.5;
// Check for recent team changes
const recentChanges = project.team.filter(member =>
member.joinedAt && (Date.now() - member.joinedAt) < 30 * 24 * 60 * 60 * 1000 // 30 days
).length;
const stabilityScore = 1 - (recentChanges / project.team.length) * 0.5;
return Math.max(0.3, stabilityScore);
}
calculateScopeCreepRisk(project) {
// Simulate scope creep risk calculation
const initialTaskCount = project.initialTaskCount || project.tasks.length;
const currentTaskCount = project.tasks.length;
if (initialTaskCount === 0) return 0.3;
const scopeIncrease = (currentTaskCount - initialTaskCount) / initialTaskCount;
return Math.min(scopeIncrease * 0.5, 0.8);
}
calculateExternalDependencyRisk(project) {
const externalDeps = project.tasks.reduce((count, task) => {
return count + (task.dependencies?.filter(dep => !project.tasks.find(t => t.id === dep)).length || 0);
}, 0);
const totalTasks = project.tasks.length;
if (totalTasks === 0) return 0.3;
return Math.min(externalDeps / totalTasks, 0.7);
}
calculateVelocityConsistency(project) {
const completedTasks = project.tasks.filter(t => t.status === 'completed' && t.actualHours);
if (completedTasks.length < 3) return 0.8;
const velocities = completedTasks.map(task => task.estimatedHours / task.actualHours);
const avgVelocity = velocities.reduce((sum, v) => sum + v, 0) / velocities.length;
const variance = velocities.reduce((sum, v) => sum + Math.pow(v - avgVelocity, 2), 0) / velocities.length;
const standardDeviation = Math.sqrt(variance);
// Lower standard deviation = higher consistency
return Math.max(0.5, 1 - standardDeviation);
}
calculateOverallScopeClarity(project) {
if (project.tasks.length === 0) return 0.8;
const clarityScores = project.tasks.map(task => this.calculateScopeClarity(task));
return clarityScores.reduce((sum, score) => sum + score, 0) / clarityScores.length;
}
calculateWorkloadDistribution(project) {
if (!project.team || project.team.length === 0) return 0.5;
const workloads = project.team.map(member => this.calculateAssigneeWorkload(member.id, project));
const avgWorkload = workloads.reduce((sum, w) => sum + w, 0) / workloads.length;
// Calculate variance in workload distribution
const variance = workloads.reduce((sum, w) => sum + Math.pow(w - avgWorkload, 2), 0) / workloads.length;
const standardDeviation = Math.sqrt(variance);
// Lower variance = better distribution
return Math.max(0.3, 1 - standardDeviation);
}
calculateSkillMatching(project) {
let matchingScore = 0.8;
for (const task of project.tasks) {
if (task.assignee && task.type) {
const assignee = project.team.find(member => member.id === task.assignee);
if (assignee && assignee.skills) {
const skillLevel = assignee.skills[task.type] || 0.5;
matchingScore *= (0.5 + skillLevel * 0.5);
}
}
}
return Math.max(0.3, matchingScore);
}
calculateUtilizationEfficiency(project) {
if (!project.team || project.team.length === 0) return 0.5;
const utilizationScores = project.team.map(member => {
const workload = this.calculateAssigneeWorkload(member.id, project);
// Optimal utilization is around 0.8 (80%)
if (workload < 0.5) return workload * 2; // Underutilized
if (workload > 0.9) return (1 - workload) * 10; // Overutilized
return 1.0; // Well utilized
});
return utilizationScores.reduce((sum, score) => sum + score, 0) / utilizationScores.length;
}
async updateTimelineEstimates(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) return;
const remainingTasks = project.tasks.filter(t => t.status !== 'completed');
let totalEstimatedHours = 0;
for (const task of remainingTasks) {
const baseEstimate = task.estimatedHours || 8;
const resourceMultiplier = task.predictions?.resourceRequirement || 1.0;
const adjustedEstimate = baseEstimate * resourceMultiplier;
totalEstimatedHours += adjustedEstimate;
}
// Calculate team capacity
const teamCapacity = this.calculateTeamCapacity(project);
const effectiveHoursPerWeek = (project.team?.length || 1) * 40 * teamCapacity;
if (effectiveHoursPerWeek > 0) {
const weeksToComplete = totalEstimatedHours / effectiveHoursPerWeek;
const estimatedCompletionDate = Date.now() + (weeksToComplete * 7 * 24 * 60 * 60 * 1000);
project.timeline.estimatedEndDate = estimatedCompletionDate;
this.timelineEstimates.set(projectId, {
projectId,
estimatedCompletionDate,
totalEstimatedHours,
weeksToComplete,
confidence: this.calculateTimelineConfidence(project),
lastUpdated: Date.now()
});
}
}
calculateTimelineConfidence(project) {
let confidence = 0.8;
// Adjust based on historical accuracy
const timelineAccuracy = project.predictions?.timelineAccuracy || 0.8;
confidence *= timelineAccuracy;
// Adjust based on scope clarity
const scopeClarity = this.calculateOverallScopeClarity(project);
confidence *= scopeClarity;
// Adjust based on team stability
const teamStability = this.calculateTeamStability(project);
confidence *= teamStability;
return Math.max(0.3, Math.min(confidence, 0.95));
}
startRiskAssessment() {
setInterval(() => {
this.performRiskAssessment();
}, this.riskAssessmentInterval);
console.log(`⚠️ Risk assessment started (${this.riskAssessmentInterval / 1000}s interval)`);
}
async performRiskAssessment() {
for (const [projectId, project] of this.activeProjects) {
if (project.status === 'active' || project.status === 'planning') {
await this.assessProjectRisks(projectId);
}
}
}
async assessProjectRisks(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) return;
const risks = [];
// Check for high-risk tasks
const highRiskTasks = project.tasks.filter(task =>
(task.predictions?.failureRisk || 0) > 0.7
);
if (highRiskTasks.length > 0) {
risks.push({
type: 'high_risk_tasks',
severity: 'high',
description: `${highRiskTasks.length} tasks have high failure risk`,
tasks: highRiskTasks.map(t => t.id),
recommendation: 'Review task complexity and resource allocation'
});
}
// Check for timeline risks
if (project.timeline.estimatedEndDate && project.timeline.estimatedEndDate < Date.now()) {
risks.push({
type: 'timeline_overrun',
severity: 'critical',
description: 'Project is behind schedule',
recommendation: 'Reassess scope and resource allocation'
});
}
// Check for resource risks
const overloadedMembers = project.team?.filter(member =>
this.calculateAssigneeWorkload(member.id, project) > 0.9
) || [];
if (overloadedMembers.length > 0) {
risks.push({
type: 'resource_overload',
severity: 'medium',
description: `${overloadedMembers.length} team members are overloaded`,
members: overloadedMembers.map(m => m.id),
recommendation: 'Redistribute workload or add resources'
});
}
// Update project risks
project.risks = risks;
project.lastUpdated = Date.now();
// Store in risk factors cache
this.riskFactors.set(projectId, {
projectId,
risks,
riskLevel: this.calculateOverallRiskLevel(risks),
timestamp: Date.now()
});
if (risks.length > 0) {
console.log(`⚠️ Identified ${risks.length} risks for project ${projectId}`);
this.emit('project:risks_identified', {
projectId,
risks,
riskLevel: this.calculateOverallRiskLevel(risks)
});
}
}
calculateOverallRiskLevel(risks) {
if (risks.length === 0) return 'low';
const severityWeights = { low: 1, medium: 2, high: 3, critical: 4 };
const totalWeight = risks.reduce((sum, risk) => sum + severityWeights[risk.severity], 0);
const avgWeight = totalWeight / risks.length;
if (avgWeight >= 3.5) return 'critical';
if (avgWeight >= 2.5) return 'high';
if (avgWeight >= 1.5) return 'medium';
return 'low';
}
async updateTaskStatus(projectId, taskId, newStatus, actualHours = null) {
const project = this.activeProjects.get(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
const task = project.tasks.find(t => t.id === taskId);
if (!task) {
throw new Error(`Task ${taskId} not found in project ${projectId}`);
}
const oldStatus = task.status;
task.status = newStatus;
if (newStatus === 'in_progress' && !task.startedAt) {
task.startedAt = Date.now();
}
if (newStatus === 'completed') {
task.completedAt = Date.now();
if (actualHours !== null) {
task.actualHours = actualHours;
}
// Add to historical data
this.addToHistory(task, project);
}
project.lastUpdated = Date.now();
// Regenerate predictions
await this.generateTaskPredictions(projectId, taskId);
await this.generateProjectPredictions(projectId);
console.log(`📊 Updated task ${taskId} status: ${oldStatus} → ${newStatus}`);
this.emit('task:status_updated', {
projectId,
taskId,
oldStatus,
newStatus,
task
});
}
addToHistory(task, project) {
const historyEntry = {
id: task.id,
projectId: project.id,
title: task.title,
type: task.type,
complexity: task.complexity,
priority: task.priority,
estimatedHours: task.estimatedHours,
actualHours: task.actualHours,
status: task.status,
createdAt: task.createdAt,
startedAt: task.startedAt,
completedAt: task.completedAt,
teamSize: project.team?.length || 1,
dependencies: task.dependencies?.length || 0,
predictions: task.predictions,
timestamp: Date.now()
};
this.taskHistory.push(historyEntry);
// Keep only recent history
if (this.taskHistory.length > this.maxHistoryEntries) {
this.taskHistory = this.taskHistory.slice(-this.maxHistoryEntries);
}
// Save periodically
if (this.taskHistory.length % 10 === 0) {
this.saveHistoricalData();
}
}
getProjectPredictions(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) return null;
return {
projectId,
predictions: project.predictions,
risks: project.risks,
timeline: this.timelineEstimates.get(projectId),
lastUpdated: project.lastUpdated
};
}
getTaskPredictions(projectId, taskId) {
const project = this.activeProjects.get(projectId);
if (!project) return null;
const task = project.tasks.find(t => t.id === taskId);
if (!task) return null;
return {
taskId,
projectId,
predictions: task.predictions,
confidence: this.failurePredictions.get(taskId)?.confidence || 0.8,
lastUpdated: project.lastUpdated
};
}
getPredictiveAnalytics() {
const totalProjects = this.activeProjects.size;
const totalTasks = Array.from(this.activeProjects.values())
.reduce((sum, project) => sum + project.tasks.length, 0);
const highRiskProjects = Array.from(this.activeProjects.values())
.filter(project => (project.predictions?.failureRisk || 0) > 0.7).length;
const highRiskTasks = Array.from(this.activeProjects.values())
.flatMap(project => project.tasks)
.filter(task => (task.predictions?.failureRisk || 0) > 0.7).length;
const avgProjectHealth = Array.from(this.activeProjects.values())
.reduce((sum, project) => sum + (project.predictions?.overallHealth || 0.8), 0) /
Math.max(totalProjects, 1);
const modelAccuracy = Array.from(this.predictionModels.values())
.reduce((sum, model) => sum + model.accuracy, 0) / this.predictionModels.size;
return {
totalProjects,
totalTasks,
highRiskProjects,
highRiskTasks,
avgProjectHealth,
modelAccuracy,
historicalDataPoints: this.taskHistory.length,
predictionModels: Array.from(this.predictionModels.values()),
riskAssessmentActive: true
};
}
getResourceOptimizationSuggestions(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) return [];
const suggestions = [];
// Check for workload imbalances
if (project.team && project.team.length > 1) {
const workloads = project.team.map(member => ({
id: member.id,
workload: this.calculateAssigneeWorkload(member.id, project)
}));
const overloaded = workloads.filter(w => w.workload > 0.9);
const underutilized = workloads.filter(w => w.workload < 0.5);
if (overloaded.length > 0 && underutilized.length > 0) {
suggestions.push({
type: 'workload_rebalancing',
priority: 'high',
description: 'Redistribute tasks to balance workload',
overloaded: overloaded.map(w => w.id),
underutilized: underutilized.map(w => w.id)
});
}
}
// Check for skill mismatches
const mismatchedTasks = project.tasks.filter(task => {
if (!task.assignee || !task.type) return false;
const assignee = project.team?.find(member => member.id === task.assignee);
if (!assignee || !assignee.skills) return false;
return (assignee.skills[task.type] || 0.5) < 0.6;
});
if (mismatchedTasks.length > 0) {
suggestions.push({
type: 'skill_optimization',
priority: 'medium',
description: 'Reassign tasks to better match team member skills',
tasks: mismatchedTasks.map(t => t.id)
});
}
// Check for dependency bottlenecks
const blockedTasks = project.tasks.filter(task => {
return task.dependencies?.some(depId => {
const depTask = project.tasks.find(t => t.id === depId);
return depTask && depTask.status !== 'completed';
});
});
if (blockedTasks.length > project.tasks.length * 0.3) {
suggestions.push({
type: 'dependency_optimization',
priority: 'high',
description: 'Resolve dependency bottlenecks to unblock tasks',
blockedTasks: blockedTasks.map(t => t.id)
});
}
return suggestions;
}
async optimizeProjectTimeline(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
const optimizations = [];
// Identify critical path
const criticalPath = this.calculateCriticalPath(project);
// Suggest parallel execution opportunities
const parallelOpportunities = this.identifyParallelOpportunities(project);
if (parallelOpportunities.length > 0) {
optimizations.push({
type: 'parallel_execution',
impact: 'high',
description: 'Execute independent tasks in parallel',
opportunities: parallelOpportunities
});
}
// Suggest resource reallocation
const resourceSuggestions = this.getResourceOptimizationSuggestions(projectId);
optimizations.push(...resourceSuggestions);
// Suggest scope adjustments for high-risk tasks
const highRiskTasks = project.tasks.filter(task =>
(task.predictions?.failureRisk || 0) > 0.8
);
if (highRiskTasks.length > 0) {
optimizations.push({
type: 'scope_adjustment',
impact: 'medium',
description: 'Consider reducing scope or adding resources for high-risk tasks',
tasks: highRiskTasks.map(t => t.id)
});
}
return {
projectId,
criticalPath,
optimizations,
potentialTimeSaving: this.calculatePotentialTimeSaving(optimizations),
confidence: 0.8
};
}
calculateCriticalPath(project) {
// Simplified critical path calculation
const tasks = project.tasks.filter(t => t.status !== 'completed');
// Build dependency graph
const dependencyMap = new Map();
tasks.forEach(task => {
dependencyMap.set(task.id, task.dependencies || []);
});
// Find tasks with no dependencies (can start immediately)
const startTasks = tasks.filter(task =>
!task.dependencies || task.dependencies.length === 0
);
// Calculate longest path (simplified)
const criticalTasks = [];
let currentTasks = startTasks;
while (currentTasks.length > 0) {
// Find task with highest estimated hours
const criticalTask = currentTasks.reduce((max, task) =>
(task.estimatedHours || 0) > (max.estimatedHours || 0) ? task : max
);
criticalTasks.push(criticalTask.id);
// Find next tasks that depend on this one
currentTasks = tasks.filter(task =>
task.dependencies?.includes(criticalTask.id)
);
}
return criticalTasks;
}
identifyParallelOpportunities(project) {
const opportunities = [];
const incompleteTasks = project.tasks.filter(t => t.status !== 'completed');
// Group tasks by dependencies
const independentTasks = incompleteTasks.filter(task =>
!task.dependencies || task.dependencies.length === 0
);
if (independentTasks.length > 1) {
opportunities.push({
type: 'independent_tasks',
tasks: independentTasks.map(t => t.id),
description: 'These tasks can be executed in parallel'
});
}
return opportunities;
}
calculatePotentialTimeSaving(optimizations) {
let timeSaving = 0;
for (const optimization of optimizations) {
switch (optimization.type) {
case 'parallel_execution':
timeSaving += 0.3; // 30% time saving potential
break;
case 'workload_rebalancing':
timeSaving += 0.15; // 15% time saving potential
break;
case 'skill_optimization':
timeSaving += 0.2; // 20% time saving potential
break;
case 'dependency_optimization':
timeSaving += 0.25; // 25% time saving potential
break;
}
}
return Math.min(timeSaving, 0.6); // Cap at 60% time saving
}
async deleteProject(projectId) {
const project = this.activeProjects.get(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
// Clean up related data
this.activeProjects.delete(projectId);
this.timelineEstimates.delete(projectId);
this.riskFactors.delete(projectId);
// Remove task predictions
project.tasks.forEach(task => {
this.failurePredictions.delete(task.id);
});
console.log(`🗑️ Deleted project ${projectId} and associated predictions`);
this.emit('project:deleted', { projectId });
}
async trainPredictionModels() {
if (this.taskHistory.length < 50) {
console.log('⚠️ Insufficient historical data for model training');
return;
}
// Simulate model training with historical data
for (const [modelId, model] of this.predictionModels) {
const relevantData = this.taskHistory.filter(entry =>
this.isRelevantForModel(entry, model)
);
if (relevantData.length >= 20) {
// Simulate accuracy improvement
const improvementFactor = Math.min(relevantData.length / 100, 0.1);
model.accuracy = Math.min(model.accuracy + improvementFactor, 0.95);
model.lastTrained = Date.now();
console.log(`🤖 Trained ${model.name}: accuracy ${(model.accuracy * 100).toFixed(1)}%`);
}
}
this.emit('models:trained', {
modelsUpdated: this.predictionModels.size,
averageAccuracy: Array.from(this.predictionModels.values())
.reduce((sum, model) => sum + model.accuracy, 0) / this.predictionModels.size
});
}
isRelevantForModel(historyEntry, model) {
// Check if history entry has the features needed for this model
return model.features.every(feature => {
switch (feature) {
case 'complexity':
return historyEntry.complexity !== undefined;
case 'team_experience':
return historyEntry.teamSize !== undefined;
case 'dependencies':
return historyEntry.dependencies !== undefined;
case 'timeline_pressure':
return historyEntry.estimatedHours && historyEntry.actualHours;
default:
return true;
}
});
}
}