aios-core 4.2.13 → 4.2.14

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.
Files changed (97) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/scripts/package-synapse.js +5 -5
  64. package/scripts/validate-package-completeness.js +3 -3
  65. package/.aios-core/.session/current-session.json +0 -14
  66. package/.aios-core/data/registry-update-log.jsonl +0 -191
  67. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  68. package/.aios-core/docs/component-creation-guide.md +0 -458
  69. package/.aios-core/docs/session-update-pattern.md +0 -307
  70. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  71. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  72. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  73. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  74. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  75. package/.aios-core/docs/template-syntax.md +0 -267
  76. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  77. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  78. package/.aios-core/manifests/agents.csv +0 -29
  79. package/.aios-core/manifests/tasks.csv +0 -198
  80. package/.aios-core/manifests/workers.csv +0 -204
  81. package/.claude/rules/agent-authority.md +0 -105
  82. package/.claude/rules/coderabbit-integration.md +0 -93
  83. package/.claude/rules/ids-principles.md +0 -112
  84. package/.claude/rules/story-lifecycle.md +0 -139
  85. package/.claude/rules/workflow-execution.md +0 -150
  86. package/pro/README.md +0 -66
  87. package/pro/license/degradation.js +0 -220
  88. package/pro/license/errors.js +0 -450
  89. package/pro/license/feature-gate.js +0 -354
  90. package/pro/license/index.js +0 -181
  91. package/pro/license/license-api.js +0 -651
  92. package/pro/license/license-cache.js +0 -523
  93. package/pro/license/license-crypto.js +0 -303
  94. package/scripts/glue/README.md +0 -355
  95. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  96. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  97. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,776 +1,776 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const chalk = require('chalk');
