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.
- package/ERROR_REPORTING_API.md +212 -0
- package/ERROR_REPORTING_USAGE.md +380 -0
- package/__tests__/provider-manager-fallback.test.js +43 -0
- package/__tests__/provider-manager-rate-limit.test.js +61 -0
- package/__tests__/utils/git-branch-manager.test.js +61 -0
- package/package.json +1 -1
- package/src/beta-request.js +160 -0
- package/src/compliance/compliance-manager.js +5 -2
- package/src/database/migrations.js +135 -12
- package/src/database/user-database-client.js +127 -8
- package/src/database/user-schema.js +28 -0
- package/src/health-tracking/__tests__/ide-health-tracker.test.js +420 -0
- package/src/health-tracking/__tests__/interaction-recorder.test.js +392 -0
- package/src/health-tracking/errors.js +50 -0
- package/src/health-tracking/health-reporter.js +331 -0
- package/src/health-tracking/ide-health-tracker.js +446 -0
- package/src/health-tracking/interaction-recorder.js +161 -0
- package/src/health-tracking/json-storage.js +276 -0
- package/src/health-tracking/storage-interface.js +63 -0
- package/src/health-tracking/validators.js +277 -0
- package/src/ide-integration/applescript-manager.cjs +1087 -9
- package/src/ide-integration/applescript-manager.js +565 -15
- package/src/ide-integration/applescript-utils.js +26 -18
- package/src/ide-integration/provider-manager.cjs +158 -28
- package/src/ide-integration/quota-detector.cjs +339 -16
- package/src/ide-integration/quota-detector.js +6 -1
- package/src/index.cjs +36 -1
- package/src/index.js +20 -0
- package/src/localization/translations/en.js +15 -1
- package/src/localization/translations/es.js +14 -0
- package/src/requirement-numbering.js +164 -0
- package/src/sync/aws-setup.js +4 -4
- package/src/utils/admin-utils.js +33 -0
- package/src/utils/error-reporter.js +117 -0
- package/src/utils/git-branch-manager.js +278 -0
- package/src/utils/requirement-helpers.js +44 -5
- package/src/utils/requirements-parser.js +28 -3
- package/tests/health-tracking/health-reporter.test.js +329 -0
- package/tests/health-tracking/ide-health-tracker.test.js +368 -0
- 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 };
|