vibecodingmachine-core 2025.12.25-25 → 2026.1.22-1441

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 (40) hide show
  1. package/ERROR_REPORTING_API.md +212 -0
  2. package/ERROR_REPORTING_USAGE.md +380 -0
  3. package/__tests__/provider-manager-fallback.test.js +43 -0
  4. package/__tests__/provider-manager-rate-limit.test.js +61 -0
  5. package/__tests__/utils/git-branch-manager.test.js +61 -0
  6. package/package.json +1 -1
  7. package/src/beta-request.js +160 -0
  8. package/src/compliance/compliance-manager.js +5 -2
  9. package/src/database/migrations.js +135 -12
  10. package/src/database/user-database-client.js +127 -8
  11. package/src/database/user-schema.js +28 -0
  12. package/src/health-tracking/__tests__/ide-health-tracker.test.js +420 -0
  13. package/src/health-tracking/__tests__/interaction-recorder.test.js +392 -0
  14. package/src/health-tracking/errors.js +50 -0
  15. package/src/health-tracking/health-reporter.js +331 -0
  16. package/src/health-tracking/ide-health-tracker.js +446 -0
  17. package/src/health-tracking/interaction-recorder.js +161 -0
  18. package/src/health-tracking/json-storage.js +276 -0
  19. package/src/health-tracking/storage-interface.js +63 -0
  20. package/src/health-tracking/validators.js +277 -0
  21. package/src/ide-integration/applescript-manager.cjs +1087 -9
  22. package/src/ide-integration/applescript-manager.js +565 -15
  23. package/src/ide-integration/applescript-utils.js +26 -18
  24. package/src/ide-integration/provider-manager.cjs +158 -28
  25. package/src/ide-integration/quota-detector.cjs +339 -16
  26. package/src/ide-integration/quota-detector.js +6 -1
  27. package/src/index.cjs +36 -1
  28. package/src/index.js +20 -0
  29. package/src/localization/translations/en.js +15 -1
  30. package/src/localization/translations/es.js +14 -0
  31. package/src/requirement-numbering.js +164 -0
  32. package/src/sync/aws-setup.js +4 -4
  33. package/src/utils/admin-utils.js +33 -0
  34. package/src/utils/error-reporter.js +117 -0
  35. package/src/utils/git-branch-manager.js +278 -0
  36. package/src/utils/requirement-helpers.js +44 -5
  37. package/src/utils/requirements-parser.js +28 -3
  38. package/tests/health-tracking/health-reporter.test.js +329 -0
  39. package/tests/health-tracking/ide-health-tracker.test.js +368 -0
  40. package/tests/health-tracking/interaction-recorder.test.js +309 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Health Reporter
