agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
764 lines (761 loc) ⢠33.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyzeCommand = void 0;
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
class AnalyzeCommand {
static async execute(target, options) {
console.log(chalk_1.default.blue.bold('\nš Analyzing Test Results and Quality Metrics\n'));
try {
const spinner = (0, ora_1.default)('Initializing analysis...').start();
// Validate inputs
await this.validateInputs(target, options);
spinner.text = 'Loading test data...';
// Load test execution data
const testData = await this.loadTestData();
spinner.text = 'Performing analysis...';
// Perform specific analysis based on target
let analysisResults;
switch (target) {
case 'coverage':
analysisResults = await this.analyzeCoverage(testData, options);
break;
case 'quality':
analysisResults = await this.analyzeQuality(testData, options);
break;
case 'trends':
analysisResults = await this.analyzeTrends(testData, options);
break;
case 'gaps':
analysisResults = await this.analyzeGaps(testData, options);
break;
default:
analysisResults = await this.analyzeAll(testData, options);
}
spinner.text = 'Generating recommendations...';
// Generate recommendations
const recommendations = await this.generateRecommendations(analysisResults, options);
spinner.text = 'Creating reports...';
// Generate reports
await this.generateReports(analysisResults, recommendations, options);
spinner.succeed(chalk_1.default.green('Analysis completed successfully!'));
// Display analysis summary
this.displayAnalysisSummary(analysisResults, recommendations, options);
// Store analysis results in coordination
await this.storeAnalysisResults(analysisResults, recommendations);
}
catch (error) {
console.error(chalk_1.default.red('ā Analysis failed:'), error.message);
if (options.verbose) {
console.error(chalk_1.default.gray(error.stack));
}
process.exit(1);
}
}
static async validateInputs(target, options) {
const validTargets = ['coverage', 'quality', 'trends', 'gaps'];
if (!validTargets.includes(target)) {
throw new Error(`Invalid target '${target}'. Must be one of: ${validTargets.join(', ')}`);
}
const validFormats = ['json', 'html', 'csv'];
if (!validFormats.includes(options.format)) {
throw new Error(`Invalid format '${options.format}'. Must be one of: ${validFormats.join(', ')}`);
}
const threshold = parseInt(options.threshold);
if (threshold < 0 || threshold > 100) {
throw new Error('Threshold must be between 0 and 100');
}
// Check if reports directory exists
if (!await fs.pathExists('.agentic-qe/reports')) {
throw new Error('No test execution reports found. Run tests first: agentic-qe run tests');
}
}
static async loadTestData() {
const reportsDir = '.agentic-qe/reports';
const reportFiles = await fs.readdir(reportsDir);
const executionFiles = reportFiles
.filter(file => file.startsWith('execution-') && file.endsWith('.json'))
.sort()
.reverse(); // Most recent first
if (executionFiles.length === 0) {
throw new Error('No test execution reports found');
}
const testData = {
latest: null,
history: [],
coverage: [],
quality: []
};
// Load latest execution
const latestFile = path.join(reportsDir, executionFiles[0]);
testData.latest = await fs.readJson(latestFile);
// Load historical data (up to 30 most recent)
for (const file of executionFiles.slice(0, 30)) {
const filePath = path.join(reportsDir, file);
const data = await fs.readJson(filePath);
testData.history.push(data);
}
// Load coverage data if available
const coverageFiles = reportFiles.filter(file => file.startsWith('coverage-'));
for (const file of coverageFiles.slice(0, 10)) {
const filePath = path.join(reportsDir, file);
if (await fs.pathExists(filePath)) {
const coverage = await fs.readJson(filePath);
testData.coverage.push(coverage);
}
}
return testData;
}
static async analyzeCoverage(testData, options) {
const analysis = {
type: 'coverage',
timestamp: new Date().toISOString(),
current: {
overall: testData.latest?.coverage?.overall || 0,
details: testData.latest?.coverage?.details || {}
},
trends: {
direction: 'stable',
change: 0,
period: options.period
},
gaps: [],
recommendations: []
};
// Calculate coverage trends
if (testData.history.length > 1) {
const recentCoverage = testData.history.slice(0, 5).map((h) => h.coverage?.overall || 0);
const avgRecent = recentCoverage.reduce((sum, c) => sum + c, 0) / recentCoverage.length;
const olderCoverage = testData.history.slice(5, 10).map((h) => h.coverage?.overall || 0);
const avgOlder = olderCoverage.length > 0
? olderCoverage.reduce((sum, c) => sum + c, 0) / olderCoverage.length
: avgRecent;
analysis.trends.change = avgRecent - avgOlder;
analysis.trends.direction = analysis.trends.change > 1 ? 'improving' :
analysis.trends.change < -1 ? 'declining' : 'stable';
}
// Identify coverage gaps if requested
if (options.gaps) {
analysis.gaps = await this.identifyCoverageGaps(testData);
}
// Add threshold-based alerts
const threshold = parseInt(options.threshold);
if (analysis.current.overall < threshold) {
analysis.recommendations.push({
type: 'coverage_below_threshold',
priority: 'high',
message: `Coverage ${analysis.current.overall.toFixed(1)}% is below threshold ${threshold}%`,
actions: ['Generate additional tests', 'Review uncovered code paths']
});
}
return analysis;
}
static async analyzeQuality(testData, options) {
const analysis = {
type: 'quality',
timestamp: new Date().toISOString(),
metrics: {
testReliability: 0,
passRate: 0,
flakyTests: 0,
testMaintainability: 0,
qualityScore: 0
},
trends: {
direction: 'stable',
period: options.period,
changes: {}
},
issues: [],
recommendations: []
};
// Calculate quality metrics from latest data
const latest = testData.latest;
if (latest?.summary) {
analysis.metrics.passRate = latest.summary.total > 0
? (latest.summary.passed / latest.summary.total) * 100
: 0;
// Analyze test reliability over time
const reliabilityData = testData.history.slice(0, 10).map((h) => ({
total: h.summary?.total || 0,
passed: h.summary?.passed || 0,
failed: h.summary?.failed || 0
}));
analysis.metrics.testReliability = this.calculateTestReliability(reliabilityData);
analysis.metrics.flakyTests = this.identifyFlakyTests(reliabilityData);
}
// Calculate composite quality score
analysis.metrics.qualityScore = this.calculateQualityScore(analysis.metrics);
// Identify quality issues
analysis.issues = await this.identifyQualityIssues(testData, analysis.metrics);
return analysis;
}
static async analyzeTrends(testData, options) {
const analysis = {
type: 'trends',
timestamp: new Date().toISOString(),
period: options.period,
trends: {
coverage: [],
passRate: [],
testCount: [],
executionTime: []
},
insights: [],
predictions: []
};
// Analyze trends over time
const historicalData = testData.history.slice(0, 20); // Last 20 executions
for (const data of historicalData) {
const timestamp = data.timestamp;
analysis.trends.coverage.push({
timestamp,
value: data.coverage?.overall || 0
});
analysis.trends.passRate.push({
timestamp,
value: data.summary?.total > 0 ? (data.summary.passed / data.summary.total) * 100 : 0
});
analysis.trends.testCount.push({
timestamp,
value: data.summary?.total || 0
});
analysis.trends.executionTime.push({
timestamp,
value: data.summary?.duration || 0
});
}
// Generate insights
analysis.insights = this.generateTrendInsights(analysis.trends);
// Simple predictions based on trends
analysis.predictions = this.generatePredictions(analysis.trends);
return analysis;
}
static async analyzeGaps(testData, options) {
const analysis = {
type: 'gaps',
timestamp: new Date().toISOString(),
coverageGaps: [],
testingGaps: [],
qualityGaps: [],
recommendations: []
};
// Identify coverage gaps
analysis.coverageGaps = await this.identifyCoverageGaps(testData);
// Identify testing gaps
analysis.testingGaps = await this.identifyTestingGaps();
// Identify quality gaps
analysis.qualityGaps = await this.identifyQualityGaps(testData);
return analysis;
}
static async analyzeAll(testData, options) {
const coverage = await this.analyzeCoverage(testData, options);
const quality = await this.analyzeQuality(testData, options);
const trends = await this.analyzeTrends(testData, options);
const gaps = await this.analyzeGaps(testData, options);
return {
type: 'comprehensive',
timestamp: new Date().toISOString(),
coverage,
quality,
trends,
gaps,
summary: {
overallScore: this.calculateOverallScore([coverage, quality, trends, gaps]),
criticalIssues: this.identifyCriticalIssues([coverage, quality, trends, gaps]),
topRecommendations: this.prioritizeRecommendations([coverage, quality, trends, gaps])
}
};
}
static async identifyCoverageGaps(testData) {
const gaps = [];
// Check for missing test types
const testTypes = ['unit', 'integration', 'e2e', 'performance', 'security'];
const testDirs = await Promise.all(testTypes.map(async (type) => ({
type,
exists: await fs.pathExists(`tests/${type}`)
})));
for (const { type, exists } of testDirs) {
if (!exists) {
gaps.push({
type: 'missing_test_type',
category: type,
severity: type === 'unit' ? 'high' : 'medium',
description: `No ${type} tests found`,
suggestion: `Create ${type} test directory and generate tests`
});
}
}
// Check coverage by file (if coverage details available)
const coverageDetails = testData.latest?.coverage?.details || {};
Object.entries(coverageDetails).forEach(([file, coverage]) => {
if (coverage < 80) {
gaps.push({
type: 'low_file_coverage',
file,
coverage,
severity: coverage < 50 ? 'high' : 'medium',
description: `Low coverage in ${file}: ${coverage}%`,
suggestion: 'Add more comprehensive tests for this file'
});
}
});
return gaps;
}
static async identifyTestingGaps() {
const gaps = [];
// Check for test configuration files
const configFiles = [
{ file: 'jest.config.js', type: 'Jest configuration' },
{ file: '.agentic-qe/config/jest.config.json', type: 'QE Jest configuration' },
{ file: 'cypress.json', type: 'Cypress configuration' },
{ file: 'playwright.config.js', type: 'Playwright configuration' }
];
for (const { file, type } of configFiles) {
if (!await fs.pathExists(file)) {
gaps.push({
type: 'missing_config',
file,
severity: 'low',
description: `Missing ${type}`,
suggestion: `Consider adding ${file} for better test configuration`
});
}
}
return gaps;
}
static async identifyQualityGaps(testData) {
const gaps = [];
const latest = testData.latest;
if (!latest)
return gaps;
// Check for quality issues
if (latest.summary?.failed > 0) {
gaps.push({
type: 'failing_tests',
count: latest.summary.failed,
severity: 'high',
description: `${latest.summary.failed} tests are failing`,
suggestion: 'Fix failing tests to improve quality'
});
}
if (latest.errors?.length > 0) {
gaps.push({
type: 'test_errors',
count: latest.errors.length,
severity: 'medium',
description: `${latest.errors.length} test execution errors`,
suggestion: 'Review and fix test execution errors'
});
}
return gaps;
}
static calculateTestReliability(reliabilityData) {
if (reliabilityData.length === 0)
return 0;
const consistentResults = reliabilityData.filter((data, index) => {
if (index === 0)
return true;
const prev = reliabilityData[index - 1];
const passRateDiff = Math.abs((data.passed / data.total) - (prev.passed / prev.total));
return passRateDiff < 0.1; // Less than 10% variation
});
return (consistentResults.length / reliabilityData.length) * 100;
}
static identifyFlakyTests(reliabilityData) {
// Simple heuristic: if pass rate varies significantly, there might be flaky tests
const passRates = reliabilityData.map(d => d.total > 0 ? d.passed / d.total : 0);
const variance = this.calculateVariance(passRates);
return variance > 0.05 ? Math.floor(variance * 100) : 0;
}
static calculateVariance(values) {
if (values.length === 0)
return 0;
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
const squaredDiffs = values.map(val => Math.pow(val - mean, 2));
return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length;
}
static calculateQualityScore(metrics) {
const weights = {
passRate: 0.4,
testReliability: 0.3,
coverage: 0.2,
flakyTests: 0.1
};
const score = metrics.passRate * weights.passRate +
metrics.testReliability * weights.testReliability +
(metrics.coverage || 0) * weights.coverage +
(100 - metrics.flakyTests) * weights.flakyTests;
return Math.max(0, Math.min(100, score));
}
static async identifyQualityIssues(testData, metrics) {
const issues = [];
if (metrics.passRate < 90) {
issues.push({
type: 'low_pass_rate',
severity: metrics.passRate < 80 ? 'high' : 'medium',
value: metrics.passRate,
description: `Test pass rate is ${metrics.passRate.toFixed(1)}%`,
impact: 'Quality confidence reduced'
});
}
if (metrics.testReliability < 95) {
issues.push({
type: 'unreliable_tests',
severity: 'medium',
value: metrics.testReliability,
description: `Test reliability is ${metrics.testReliability.toFixed(1)}%`,
impact: 'Inconsistent test results'
});
}
if (metrics.flakyTests > 5) {
issues.push({
type: 'flaky_tests',
severity: 'high',
value: metrics.flakyTests,
description: `${metrics.flakyTests} potentially flaky tests detected`,
impact: 'Reduced confidence in test results'
});
}
return issues;
}
static generateTrendInsights(trends) {
const insights = [];
// Coverage trend
const coverageTrend = this.calculateTrend(trends.coverage);
if (coverageTrend.direction !== 'stable') {
insights.push({
type: 'coverage_trend',
direction: coverageTrend.direction,
change: coverageTrend.change,
description: `Coverage is ${coverageTrend.direction} by ${Math.abs(coverageTrend.change).toFixed(1)}%`
});
}
// Pass rate trend
const passRateTrend = this.calculateTrend(trends.passRate);
if (passRateTrend.direction !== 'stable') {
insights.push({
type: 'pass_rate_trend',
direction: passRateTrend.direction,
change: passRateTrend.change,
description: `Pass rate is ${passRateTrend.direction} by ${Math.abs(passRateTrend.change).toFixed(1)}%`
});
}
// Execution time trend
const timeTrend = this.calculateTrend(trends.executionTime);
if (timeTrend.direction !== 'stable' && Math.abs(timeTrend.change) > 1000) {
insights.push({
type: 'execution_time_trend',
direction: timeTrend.direction === 'improving' ? 'decreasing' : 'increasing',
change: timeTrend.change,
description: `Test execution time is ${timeTrend.direction === 'improving' ? 'decreasing' : 'increasing'}`
});
}
return insights;
}
static calculateTrend(dataPoints) {
if (dataPoints.length < 3) {
return { direction: 'stable', change: 0 };
}
const values = dataPoints.map(point => point.value);
const recentAvg = values.slice(0, Math.floor(values.length / 2)).reduce((sum, val) => sum + val, 0) / Math.floor(values.length / 2);
const olderAvg = values.slice(Math.floor(values.length / 2)).reduce((sum, val) => sum + val, 0) / (values.length - Math.floor(values.length / 2));
const change = recentAvg - olderAvg;
const direction = Math.abs(change) < 1 ? 'stable' : change > 0 ? 'improving' : 'declining';
return { direction, change };
}
static generatePredictions(trends) {
const predictions = [];
// Simple linear prediction for coverage
const coverageTrend = this.calculateTrend(trends.coverage);
if (coverageTrend.direction !== 'stable') {
const predicted = trends.coverage[0]?.value + (coverageTrend.change * 2);
predictions.push({
type: 'coverage',
timeframe: '2 cycles',
predicted: Math.max(0, Math.min(100, predicted)),
confidence: 'medium',
description: `Coverage predicted to be ${predicted.toFixed(1)}% in 2 execution cycles`
});
}
return predictions;
}
static calculateOverallScore(analyses) {
const scores = analyses
.filter(a => a.metrics?.qualityScore)
.map(a => a.metrics.qualityScore);
return scores.length > 0
? scores.reduce((sum, score) => sum + score, 0) / scores.length
: 0;
}
static identifyCriticalIssues(analyses) {
const criticalIssues = [];
analyses.forEach(analysis => {
if (analysis.issues) {
const critical = analysis.issues.filter((issue) => issue.severity === 'high');
criticalIssues.push(...critical);
}
if (analysis.gaps) {
const critical = analysis.gaps.filter((gap) => gap.severity === 'high');
criticalIssues.push(...critical);
}
});
return criticalIssues;
}
static prioritizeRecommendations(analyses) {
const allRecommendations = [];
analyses.forEach(analysis => {
if (analysis.recommendations) {
allRecommendations.push(...analysis.recommendations);
}
});
// Sort by priority
return allRecommendations
.sort((a, b) => {
const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 };
return (priorityOrder[b.priority] || 0) - (priorityOrder[a.priority] || 0);
})
.slice(0, 5); // Top 5 recommendations
}
static async generateRecommendations(analysis, options) {
const recommendations = [];
// Coverage-specific recommendations
if (analysis.type === 'coverage' || analysis.coverage) {
const coverageData = analysis.coverage || analysis.current;
if (coverageData.overall < 80) {
recommendations.push({
type: 'improve_coverage',
priority: 'high',
title: 'Improve Test Coverage',
description: `Current coverage (${coverageData.overall.toFixed(1)}%) is below recommended 80%`,
actions: [
'Run: agentic-qe generate tests --coverage-target 85',
'Focus on uncovered code paths',
'Add integration tests for complex flows'
],
estimatedImpact: 'High'
});
}
}
// Quality-specific recommendations
if (analysis.type === 'quality' || analysis.quality) {
const qualityData = analysis.quality || analysis;
if (qualityData.metrics?.flakyTests > 0) {
recommendations.push({
type: 'fix_flaky_tests',
priority: 'medium',
title: 'Address Flaky Tests',
description: `${qualityData.metrics.flakyTests} potentially flaky tests detected`,
actions: [
'Review test execution logs',
'Add proper test isolation',
'Check for timing dependencies'
],
estimatedImpact: 'Medium'
});
}
}
// Gap-specific recommendations
if (analysis.gaps) {
analysis.gaps.forEach((gap) => {
if (gap.severity === 'high') {
recommendations.push({
type: 'address_gap',
priority: 'high',
title: `Address ${gap.type}`,
description: gap.description,
actions: [gap.suggestion],
estimatedImpact: 'High'
});
}
});
}
return recommendations;
}
static async generateReports(analysis, recommendations, options) {
const reportsDir = '.agentic-qe/reports';
await fs.ensureDir(reportsDir);
const timestamp = new Date().toISOString().replace(/:/g, '-');
const reportData = {
analysis,
recommendations,
metadata: {
generated: new Date().toISOString(),
format: options.format,
options
}
};
// Generate JSON report
if (options.format === 'json' || options.format === 'all') {
const jsonFile = `${reportsDir}/analysis-${analysis.type}-${timestamp}.json`;
await fs.writeJson(jsonFile, reportData, { spaces: 2 });
}
// Generate HTML report
if (options.format === 'html' || options.format === 'all') {
const htmlContent = this.generateHtmlReport(reportData);
const htmlFile = `${reportsDir}/analysis-${analysis.type}-${timestamp}.html`;
await fs.writeFile(htmlFile, htmlContent);
}
// Generate CSV for trends
if (options.format === 'csv' && analysis.trends) {
const csvContent = this.generateCsvReport(analysis.trends);
const csvFile = `${reportsDir}/trends-${timestamp}.csv`;
await fs.writeFile(csvFile, csvContent);
}
}
static generateHtmlReport(reportData) {
const { analysis, recommendations } = reportData;
return `<!DOCTYPE html>
<html>
<head>
<title>Agentic QE Analysis Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background: #f0f8ff; padding: 20px; border-radius: 5px; }
.section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.metric { display: inline-block; margin: 10px; padding: 10px; background: #f9f9f9; border-radius: 3px; }
.high { color: #d9534f; }
.medium { color: #f0ad4e; }
.low { color: #5bc0de; }
.recommendation { margin: 10px 0; padding: 10px; background: #dff0d8; border-radius: 3px; }
</style>
</head>
<body>
<div class="header">
<h1>Agentic QE Analysis Report</h1>
<p>Generated: ${reportData.metadata.generated}</p>
<p>Type: ${analysis.type}</p>
</div>
${analysis.current ? `
<div class="section">
<h2>Current Metrics</h2>
<div class="metric">Coverage: ${analysis.current.overall.toFixed(1)}%</div>
${analysis.metrics ? `
<div class="metric">Pass Rate: ${analysis.metrics.passRate.toFixed(1)}%</div>
<div class="metric">Quality Score: ${analysis.metrics.qualityScore.toFixed(1)}</div>
` : ''}
</div>
` : ''}
${recommendations.length > 0 ? `
<div class="section">
<h2>Recommendations</h2>
${recommendations.map((rec) => `
<div class="recommendation">
<h3 class="${rec.priority}">${rec.title}</h3>
<p>${rec.description}</p>
<ul>
${rec.actions.map((action) => `<li>${action}</li>`).join('')}
</ul>
</div>
`).join('')}
</div>
` : ''}
<div class="section">
<h2>Raw Data</h2>
<pre>${JSON.stringify(analysis, null, 2)}</pre>
</div>
</body>
</html>`;
}
static generateCsvReport(trends) {
let csv = 'Timestamp,Coverage,PassRate,TestCount,ExecutionTime\n';
const maxLength = Math.max(trends.coverage?.length || 0, trends.passRate?.length || 0, trends.testCount?.length || 0, trends.executionTime?.length || 0);
for (let i = 0; i < maxLength; i++) {
const timestamp = trends.coverage?.[i]?.timestamp || '';
const coverage = trends.coverage?.[i]?.value || '';
const passRate = trends.passRate?.[i]?.value || '';
const testCount = trends.testCount?.[i]?.value || '';
const executionTime = trends.executionTime?.[i]?.value || '';
csv += `${timestamp},${coverage},${passRate},${testCount},${executionTime}\n`;
}
return csv;
}
static displayAnalysisSummary(analysis, recommendations, options) {
console.log(chalk_1.default.yellow('\nš Analysis Summary:'));
console.log(chalk_1.default.gray(` Type: ${analysis.type}`));
console.log(chalk_1.default.gray(` Timestamp: ${analysis.timestamp}`));
// Display key metrics based on analysis type
if (analysis.current) {
console.log(chalk_1.default.gray(` Coverage: ${analysis.current.overall.toFixed(1)}%`));
}
if (analysis.metrics) {
console.log(chalk_1.default.gray(` Pass Rate: ${analysis.metrics.passRate.toFixed(1)}%`));
console.log(chalk_1.default.gray(` Quality Score: ${analysis.metrics.qualityScore.toFixed(1)}`));
}
if (analysis.trends?.direction) {
console.log(chalk_1.default.gray(` Trend: ${analysis.trends.direction}`));
}
// Display critical issues
const criticalIssues = analysis.issues?.filter((issue) => issue.severity === 'high') || [];
if (criticalIssues.length > 0) {
console.log(chalk_1.default.red('\nšØ Critical Issues:'));
criticalIssues.forEach((issue) => {
console.log(chalk_1.default.red(` ⢠${issue.description}`));
});
}
// Display top recommendations
if (recommendations.length > 0) {
console.log(chalk_1.default.yellow('\nš” Top Recommendations:'));
recommendations.slice(0, 3).forEach((rec, index) => {
console.log(chalk_1.default.gray(` ${index + 1}. ${rec.title}`));
console.log(chalk_1.default.gray(` ${rec.description}`));
});
}
console.log(chalk_1.default.yellow('\nš Reports Generated:'));
console.log(chalk_1.default.gray(` Format: ${options.format}`));
console.log(chalk_1.default.gray(` Location: .agentic-qe/reports/`));
console.log(chalk_1.default.yellow('\nš” Next Steps:'));
if (recommendations.length > 0) {
console.log(chalk_1.default.gray(' 1. Review and implement top recommendations'));
console.log(chalk_1.default.gray(' 2. Re-run analysis after improvements'));
}
console.log(chalk_1.default.gray(' 3. Monitor trends over time for continuous improvement'));
}
static async storeAnalysisResults(analysis, recommendations) {
const summary = {
type: analysis.type,
timestamp: analysis.timestamp,
metrics: analysis.metrics || analysis.current,
issues: (analysis.issues || []).length,
recommendations: recommendations.length,
status: 'completed'
};
// Store in coordination memory
const coordinationScript = `
npx claude-flow@alpha memory store --key "agentic-qe/analysis/latest" --value '${JSON.stringify(summary)}'
npx claude-flow@alpha hooks notify --message "Analysis completed: ${analysis.type} analysis with ${recommendations.length} recommendations"
`;
await fs.writeFile('.agentic-qe/scripts/store-analysis-results.sh', coordinationScript);
await fs.chmod('.agentic-qe/scripts/store-analysis-results.sh', '755');
}
}
exports.AnalyzeCommand = AnalyzeCommand;
//# sourceMappingURL=analyze.js.map