@nclamvn/vibecode-cli 2.0.0 → 2.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 (53) hide show
  1. package/.vibecode/learning/fixes.json +1 -0
  2. package/.vibecode/learning/preferences.json +1 -0
  3. package/README.md +310 -49
  4. package/SESSION_NOTES.md +154 -0
  5. package/bin/vibecode.js +235 -2
  6. package/package.json +5 -2
  7. package/src/agent/decomposition.js +476 -0
  8. package/src/agent/index.js +391 -0
  9. package/src/agent/memory.js +542 -0
  10. package/src/agent/orchestrator.js +917 -0
  11. package/src/agent/self-healing.js +516 -0
  12. package/src/commands/agent.js +349 -0
  13. package/src/commands/ask.js +230 -0
  14. package/src/commands/assist.js +413 -0
  15. package/src/commands/build.js +345 -4
  16. package/src/commands/debug.js +565 -0
  17. package/src/commands/docs.js +167 -0
  18. package/src/commands/git.js +1024 -0
  19. package/src/commands/go.js +635 -0
  20. package/src/commands/learn.js +294 -0
  21. package/src/commands/migrate.js +341 -0
  22. package/src/commands/plan.js +8 -2
  23. package/src/commands/refactor.js +205 -0
  24. package/src/commands/review.js +126 -1
  25. package/src/commands/security.js +229 -0
  26. package/src/commands/shell.js +486 -0
  27. package/src/commands/templates.js +397 -0
  28. package/src/commands/test.js +194 -0
  29. package/src/commands/undo.js +281 -0
  30. package/src/commands/watch.js +556 -0
  31. package/src/commands/wizard.js +322 -0
  32. package/src/config/constants.js +5 -1
  33. package/src/config/templates.js +146 -15
  34. package/src/core/backup.js +325 -0
  35. package/src/core/error-analyzer.js +237 -0
  36. package/src/core/fix-generator.js +195 -0
  37. package/src/core/iteration.js +226 -0
  38. package/src/core/learning.js +295 -0
  39. package/src/core/session.js +18 -2
  40. package/src/core/test-runner.js +281 -0
  41. package/src/debug/analyzer.js +329 -0
  42. package/src/debug/evidence.js +228 -0
  43. package/src/debug/fixer.js +348 -0
  44. package/src/debug/image-analyzer.js +304 -0
  45. package/src/debug/index.js +378 -0
  46. package/src/debug/verifier.js +346 -0
  47. package/src/index.js +102 -0
  48. package/src/providers/claude-code.js +12 -7
  49. package/src/templates/index.js +724 -0
  50. package/src/ui/__tests__/error-translator.test.js +390 -0
  51. package/src/ui/dashboard.js +364 -0
  52. package/src/ui/error-translator.js +775 -0
  53. package/src/utils/image.js +222 -0
