code-simplifier 1.0.0 ā 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -1
- package/bin/code-simplifier.js +73 -1
- package/cypress.config.js +45 -0
- package/jest.config.js +46 -0
- package/lib/ai-auto-fix.js +829 -0
- package/lib/ai-code-review.js +705 -0
- package/lib/ai-knowledge-base.js +757 -0
- package/lib/ai-quality-analyzer.js +1095 -0
- package/lib/ai-refactor-advisor.js +853 -0
- package/lib/ai-trend-analyzer.js +674 -0
- package/lib/auto-fix.js +382 -0
- package/lib/eslint-integration.js +328 -0
- package/lib/git-hooks.js +353 -0
- package/lib/master.js +117 -0
- package/lib/multi-language-analyzer.js +397 -0
- package/lib/ralph-integration.js +541 -0
- package/package.json +87 -61
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AI-Powered Trend Analyzer
|
|
5
|
+
* Analyzes code quality trends and predicts future issues
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs-extra')
|
|
9
|
+
const path = require('path')
|
|
10
|
+
const chalk = require('chalk')
|
|
11
|
+
const ora = require('ora')
|
|
12
|
+
const simpleStatistics = require('simple-statistics')
|
|
13
|
+
|
|
14
|
+
class AITrendAnalyzer {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.dataStore = new Map()
|
|
17
|
+
this.predictions = []
|
|
18
|
+
this.alerts = []
|
|
19
|
+
this.thresholdSettings = {
|
|
20
|
+
qualityScore: 70,
|
|
21
|
+
issuesPerWeek: 10,
|
|
22
|
+
complexityIncrease: 0.15,
|
|
23
|
+
securityIssues: 0,
|
|
24
|
+
performanceDegradation: 0.10
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Analyze quality trends from historical data
|
|
30
|
+
*/
|
|
31
|
+
async analyzeTrends(historicalData, options = {}) {
|
|
32
|
+
const {
|
|
33
|
+
days = 30,
|
|
34
|
+
granularity = 'day',
|
|
35
|
+
predictDays = 7
|
|
36
|
+
} = options
|
|
37
|
+
|
|
38
|
+
console.log(chalk.cyan.bold('\nš AI-Powered Trend Analyzer'))
|
|
39
|
+
console.log(chalk.gray('='.repeat(70)))
|
|
40
|
+
|
|
41
|
+
const spinner = ora(chalk.blue('Analyzing quality trends...')).start()
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// 1. Process historical data
|
|
45
|
+
spinner.text = chalk.blue('Processing historical data...')
|
|
46
|
+
const processedData = this.processHistoricalData(historicalData, granularity)
|
|
47
|
+
|
|
48
|
+
// 2. Calculate trends
|
|
49
|
+
spinner.text = chalk.blue('Calculating quality trends...')
|
|
50
|
+
const trends = this.calculateTrends(processedData)
|
|
51
|
+
|
|
52
|
+
// 3. Generate predictions
|
|
53
|
+
spinner.text = chalk.blue('Generating predictions...')
|
|
54
|
+
const predictions = await this.generatePredictions(trends, predictDays)
|
|
55
|
+
|
|
56
|
+
// 4. Identify risk points
|
|
57
|
+
spinner.text = chalk.blue('Identifying risk points...')
|
|
58
|
+
const risks = this.identifyRisks(trends, predictions)
|
|
59
|
+
|
|
60
|
+
// 5. Generate recommendations
|
|
61
|
+
spinner.text = chalk.blue('Generating recommendations...')
|
|
62
|
+
const recommendations = this.generateRecommendations(trends, predictions, risks)
|
|
63
|
+
|
|
64
|
+
// 6. Create report
|
|
65
|
+
spinner.text = chalk.blue('Creating trend report...')
|
|
66
|
+
const report = this.createTrendReport({
|
|
67
|
+
trends,
|
|
68
|
+
predictions,
|
|
69
|
+
risks,
|
|
70
|
+
recommendations,
|
|
71
|
+
metadata: {
|
|
72
|
+
analysisDate: new Date().toISOString(),
|
|
73
|
+
dataPeriod: days,
|
|
74
|
+
granularity,
|
|
75
|
+
predictionHorizon: predictDays
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
spinner.succeed(chalk.green('ā Trend analysis complete'))
|
|
80
|
+
|
|
81
|
+
return report
|
|
82
|
+
} catch (error) {
|
|
83
|
+
spinner.fail(chalk.red('Trend analysis failed'))
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Process historical data into time series
|
|
90
|
+
*/
|
|
91
|
+
processHistoricalData(rawData, granularity) {
|
|
92
|
+
const timeSeries = {}
|
|
93
|
+
|
|
94
|
+
// Group data by time period
|
|
95
|
+
rawData.forEach(record => {
|
|
96
|
+
const timestamp = new Date(record.timestamp)
|
|
97
|
+
const periodKey = this.getPeriodKey(timestamp, granularity)
|
|
98
|
+
|
|
99
|
+
if (!timeSeries[periodKey]) {
|
|
100
|
+
timeSeries[periodKey] = {
|
|
101
|
+
date: periodKey,
|
|
102
|
+
qualityScore: [],
|
|
103
|
+
issues: [],
|
|
104
|
+
complexity: [],
|
|
105
|
+
security: [],
|
|
106
|
+
performance: [],
|
|
107
|
+
count: 0
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
timeSeries[periodKey].qualityScore.push(record.qualityScore || 0)
|
|
112
|
+
timeSeries[periodKey].issues.push(record.issues || 0)
|
|
113
|
+
timeSeries[periodKey].complexity.push(record.complexity || 0)
|
|
114
|
+
timeSeries[periodKey].security.push(record.security || 0)
|
|
115
|
+
timeSeries[periodKey].performance.push(record.performance || 0)
|
|
116
|
+
timeSeries[periodKey].count++
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Calculate averages for each period
|
|
120
|
+
const processed = Object.values(timeSeries).map(period => ({
|
|
121
|
+
date: period.date,
|
|
122
|
+
qualityScore: this.average(period.qualityScore),
|
|
123
|
+
issues: this.sum(period.issues),
|
|
124
|
+
complexity: this.average(period.complexity),
|
|
125
|
+
security: this.average(period.security),
|
|
126
|
+
performance: this.average(period.performance)
|
|
127
|
+
}))
|
|
128
|
+
|
|
129
|
+
// Sort by date
|
|
130
|
+
processed.sort((a, b) => new Date(a.date) - new Date(b.date))
|
|
131
|
+
|
|
132
|
+
return processed
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Calculate quality trends
|
|
137
|
+
*/
|
|
138
|
+
calculateTrends(timeSeries) {
|
|
139
|
+
const trends = {
|
|
140
|
+
quality: this.calculateMetricTrend(timeSeries, 'qualityScore'),
|
|
141
|
+
issues: this.calculateMetricTrend(timeSeries, 'issues'),
|
|
142
|
+
complexity: this.calculateMetricTrend(timeSeries, 'complexity'),
|
|
143
|
+
security: this.calculateMetricTrend(timeSeries, 'security'),
|
|
144
|
+
performance: this.calculateMetricTrend(timeSeries, 'performance')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Overall quality trend
|
|
148
|
+
trends.overall = this.calculateOverallTrend(timeSeries)
|
|
149
|
+
|
|
150
|
+
return trends
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Calculate trend for a specific metric
|
|
155
|
+
*/
|
|
156
|
+
calculateMetricTrend(timeSeries, metric) {
|
|
157
|
+
if (timeSeries.length < 2) {
|
|
158
|
+
return {
|
|
159
|
+
direction: 'stable',
|
|
160
|
+
rate: 0,
|
|
161
|
+
confidence: 0
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const values = timeSeries.map(d => d[metric])
|
|
166
|
+
const dates = timeSeries.map(d => new Date(d.date).getTime())
|
|
167
|
+
|
|
168
|
+
// Linear regression
|
|
169
|
+
const regression = simpleStatistics.linearRegression(
|
|
170
|
+
dates.map((date, i) => [date, values[i]])
|
|
171
|
+
)
|
|
172
|
+
const correlation = simpleStatistics.sampleCorrelation(dates, values)
|
|
173
|
+
|
|
174
|
+
const rate = regression.m
|
|
175
|
+
const confidence = Math.abs(correlation)
|
|
176
|
+
|
|
177
|
+
// Determine direction
|
|
178
|
+
let direction = 'stable'
|
|
179
|
+
if (Math.abs(rate) > 0.01) {
|
|
180
|
+
direction = rate > 0 ? (metric === 'qualityScore' ? 'improving' : 'declining')
|
|
181
|
+
: (metric === 'qualityScore' ? 'declining' : 'improving')
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
direction,
|
|
186
|
+
rate,
|
|
187
|
+
confidence,
|
|
188
|
+
values,
|
|
189
|
+
dates,
|
|
190
|
+
regression
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Calculate overall quality trend
|
|
196
|
+
*/
|
|
197
|
+
calculateOverallTrend(timeSeries) {
|
|
198
|
+
const recentPeriods = timeSeries.slice(-7) // Last 7 periods
|
|
199
|
+
const previousPeriods = timeSeries.slice(-14, -7) // Previous 7 periods
|
|
200
|
+
|
|
201
|
+
if (recentPeriods.length === 0 || previousPeriods.length === 0) {
|
|
202
|
+
return {
|
|
203
|
+
direction: 'stable',
|
|
204
|
+
score: 0,
|
|
205
|
+
change: 0
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const recentScore = this.average(recentPeriods.map(d => d.qualityScore))
|
|
210
|
+
const previousScore = this.average(previousPeriods.map(d => d.qualityScore))
|
|
211
|
+
const change = ((recentScore - previousScore) / previousScore) * 100
|
|
212
|
+
|
|
213
|
+
let direction = 'stable'
|
|
214
|
+
if (Math.abs(change) > 5) {
|
|
215
|
+
direction = change > 0 ? 'improving' : 'declining'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
direction,
|
|
220
|
+
score: recentScore,
|
|
221
|
+
change,
|
|
222
|
+
previousScore
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Generate predictions using AI/ML
|
|
228
|
+
*/
|
|
229
|
+
async generatePredictions(trends, days) {
|
|
230
|
+
const predictions = {
|
|
231
|
+
quality: this.predictMetric(trends.quality, days),
|
|
232
|
+
issues: this.predictMetric(trends.issues, days),
|
|
233
|
+
complexity: this.predictMetric(trends.complexity, days),
|
|
234
|
+
security: this.predictMetric(trends.security, days),
|
|
235
|
+
performance: this.predictMetric(trends.performance, days),
|
|
236
|
+
overall: this.predictOverall(trends.overall, days)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return predictions
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Predict future values for a metric
|
|
244
|
+
*/
|
|
245
|
+
predictMetric(trend, days) {
|
|
246
|
+
if (!trend.regression || trend.confidence < 0.5) {
|
|
247
|
+
return {
|
|
248
|
+
predicted: trend.values[trend.values.length - 1] || 0,
|
|
249
|
+
confidence: trend.confidence,
|
|
250
|
+
warning: 'Low confidence prediction due to insufficient data'
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const lastDate = trend.dates[trend.dates.length - 1]
|
|
255
|
+
const futureDate = lastDate + (days * 24 * 60 * 60 * 1000)
|
|
256
|
+
|
|
257
|
+
const predicted = simpleStatistics.linearRegressionLine(trend.regression)(futureDate)
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
predicted,
|
|
261
|
+
confidence: trend.confidence,
|
|
262
|
+
direction: predicted > trend.values[trend.values.length - 1] ? 'up' : 'down'
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Predict overall quality
|
|
268
|
+
*/
|
|
269
|
+
predictOverall(trend, days) {
|
|
270
|
+
const current = trend.score
|
|
271
|
+
const change = trend.change
|
|
272
|
+
|
|
273
|
+
// Predict based on current trend
|
|
274
|
+
const predictedChange = (change / 7) * days // Extrapolate daily change
|
|
275
|
+
const predicted = current + predictedChange
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
predicted,
|
|
279
|
+
direction: predicted > current ? 'improving' : 'declining',
|
|
280
|
+
confidence: Math.min(100, Math.abs(change) * 2)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Identify risk points
|
|
286
|
+
*/
|
|
287
|
+
identifyRisks(trends, predictions) {
|
|
288
|
+
const risks = []
|
|
289
|
+
|
|
290
|
+
// Quality degradation risk
|
|
291
|
+
if (trends.quality.direction === 'declining' && trends.quality.confidence > 0.7) {
|
|
292
|
+
risks.push({
|
|
293
|
+
type: 'quality_degradation',
|
|
294
|
+
severity: 'high',
|
|
295
|
+
metric: 'qualityScore',
|
|
296
|
+
description: 'Quality score is declining significantly',
|
|
297
|
+
trend: trends.quality.direction,
|
|
298
|
+
confidence: trends.quality.confidence,
|
|
299
|
+
prediction: predictions.quality.predicted
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Security issues risk
|
|
304
|
+
if (trends.security.direction === 'declining' || predictions.security.predicted > 0) {
|
|
305
|
+
risks.push({
|
|
306
|
+
type: 'security_issues',
|
|
307
|
+
severity: 'critical',
|
|
308
|
+
metric: 'security',
|
|
309
|
+
description: 'Potential security issues detected',
|
|
310
|
+
trend: trends.security.direction,
|
|
311
|
+
confidence: trends.security.confidence
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Complexity growth risk
|
|
316
|
+
if (trends.complexity.direction === 'declining' && trends.complexity.confidence > 0.6) {
|
|
317
|
+
risks.push({
|
|
318
|
+
type: 'complexity_growth',
|
|
319
|
+
severity: 'medium',
|
|
320
|
+
metric: 'complexity',
|
|
321
|
+
description: 'Code complexity is increasing',
|
|
322
|
+
trend: trends.complexity.direction,
|
|
323
|
+
confidence: trends.complexity.confidence
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Performance degradation risk
|
|
328
|
+
if (trends.performance.direction === 'declining' || predictions.performance.predicted > 10) {
|
|
329
|
+
risks.push({
|
|
330
|
+
type: 'performance_degradation',
|
|
331
|
+
severity: 'high',
|
|
332
|
+
metric: 'performance',
|
|
333
|
+
description: 'Performance issues may arise',
|
|
334
|
+
trend: trends.performance.direction,
|
|
335
|
+
confidence: trends.performance.confidence
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Issue volume risk
|
|
340
|
+
if (trends.issues.direction === 'declining' && trends.issues.confidence > 0.7) {
|
|
341
|
+
risks.push({
|
|
342
|
+
type: 'issue_volume',
|
|
343
|
+
severity: 'medium',
|
|
344
|
+
metric: 'issues',
|
|
345
|
+
description: 'Number of issues is increasing',
|
|
346
|
+
trend: trends.issues.direction,
|
|
347
|
+
confidence: trends.issues.confidence
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return risks
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Generate recommendations
|
|
356
|
+
*/
|
|
357
|
+
generateRecommendations(trends, predictions, risks) {
|
|
358
|
+
const recommendations = []
|
|
359
|
+
|
|
360
|
+
// Overall quality recommendations
|
|
361
|
+
if (trends.overall.direction === 'declining') {
|
|
362
|
+
recommendations.push({
|
|
363
|
+
priority: 'high',
|
|
364
|
+
category: 'quality',
|
|
365
|
+
title: 'Focus on Quality Improvement',
|
|
366
|
+
description: 'Overall quality is declining. Prioritize code reviews and refactoring.',
|
|
367
|
+
actions: [
|
|
368
|
+
'Schedule mandatory code reviews',
|
|
369
|
+
'Implement stricter quality gates',
|
|
370
|
+
'Allocate time for technical debt reduction'
|
|
371
|
+
]
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Security recommendations
|
|
376
|
+
const securityRisk = risks.find(r => r.type === 'security_issues')
|
|
377
|
+
if (securityRisk) {
|
|
378
|
+
recommendations.push({
|
|
379
|
+
priority: 'critical',
|
|
380
|
+
category: 'security',
|
|
381
|
+
title: 'Address Security Issues',
|
|
382
|
+
description: 'Security risk detected. Immediate action required.',
|
|
383
|
+
actions: [
|
|
384
|
+
'Run security audit',
|
|
385
|
+
'Review all user input handling',
|
|
386
|
+
'Update dependencies with known vulnerabilities',
|
|
387
|
+
'Implement security best practices'
|
|
388
|
+
]
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Complexity recommendations
|
|
393
|
+
const complexityRisk = risks.find(r => r.type === 'complexity_growth')
|
|
394
|
+
if (complexityRisk) {
|
|
395
|
+
recommendations.push({
|
|
396
|
+
priority: 'medium',
|
|
397
|
+
category: 'complexity',
|
|
398
|
+
title: 'Reduce Code Complexity',
|
|
399
|
+
description: 'Code complexity is increasing. Consider refactoring.',
|
|
400
|
+
actions: [
|
|
401
|
+
'Identify and refactor complex functions',
|
|
402
|
+
'Apply SOLID principles',
|
|
403
|
+
'Break down large functions',
|
|
404
|
+
'Improve code documentation'
|
|
405
|
+
]
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Performance recommendations
|
|
410
|
+
const performanceRisk = risks.find(r => r.type === 'performance_degradation')
|
|
411
|
+
if (performanceRisk) {
|
|
412
|
+
recommendations.push({
|
|
413
|
+
priority: 'high',
|
|
414
|
+
category: 'performance',
|
|
415
|
+
title: 'Optimize Performance',
|
|
416
|
+
description: 'Performance degradation predicted. Take preventive action.',
|
|
417
|
+
actions: [
|
|
418
|
+
'Profile performance bottlenecks',
|
|
419
|
+
'Optimize database queries',
|
|
420
|
+
'Implement caching strategies',
|
|
421
|
+
'Review algorithm efficiency'
|
|
422
|
+
]
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Preventive recommendations
|
|
427
|
+
if (predictions.overall.confidence > 70) {
|
|
428
|
+
recommendations.push({
|
|
429
|
+
priority: 'low',
|
|
430
|
+
category: 'prevention',
|
|
431
|
+
title: 'Implement Preventive Measures',
|
|
432
|
+
description: 'Predictions suggest proactive monitoring.',
|
|
433
|
+
actions: [
|
|
434
|
+
'Set up automated quality monitoring',
|
|
435
|
+
'Create quality dashboards',
|
|
436
|
+
'Establish quality metrics baseline',
|
|
437
|
+
'Schedule regular quality assessments'
|
|
438
|
+
]
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return recommendations
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Create comprehensive trend report
|
|
447
|
+
*/
|
|
448
|
+
createTrendReport(data) {
|
|
449
|
+
const { trends, predictions, risks, recommendations, metadata } = data
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
metadata,
|
|
453
|
+
summary: {
|
|
454
|
+
overallTrend: trends.overall.direction,
|
|
455
|
+
overallScore: trends.overall.score,
|
|
456
|
+
overallChange: trends.overall.change,
|
|
457
|
+
riskLevel: this.calculateRiskLevel(risks),
|
|
458
|
+
totalRisks: risks.length,
|
|
459
|
+
totalRecommendations: recommendations.length
|
|
460
|
+
},
|
|
461
|
+
trends,
|
|
462
|
+
predictions,
|
|
463
|
+
risks,
|
|
464
|
+
recommendations,
|
|
465
|
+
alerts: this.generateAlerts(risks, recommendations),
|
|
466
|
+
timestamp: new Date().toISOString()
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Calculate overall risk level
|
|
472
|
+
*/
|
|
473
|
+
calculateRiskLevel(risks) {
|
|
474
|
+
if (risks.length === 0) return 'low'
|
|
475
|
+
|
|
476
|
+
const severityWeights = { low: 1, medium: 2, high: 3, critical: 4 }
|
|
477
|
+
const totalScore = risks.reduce((sum, risk) => {
|
|
478
|
+
return sum + (severityWeights[risk.severity] || 1)
|
|
479
|
+
}, 0)
|
|
480
|
+
|
|
481
|
+
const averageScore = totalScore / risks.length
|
|
482
|
+
|
|
483
|
+
if (averageScore >= 3.5) return 'critical'
|
|
484
|
+
if (averageScore >= 2.5) return 'high'
|
|
485
|
+
if (averageScore >= 1.5) return 'medium'
|
|
486
|
+
return 'low'
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Generate alerts for critical risks
|
|
491
|
+
*/
|
|
492
|
+
generateAlerts(risks, recommendations) {
|
|
493
|
+
const alerts = []
|
|
494
|
+
|
|
495
|
+
risks.forEach(risk => {
|
|
496
|
+
if (risk.severity === 'critical' || risk.severity === 'high') {
|
|
497
|
+
alerts.push({
|
|
498
|
+
type: risk.type,
|
|
499
|
+
severity: risk.severity,
|
|
500
|
+
message: risk.description,
|
|
501
|
+
timestamp: new Date().toISOString(),
|
|
502
|
+
actionable: true,
|
|
503
|
+
recommendation: recommendations.find(rec => rec.category === risk.metric)
|
|
504
|
+
})
|
|
505
|
+
}
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
return alerts
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Export trend report to Markdown
|
|
513
|
+
*/
|
|
514
|
+
exportToMarkdown(report, outputPath) {
|
|
515
|
+
const markdown = this.generateMarkdownReport(report)
|
|
516
|
+
return fs.writeFile(outputPath, markdown, 'utf8')
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Generate Markdown report
|
|
521
|
+
*/
|
|
522
|
+
generateMarkdownReport(report) {
|
|
523
|
+
const { metadata, summary, trends, predictions, risks, recommendations, alerts } = report
|
|
524
|
+
|
|
525
|
+
let md = `# Code Quality Trend Analysis Report\n\n`
|
|
526
|
+
md += `**Generated**: ${new Date(metadata.analysisDate).toLocaleString()}\n`
|
|
527
|
+
md += `**Data Period**: ${metadata.dataPeriod} days\n`
|
|
528
|
+
md += `**Prediction Horizon**: ${metadata.predictionHorizon} days\n\n`
|
|
529
|
+
|
|
530
|
+
// Summary
|
|
531
|
+
md += `## š Summary\n\n`
|
|
532
|
+
md += `- **Overall Trend**: ${summary.overallTrend}\n`
|
|
533
|
+
md += `- **Quality Score**: ${summary.overallScore.toFixed(2)}\n`
|
|
534
|
+
md += `- **Change**: ${summary.overallChange > 0 ? '+' : ''}${summary.overallChange.toFixed(2)}%\n`
|
|
535
|
+
md += `- **Risk Level**: ${summary.riskLevel}\n`
|
|
536
|
+
md += `- **Total Risks**: ${summary.totalRisks}\n`
|
|
537
|
+
md += `- **Recommendations**: ${summary.totalRecommendations}\n\n`
|
|
538
|
+
|
|
539
|
+
// Trends
|
|
540
|
+
md += `## š Trends\n\n`
|
|
541
|
+
md += `### Overall Quality\n`
|
|
542
|
+
md += `- Direction: ${trends.overall.direction}\n`
|
|
543
|
+
md += `- Score: ${trends.overall.score.toFixed(2)}\n`
|
|
544
|
+
md += `- Change: ${trends.overall.change > 0 ? '+' : ''}${trends.overall.change.toFixed(2)}%\n\n`
|
|
545
|
+
|
|
546
|
+
md += `### Quality Score\n`
|
|
547
|
+
md += `- Direction: ${trends.quality.direction}\n`
|
|
548
|
+
md += `- Rate: ${trends.quality.rate.toFixed(4)}\n`
|
|
549
|
+
md += `- Confidence: ${(trends.quality.confidence * 100).toFixed(2)}%\n\n`
|
|
550
|
+
|
|
551
|
+
md += `### Issues\n`
|
|
552
|
+
md += `- Direction: ${trends.issues.direction}\n`
|
|
553
|
+
md += `- Confidence: ${(trends.issues.confidence * 100).toFixed(2)}%\n\n`
|
|
554
|
+
|
|
555
|
+
md += `### Complexity\n`
|
|
556
|
+
md += `- Direction: ${trends.complexity.direction}\n`
|
|
557
|
+
md += `- Confidence: ${(trends.complexity.confidence * 100).toFixed(2)}%\n\n`
|
|
558
|
+
|
|
559
|
+
md += `### Security\n`
|
|
560
|
+
md += `- Direction: ${trends.security.direction}\n`
|
|
561
|
+
md += `- Confidence: ${(trends.security.confidence * 100).toFixed(2)}%\n\n`
|
|
562
|
+
|
|
563
|
+
md += `### Performance\n`
|
|
564
|
+
md += `- Direction: ${trends.performance.direction}\n`
|
|
565
|
+
md += `- Confidence: ${(trends.performance.confidence * 100).toFixed(2)}%\n\n`
|
|
566
|
+
|
|
567
|
+
// Predictions
|
|
568
|
+
md += `## š® Predictions (Next ${metadata.predictionHorizon} days)\n\n`
|
|
569
|
+
md += `### Overall Quality\n`
|
|
570
|
+
md += `- Predicted Score: ${predictions.overall.predicted.toFixed(2)}\n`
|
|
571
|
+
md += `- Direction: ${predictions.overall.direction}\n`
|
|
572
|
+
md += `- Confidence: ${predictions.overall.confidence.toFixed(2)}%\n\n`
|
|
573
|
+
|
|
574
|
+
// Risks
|
|
575
|
+
md += `## ā ļø Risk Assessment\n\n`
|
|
576
|
+
if (risks.length === 0) {
|
|
577
|
+
md += `No significant risks identified.\n\n`
|
|
578
|
+
} else {
|
|
579
|
+
risks.forEach((risk, i) => {
|
|
580
|
+
const severityIcon = risk.severity === 'critical' ? 'š“' :
|
|
581
|
+
risk.severity === 'high' ? 'š ' :
|
|
582
|
+
risk.severity === 'medium' ? 'š”' : 'š¢'
|
|
583
|
+
md += `### ${severityIcon} Risk ${i + 1}: ${risk.type}\n`
|
|
584
|
+
md += `- **Severity**: ${risk.severity}\n`
|
|
585
|
+
md += `- **Metric**: ${risk.metric}\n`
|
|
586
|
+
md += `- **Description**: ${risk.description}\n`
|
|
587
|
+
md += `- **Trend**: ${risk.trend}\n`
|
|
588
|
+
md += `- **Confidence**: ${(risk.confidence * 100).toFixed(2)}%\n\n`
|
|
589
|
+
})
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Recommendations
|
|
593
|
+
md += `## š” Recommendations\n\n`
|
|
594
|
+
recommendations.forEach((rec, i) => {
|
|
595
|
+
const priorityIcon = rec.priority === 'critical' ? 'šØ' :
|
|
596
|
+
rec.priority === 'high' ? 'ā”' :
|
|
597
|
+
rec.priority === 'medium' ? 'š' : 'š”'
|
|
598
|
+
md += `### ${priorityIcon} ${i + 1}. ${rec.title}\n`
|
|
599
|
+
md += `- **Priority**: ${rec.priority}\n`
|
|
600
|
+
md += `- **Category**: ${rec.category}\n`
|
|
601
|
+
md += `- **Description**: ${rec.description}\n`
|
|
602
|
+
md += `- **Actions**:\n`
|
|
603
|
+
rec.actions.forEach(action => {
|
|
604
|
+
md += ` - ${action}\n`
|
|
605
|
+
})
|
|
606
|
+
md += `\n`
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
// Alerts
|
|
610
|
+
if (alerts.length > 0) {
|
|
611
|
+
md += `## šØ Alerts\n\n`
|
|
612
|
+
alerts.forEach(alert => {
|
|
613
|
+
md += `- **${alert.type}**: ${alert.message}\n`
|
|
614
|
+
})
|
|
615
|
+
md += `\n`
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
md += `---\n`
|
|
619
|
+
md += `*Generated by Code-Simplifier AI Trend Analyzer*\n`
|
|
620
|
+
|
|
621
|
+
return md
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Get period key for time series
|
|
626
|
+
*/
|
|
627
|
+
getPeriodKey(timestamp, granularity) {
|
|
628
|
+
const date = new Date(timestamp)
|
|
629
|
+
|
|
630
|
+
switch (granularity) {
|
|
631
|
+
case 'hour':
|
|
632
|
+
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:00`
|
|
633
|
+
case 'day':
|
|
634
|
+
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
|
|
635
|
+
case 'week': {
|
|
636
|
+
const weekStart = new Date(date)
|
|
637
|
+
weekStart.setDate(date.getDate() - date.getDay())
|
|
638
|
+
return `${weekStart.getFullYear()}-W${this.getWeekNumber(weekStart)}`
|
|
639
|
+
}
|
|
640
|
+
case 'month':
|
|
641
|
+
return `${date.getFullYear()}-${date.getMonth() + 1}`
|
|
642
|
+
default:
|
|
643
|
+
return date.toISOString()
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Get week number
|
|
649
|
+
*/
|
|
650
|
+
getWeekNumber(date) {
|
|
651
|
+
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
652
|
+
const dayNum = d.getUTCDay() || 7
|
|
653
|
+
d.setUTCDate(d.getUTCDate() + 4 - dayNum)
|
|
654
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1))
|
|
655
|
+
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Calculate average
|
|
660
|
+
*/
|
|
661
|
+
average(arr) {
|
|
662
|
+
if (arr.length === 0) return 0
|
|
663
|
+
return arr.reduce((a, b) => a + b, 0) / arr.length
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Calculate sum
|
|
668
|
+
*/
|
|
669
|
+
sum(arr) {
|
|
670
|
+
return arr.reduce((a, b) => a + b, 0)
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
module.exports = new AITrendAnalyzer()
|