3
+ *
4
+ * Formats IDE health data for display in CLI and Electron UI.
5
+ * Provides summaries and recommendations based on health metrics.
6
+ */
7
+
8
+ /**
9
+ * Formats IDE health data for different display contexts
10
+ */
11
+ class HealthReporter {
12
+ /**
13
+ * Create health reporter
14
+ */
15
+ constructor() {
16
+ this.ideNames = {
17
+ 'cursor': 'Cursor',
18
+ 'vscode': 'VS Code',
19
+ 'windsurf': 'Windsurf',
20
+ 'cline': 'Cline',
21
+ 'github-copilot': 'GitHub Copilot',
22
+ 'amazon-q': 'Amazon Q',
23
+ 'replit': 'Replit',
24
+ 'antigravity': 'Antigravity',
25
+ 'kiro': 'Kiro'
26
+ };
27
+
28
+ this.ideIcons = {
29
+ 'cursor': 'šŸ–„ļø',
30
+ 'vscode': 'šŸ’»',
31
+ 'windsurf': '🌊',
32
+ 'cline': 'šŸ¤–',
33
+ 'github-copilot': 'šŸ™',
34
+ 'amazon-q': 'šŸ”·',
35
+ 'replit': 'šŸ”§',
36
+ 'antigravity': 'šŸš€',
37
+ 'kiro': '⚔'
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Format inline health counters for IDE list display
43
+ * @static
44
+ * @param {Object} metrics - Health metrics for a single IDE
45
+ * @returns {string} Formatted as "+N -M", "+N", or "-M" (hides zeros)
46
+ */
47
+ static formatInlineCounters(metrics) {
48
+ if (!metrics || (!metrics.successCount && !metrics.failureCount)) {
49
+ return '';
50
+ }
51
+
52
+ const successCount = metrics.successCount || 0;
53
+ const failureCount = metrics.failureCount || 0;
54
+
55
+ const parts = [];
56
+ if (successCount > 0) parts.push(`+${successCount}`);
57
+ if (failureCount > 0) parts.push(`-${failureCount}`);
58
+
59
+ return parts.join(' ');
60
+ }
61
+
62
+ /**
63
+ * Format health metrics for CLI display
64
+ * @param {Object} metrics - IDE health metrics (ideId -> metrics)
65
+ * @param {Date} [now] - Reference time for relative timestamps
66
+ * @returns {string} Formatted string for CLI output
67
+ */
68
+ formatForCLI(metrics, now = new Date()) {
69
+ if (!metrics || Object.keys(metrics).length === 0) {
70
+ return 'No IDE health data available';
71
+ }
72
+
73
+ let output = '\nšŸ“Š IDE Health Metrics\n';
74
+ output += '─'.repeat(50) + '\n\n';
75
+
76
+ for (const [ideId, ideMetrics] of Object.entries(metrics)) {
77
+ const ideName = this.ideNames[ideId] || ideId;
78
+ const icon = this.ideIcons[ideId] || 'šŸ”§';
79
+
80
+ output += `${icon} ${ideName}\n`;
81
+ output += ` Success Rate: ${this._formatSuccessRate(ideMetrics.successRate)}\n`;
82
+ output += ` Interactions: ${ideMetrics.totalInteractions || 0}\n`;
83
+ output += ` Avg Response: ${this.formatResponseTime(ideMetrics.averageResponseTime)}\n`;
84
+ output += ` Health: ${this._formatHealthStatus(ideMetrics)}\n`;
85
+
86
+ if (ideMetrics.lastSuccess) {
87
+ output += ` Last Success: ${this.formatTimestamp(ideMetrics.lastSuccess, now)}\n`;
88
+ }
89
+
90
+ if (ideMetrics.lastFailure) {
91
+ output += ` Last Failure: ${this.formatTimestamp(ideMetrics.lastFailure, now)}\n`;
92
+ }
93
+
94
+ output += '\n';
95
+ }
96
+
97
+ return output;
98
+ }
99
+
100
+ /**
101
+ * Format health metrics for Electron UI
102
+ * @param {Object} metrics - IDE health metrics (ideId -> metrics)
103
+ * @param {Date} [now] - Reference time for relative timestamps
104
+ * @returns {Object} Formatted data for Electron UI
105
+ */
106
+ formatForElectron(metrics, now = new Date()) {
107
+ const formatted = {};
108
+
109
+ for (const [ideId, ideMetrics] of Object.entries(metrics)) {
110
+ const ideName = this.ideNames[ideId] || ideId;
111
+ const icon = this.ideIcons[ideId] || 'šŸ”§';
112
+
113
+ formatted[ideId] = {
114
+ id: ideId,
115
+ name: ideName,
116
+ icon,
117
+ successCount: ideMetrics.successCount || 0,
118
+ failureCount: ideMetrics.failureCount || 0,
119
+ successRate: this._formatSuccessRate(ideMetrics.successRate),
120
+ averageResponseTime: this.formatResponseTime(ideMetrics.averageResponseTime),
121
+ currentTimeout: this.formatResponseTime(ideMetrics.currentTimeout),
122
+ status: this.getHealthStatus(ideMetrics),
123
+ lastSuccess: this.formatTimestamp(ideMetrics.lastSuccess, now),
124
+ lastFailure: this.formatTimestamp(ideMetrics.lastFailure, now),
125
+ totalInteractions: ideMetrics.totalInteractions || 0,
126
+ recentInteractions: this._formatRecentInteractions(ideMetrics.recentInteractions || [])
127
+ };
128
+ }
129
+
130
+ return formatted;
131
+ }
132
+
133
+ /**
134
+ * Generate overall health summary
135
+ * @param {Object} metrics - IDE health metrics (ideId -> metrics)
136
+ * @returns {Object} Summary statistics
137
+ */
138
+ generateSummary(metrics) {
139
+ if (!metrics || Object.keys(metrics).length === 0) {
140
+ return {
141
+ totalIDEs: 0,
142
+ healthyIDEs: 0,
143
+ problematicIDEs: 0,
144
+ totalInteractions: 0,
145
+ overallSuccessRate: 0,
146
+ averageResponseTime: 0,
147
+ recommendedIDE: null
148
+ };
149
+ }
150
+
151
+ const ideEntries = Object.entries(metrics);
152
+ const totalIDEs = ideEntries.length;
153
+ let healthyIDEs = 0;
154
+ let problematicIDEs = 0;
155
+ let totalInteractions = 0;
156
+ let totalSuccesses = 0;
157
+ let totalFailures = 0;
158
+ let weightedResponseTime = 0;
159
+ let totalResponseTimeWeight = 0;
160
+
161
+ // Find recommended IDE
162
+ let recommendedIDE = null;
163
+ let bestSuccessRate = 0;
164
+ let minInteractionsForRecommendation = 10;
165
+
166
+ for (const [ideId, ideMetrics] of ideEntries) {
167
+ const status = this.getHealthStatus(ideMetrics);
168
+
169
+ if (status === 'healthy') {
170
+ healthyIDEs++;
171
+ } else if (status === 'warning' || status === 'critical') {
172
+ problematicIDEs++;
173
+ }
174
+
175
+ totalInteractions += ideMetrics.totalInteractions || 0;
176
+ totalSuccesses += ideMetrics.successCount || 0;
177
+ totalFailures += ideMetrics.failureCount || 0;
178
+
179
+ // Calculate weighted average response time
180
+ if (ideMetrics.averageResponseTime > 0 && (ideMetrics.totalInteractions || 0) > 0) {
181
+ weightedResponseTime += ideMetrics.averageResponseTime * (ideMetrics.totalInteractions || 0);
182
+ totalResponseTimeWeight += (ideMetrics.totalInteractions || 0);
183
+ }
184
+
185
+ // Find recommended IDE (highest success rate with sufficient data)
186
+ if ((ideMetrics.totalInteractions || 0) >= minInteractionsForRecommendation) {
187
+ if (ideMetrics.successRate > bestSuccessRate) {
188
+ bestSuccessRate = ideMetrics.successRate;
189
+ recommendedIDE = ideId;
190
+ }
191
+ }
192
+ }
193
+
194
+ const overallSuccessRate = totalSuccesses + totalFailures > 0
195
+ ? totalSuccesses / (totalSuccesses + totalFailures)
196
+ : 0;
197
+
198
+ const averageResponseTime = totalResponseTimeWeight > 0
199
+ ? weightedResponseTime / totalResponseTimeWeight
200
+ : 0;
201
+
202
+ return {
203
+ totalIDEs,
204
+ healthyIDEs,
205
+ problematicIDEs,
206
+ totalInteractions,
207
+ overallSuccessRate: this._formatSuccessRate(overallSuccessRate),
208
+ averageResponseTime,
209
+ recommendedIDE
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Format response time in human-readable format
215
+ * @param {number|null} responseTime - Response time in milliseconds
216
+ * @returns {string} Formatted response time
217
+ */
218
+ formatResponseTime(responseTime) {
219
+ if (responseTime === null || responseTime === undefined || responseTime === 0) {
220
+ return responseTime === 0 ? '0ms' : 'N/A';
221
+ }
222
+
223
+ if (responseTime < 1000) {
224
+ return `${Math.round(responseTime)}ms`;
225
+ } else if (responseTime < 60000) {
226
+ return `${(responseTime / 1000).toFixed(1)}s`;
227
+ } else if (responseTime < 3600000) {
228
+ return `${(responseTime / 60000).toFixed(1)}m`;
229
+ } else {
230
+ return `${(responseTime / 3600000).toFixed(1)}h`;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Format timestamp relative to now
236
+ * @param {string|null} timestamp - ISO timestamp
237
+ * @param {Date} [now] - Reference time
238
+ * @returns {string} Relative time (e.g., "5m ago", "2h ago")
239
+ */
240
+ formatTimestamp(timestamp, now = new Date()) {
241
+ if (!timestamp) {
242
+ return 'Never';
243
+ }
244
+
245
+ const then = new Date(timestamp);
246
+ const diff = now - then; // Difference in milliseconds
247
+
248
+ if (diff < 60000) { // Less than 1 minute
249
+ return 'just now';
250
+ } else if (diff < 3600000) { // Less than 1 hour
251
+ return `${Math.floor(diff / 60000)}m ago`;
252
+ } else if (diff < 86400000) { // Less than 1 day
253
+ return `${Math.floor(diff / 3600000)}h ago`;
254
+ } else {
255
+ return `${Math.floor(diff / 86400000)}d ago`;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Get health status for an IDE
261
+ * @param {Object} metrics - IDE health metrics
262
+ * @returns {string} Health status ('healthy', 'warning', 'critical', 'no-data')
263
+ */
264
+ getHealthStatus(metrics) {
265
+ if (!metrics.totalInteractions || metrics.totalInteractions === 0) {
266
+ return 'no-data';
267
+ }
268
+
269
+ if (metrics.consecutiveFailures === 0) {
270
+ return 'healthy';
271
+ } else if (metrics.consecutiveFailures <= 3) {
272
+ return 'warning';
273
+ } else {
274
+ return 'critical';
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Format success rate as percentage
280
+ * @private
281
+ * @param {number} successRate - Success rate (0-1)
282
+ * @returns {string} Formatted percentage
283
+ */
284
+ _formatSuccessRate(successRate) {
285
+ if (successRate === null || successRate === undefined || successRate === 0) {
286
+ return '0%';
287
+ }
288
+ return `${(successRate * 100).toFixed(1)}%`;
289
+ }
290
+
291
+ /**
292
+ * Format health status with emoji
293
+ * @private
294
+ * @param {Object} metrics - IDE health metrics
295
+ * @returns {string} Formatted status with emoji
296
+ */
297
+ _formatHealthStatus(metrics) {
298
+ const status = this.getHealthStatus(metrics);
299
+
300
+ switch (status) {
301
+ case 'healthy':
302
+ return 'āœ… Healthy';
303
+ case 'warning':
304
+ return `āš ļø ${metrics.consecutiveFailures} consecutive failures`;
305
+ case 'critical':
306
+ return `🚨 ${metrics.consecutiveFailures} consecutive failures`;
307
+ case 'no-data':
308
+ return 'šŸ“Š No data';
309
+ default:
310
+ return 'ā“ Unknown';
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Format recent interactions for display
316
+ * @private
317
+ * @param {Array} interactions - Recent interaction array
318
+ * @returns {Array} Formatted interactions
319
+ */
320
+ _formatRecentInteractions(interactions) {
321
+ return interactions.slice(-5).map(interaction => ({
322
+ timestamp: interaction.timestamp,
323
+ outcome: interaction.outcome,
324
+ displayTime: this.formatResponseTime(interaction.responseTime),
325
+ requirementId: interaction.requirementId,
326
+ errorMessage: interaction.errorMessage
327
+ }));
328
+ }
329
+ }
330
+
331
+ module.exports = { HealthReporter };