@versatil/sdlc-framework
Version:
🚀 AI-Native SDLC framework with 11-MCP ecosystem, RAG memory, OPERA orchestration, and 6 specialized agents achieving ZERO CONTEXT LOSS. Features complete CI/CD pipeline with 7 GitHub workflows (MCP testing, security scanning, performance benchmarking),
466 lines (419 loc) • 13.9 kB
text/typescript
/**
* VERSATIL SDLC Framework - OPERA MCP Integration
* Connects Quality Dashboard with Model Context Protocol for real-time agent orchestration
*/
import { OPERAQualityDashboard, QualityMetrics } from './opera-quality-dashboard.js';
import { VERSATILLogger } from '../utils/logger.js';
export interface MCPDashboardTools {
name: string;
description: string;
parameters: {
type: 'object';
properties: Record<string, any>;
required?: string[];
};
}
export class OPERAMCPIntegration {
private dashboard: OPERAQualityDashboard;
private logger: VERSATILLogger;
private isActive: boolean = false;
constructor() {
this.dashboard = new OPERAQualityDashboard({
enableRealTimeUpdates: true,
refreshInterval: 3000,
qualityThresholds: {
overall: 80,
performance: 90,
accessibility: 95,
security: 90
}
});
this.logger = VERSATILLogger.getInstance();
this.setupEventListeners();
}
/**
* Start MCP integration with dashboard
*/
async start(): Promise<void> {
if (this.isActive) return;
this.isActive = true;
this.logger.info('🚀 OPERA MCP Integration Started', {
tools: this.getAvailableTools().map(t => t.name),
realTimeUpdates: true
}, 'opera-mcp');
}
/**
* Stop MCP integration
*/
async stop(): Promise<void> {
if (!this.isActive) return;
this.isActive = false;
this.dashboard.stop();
this.logger.info('🛑 OPERA MCP Integration Stopped', {}, 'opera-mcp');
}
/**
* Get available MCP tools for dashboard interaction
*/
getAvailableTools(): MCPDashboardTools[] {
return [
{
name: 'opera_trigger_ui_test',
description: 'Trigger UI/UX testing flywheel for specific file changes',
parameters: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the changed file'
},
changeType: {
type: 'string',
enum: ['component', 'route', 'style', 'configuration'],
description: 'Type of change made to the file'
}
},
required: ['filePath', 'changeType']
}
},
{
name: 'opera_get_quality_metrics',
description: 'Get current quality metrics and dashboard data',
parameters: {
type: 'object',
properties: {
includeHistory: {
type: 'boolean',
description: 'Include workflow history in response'
},
limit: {
type: 'number',
description: 'Limit number of historical records'
}
}
}
},
{
name: 'opera_get_agent_status',
description: 'Get current status of all OPERA agents',
parameters: {
type: 'object',
properties: {
agentName: {
type: 'string',
description: 'Specific agent name to get status for (optional)'
}
}
}
},
{
name: 'opera_generate_quality_report',
description: 'Generate comprehensive quality report with recommendations',
parameters: {
type: 'object',
properties: {
reportType: {
type: 'string',
enum: ['summary', 'detailed', 'trends'],
description: 'Type of quality report to generate'
},
timeRange: {
type: 'string',
enum: ['1h', '24h', '7d', '30d'],
description: 'Time range for report data'
}
}
}
},
{
name: 'opera_execute_quality_check',
description: 'Execute immediate quality check for current codebase state',
parameters: {
type: 'object',
properties: {
scope: {
type: 'string',
enum: ['full', 'changed-files', 'critical-paths'],
description: 'Scope of quality check to perform'
},
includePerformance: {
type: 'boolean',
description: 'Include performance testing in quality check'
},
includeAccessibility: {
type: 'boolean',
description: 'Include accessibility testing in quality check'
}
}
}
}
];
}
/**
* Handle MCP tool calls
*/
async handleToolCall(toolName: string, parameters: any): Promise<any> {
if (!this.isActive) {
throw new Error('OPERA MCP Integration is not active');
}
this.logger.debug('🔧 MCP Tool Call', { toolName, parameters }, 'opera-mcp');
switch (toolName) {
case 'opera_trigger_ui_test':
return await this.handleTriggerUITest(parameters);
case 'opera_get_quality_metrics':
return await this.handleGetQualityMetrics(parameters);
case 'opera_get_agent_status':
return await this.handleGetAgentStatus(parameters);
case 'opera_generate_quality_report':
return await this.handleGenerateQualityReport(parameters);
case 'opera_execute_quality_check':
return await this.handleExecuteQualityCheck(parameters);
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
/**
* Private tool handlers
*/
private async handleTriggerUITest(params: { filePath: string; changeType: string }): Promise<any> {
try {
const result = await this.dashboard.executeUIUXTestingFlywheel(
params.filePath,
params.changeType as any
);
return {
success: true,
workflowId: `workflow-${Date.now()}`,
result: {
qualityScore: result.qualityScore,
agent: result.agent,
issues: result.issues.length,
recommendations: result.recommendations.length,
success: result.success,
nextSteps: result.nextSteps
},
message: `UI/UX testing flywheel triggered for ${params.filePath} (${params.changeType})`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
message: 'Failed to trigger UI/UX testing flywheel'
};
}
}
private async handleGetQualityMetrics(params: { includeHistory?: boolean; limit?: number }): Promise<any> {
try {
const dashboardData = this.dashboard.getDashboardData();
const metrics = this.dashboard.getQualityMetrics();
const response: any = {
metrics,
activeWorkflows: dashboardData.activeWorkflows,
alerts: dashboardData.alerts,
timestamp: new Date().toISOString()
};
if (params.includeHistory) {
response.workflowHistory = this.dashboard.getWorkflowHistory(params.limit || 10);
}
return {
success: true,
data: response,
message: 'Quality metrics retrieved successfully'
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
message: 'Failed to get quality metrics'
};
}
}
private async handleGetAgentStatus(params: { agentName?: string }): Promise<any> {
try {
const metrics = this.dashboard.getQualityMetrics();
const agentUtilization = metrics.agentUtilization;
if (params.agentName) {
const agentStatus = agentUtilization[params.agentName];
if (!agentStatus) {
return {
success: false,
error: `Agent not found: ${params.agentName}`,
message: 'Agent status not available'
};
}
return {
success: true,
data: {
agentName: params.agentName,
status: agentStatus,
isActive: agentStatus.activeJobs > 0
},
message: `Status for ${params.agentName} retrieved`
};
}
// Return status for all agents
const agentStatuses = Object.entries(agentUtilization).map(([name, status]) => ({
agentName: name,
status,
isActive: status.activeJobs > 0
}));
return {
success: true,
data: {
agents: agentStatuses,
totalActiveJobs: Object.values(agentUtilization).reduce((sum, a) => sum + a.activeJobs, 0)
},
message: 'Agent statuses retrieved successfully'
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
message: 'Failed to get agent status'
};
}
}
private async handleGenerateQualityReport(params: { reportType?: string; timeRange?: string }): Promise<any> {
try {
const reportType = params.reportType || 'summary';
const timeRange = params.timeRange || '24h';
const dashboardData = this.dashboard.getDashboardData();
const metrics = this.dashboard.getQualityMetrics();
const report = {
reportType,
timeRange,
generatedAt: new Date().toISOString(),
metrics,
summary: {
overallHealth: this.calculateOverallHealth(metrics),
criticalIssues: dashboardData.alerts.filter(a => a.severity === 'critical').length,
recommendationsCount: dashboardData.recentWorkflows.reduce((sum, w) => sum + w.recommendations.length, 0),
workflowsExecuted: dashboardData.recentWorkflows.length
},
recommendations: this.generateRecommendations(metrics, dashboardData.alerts),
trends: reportType === 'trends' ? this.calculateTrends() : undefined
};
return {
success: true,
data: report,
message: `Quality report (${reportType}) generated successfully`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
message: 'Failed to generate quality report'
};
}
}
private async handleExecuteQualityCheck(params: {
scope?: string;
includePerformance?: boolean;
includeAccessibility?: boolean;
}): Promise<any> {
try {
const scope = params.scope || 'changed-files';
// Simulate quality check execution
const checkResult = {
scope,
executedAt: new Date().toISOString(),
qualityScore: Math.floor(Math.random() * 30) + 70, // 70-100
checks: {
performance: params.includePerformance ? Math.floor(Math.random() * 20) + 80 : null,
accessibility: params.includeAccessibility ? Math.floor(Math.random() * 10) + 90 : null,
security: Math.floor(Math.random() * 15) + 85,
codeQuality: Math.floor(Math.random() * 25) + 75
},
issues: [
{
type: 'medium',
category: 'performance',
description: 'Bundle size increased by 12KB',
recommendation: 'Consider lazy loading for non-critical components'
},
{
type: 'low',
category: 'accessibility',
description: 'Missing alt text on 2 images',
recommendation: 'Add descriptive alt text to all images'
}
]
};
return {
success: true,
data: checkResult,
message: `Quality check (${scope}) completed successfully`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
message: 'Failed to execute quality check'
};
}
}
/**
* Helper methods
*/
private setupEventListeners(): void {
this.dashboard.on('workflowComplete', (result) => {
this.logger.info('🎯 OPERA Workflow Complete', {
agent: result.agent,
qualityScore: result.qualityScore,
success: result.success
}, 'opera-mcp');
});
this.dashboard.on('alert', (alert) => {
this.logger.warn('🚨 OPERA Quality Alert', alert, 'opera-mcp');
});
this.dashboard.on('metricsUpdate', (metrics) => {
this.logger.debug('📊 OPERA Metrics Updated', {
overallScore: metrics.overallScore,
activeWorkflows: metrics.activeWorkflows
}, 'opera-mcp');
});
}
private calculateOverallHealth(metrics: QualityMetrics): 'excellent' | 'good' | 'fair' | 'poor' {
const score = metrics.overallScore;
if (score >= 90) return 'excellent';
if (score >= 80) return 'good';
if (score >= 70) return 'fair';
return 'poor';
}
private generateRecommendations(metrics: QualityMetrics, alerts: any[]): string[] {
const recommendations: string[] = [];
if (metrics.overallScore < 80) {
recommendations.push('Focus on addressing critical and high-priority issues');
}
if (metrics.accessibilityScore < 95) {
recommendations.push('Improve accessibility compliance with WCAG 2.1 AA standards');
}
if (metrics.performanceScore < 90) {
recommendations.push('Optimize performance with bundle analysis and Core Web Vitals');
}
if (alerts.some(a => a.type === 'visual-regression')) {
recommendations.push('Review and update visual regression baselines');
}
return recommendations;
}
private calculateTrends(): any {
// Simplified trend calculation - in production, this would analyze historical data
return {
qualityScore: {
trend: 'improving',
change: '+5%',
period: '7d'
},
testCoverage: {
trend: 'stable',
change: '+0.2%',
period: '7d'
},
performanceScore: {
trend: 'declining',
change: '-2%',
period: '7d'
}
};
}
}
export default OPERAMCPIntegration;