4
-
5
- /**
6
- * Tracks metrics for self-improvement operations
7
- */
8
- class MetricsTracker {
9
- constructor(options = {}) {
10
- this.rootPath = options.rootPath || process.cwd();
11
- this.metricsFile = path.join(this.rootPath, '.aios', 'improvement-metrics.json');
12
- this.maxEntries = options.maxEntries || 1000;
13
-
14
- // Metric categories
15
- this.categories = {
16
- performance: ['execution_time', 'memory_usage', 'cpu_usage'],
17
- quality: ['test_coverage', 'code_complexity', 'error_rate'],
18
- impact: ['files_modified', 'functions_improved', 'bugs_fixed'],
19
- user: ['approval_rate', 'rollback_rate', 'satisfaction_score']
20
- };
21
- }
22
-
23
- /**
24
- * Initialize metrics system
25
- * @returns {Promise<void>}
26
- */
27
- async initialize() {
28
- const metricsDir = path.dirname(this.metricsFile);
29
-
30
- try {
31
- await fs.mkdir(metricsDir, { recursive: true });
32
-
33
- // Initialize file if doesn't exist
34
- try {
35
- await fs.access(this.metricsFile);
36
- } catch {
37
- await this.saveMetrics({
38
- version: '1.0.0',
39
- created: new Date().toISOString(),
40
- improvements: [],
41
- aggregates: this.initializeAggregates(),
42
- trends: {}
43
- });
44
- }
45
- } catch (error) {
46
- console.error(chalk.red(`Failed to initialize metrics: ${error.message}`));
47
- }
48
- }
49
-
50
- /**
51
- * Record improvement metrics
52
- * @param {Object} improvement - Improvement data
53
- * @returns {Promise<void>}
54
- */
55
- async recordImprovement(improvement) {
56
- await this.initialize();
57
-
58
- const metrics = await this.loadMetrics();
59
-
60
- const entry = {
61
- improvement_id: improvement.improvement_id,
62
- timestamp: new Date().toISOString(),
63
- metrics: improvement.metrics || {},
64
- analysis: improvement.analysis || {},
65
- plan: improvement.plan || {},
66
- outcome: 'pending',
67
- measurements: await this.gatherMeasurements(improvement)
68
- };
69
-
70
- metrics.improvements.push(entry);
71
-
72
- // Keep only recent entries
73
- if (metrics.improvements.length > this.maxEntries) {
74
- metrics.improvements = metrics.improvements.slice(-this.maxEntries);
75
- }
76
-
77
- // Update aggregates
78
- await this.updateAggregates(metrics, entry);
79
-
80
- // Calculate trends
81
- metrics.trends = await this.calculateTrends(metrics);
82
-
83
- await this.saveMetrics(metrics);
84
-
85
- console.log(chalk.green(`📊 Metrics recorded for improvement: ${improvement.improvement_id}`));
86
- }
87
-
88
- /**
89
- * Update improvement outcome
90
- * @param {string} improvementId - Improvement ID
91
- * @param {Object} outcome - Outcome data
92
- * @returns {Promise<void>}
93
- */
94
- async updateOutcome(improvementId, outcome) {
95
- const metrics = await this.loadMetrics();
96
-
97
- const entry = metrics.improvements.find(i => i.improvement_id === improvementId);
98
- if (!entry) {
99
- throw new Error(`Improvement not found: ${improvementId}`);
100
- }
101
-
102
- entry.outcome = outcome.status; // 'success', 'failed', 'rolled_back'
103
- entry.outcome_details = outcome;
104
- entry.end_timestamp = new Date().toISOString();
105
-
106
- // Calculate duration
107
- const start = new Date(entry.timestamp);
108
- const end = new Date(entry.end_timestamp);
109
- entry.duration_ms = end - start;
110
-
111
- // Update aggregates based on outcome
112
- await this.updateOutcomeAggregates(metrics, entry);
113
-
114
- await this.saveMetrics(metrics);
115
- }
116
-
117
- /**
118
- * Get improvement report
119
- * @param {string} improvementId - Improvement ID
120
- * @returns {Promise<Object>} Improvement report
121
- */
122
- async getImprovementReport(improvementId) {
123
- const metrics = await this.loadMetrics();
124
- const entry = metrics.improvements.find(i => i.improvement_id === improvementId);
125
-
126
- if (!entry) {
127
- throw new Error(`Improvement not found: ${improvementId}`);
128
- }
129
-
130
- const report = {
131
- improvement_id: improvementId,
132
- timestamp: entry.timestamp,
133
- outcome: entry.outcome,
134
- duration: entry.duration_ms ? `${(entry.duration_ms / 1000).toFixed(2)}s` : 'ongoing',
135
- metrics: entry.metrics,
136
- measurements: entry.measurements,
137
- impact_summary: this.generateImpactSummary(entry),
138
- recommendations: this.generateRecommendations(entry)
139
- };
140
-
141
- return report;
142
- }
143
-
144
- /**
145
- * Get dashboard data
146
- * @param {Object} options - Dashboard options
147
- * @returns {Promise<Object>} Dashboard data
148
- */
149
- async getDashboard(options = {}) {
150
- const { period = '7d' } = options;
151
- const metrics = await this.loadMetrics();
152
-
153
- const cutoff = this.getPeriodCutoff(period);
154
- const recentImprovements = metrics.improvements.filter(
155
- i => new Date(i.timestamp) > cutoff
156
- );
157
-
158
- const dashboard = {
159
- period,
160
- summary: {
161
- total_improvements: recentImprovements.length,
162
- successful: recentImprovements.filter(i => i.outcome === 'success').length,
163
- failed: recentImprovements.filter(i => i.outcome === 'failed').length,
164
- rolled_back: recentImprovements.filter(i => i.outcome === 'rolled_back').length,
165
- pending: recentImprovements.filter(i => i.outcome === 'pending').length
166
- },
167
- performance: this.calculatePerformanceMetrics(recentImprovements),
168
- quality: this.calculateQualityMetrics(recentImprovements),
169
- trends: metrics.trends,
170
- top_improvements: this.getTopImprovements(recentImprovements, 5),
171
- recommendations: this.generateDashboardRecommendations(metrics)
172
- };
173
-
174
- return dashboard;
175
- }
176
-
177
- /**
178
- * Generate analytics report
179
- * @param {Object} options - Report options
180
- * @returns {Promise<Object>} Analytics report
181
- */
182
- async generateAnalytics(options = {}) {
183
- const metrics = await this.loadMetrics();
184
-
185
- const analytics = {
186
- generated: new Date().toISOString(),
187
- period: options.period || 'all-time',
188
- improvements: {
189
- total: metrics.improvements.length,
190
- by_outcome: this.groupByOutcome(metrics.improvements),
191
- by_category: this.groupByCategory(metrics.improvements),
192
- by_month: this.groupByMonth(metrics.improvements)
193
- },
194
- performance: {
195
- average_duration: this.calculateAverageDuration(metrics.improvements),
196
- success_rate: this.calculateSuccessRate(metrics.improvements),
197
- improvement_velocity: this.calculateVelocity(metrics.improvements)
198
- },
199
- impact: {
200
- total_files_modified: metrics.aggregates.total_files_modified,
201
- total_functions_improved: metrics.aggregates.total_functions_improved,
202
- average_improvement_score: this.calculateAverageImprovementScore(metrics.improvements)
203
- },
204
- patterns: this.identifyPatterns(metrics.improvements),
205
- insights: this.generateInsights(metrics)
206
- };
207
-
208
- return analytics;
209
- }
210
-
211
- /**
212
- * Gather measurements for improvement
213
- * @private
214
- */
215
- async gatherMeasurements(improvement) {
216
- const measurements = {
217
- baseline: {},
218
- projected: {},
219
- actual: {}
220
- };
221
-
222
- // Baseline measurements from analysis
223
- if (improvement.analysis) {
224
- measurements.baseline = {
225
- overall_score: improvement.analysis.overall_score,
226
- category_scores: improvement.analysis.categories
227
- ? Object.entries(improvement.analysis.categories).reduce((acc, [cat, data]) => {
228
- acc[cat] = data.score;
229
- return acc;
230
- }, {})
231
- : {}
232
- };
233
- }
234
-
235
- // Projected improvements from plan
236
- if (improvement.plan) {
237
- measurements.projected = {
238
- impact: improvement.plan.estimatedImpact,
239
- effort: improvement.plan.estimatedEffort,
240
- risk: improvement.plan.riskLevel,
241
- files: improvement.plan.affectedFiles?.length || 0
242
- };
243
- }
244
-
245
- // Actual measurements will be filled later
246
- measurements.actual = {
247
- timestamp: new Date().toISOString()
248
- };
249
-
250
- return measurements;
251
- }
252
-
253
- /**
254
- * Update aggregates
255
- * @private
256
- */
257
- async updateAggregates(metrics, entry) {
258
- const agg = metrics.aggregates;
259
-
260
- agg.total_improvements++;
261
-
262
- if (entry.measurements.projected.files) {
263
- agg.total_files_modified += entry.measurements.projected.files;
264
- }
265
-
266
- // Update category counts
267
- if (entry.plan && entry.plan.target_areas) {
268
- entry.plan.target_areas.forEach(area => {
269
- agg.improvements_by_category[area] = (agg.improvements_by_category[area] || 0) + 1;
270
- });
271
- }
272
-
273
- // Update hourly distribution
274
- const hour = new Date(entry.timestamp).getHours();
275
- agg.improvements_by_hour[hour] = (agg.improvements_by_hour[hour] || 0) + 1;
276
- }
277
-
278
- /**
279
- * Update outcome aggregates
280
- * @private
281
- */
282
- async updateOutcomeAggregates(metrics, entry) {
283
- const agg = metrics.aggregates;
284
-
285
- switch (entry.outcome) {
286
- case 'success':
287
- agg.successful_improvements++;
288
- if (entry.duration_ms) {
289
- agg.total_duration_ms += entry.duration_ms;
290
- }
291
- break;
292
- case 'failed':
293
- agg.failed_improvements++;
294
- break;
295
- case 'rolled_back':
296
- agg.rolled_back_improvements++;
297
- break;
298
- }
299
-
300
- // Update success rate
301
- const total = agg.successful_improvements + agg.failed_improvements + agg.rolled_back_improvements;
302
- agg.success_rate = total > 0 ? (agg.successful_improvements / total) * 100 : 0;
303
- }
304
-
305
- /**
306
- * Calculate trends
307
- * @private
308
- */
309
- async calculateTrends(metrics) {
310
- const trends = {};
311
-
312
- // Success rate trend (last 5 periods)
313
- const periods = 5;
314
- const periodLength = 7 * 24 * 60 * 60 * 1000; // 7 days
315
-
316
- trends.success_rate = [];
317
-
318
- for (let i = 0; i < periods; i++) {
319
- const end = Date.now() - (i * periodLength);
320
- const start = end - periodLength;
321
-
322
- const periodImprovements = metrics.improvements.filter(imp => {
323
- const timestamp = new Date(imp.timestamp).getTime();
324
- return timestamp >= start && timestamp < end;
325
- });
326
-
327
- const successRate = this.calculateSuccessRate(periodImprovements);
328
- trends.success_rate.unshift({
329
- period: i,
330
- rate: successRate,
331
- count: periodImprovements.length
332
- });
333
- }
334
-
335
- // Velocity trend
336
- trends.velocity = this.calculateVelocityTrend(metrics.improvements);
337
-
338
- // Category trends
339
- trends.categories = this.calculateCategoryTrends(metrics.improvements);
340
-
341
- return trends;
342
- }
343
-
344
- /**
345
- * Calculate success rate
346
- * @private
347
- */
348
- calculateSuccessRate(_improvements) {
349
- const completed = improvements.filter(i => i.outcome !== 'pending');
350
- if (completed.length === 0) return 0;
351
-
352
- const successful = completed.filter(i => i.outcome === 'success').length;
353
- return (successful / completed.length) * 100;
354
- }
355
-
356
- /**
357
- * Calculate velocity trend
358
- * @private
359
- */
360
- calculateVelocityTrend(_improvements) {
361
- const last30Days = Date.now() - (30 * 24 * 60 * 60 * 1000);
362
- const last60Days = Date.now() - (60 * 24 * 60 * 60 * 1000);
363
-
364
- const recent = improvements.filter(i => new Date(i.timestamp) > last30Days).length;
365
- const previous = improvements.filter(i => {
366
- const timestamp = new Date(i.timestamp);
367
- return timestamp > last60Days && timestamp <= last30Days;
368
- }).length;
369
-
370
- const change = previous > 0 ? ((recent - previous) / previous) * 100 : 0;
371
-
372
- return {
373
- current: recent,
374
- previous,
375
- change: change.toFixed(1),
376
- direction: change > 0 ? 'up' : change < 0 ? 'down' : 'stable'
377
- };
378
- }
379
-
380
- /**
381
- * Generate impact summary
382
- * @private
383
- */
384
- generateImpactSummary(entry) {
385
- const summary = {
386
- scope: 'unknown',
387
- magnitude: 'unknown',
388
- areas_affected: []
389
- };
390
-
391
- if (entry.measurements.projected) {
392
- const files = entry.measurements.projected.files || 0;
393
-
394
- if (files === 0) summary.scope = 'none';
395
- else if (files <= 3) summary.scope = 'small';
396
- else if (files <= 10) summary.scope = 'medium';
397
- else summary.scope = 'large';
398
-
399
- summary.magnitude = entry.measurements.projected.impact || 'unknown';
400
- }
401
-
402
- if (entry.plan && entry.plan.target_areas) {
403
- summary.areas_affected = entry.plan.target_areas;
404
- }
405
-
406
- return summary;
407
- }
408
-
409
- /**
410
- * Generate recommendations
411
- * @private
412
- */
413
- generateRecommendations(entry) {
414
- const recommendations = [];
415
-
416
- if (entry.outcome === 'failed') {
417
- recommendations.push({
418
- type: 'investigation',
419
- message: 'Investigate failure cause and adjust validation criteria'
420
- });
421
- }
422
-
423
- if (entry.outcome === 'rolled_back') {
424
- recommendations.push({
425
- type: 'review',
426
- message: 'Review rollback reasons and improve testing coverage'
427
- });
428
- }
429
-
430
- if (entry.measurements.projected && entry.measurements.projected.risk === 'high') {
431
- recommendations.push({
432
- type: 'caution',
433
- message: 'Consider breaking high-risk improvements into smaller changes'
434
- });
435
- }
436
-
437
- return recommendations;
438
- }
439
-
440
- /**
441
- * Generate dashboard recommendations
442
- * @private
443
- */
444
- generateDashboardRecommendations(metrics) {
445
- const recommendations = [];
446
-
447
- if (metrics.aggregates.success_rate < 70) {
448
- recommendations.push({
449
- priority: 'high',
450
- message: 'Success rate below 70% - review validation and testing processes'
451
- });
452
- }
453
-
454
- if (metrics.aggregates.rolled_back_improvements > metrics.aggregates.successful_improvements * 0.2) {
455
- recommendations.push({
456
- priority: 'medium',
457
- message: 'High rollback rate detected - improve sandbox testing'
458
- });
459
- }
460
-
461
- const recentTrend = metrics.trends.velocity;
462
- if (recentTrend && recentTrend.direction === 'down' && recentTrend.change < -50) {
463
- recommendations.push({
464
- priority: 'low',
465
- message: 'Improvement velocity decreasing - consider process optimization'
466
- });
467
- }
468
-
469
- return recommendations;
470
- }
471
-
472
- /**
473
- * Get top improvements
474
- * @private
475
- */
476
- getTopImprovements(_improvements, limit) {
477
- return improvements
478
- .filter(i => i.outcome === 'success')
479
- .sort((a, b) => {
480
- const scoreA = a.measurements.projected?.impact || 0;
481
- const scoreB = b.measurements.projected?.impact || 0;
482
- return scoreB - scoreA;
483
- })
484
- .slice(0, limit)
485
- .map(i => ({
486
- id: i.improvement_id,
487
- timestamp: i.timestamp,
488
- impact: i.measurements.projected?.impact,
489
- areas: i.plan?.target_areas || []
490
- }));
491
- }
492
-
493
- /**
494
- * Identify patterns
495
- * @private
496
- */
497
- identifyPatterns(_improvements) {
498
- const patterns = {
499
- common_failures: {},
500
- success_factors: [],
501
- time_patterns: {}
502
- };
503
-
504
- // Analyze failures
505
- const failures = improvements.filter(i => i.outcome === 'failed');
506
- failures.forEach(f => {
507
- if (f.plan && f.plan.target_areas) {
508
- f.plan.target_areas.forEach(area => {
509
- patterns.common_failures[area] = (patterns.common_failures[area] || 0) + 1;
510
- });
511
- }
512
- });
513
-
514
- // Success patterns
515
- const successes = improvements.filter(i => i.outcome === 'success');
516
- if (successes.length > 0) {
517
- const avgFiles = successes.reduce((sum, s) =>
518
- sum + (s.measurements.projected?.files || 0), 0) / successes.length;
519
-
520
- patterns.success_factors.push({
521
- factor: 'optimal_file_count',
522
- value: Math.round(avgFiles),
523
- confidence: 0.7
524
- });
525
- }
526
-
527
- return patterns;
528
- }
529
-
530
- /**
531
- * Generate insights
532
- * @private
533
- */
534
- generateInsights(metrics) {
535
- const insights = [];
536
-
537
- // Time-based insights
538
- const hourlyDist = metrics.aggregates.improvements_by_hour;
539
- const peakHour = Object.entries(hourlyDist)
540
- .sort(([,a], [,b]) => b - a)[0];
541
-
542
- if (peakHour) {
543
- insights.push({
544
- type: 'timing',
545
- message: `Most improvements occur at ${peakHour[0]}:00 hours`,
546
- data: { hour: peakHour[0], count: peakHour[1] }
547
- });
548
- }
549
-
550
- // Category insights
551
- const categories = metrics.aggregates.improvements_by_category;
552
- const topCategory = Object.entries(categories)
553
- .sort(([,a], [,b]) => b - a)[0];
554
-
555
- if (topCategory) {
556
- insights.push({
557
- type: 'focus',
558
- message: `${topCategory[0]} improvements are most common (${topCategory[1]} times)`,
559
- data: { category: topCategory[0], count: topCategory[1] }
560
- });
561
- }
562
-
563
- return insights;
564
- }
565
-
566
- /**
567
- * Initialize aggregates
568
- * @private
569
- */
570
- initializeAggregates() {
571
- return {
572
- total_improvements: 0,
573
- successful_improvements: 0,
574
- failed_improvements: 0,
575
- rolled_back_improvements: 0,
576
- total_files_modified: 0,
577
- total_functions_improved: 0,
578
- total_duration_ms: 0,
579
- success_rate: 0,
580
- improvements_by_category: {},
581
- improvements_by_hour: {}
582
- };
583
- }
584
-
585
- /**
586
- * Get period cutoff date
587
- * @private
588
- */
589
- getPeriodCutoff(period) {
590
- const now = new Date();
591
-
592
- switch (period) {
593
- case '24h':
594
- return new Date(now - 24 * 60 * 60 * 1000);
595
- case '7d':
596
- return new Date(now - 7 * 24 * 60 * 60 * 1000);
597
- case '30d':
598
- return new Date(now - 30 * 24 * 60 * 60 * 1000);
599
- case '90d':
600
- return new Date(now - 90 * 24 * 60 * 60 * 1000);
601
- default:
602
- return new Date(0); // All time
603
- }
604
- }
605
-
606
- /**
607
- * Calculate average duration
608
- * @private
609
- */
610
- calculateAverageDuration(_improvements) {
611
- const completed = improvements.filter(i => i.duration_ms);
612
- if (completed.length === 0) return 0;
613
-
614
- const total = completed.reduce((sum, i) => sum + i.duration_ms, 0);
615
- return Math.round(total / completed.length);
616
- }
617
-
618
- /**
619
- * Calculate improvement velocity
620
- * @private
621
- */
622
- calculateVelocity(_improvements) {
623
- const last7Days = this.getPeriodCutoff('7d');
624
- const recent = improvements.filter(i => new Date(i.timestamp) > last7Days);
625
- return recent.length / 7; // Per day
626
- }
627
-
628
- /**
629
- * Calculate average improvement score
630
- * @private
631
- */
632
- calculateAverageImprovementScore(_improvements) {
633
- const withScores = improvements.filter(i =>
634
- i.measurements?.baseline?.overall_score &&
635
- i.outcome === 'success'
636
- );
637
-
638
- if (withScores.length === 0) return 0;
639
-
640
- const totalImprovement = withScores.reduce((sum, i) => {
641
- const baseline = parseFloat(i.measurements.baseline.overall_score) || 0;
642
- const projected = baseline + (i.measurements.projected?.impact || 0);
643
- return sum + (projected - baseline);
644
- }, 0);
645
-
646
- return (totalImprovement / withScores.length).toFixed(2);
647
- }
648
-
649
- /**
650
- * Group improvements by outcome
651
- * @private
652
- */
653
- groupByOutcome(_improvements) {
654
- return improvements.reduce((groups, imp) => {
655
- const outcome = imp.outcome || 'pending';
656
- groups[outcome] = (groups[outcome] || 0) + 1;
657
- return groups;
658
- }, {});
659
- }
660
-
661
- /**
662
- * Group improvements by category
663
- * @private
664
- */
665
- groupByCategory(_improvements) {
666
- const groups = {};
667
-
668
- improvements.forEach(imp => {
669
- if (imp.plan && imp.plan.target_areas) {
670
- imp.plan.target_areas.forEach(area => {
671
- groups[area] = (groups[area] || 0) + 1;
672
- });
673
- }
674
- });
675
-
676
- return groups;
677
- }
678
-
679
- /**
680
- * Group improvements by month
681
- * @private
682
- */
683
- groupByMonth(_improvements) {
684
- return improvements.reduce((groups, imp) => {
685
- const month = new Date(imp.timestamp).toISOString().substring(0, 7);
686
- groups[month] = (groups[month] || 0) + 1;
687
- return groups;
688
- }, {});
689
- }
690
-
691
- /**
692
- * Calculate performance metrics
693
- * @private
694
- */
695
- calculatePerformanceMetrics(_improvements) {
696
- const successful = improvements.filter(i => i.outcome === 'success');
697
-
698
- return {
699
- average_duration: this.calculateAverageDuration(successful),
700
- fastest_improvement: successful
701
- .filter(i => i.duration_ms)
702
- .sort((a, b) => a.duration_ms - b.duration_ms)[0]?.duration_ms || null,
703
- slowest_improvement: successful
704
- .filter(i => i.duration_ms)
705
- .sort((a, b) => b.duration_ms - a.duration_ms)[0]?.duration_ms || null
706
- };
707
- }
708
-
709
- /**
710
- * Calculate quality metrics
711
- * @private
712
- */
713
- calculateQualityMetrics(_improvements) {
714
- return {
715
- test_coverage_impact: 'N/A', // Would need actual test data
716
- complexity_reduction: 'N/A', // Would need complexity analysis
717
- error_rate_change: 'N/A' // Would need error tracking
718
- };
719
- }
720
-
721
- /**
722
- * Calculate category trends
723
- * @private
724
- */
725
- calculateCategoryTrends(_improvements) {
726
- const trends = {};
727
- const categories = Object.keys(this.categories);
728
-
729
- categories.forEach(cat => {
730
- const catImprovements = improvements.filter(i =>
731
- i.plan?.target_areas?.includes(cat)
732
- );
733
-
734
- trends[cat] = {
735
- total: catImprovements.length,
736
- success_rate: this.calculateSuccessRate(catImprovements),
737
- recent_activity: catImprovements.filter(i =>
738
- new Date(i.timestamp) > this.getPeriodCutoff('7d')
739
- ).length
740
- };
741
- });
742
-
743
- return trends;
744
- }
745
-
746
- /**
747
- * Load metrics
748
- * @private
749
- */
750
- async loadMetrics() {
751
- try {
752
- const content = await fs.readFile(this.metricsFile, 'utf-8');
753
- return JSON.parse(content);
754
- } catch {
755
- return {
756
- version: '1.0.0',
757
- improvements: [],
758
- aggregates: this.initializeAggregates(),
759
- trends: {}
760
- };
761
- }
762
- }
763
-
764
- /**
765
- * Save metrics
766
- * @private
767
- */
768
- async saveMetrics(metrics) {
769
- await fs.writeFile(
770
- this.metricsFile,
771
- JSON.stringify(metrics, null, 2)
772
- );
773
- }
774
- }
775
-
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * Tracks metrics for self-improvement operations
7
+ */
8
+ class MetricsTracker {
9
+ constructor(options = {}) {
10
+ this.rootPath = options.rootPath || process.cwd();
11
+ this.metricsFile = path.join(this.rootPath, '.aios', 'improvement-metrics.json');
12
+ this.maxEntries = options.maxEntries || 1000;
13
+
14
+ // Metric categories
15
+ this.categories = {
16
+ performance: ['execution_time', 'memory_usage', 'cpu_usage'],
17
+ quality: ['test_coverage', 'code_complexity', 'error_rate'],
18
+ impact: ['files_modified', 'functions_improved', 'bugs_fixed'],
19
+ user: ['approval_rate', 'rollback_rate', 'satisfaction_score']
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Initialize metrics system
25
+ * @returns {Promise<void>}
26
+ */
27
+ async initialize() {
28
+ const metricsDir = path.dirname(this.metricsFile);
29
+
30
+ try {
31
+ await fs.mkdir(metricsDir, { recursive: true });
32
+
33
+ // Initialize file if doesn't exist
34
+ try {
35
+ await fs.access(this.metricsFile);
36
+ } catch {
37
+ await this.saveMetrics({
38
+ version: '1.0.0',
39
+ created: new Date().toISOString(),
40
+ improvements: [],
41
+ aggregates: this.initializeAggregates(),
42
+ trends: {}
43
+ });
44
+ }
45
+ } catch (error) {
46
+ console.error(chalk.red(`Failed to initialize metrics: ${error.message}`));
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Record improvement metrics
52
+ * @param {Object} improvement - Improvement data
53
+ * @returns {Promise<void>}
54
+ */
55
+ async recordImprovement(improvement) {
56
+ await this.initialize();
57
+
58
+ const metrics = await this.loadMetrics();
59
+
60
+ const entry = {
61
+ improvement_id: improvement.improvement_id,
62
+ timestamp: new Date().toISOString(),
63
+ metrics: improvement.metrics || {},
64
+ analysis: improvement.analysis || {},
65
+ plan: improvement.plan || {},
66
+ outcome: 'pending',
67
+ measurements: await this.gatherMeasurements(improvement)
68
+ };
69
+
70
+ metrics.improvements.push(entry);
71
+
72
+ // Keep only recent entries
73
+ if (metrics.improvements.length > this.maxEntries) {
74
+ metrics.improvements = metrics.improvements.slice(-this.maxEntries);
75
+ }
76
+
77
+ // Update aggregates
78
+ await this.updateAggregates(metrics, entry);
79
+
80
+ // Calculate trends
81
+ metrics.trends = await this.calculateTrends(metrics);
82
+
83
+ await this.saveMetrics(metrics);
84
+
85
+ console.log(chalk.green(`📊 Metrics recorded for improvement: ${improvement.improvement_id}`));
86
+ }
87
+
88
+ /**
89
+ * Update improvement outcome
90
+ * @param {string} improvementId - Improvement ID
91
+ * @param {Object} outcome - Outcome data
92
+ * @returns {Promise<void>}
93
+ */
94
+ async updateOutcome(improvementId, outcome) {
95
+ const metrics = await this.loadMetrics();
96
+
97
+ const entry = metrics.improvements.find(i => i.improvement_id === improvementId);
98
+ if (!entry) {
99
+ throw new Error(`Improvement not found: ${improvementId}`);
100
+ }
101
+
102
+ entry.outcome = outcome.status; // 'success', 'failed', 'rolled_back'
103
+ entry.outcome_details = outcome;
104
+ entry.end_timestamp = new Date().toISOString();
105
+
106
+ // Calculate duration
107
+ const start = new Date(entry.timestamp);
108
+ const end = new Date(entry.end_timestamp);
109
+ entry.duration_ms = end - start;
110
+
111
+ // Update aggregates based on outcome
112
+ await this.updateOutcomeAggregates(metrics, entry);
113
+
114
+ await this.saveMetrics(metrics);
115
+ }
116
+
117
+ /**
118
+ * Get improvement report
119
+ * @param {string} improvementId - Improvement ID
120
+ * @returns {Promise<Object>} Improvement report
121
+ */
122
+ async getImprovementReport(improvementId) {
123
+ const metrics = await this.loadMetrics();
124
+ const entry = metrics.improvements.find(i => i.improvement_id === improvementId);
125
+
126
+ if (!entry) {
127
+ throw new Error(`Improvement not found: ${improvementId}`);
128
+ }
129
+
130
+ const report = {
131
+ improvement_id: improvementId,
132
+ timestamp: entry.timestamp,
133
+ outcome: entry.outcome,
134
+ duration: entry.duration_ms ? `${(entry.duration_ms / 1000).toFixed(2)}s` : 'ongoing',
135
+ metrics: entry.metrics,
136
+ measurements: entry.measurements,
137
+ impact_summary: this.generateImpactSummary(entry),
138
+ recommendations: this.generateRecommendations(entry)
139
+ };
140
+
141
+ return report;
142
+ }
143
+
144
+ /**
145
+ * Get dashboard data
146
+ * @param {Object} options - Dashboard options
147
+ * @returns {Promise<Object>} Dashboard data
148
+ */
149
+ async getDashboard(options = {}) {
150
+ const { period = '7d' } = options;
151
+ const metrics = await this.loadMetrics();
152
+
153
+ const cutoff = this.getPeriodCutoff(period);
154
+ const recentImprovements = metrics.improvements.filter(
155
+ i => new Date(i.timestamp) > cutoff
156
+ );
157
+
158
+ const dashboard = {
159
+ period,
160
+ summary: {
161
+ total_improvements: recentImprovements.length,
162
+ successful: recentImprovements.filter(i => i.outcome === 'success').length,
163
+ failed: recentImprovements.filter(i => i.outcome === 'failed').length,
164
+ rolled_back: recentImprovements.filter(i => i.outcome === 'rolled_back').length,
165
+ pending: recentImprovements.filter(i => i.outcome === 'pending').length
166
+ },
167
+ performance: this.calculatePerformanceMetrics(recentImprovements),
168
+ quality: this.calculateQualityMetrics(recentImprovements),
169
+ trends: metrics.trends,
170
+ top_improvements: this.getTopImprovements(recentImprovements, 5),
171
+ recommendations: this.generateDashboardRecommendations(metrics)
172
+ };
173
+
174
+ return dashboard;
175
+ }
176
+
177
+ /**
178
+ * Generate analytics report
179
+ * @param {Object} options - Report options
180
+ * @returns {Promise<Object>} Analytics report
181
+ */
182
+ async generateAnalytics(options = {}) {
183
+ const metrics = await this.loadMetrics();
184
+
185
+ const analytics = {
186
+ generated: new Date().toISOString(),
187
+ period: options.period || 'all-time',
188
+ improvements: {
189
+ total: metrics.improvements.length,
190
+ by_outcome: this.groupByOutcome(metrics.improvements),
191
+ by_category: this.groupByCategory(metrics.improvements),
192
+ by_month: this.groupByMonth(metrics.improvements)
193
+ },
194
+ performance: {
195
+ average_duration: this.calculateAverageDuration(metrics.improvements),
196
+ success_rate: this.calculateSuccessRate(metrics.improvements),
197
+ improvement_velocity: this.calculateVelocity(metrics.improvements)
198
+ },
199
+ impact: {
200
+ total_files_modified: metrics.aggregates.total_files_modified,
201
+ total_functions_improved: metrics.aggregates.total_functions_improved,
202
+ average_improvement_score: this.calculateAverageImprovementScore(metrics.improvements)
203
+ },
204
+ patterns: this.identifyPatterns(metrics.improvements),
205
+ insights: this.generateInsights(metrics)
206
+ };
207
+
208
+ return analytics;
209
+ }
210
+
211
+ /**
212
+ * Gather measurements for improvement
213
+ * @private
214
+ */
215
+ async gatherMeasurements(improvement) {
216
+ const measurements = {
217
+ baseline: {},
218
+ projected: {},
219
+ actual: {}
220
+ };
221
+
222
+ // Baseline measurements from analysis
223
+ if (improvement.analysis) {
224
+ measurements.baseline = {
225
+ overall_score: improvement.analysis.overall_score,
226
+ category_scores: improvement.analysis.categories
227
+ ? Object.entries(improvement.analysis.categories).reduce((acc, [cat, data]) => {
228
+ acc[cat] = data.score;
229
+ return acc;
230
+ }, {})
231
+ : {}
232
+ };
233
+ }
234
+
235
+ // Projected improvements from plan
236
+ if (improvement.plan) {
237
+ measurements.projected = {
238
+ impact: improvement.plan.estimatedImpact,
239
+ effort: improvement.plan.estimatedEffort,
240
+ risk: improvement.plan.riskLevel,
241
+ files: improvement.plan.affectedFiles?.length || 0
242
+ };
243
+ }
244
+
245
+ // Actual measurements will be filled later
246
+ measurements.actual = {
247
+ timestamp: new Date().toISOString()
248
+ };
249
+
250
+ return measurements;
251
+ }
252
+
253
+ /**
254
+ * Update aggregates
255
+ * @private
256
+ */
257
+ async updateAggregates(metrics, entry) {
258
+ const agg = metrics.aggregates;
259
+
260
+ agg.total_improvements++;
261
+
262
+ if (entry.measurements.projected.files) {
263
+ agg.total_files_modified += entry.measurements.projected.files;
264
+ }
265
+
266
+ // Update category counts
267
+ if (entry.plan && entry.plan.target_areas) {
268
+ entry.plan.target_areas.forEach(area => {
269
+ agg.improvements_by_category[area] = (agg.improvements_by_category[area] || 0) + 1;
270
+ });
271
+ }
272
+
273
+ // Update hourly distribution
274
+ const hour = new Date(entry.timestamp).getHours();
275
+ agg.improvements_by_hour[hour] = (agg.improvements_by_hour[hour] || 0) + 1;
276
+ }
277
+
278
+ /**
279
+ * Update outcome aggregates
280
+ * @private
281
+ */
282
+ async updateOutcomeAggregates(metrics, entry) {
283
+ const agg = metrics.aggregates;
284
+
285
+ switch (entry.outcome) {
286
+ case 'success':
287
+ agg.successful_improvements++;
288
+ if (entry.duration_ms) {
289
+ agg.total_duration_ms += entry.duration_ms;
290
+ }
291
+ break;
292
+ case 'failed':
293
+ agg.failed_improvements++;
294
+ break;
295
+ case 'rolled_back':
296
+ agg.rolled_back_improvements++;
297
+ break;
298
+ }
299
+
300
+ // Update success rate
301
+ const total = agg.successful_improvements + agg.failed_improvements + agg.rolled_back_improvements;
302
+ agg.success_rate = total > 0 ? (agg.successful_improvements / total) * 100 : 0;
303
+ }
304
+
305
+ /**
306
+ * Calculate trends
307
+ * @private
308
+ */
309
+ async calculateTrends(metrics) {
310
+ const trends = {};
311
+
312
+ // Success rate trend (last 5 periods)
313
+ const periods = 5;
314
+ const periodLength = 7 * 24 * 60 * 60 * 1000; // 7 days
315
+
316
+ trends.success_rate = [];
317
+
318
+ for (let i = 0; i < periods; i++) {
319
+ const end = Date.now() - (i * periodLength);
320
+ const start = end - periodLength;
321
+
322
+ const periodImprovements = metrics.improvements.filter(imp => {
323
+ const timestamp = new Date(imp.timestamp).getTime();
324
+ return timestamp >= start && timestamp < end;
325
+ });
326
+
327
+ const successRate = this.calculateSuccessRate(periodImprovements);
328
+ trends.success_rate.unshift({
329
+ period: i,
330
+ rate: successRate,
331
+ count: periodImprovements.length
332
+ });
333
+ }
334
+
335
+ // Velocity trend
336
+ trends.velocity = this.calculateVelocityTrend(metrics.improvements);
337
+
338
+ // Category trends
339
+ trends.categories = this.calculateCategoryTrends(metrics.improvements);
340
+
341
+ return trends;
342
+ }
343
+
344
+ /**
345
+ * Calculate success rate
346
+ * @private
347
+ */
348
+ calculateSuccessRate(_improvements) {
349
+ const completed = improvements.filter(i => i.outcome !== 'pending');
350
+ if (completed.length === 0) return 0;
351
+
352
+ const successful = completed.filter(i => i.outcome === 'success').length;
353
+ return (successful / completed.length) * 100;
354
+ }
355
+
356
+ /**
357
+ * Calculate velocity trend
358
+ * @private
359
+ */
360
+ calculateVelocityTrend(_improvements) {
361
+ const last30Days = Date.now() - (30 * 24 * 60 * 60 * 1000);
362
+ const last60Days = Date.now() - (60 * 24 * 60 * 60 * 1000);
363
+
364
+ const recent = improvements.filter(i => new Date(i.timestamp) > last30Days).length;
365
+ const previous = improvements.filter(i => {
366
+ const timestamp = new Date(i.timestamp);
367
+ return timestamp > last60Days && timestamp <= last30Days;
368
+ }).length;
369
+
370
+ const change = previous > 0 ? ((recent - previous) / previous) * 100 : 0;
371
+
372
+ return {
373
+ current: recent,
374
+ previous,
375
+ change: change.toFixed(1),
376
+ direction: change > 0 ? 'up' : change < 0 ? 'down' : 'stable'
377
+ };
378
+ }
379
+
380
+ /**
381
+ * Generate impact summary
382
+ * @private
383
+ */
384
+ generateImpactSummary(entry) {
385
+ const summary = {
386
+ scope: 'unknown',
387
+ magnitude: 'unknown',
388
+ areas_affected: []
389
+ };
390
+
391
+ if (entry.measurements.projected) {
392
+ const files = entry.measurements.projected.files || 0;
393
+
394
+ if (files === 0) summary.scope = 'none';
395
+ else if (files <= 3) summary.scope = 'small';
396
+ else if (files <= 10) summary.scope = 'medium';
397
+ else summary.scope = 'large';
398
+
399
+ summary.magnitude = entry.measurements.projected.impact || 'unknown';
400
+ }
401
+
402
+ if (entry.plan && entry.plan.target_areas) {
403
+ summary.areas_affected = entry.plan.target_areas;
404
+ }
405
+
406
+ return summary;
407
+ }
408
+
409
+ /**
410
+ * Generate recommendations
411
+ * @private
412
+ */
413
+ generateRecommendations(entry) {
414
+ const recommendations = [];
415
+
416
+ if (entry.outcome === 'failed') {
417
+ recommendations.push({
418
+ type: 'investigation',
419
+ message: 'Investigate failure cause and adjust validation criteria'
420
+ });
421
+ }
422
+
423
+ if (entry.outcome === 'rolled_back') {
424
+ recommendations.push({
425
+ type: 'review',
426
+ message: 'Review rollback reasons and improve testing coverage'
427
+ });
428
+ }
429
+
430
+ if (entry.measurements.projected && entry.measurements.projected.risk === 'high') {
431
+ recommendations.push({
432
+ type: 'caution',
433
+ message: 'Consider breaking high-risk improvements into smaller changes'
434
+ });
435
+ }
436
+
437
+ return recommendations;
438
+ }
439
+
440
+ /**
441
+ * Generate dashboard recommendations
442
+ * @private
443
+ */
444
+ generateDashboardRecommendations(metrics) {
445
+ const recommendations = [];
446
+
447
+ if (metrics.aggregates.success_rate < 70) {
448
+ recommendations.push({
449
+ priority: 'high',
450
+ message: 'Success rate below 70% - review validation and testing processes'
451
+ });
452
+ }
453
+
454
+ if (metrics.aggregates.rolled_back_improvements > metrics.aggregates.successful_improvements * 0.2) {
455
+ recommendations.push({
456
+ priority: 'medium',
457
+ message: 'High rollback rate detected - improve sandbox testing'
458
+ });
459
+ }
460
+
461
+ const recentTrend = metrics.trends.velocity;
462
+ if (recentTrend && recentTrend.direction === 'down' && recentTrend.change < -50) {
463
+ recommendations.push({
464
+ priority: 'low',
465
+ message: 'Improvement velocity decreasing - consider process optimization'
466
+ });
467
+ }
468
+
469
+ return recommendations;
470
+ }
471
+
472
+ /**
473
+ * Get top improvements
474
+ * @private
475
+ */
476
+ getTopImprovements(_improvements, limit) {
477
+ return improvements
478
+ .filter(i => i.outcome === 'success')
479
+ .sort((a, b) => {
480
+ const scoreA = a.measurements.projected?.impact || 0;
481
+ const scoreB = b.measurements.projected?.impact || 0;
482
+ return scoreB - scoreA;
483
+ })
484
+ .slice(0, limit)
485
+ .map(i => ({
486
+ id: i.improvement_id,
487
+ timestamp: i.timestamp,
488
+ impact: i.measurements.projected?.impact,
489
+ areas: i.plan?.target_areas || []
490
+ }));
491
+ }
492
+
493
+ /**
494
+ * Identify patterns
495
+ * @private
496
+ */
497
+ identifyPatterns(_improvements) {
498
+ const patterns = {
499
+ common_failures: {},
500
+ success_factors: [],
501
+ time_patterns: {}
502
+ };
503
+
504
+ // Analyze failures
505
+ const failures = improvements.filter(i => i.outcome === 'failed');
506
+ failures.forEach(f => {
507
+ if (f.plan && f.plan.target_areas) {
508
+ f.plan.target_areas.forEach(area => {
509
+ patterns.common_failures[area] = (patterns.common_failures[area] || 0) + 1;
510
+ });
511
+ }
512
+ });
513
+
514
+ // Success patterns
515
+ const successes = improvements.filter(i => i.outcome === 'success');
516
+ if (successes.length > 0) {
517
+ const avgFiles = successes.reduce((sum, s) =>
518
+ sum + (s.measurements.projected?.files || 0), 0) / successes.length;
519
+
520
+ patterns.success_factors.push({
521
+ factor: 'optimal_file_count',
522
+ value: Math.round(avgFiles),
523
+ confidence: 0.7
524
+ });
525
+ }
526
+
527
+ return patterns;
528
+ }
529
+
530
+ /**
531
+ * Generate insights
532
+ * @private
533
+ */
534
+ generateInsights(metrics) {
535
+ const insights = [];
536
+
537
+ // Time-based insights
538
+ const hourlyDist = metrics.aggregates.improvements_by_hour;
539
+ const peakHour = Object.entries(hourlyDist)
540
+ .sort(([,a], [,b]) => b - a)[0];
541
+
542
+ if (peakHour) {
543
+ insights.push({
544
+ type: 'timing',
545
+ message: `Most improvements occur at ${peakHour[0]}:00 hours`,
546
+ data: { hour: peakHour[0], count: peakHour[1] }
547
+ });
548
+ }
549
+
550
+ // Category insights
551
+ const categories = metrics.aggregates.improvements_by_category;
552
+ const topCategory = Object.entries(categories)
553
+ .sort(([,a], [,b]) => b - a)[0];
554
+
555
+ if (topCategory) {
556
+ insights.push({
557
+ type: 'focus',
558
+ message: `${topCategory[0]} improvements are most common (${topCategory[1]} times)`,
559
+ data: { category: topCategory[0], count: topCategory[1] }
560
+ });
561
+ }
562
+
563
+ return insights;
564
+ }
565
+
566
+ /**
567
+ * Initialize aggregates
568
+ * @private
569
+ */
570
+ initializeAggregates() {
571
+ return {
572
+ total_improvements: 0,
573
+ successful_improvements: 0,
574
+ failed_improvements: 0,
575
+ rolled_back_improvements: 0,
576
+ total_files_modified: 0,
577
+ total_functions_improved: 0,
578
+ total_duration_ms: 0,
579
+ success_rate: 0,
580
+ improvements_by_category: {},
581
+ improvements_by_hour: {}
582
+ };
583
+ }
584
+
585
+ /**
586
+ * Get period cutoff date
587
+ * @private
588
+ */
589
+ getPeriodCutoff(period) {
590
+ const now = new Date();
591
+
592
+ switch (period) {
593
+ case '24h':
594
+ return new Date(now - 24 * 60 * 60 * 1000);
595
+ case '7d':
596
+ return new Date(now - 7 * 24 * 60 * 60 * 1000);
597
+ case '30d':
598
+ return new Date(now - 30 * 24 * 60 * 60 * 1000);
599
+ case '90d':
600
+ return new Date(now - 90 * 24 * 60 * 60 * 1000);
601
+ default:
602
+ return new Date(0); // All time
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Calculate average duration
608
+ * @private
609
+ */
610
+ calculateAverageDuration(_improvements) {
611
+ const completed = improvements.filter(i => i.duration_ms);
612
+ if (completed.length === 0) return 0;
613
+
614
+ const total = completed.reduce((sum, i) => sum + i.duration_ms, 0);
615
+ return Math.round(total / completed.length);
616
+ }
617
+
618
+ /**
619
+ * Calculate improvement velocity
620
+ * @private
621
+ */
622
+ calculateVelocity(_improvements) {
623
+ const last7Days = this.getPeriodCutoff('7d');
624
+ const recent = improvements.filter(i => new Date(i.timestamp) > last7Days);
625
+ return recent.length / 7; // Per day
626
+ }
627
+
628
+ /**
629
+ * Calculate average improvement score
630
+ * @private
631
+ */
632
+ calculateAverageImprovementScore(_improvements) {
633
+ const withScores = improvements.filter(i =>
634
+ i.measurements?.baseline?.overall_score &&
635
+ i.outcome === 'success'
636
+ );
637
+
638
+ if (withScores.length === 0) return 0;
639
+
640
+ const totalImprovement = withScores.reduce((sum, i) => {
641
+ const baseline = parseFloat(i.measurements.baseline.overall_score) || 0;
642
+ const projected = baseline + (i.measurements.projected?.impact || 0);
643
+ return sum + (projected - baseline);
644
+ }, 0);
645
+
646
+ return (totalImprovement / withScores.length).toFixed(2);
647
+ }
648
+
649
+ /**
650
+ * Group improvements by outcome
651
+ * @private
652
+ */
653
+ groupByOutcome(_improvements) {
654
+ return improvements.reduce((groups, imp) => {
655
+ const outcome = imp.outcome || 'pending';
656
+ groups[outcome] = (groups[outcome] || 0) + 1;
657
+ return groups;
658
+ }, {});
659
+ }
660
+
661
+ /**
662
+ * Group improvements by category
663
+ * @private
664
+ */
665
+ groupByCategory(_improvements) {
666
+ const groups = {};
667
+
668
+ improvements.forEach(imp => {
669
+ if (imp.plan && imp.plan.target_areas) {
670
+ imp.plan.target_areas.forEach(area => {
671
+ groups[area] = (groups[area] || 0) + 1;
672
+ });
673
+ }
674
+ });
675
+
676
+ return groups;
677
+ }
678
+
679
+ /**
680
+ * Group improvements by month
681
+ * @private
682
+ */
683
+ groupByMonth(_improvements) {
684
+ return improvements.reduce((groups, imp) => {
685
+ const month = new Date(imp.timestamp).toISOString().substring(0, 7);
686
+ groups[month] = (groups[month] || 0) + 1;
687
+ return groups;
688
+ }, {});
689
+ }
690
+
691
+ /**
692
+ * Calculate performance metrics
693
+ * @private
694
+ */
695
+ calculatePerformanceMetrics(_improvements) {
696
+ const successful = improvements.filter(i => i.outcome === 'success');
697
+
698
+ return {
699
+ average_duration: this.calculateAverageDuration(successful),
700
+ fastest_improvement: successful
701
+ .filter(i => i.duration_ms)
702
+ .sort((a, b) => a.duration_ms - b.duration_ms)[0]?.duration_ms || null,
703
+ slowest_improvement: successful
704
+ .filter(i => i.duration_ms)
705
+ .sort((a, b) => b.duration_ms - a.duration_ms)[0]?.duration_ms || null
706
+ };
707
+ }
708
+
709
+ /**
710
+ * Calculate quality metrics
711
+ * @private
712
+ */
713
+ calculateQualityMetrics(_improvements) {
714
+ return {
715
+ test_coverage_impact: 'N/A', // Would need actual test data
716
+ complexity_reduction: 'N/A', // Would need complexity analysis
717
+ error_rate_change: 'N/A' // Would need error tracking
718
+ };
719
+ }
720
+
721
+ /**
722
+ * Calculate category trends
723
+ * @private
724
+ */
725
+ calculateCategoryTrends(_improvements) {
726
+ const trends = {};
727
+ const categories = Object.keys(this.categories);
728
+
729
+ categories.forEach(cat => {
730
+ const catImprovements = improvements.filter(i =>
731
+ i.plan?.target_areas?.includes(cat)
732
+ );
733
+
734
+ trends[cat] = {
735
+ total: catImprovements.length,
736
+ success_rate: this.calculateSuccessRate(catImprovements),
737
+ recent_activity: catImprovements.filter(i =>
738
+ new Date(i.timestamp) > this.getPeriodCutoff('7d')
739
+ ).length
740
+ };
741
+ });
742
+
743
+ return trends;
744
+ }
745
+
746
+ /**
747
+ * Load metrics
748
+ * @private
749
+ */
750
+ async loadMetrics() {
751
+ try {
752
+ const content = await fs.readFile(this.metricsFile, 'utf-8');
753
+ return JSON.parse(content);
754
+ } catch {
755
+ return {
756
+ version: '1.0.0',
757
+ improvements: [],
758
+ aggregates: this.initializeAggregates(),
759
+ trends: {}
760
+ };
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Save metrics
766
+ * @private
767
+ */
768
+ async saveMetrics(metrics) {
769
+ await fs.writeFile(
770
+ this.metricsFile,
771
+ JSON.stringify(metrics, null, 2)
772
+ );
773
+ }
774
+ }
775
+
776
776
  module.exports = MetricsTracker;