aios-core 4.1.0 → 4.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.
Files changed (145) hide show
  1. package/.aios-core/.session/current-session.json +14 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/registry/service-registry.json +6585 -6585
  4. package/.aios-core/data/entity-registry.yaml +208 -8
  5. package/.aios-core/data/registry-update-log.jsonl +165 -0
  6. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  7. package/.aios-core/development/scripts/backup-manager.js +606 -606
  8. package/.aios-core/development/scripts/branch-manager.js +389 -389
  9. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  10. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  11. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  12. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  13. package/.aios-core/development/scripts/diff-generator.js +351 -351
  14. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  15. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  16. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  17. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  18. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  19. package/.aios-core/development/scripts/modification-validator.js +554 -554
  20. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  21. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  22. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  23. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  24. package/.aios-core/development/scripts/security-checker.js +358 -358
  25. package/.aios-core/development/scripts/template-engine.js +239 -239
  26. package/.aios-core/development/scripts/template-validator.js +278 -278
  27. package/.aios-core/development/scripts/test-generator.js +843 -843
  28. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  29. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  30. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  31. package/.aios-core/development/scripts/version-tracker.js +526 -526
  32. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  33. package/.aios-core/development/tasks/validate-next-story.md +99 -2
  34. package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
  35. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
  36. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
  37. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
  38. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
  39. package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
  40. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
  41. package/.aios-core/development/templates/squad-template/LICENSE +21 -21
  42. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -0
  43. package/.aios-core/docs/component-creation-guide.md +458 -0
  44. package/.aios-core/docs/session-update-pattern.md +307 -0
  45. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -0
  46. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -0
  47. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -0
  48. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -0
  49. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -0
  50. package/.aios-core/docs/template-syntax.md +267 -0
  51. package/.aios-core/docs/troubleshooting-guide.md +625 -0
  52. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +193 -193
  53. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  54. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  55. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  56. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  57. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  58. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  59. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  60. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  61. package/.aios-core/infrastructure/tests/utilities-audit-results.json +501 -0
  62. package/.aios-core/install-manifest.yaml +101 -101
  63. package/.aios-core/local-config.yaml.template +70 -70
  64. package/.aios-core/manifests/agents.csv +29 -0
  65. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  66. package/.aios-core/manifests/tasks.csv +198 -0
  67. package/.aios-core/manifests/workers.csv +204 -0
  68. package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
  69. package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
  70. package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
  71. package/.aios-core/monitor/hooks/notification.py +29 -29
  72. package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
  73. package/.aios-core/monitor/hooks/pre_compact.py +29 -29
  74. package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
  75. package/.aios-core/monitor/hooks/stop.py +29 -29
  76. package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
  77. package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
  78. package/.aios-core/product/templates/adr.hbs +125 -125
  79. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  80. package/.aios-core/product/templates/dbdr.hbs +241 -241
  81. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  82. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  83. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  84. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  85. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  86. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  87. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  88. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  89. package/.aios-core/product/templates/epic.hbs +212 -212
  90. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  91. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  92. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  93. package/.aios-core/product/templates/pmdr.hbs +186 -186
  94. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  95. package/.aios-core/product/templates/prd.hbs +201 -201
  96. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  97. package/.aios-core/product/templates/story.hbs +263 -263
  98. package/.aios-core/product/templates/task.hbs +170 -170
  99. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  100. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  101. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  102. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  103. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  104. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  105. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  106. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  107. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  108. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  109. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  110. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  111. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  112. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  113. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  114. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  115. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  116. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  117. package/.aios-core/scripts/pm.sh +0 -0
  118. package/.claude/hooks/enforce-architecture-first.py +196 -196
  119. package/.claude/hooks/mind-clone-governance.py +192 -192
  120. package/.claude/hooks/read-protection.py +151 -151
  121. package/.claude/hooks/slug-validation.py +176 -176
  122. package/.claude/hooks/sql-governance.py +182 -182
  123. package/.claude/hooks/write-path-validation.py +194 -194
  124. package/.claude/rules/agent-authority.md +105 -0
  125. package/.claude/rules/coderabbit-integration.md +93 -0
  126. package/.claude/rules/ids-principles.md +112 -0
  127. package/.claude/rules/story-lifecycle.md +139 -0
  128. package/.claude/rules/workflow-execution.md +150 -0
  129. package/LICENSE +48 -48
  130. package/bin/aios-minimal.js +0 -0
  131. package/bin/aios.js +0 -0
  132. package/package.json +1 -1
  133. package/packages/aios-install/bin/aios-install.js +0 -0
  134. package/packages/aios-install/bin/edmcp.js +0 -0
  135. package/packages/aios-pro-cli/bin/aios-pro.js +0 -0
  136. package/packages/installer/src/wizard/pro-setup.js +433 -49
  137. package/scripts/check-markdown-links.py +352 -352
  138. package/scripts/code-intel-health-check.js +343 -0
  139. package/scripts/dashboard-parallel-dev.sh +0 -0
  140. package/scripts/dashboard-parallel-phase3.sh +0 -0
  141. package/scripts/dashboard-parallel-phase4.sh +0 -0
  142. package/scripts/glue/README.md +355 -0
  143. package/scripts/glue/compose-agent-prompt.cjs +362 -0
  144. package/scripts/install-monitor-hooks.sh +0 -0
  145. package/.aios-core/lib/build.json +0 -1
@@ -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;