@@ -0,0 +1,364 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Progress Dashboard
3
+ // Phase H2: Real-time Visual Progress with ETA
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import chalk from 'chalk';
7
+ import readline from 'readline';
8
+
9
+ /**
10
+ * ProgressDashboard - Full-screen progress display for multi-module builds
11
+ *
12
+ * Usage:
13
+ * const dashboard = new ProgressDashboard({
14
+ * title: 'VIBECODE AGENT',
15
+ * projectName: 'my-app',
16
+ * mode: 'Agent (8 modules)'
17
+ * });
18
+ * dashboard.setModules(['core', 'auth', 'dashboard']);
19
+ * dashboard.start();
20
+ * dashboard.startModule('core');
21
+ * dashboard.completeModule('core');
22
+ * dashboard.stop();
23
+ */
24
+ export class ProgressDashboard {
25
+ constructor(options = {}) {
26
+ this.title = options.title || 'VIBECODE';
27
+ this.projectName = options.projectName || 'project';
28
+ this.mode = options.mode || 'build';
29
+ this.modules = [];
30
+ this.startTime = Date.now();
31
+ this.currentModule = null;
32
+ this.completedModules = [];
33
+ this.moduleTimes = {};
34
+ this.isRendering = false;
35
+ this.renderInterval = null;
36
+ this.logs = [];
37
+ this.maxLogs = options.maxLogs || 3;
38
+ }
39
+
40
+ /**
41
+ * Start the dashboard rendering
42
+ */
43
+ start() {
44
+ this.isRendering = true;
45
+ this.startTime = Date.now();
46
+
47
+ // Hide cursor
48
+ process.stdout.write('\x1B[?25l');
49
+
50
+ // Clear screen and render
51
+ this.render();
52
+
53
+ // Update every 500ms for elapsed time
54
+ this.renderInterval = setInterval(() => {
55
+ if (this.isRendering) {
56
+ this.render();
57
+ }
58
+ }, 500);
59
+ }
60
+
61
+ /**
62
+ * Stop the dashboard rendering
63
+ */
64
+ stop() {
65
+ this.isRendering = false;
66
+
67
+ if (this.renderInterval) {
68
+ clearInterval(this.renderInterval);
69
+ this.renderInterval = null;
70
+ }
71
+
72
+ // Show cursor
73
+ process.stdout.write('\x1B[?25h');
74
+
75
+ // Final render
76
+ this.render();
77
+ console.log('\n');
78
+ }
79
+
80
+ /**
81
+ * Set modules to track
82
+ */
83
+ setModules(modules) {
84
+ this.modules = modules.map(m => ({
85
+ name: typeof m === 'string' ? m : m.name,
86
+ status: 'pending',
87
+ time: null,
88
+ startTime: null
89
+ }));
90
+ }
91
+
92
+ /**
93
+ * Mark module as started
94
+ */
95
+ startModule(moduleName) {
96
+ this.currentModule = moduleName;
97
+ const module = this.modules.find(m => m.name === moduleName);
98
+ if (module) {
99
+ module.status = 'building';
100
+ module.startTime = Date.now();
101
+ }
102
+ this.addLog(`Building: ${moduleName}`);
103
+ this.render();
104
+ }
105
+
106
+ /**
107
+ * Mark module as completed
108
+ */
109
+ completeModule(moduleName, success = true) {
110
+ const module = this.modules.find(m => m.name === moduleName);
111
+ if (module) {
112
+ module.status = success ? 'done' : 'failed';
113
+ module.time = Date.now() - (module.startTime || this.startTime);
114
+ this.completedModules.push(moduleName);
115
+ this.addLog(success ? `✓ ${moduleName} complete` : `✗ ${moduleName} failed`);
116
+ }
117
+ this.currentModule = null;
118
+ this.render();
119
+ }
120
+
121
+ /**
122
+ * Mark module as failed
123
+ */
124
+ failModule(moduleName) {
125
+ this.completeModule(moduleName, false);
126
+ }
127
+
128
+ /**
129
+ * Add a log message
130
+ */
131
+ addLog(message) {
132
+ this.logs.push({
133
+ time: Date.now(),
134
+ message
135
+ });
136
+ // Keep only last N logs
137
+ if (this.logs.length > this.maxLogs) {
138
+ this.logs.shift();
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get progress percentage
144
+ */
145
+ getProgress() {
146
+ const total = this.modules.length;
147
+ const done = this.completedModules.length;
148
+ return total > 0 ? Math.round((done / total) * 100) : 0;
149
+ }
150
+
151
+ /**
152
+ * Calculate ETA based on average module time
153
+ */
154
+ getETA() {
155
+ const done = this.completedModules.length;
156
+ const remaining = this.modules.length - done;
157
+
158
+ if (done === 0 || remaining === 0) return null;
159
+
160
+ const elapsed = Date.now() - this.startTime;
161
+ const avgPerModule = elapsed / done;
162
+ const etaMs = avgPerModule * remaining;
163
+
164
+ return this.formatTime(etaMs);
165
+ }
166
+
167
+ /**
168
+ * Format milliseconds to human readable time
169
+ */
170
+ formatTime(ms) {
171
+ const seconds = Math.floor(ms / 1000);
172
+ const minutes = Math.floor(seconds / 60);
173
+ const secs = seconds % 60;
174
+
175
+ if (minutes > 0) {
176
+ return `${minutes}m ${secs}s`;
177
+ }
178
+ return `${secs}s`;
179
+ }
180
+
181
+ /**
182
+ * Format elapsed time
183
+ */
184
+ formatElapsed() {
185
+ return this.formatTime(Date.now() - this.startTime);
186
+ }
187
+
188
+ /**
189
+ * Render progress bar
190
+ */
191
+ renderProgressBar(percent, width = 40) {
192
+ const filled = Math.round(width * percent / 100);
193
+ const empty = width - filled;
194
+ return chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
195
+ }
196
+
197
+ /**
198
+ * Render module status line
199
+ */
200
+ renderModuleStatus(module) {
201
+ const icons = {
202
+ pending: chalk.gray('○'),
203
+ building: chalk.yellow('◐'),
204
+ done: chalk.green('✓'),
205
+ failed: chalk.red('✗')
206
+ };
207
+
208
+ const icon = icons[module.status] || icons.pending;
209
+ const name = module.status === 'building'
210
+ ? chalk.yellow(module.name)
211
+ : module.status === 'done'
212
+ ? chalk.green(module.name)
213
+ : module.status === 'failed'
214
+ ? chalk.red(module.name)
215
+ : chalk.gray(module.name);
216
+
217
+ let timeStr = '';
218
+ if (module.time) {
219
+ timeStr = chalk.gray(this.formatTime(module.time));
220
+ } else if (module.status === 'building') {
221
+ timeStr = chalk.yellow('building...');
222
+ }
223
+
224
+ // Pad name to 16 chars, time to 12 chars
225
+ const paddedName = name + ' '.repeat(Math.max(0, 16 - module.name.length));
226
+ return ` ${icon} ${paddedName} ${timeStr}`;
227
+ }
228
+
229
+ /**
230
+ * Main render function
231
+ */
232
+ render() {
233
+ if (!this.isRendering && this.completedModules.length === 0) return;
234
+
235
+ // Move cursor to top-left
236
+ readline.cursorTo(process.stdout, 0, 0);
237
+ readline.clearScreenDown(process.stdout);
238
+
239
+ const progress = this.getProgress();
240
+ const eta = this.getETA();
241
+ const elapsed = this.formatElapsed();
242
+
243
+ // Build output
244
+ const lines = [];
245
+
246
+ // Header
247
+ lines.push(chalk.cyan('╭────────────────────────────────────────────────────────────────────╮'));
248
+ lines.push(chalk.cyan('│') + ` 🏗️ ${chalk.bold(this.title)}`.padEnd(76) + chalk.cyan('│'));
249
+ lines.push(chalk.cyan('│') + ''.padEnd(68) + chalk.cyan('│'));
250
+
251
+ // Project info
252
+ lines.push(chalk.cyan('│') + ` Project: ${chalk.white(this.projectName)}`.padEnd(76) + chalk.cyan('│'));
253
+ lines.push(chalk.cyan('│') + ` Mode: ${chalk.white(this.mode)}`.padEnd(76) + chalk.cyan('│'));
254
+ lines.push(chalk.cyan('│') + ''.padEnd(68) + chalk.cyan('│'));
255
+
256
+ // Progress bar
257
+ const progressBar = ` [${this.renderProgressBar(progress)}] ${String(progress).padStart(3)}%`;
258
+ lines.push(chalk.cyan('│') + progressBar.padEnd(76) + chalk.cyan('│'));
259
+ lines.push(chalk.cyan('│') + ''.padEnd(68) + chalk.cyan('│'));
260
+
261
+ // Module list (max 10 visible)
262
+ const visibleModules = this.modules.slice(0, 10);
263
+ for (const module of visibleModules) {
264
+ const statusLine = this.renderModuleStatus(module);
265
+ lines.push(chalk.cyan('│') + statusLine.padEnd(76) + chalk.cyan('│'));
266
+ }
267
+
268
+ if (this.modules.length > 10) {
269
+ lines.push(chalk.cyan('│') + chalk.gray(` ... and ${this.modules.length - 10} more`).padEnd(76) + chalk.cyan('│'));
270
+ }
271
+
272
+ lines.push(chalk.cyan('│') + ''.padEnd(68) + chalk.cyan('│'));
273
+
274
+ // Time info
275
+ const etaText = eta ? `~${eta} remaining` : 'calculating...';
276
+ const timeInfo = ` Elapsed: ${chalk.white(elapsed.padEnd(10))} │ ETA: ${chalk.white(etaText)}`;
277
+ lines.push(chalk.cyan('│') + timeInfo.padEnd(76) + chalk.cyan('│'));
278
+ lines.push(chalk.cyan('│') + ''.padEnd(68) + chalk.cyan('│'));
279
+
280
+ // Footer
281
+ lines.push(chalk.cyan('╰────────────────────────────────────────────────────────────────────╯'));
282
+
283
+ // Recent logs
284
+ if (this.logs.length > 0) {
285
+ lines.push('');
286
+ lines.push(chalk.gray(' Recent:'));
287
+ for (const log of this.logs) {
288
+ lines.push(chalk.gray(` ${log.message}`));
289
+ }
290
+ }
291
+
292
+ process.stdout.write(lines.join('\n'));
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Simpler inline progress bar for non-dashboard use
298
+ */
299
+ export function renderInlineProgress(current, total, label = '') {
300
+ const percent = Math.round((current / total) * 100);
301
+ const width = 30;
302
+ const filled = Math.round(width * percent / 100);
303
+ const empty = width - filled;
304
+ const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
305
+
306
+ return `[${bar}] ${percent}% ${label}`;
307
+ }
308
+
309
+ /**
310
+ * Quick progress update (single line, overwrites previous)
311
+ */
312
+ export function updateProgress(current, total, label = '') {
313
+ readline.clearLine(process.stdout, 0);
314
+ readline.cursorTo(process.stdout, 0);
315
+ process.stdout.write(renderInlineProgress(current, total, label));
316
+ }
317
+
318
+ /**
319
+ * Complete progress and move to next line
320
+ */
321
+ export function completeProgress(label = 'Done') {
322
+ readline.clearLine(process.stdout, 0);
323
+ readline.cursorTo(process.stdout, 0);
324
+ console.log(chalk.green(`✓ ${label}`));
325
+ }
326
+
327
+ /**
328
+ * Simple step-by-step progress tracker
329
+ */
330
+ export class StepProgress {
331
+ constructor(steps, options = {}) {
332
+ this.steps = steps;
333
+ this.currentStep = 0;
334
+ this.startTime = Date.now();
335
+ this.showTime = options.showTime !== false;
336
+ }
337
+
338
+ next(label) {
339
+ this.currentStep++;
340
+ const progress = renderInlineProgress(this.currentStep, this.steps.length, label || this.steps[this.currentStep - 1]);
341
+
342
+ readline.clearLine(process.stdout, 0);
343
+ readline.cursorTo(process.stdout, 0);
344
+ process.stdout.write(progress);
345
+ }
346
+
347
+ complete(label = 'Complete') {
348
+ const elapsed = Date.now() - this.startTime;
349
+ readline.clearLine(process.stdout, 0);
350
+ readline.cursorTo(process.stdout, 0);
351
+
352
+ if (this.showTime) {
353
+ console.log(chalk.green(`✓ ${label} (${Math.round(elapsed / 1000)}s)`));
354
+ } else {
355
+ console.log(chalk.green(`✓ ${label}`));
356
+ }
357
+ }
358
+
359
+ fail(label = 'Failed') {
360
+ readline.clearLine(process.stdout, 0);
361
+ readline.cursorTo(process.stdout, 0);
362
+ console.log(chalk.red(`✗ ${label}`));
363
+ }
364
+ }