codebakers 2.3.8 → 2.5.3

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.
@@ -0,0 +1,441 @@
1
+ import chalk from 'chalk';
2
+ import gradient from 'gradient-string';
3
+
4
+ // ============================================================================
5
+ // COLOR PALETTE
6
+ // ============================================================================
7
+
8
+ const colors = {
9
+ primary: chalk.hex('#ff6b6b'), // Red (matches logo)
10
+ secondary: chalk.hex('#4ecdc4'), // Teal
11
+ accent: chalk.hex('#ffe66d'), // Yellow
12
+ success: chalk.hex('#7ee787'), // Green
13
+ error: chalk.hex('#f85149'), // Red error
14
+ warning: chalk.hex('#d29922'), // Orange
15
+ muted: chalk.hex('#6e7681'), // Gray
16
+ white: chalk.hex('#ffffff'),
17
+ dim: chalk.dim,
18
+ };
19
+
20
+ // Gradient for headers
21
+ const headerGradient = gradient(['#ff6b6b', '#ff8e53', '#ffb347']);
22
+ const successGradient = gradient(['#7ee787', '#4ecdc4']);
23
+
24
+ // ============================================================================
25
+ // ASCII ART LOGO
26
+ // ============================================================================
27
+
28
+ export function showLogo(): void {
29
+ console.log('');
30
+ console.log(headerGradient(' ╔═══════════════════════════════════════════════════════════════╗'));
31
+ console.log(headerGradient(' ║ ║'));
32
+ console.log(headerGradient(' ║') + colors.white(' 🍞 C O D E B A K E R S ') + headerGradient('║'));
33
+ console.log(headerGradient(' ║ ║'));
34
+ console.log(headerGradient(' ║') + colors.muted(' AI Dev Team That Follows The Rules ') + headerGradient('║'));
35
+ console.log(headerGradient(' ║ ║'));
36
+ console.log(headerGradient(' ╚═══════════════════════════════════════════════════════════════╝'));
37
+ console.log('');
38
+ }
39
+
40
+ export function showMiniLogo(): void {
41
+ console.log('');
42
+ console.log(headerGradient(' ╔═══════════════════════════════════════════════════════════════╗'));
43
+ console.log(headerGradient(' ║') + colors.white(' 🍞 CODEBAKERS ') + headerGradient('║'));
44
+ console.log(headerGradient(' ╚═══════════════════════════════════════════════════════════════╝'));
45
+ console.log('');
46
+ }
47
+
48
+ // ============================================================================
49
+ // BOXES AND CONTAINERS
50
+ // ============================================================================
51
+
52
+ export function box(content: string[], style: 'default' | 'success' | 'error' | 'warning' = 'default'): void {
53
+ const borderColor = {
54
+ default: colors.muted,
55
+ success: colors.success,
56
+ error: colors.error,
57
+ warning: colors.warning,
58
+ }[style];
59
+
60
+ const maxLen = Math.max(...content.map(l => stripAnsi(l).length), 50);
61
+ const width = maxLen + 4;
62
+
63
+ const top = borderColor(' ╭' + '─'.repeat(width) + '╮');
64
+ const bottom = borderColor(' ╰' + '─'.repeat(width) + '╯');
65
+
66
+ console.log(top);
67
+ content.forEach(line => {
68
+ const stripped = stripAnsi(line);
69
+ const padding = width - stripped.length - 2;
70
+ console.log(borderColor(' │') + ' ' + line + ' '.repeat(Math.max(0, padding)) + borderColor('│'));
71
+ });
72
+ console.log(bottom);
73
+ console.log('');
74
+ }
75
+
76
+ export function doubleBox(content: string[], style: 'default' | 'success' | 'error' = 'default'): void {
77
+ const borderColor = {
78
+ default: colors.primary,
79
+ success: colors.success,
80
+ error: colors.error,
81
+ }[style];
82
+
83
+ const maxLen = Math.max(...content.map(l => stripAnsi(l).length), 50);
84
+ const width = maxLen + 4;
85
+
86
+ const top = borderColor(' ╔' + '═'.repeat(width) + '╗');
87
+ const bottom = borderColor(' ╚' + '═'.repeat(width) + '╝');
88
+
89
+ console.log(top);
90
+ content.forEach(line => {
91
+ const stripped = stripAnsi(line);
92
+ const padding = width - stripped.length - 2;
93
+ console.log(borderColor(' ║') + ' ' + line + ' '.repeat(Math.max(0, padding)) + borderColor('║'));
94
+ });
95
+ console.log(bottom);
96
+ console.log('');
97
+ }
98
+
99
+ // ============================================================================
100
+ // SECTION HEADERS
101
+ // ============================================================================
102
+
103
+ export function sectionHeader(title: string): void {
104
+ const line = '━'.repeat(60);
105
+ console.log('');
106
+ console.log(colors.muted(` ${line}`));
107
+ console.log(colors.white(` ${title.toUpperCase()}`));
108
+ console.log(colors.muted(` ${line}`));
109
+ console.log('');
110
+ }
111
+
112
+ export function subHeader(title: string): void {
113
+ console.log('');
114
+ console.log(colors.white(` ${title}`));
115
+ console.log(colors.muted(` ${'─'.repeat(title.length + 4)}`));
116
+ console.log('');
117
+ }
118
+
119
+ // ============================================================================
120
+ // MENU DISPLAY
121
+ // ============================================================================
122
+
123
+ interface MenuItem {
124
+ key: string;
125
+ icon: string;
126
+ label: string;
127
+ description: string;
128
+ }
129
+
130
+ export function showMenu(items: MenuItem[]): void {
131
+ console.log('');
132
+
133
+ items.forEach((item, i) => {
134
+ const num = colors.primary(` ${item.key} `);
135
+ const icon = `${item.icon} `;
136
+ const label = colors.white(item.label.padEnd(18));
137
+ const desc = colors.muted(item.description);
138
+ console.log(`${num} ${icon} ${label} ${desc}`);
139
+ });
140
+
141
+ console.log('');
142
+ console.log(colors.muted(' ─'.repeat(30)));
143
+ console.log('');
144
+ }
145
+
146
+ export function showMenuCards(items: MenuItem[]): void {
147
+ console.log('');
148
+
149
+ // Top border
150
+ console.log(colors.muted(' ┌─────────────────────────────────────────────────────────────┐'));
151
+
152
+ items.forEach((item, i) => {
153
+ const num = colors.primary(item.key);
154
+ const icon = item.icon;
155
+ const label = colors.white(item.label.padEnd(20));
156
+ const desc = colors.muted(item.description.substring(0, 30).padEnd(30));
157
+ console.log(colors.muted(' │') + ` ${num} ${icon} ${label} ${desc}` + colors.muted('│'));
158
+ });
159
+
160
+ // Bottom border
161
+ console.log(colors.muted(' └─────────────────────────────────────────────────────────────┘'));
162
+ console.log('');
163
+ }
164
+
165
+ // ============================================================================
166
+ // PROGRESS DISPLAY
167
+ // ============================================================================
168
+
169
+ interface ProgressStep {
170
+ name: string;
171
+ status: 'pending' | 'running' | 'done' | 'error' | 'skipped';
172
+ }
173
+
174
+ export function showProgress(title: string, subtitle: string, steps: ProgressStep[], percent: number): void {
175
+ // Clear screen and redraw (for animation)
176
+ console.log('');
177
+
178
+ // Header box
179
+ box([
180
+ colors.primary('🌐 ' + title.toUpperCase()),
181
+ '',
182
+ colors.white(subtitle),
183
+ ]);
184
+
185
+ // Section header
186
+ console.log(colors.muted(' PROGRESS'));
187
+ console.log(colors.muted(' ' + '━'.repeat(58)));
188
+ console.log('');
189
+
190
+ // Steps
191
+ steps.forEach(step => {
192
+ const icon = {
193
+ pending: colors.muted('○'),
194
+ running: colors.primary('●'),
195
+ done: colors.success('✓'),
196
+ error: colors.error('✗'),
197
+ skipped: colors.warning('⊘'),
198
+ }[step.status];
199
+
200
+ const labelColor = {
201
+ pending: colors.muted,
202
+ running: colors.white,
203
+ done: colors.success,
204
+ error: colors.error,
205
+ skipped: colors.warning,
206
+ }[step.status];
207
+
208
+ const suffix = step.status === 'running' ? colors.muted(' ...') : '';
209
+ console.log(` ${icon} ${labelColor(step.name)}${suffix}`);
210
+ });
211
+
212
+ console.log('');
213
+
214
+ // Progress bar
215
+ const barWidth = 50;
216
+ const filled = Math.round((percent / 100) * barWidth);
217
+ const empty = barWidth - filled;
218
+ const bar = colors.success('█'.repeat(filled)) + colors.muted('░'.repeat(empty));
219
+ console.log(` ${bar} ${colors.white(percent + '%')}`);
220
+
221
+ console.log('');
222
+ console.log(colors.muted(' ' + '━'.repeat(58)));
223
+ console.log('');
224
+ }
225
+
226
+ // ============================================================================
227
+ // SUCCESS / ERROR DISPLAY
228
+ // ============================================================================
229
+
230
+ interface SuccessDetails {
231
+ title: string;
232
+ message?: string;
233
+ stats?: { label: string; value: string }[];
234
+ nextSteps?: string[];
235
+ command?: string;
236
+ }
237
+
238
+ export function showSuccessScreen(details: SuccessDetails): void {
239
+ console.log('');
240
+
241
+ // Success header
242
+ console.log(colors.success(' ╔═══════════════════════════════════════════════════════════════╗'));
243
+ console.log(colors.success(' ║ ║'));
244
+ console.log(colors.success(' ║') + successGradient(' ✅ SUCCESS ') + colors.success('║'));
245
+ console.log(colors.success(' ║ ║'));
246
+ console.log(colors.success(' ║') + colors.white(' ' + details.title.padEnd(60)) + colors.success('║'));
247
+
248
+ if (details.message) {
249
+ console.log(colors.success(' ║') + colors.muted(' ' + details.message.substring(0, 60).padEnd(60)) + colors.success('║'));
250
+ }
251
+
252
+ console.log(colors.success(' ║ ║'));
253
+
254
+ // Stats
255
+ if (details.stats && details.stats.length > 0) {
256
+ console.log(colors.success(' ║') + colors.muted(' ┌─────────────────────────────────────────────────────────┐ ') + colors.success('║'));
257
+ details.stats.forEach(stat => {
258
+ const line = ` │ ${stat.label.padEnd(14)} ${colors.white(stat.value.padEnd(40))}│ `;
259
+ console.log(colors.success(' ║') + colors.muted(line) + colors.success('║'));
260
+ });
261
+ console.log(colors.success(' ║') + colors.muted(' └─────────────────────────────────────────────────────────┘ ') + colors.success('║'));
262
+ console.log(colors.success(' ║ ║'));
263
+ }
264
+
265
+ // Next steps
266
+ if (details.nextSteps && details.nextSteps.length > 0) {
267
+ console.log(colors.success(' ║') + colors.white(' NEXT STEPS ') + colors.success('║'));
268
+ console.log(colors.success(' ║') + colors.muted(' ─────────────────────────────────────────────────────────') + colors.success('║'));
269
+ details.nextSteps.forEach(step => {
270
+ console.log(colors.success(' ║') + colors.secondary(' ' + step.padEnd(60)) + colors.success('║'));
271
+ });
272
+ console.log(colors.success(' ║ ║'));
273
+ }
274
+
275
+ // Quick command
276
+ if (details.command) {
277
+ console.log(colors.success(' ║') + colors.muted(' Quick start: ') + colors.white(details.command.padEnd(44)) + colors.success('║'));
278
+ console.log(colors.success(' ║ ║'));
279
+ }
280
+
281
+ console.log(colors.success(' ╚═══════════════════════════════════════════════════════════════╝'));
282
+ console.log('');
283
+ }
284
+
285
+ interface ErrorDetails {
286
+ title: string;
287
+ message: string;
288
+ fixes?: string[];
289
+ command?: string;
290
+ }
291
+
292
+ export function showErrorScreen(details: ErrorDetails): void {
293
+ console.log('');
294
+
295
+ // Error header
296
+ console.log(colors.error(' ╔═══════════════════════════════════════════════════════════════╗'));
297
+ console.log(colors.error(' ║ ║'));
298
+ console.log(colors.error(' ║ ❌ ERROR ║'));
299
+ console.log(colors.error(' ║ ║'));
300
+ console.log(colors.error(' ║') + colors.white(' ' + details.title.padEnd(60)) + colors.error('║'));
301
+ console.log(colors.error(' ║ ║'));
302
+
303
+ // Details box
304
+ console.log(colors.error(' ║') + colors.muted(' ┌─────────────────────────────────────────────────────────┐ ') + colors.error('║'));
305
+ console.log(colors.error(' ║') + colors.muted(' │') + colors.white(' WHAT HAPPENED ') + colors.muted('│ ') + colors.error('║'));
306
+
307
+ // Word wrap the message
308
+ const words = details.message.split(' ');
309
+ let line = '';
310
+ words.forEach(word => {
311
+ if ((line + ' ' + word).length > 55) {
312
+ console.log(colors.error(' ║') + colors.muted(' │') + colors.muted(' ' + line.padEnd(56)) + colors.muted('│ ') + colors.error('║'));
313
+ line = word;
314
+ } else {
315
+ line = line ? line + ' ' + word : word;
316
+ }
317
+ });
318
+ if (line) {
319
+ console.log(colors.error(' ║') + colors.muted(' │') + colors.muted(' ' + line.padEnd(56)) + colors.muted('│ ') + colors.error('║'));
320
+ }
321
+
322
+ console.log(colors.error(' ║') + colors.muted(' │ │ ') + colors.error('║'));
323
+
324
+ // Fixes
325
+ if (details.fixes && details.fixes.length > 0) {
326
+ console.log(colors.error(' ║') + colors.muted(' │') + colors.white(' POSSIBLE FIXES ') + colors.muted('│ ') + colors.error('║'));
327
+ details.fixes.forEach((fix, i) => {
328
+ const fixText = ` ${i + 1}. ${fix}`.substring(0, 56).padEnd(56);
329
+ console.log(colors.error(' ║') + colors.muted(' │') + colors.muted(fixText) + colors.muted('│ ') + colors.error('║'));
330
+ });
331
+ }
332
+
333
+ console.log(colors.error(' ║') + colors.muted(' └─────────────────────────────────────────────────────────┘ ') + colors.error('║'));
334
+
335
+ // Command suggestion
336
+ if (details.command) {
337
+ console.log(colors.error(' ║ ║'));
338
+ console.log(colors.error(' ║') + colors.muted(' Try: ') + colors.secondary(details.command.padEnd(53)) + colors.error('║'));
339
+ }
340
+
341
+ console.log(colors.error(' ║ ║'));
342
+ console.log(colors.error(' ╚═══════════════════════════════════════════════════════════════╝'));
343
+ console.log('');
344
+ }
345
+
346
+ // ============================================================================
347
+ // FILE LIST
348
+ // ============================================================================
349
+
350
+ export function showFileTree(title: string, files: string[]): void {
351
+ console.log('');
352
+ console.log(colors.white(` 📁 ${title}`));
353
+ console.log('');
354
+
355
+ files.forEach((file, i) => {
356
+ const isLast = i === files.length - 1;
357
+ const prefix = isLast ? '└──' : '├──';
358
+ const icon = getFileIcon(file);
359
+ console.log(colors.muted(` ${prefix} `) + icon + ' ' + colors.white(file));
360
+ });
361
+
362
+ console.log('');
363
+ }
364
+
365
+ function getFileIcon(filename: string): string {
366
+ const ext = filename.split('.').pop()?.toLowerCase();
367
+ const icons: Record<string, string> = {
368
+ 'tsx': '⚛️ ',
369
+ 'ts': '📘',
370
+ 'js': '📒',
371
+ 'jsx': '⚛️ ',
372
+ 'css': '🎨',
373
+ 'json': '📋',
374
+ 'md': '📝',
375
+ 'html': '🌐',
376
+ 'env': '🔐',
377
+ };
378
+ return icons[ext || ''] || '📄';
379
+ }
380
+
381
+ // ============================================================================
382
+ // STATS DISPLAY
383
+ // ============================================================================
384
+
385
+ export function showStats(stats: { label: string; value: string; icon?: string }[]): void {
386
+ console.log('');
387
+ stats.forEach(stat => {
388
+ const icon = stat.icon ? stat.icon + ' ' : '';
389
+ console.log(colors.muted(` ${icon}${stat.label}: `) + colors.white(stat.value));
390
+ });
391
+ console.log('');
392
+ }
393
+
394
+ // ============================================================================
395
+ // DIVIDERS
396
+ // ============================================================================
397
+
398
+ export function divider(): void {
399
+ console.log('');
400
+ console.log(colors.muted(' ' + '─'.repeat(60)));
401
+ console.log('');
402
+ }
403
+
404
+ export function thinDivider(): void {
405
+ console.log(colors.muted(' ' + '·'.repeat(60)));
406
+ }
407
+
408
+ // ============================================================================
409
+ // WELCOME SCREEN
410
+ // ============================================================================
411
+
412
+ export function showWelcome(version: string): void {
413
+ console.clear();
414
+ showLogo();
415
+
416
+ console.log(colors.muted(` v${version}`));
417
+ console.log('');
418
+
419
+ showMenuCards([
420
+ { key: '1', icon: '🌐', label: 'Build a Website', description: 'Create a complete site' },
421
+ { key: '2', icon: '💻', label: 'Code with AI', description: 'Add features, fix bugs' },
422
+ { key: '3', icon: '🚀', label: 'Deploy', description: 'Ship to production' },
423
+ { key: '4', icon: '📋', label: 'Create PRD', description: 'Plan your project' },
424
+ { key: '5', icon: '🏗️ ', label: 'Init Project', description: 'Start a new project' },
425
+ { key: '6', icon: '⚙️ ', label: 'Settings', description: 'Configure services' },
426
+ ]);
427
+
428
+ console.log(colors.muted(' Type a number or command, then press Enter'));
429
+ console.log('');
430
+ }
431
+
432
+ // ============================================================================
433
+ // HELPERS
434
+ // ============================================================================
435
+
436
+ function stripAnsi(str: string): string {
437
+ return str.replace(/\x1B\[[0-9;]*[mGKH]/g, '');
438
+ }
439
+
440
+ // Export colors for use elsewhere
441
+ export { colors, headerGradient, successGradient };
@@ -1,6 +1,6 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import chalk from 'chalk';
3
- import * as fs from 'fs-extra';
3
+ import fs from 'fs-extra';
4
4
  import { execa } from 'execa';
5
5
 
6
6
  let voiceAvailable: boolean | null = null;