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.
- package/AUDIT_REPORT.md +138 -0
- package/dist/{advisors-GGUCFS4E.js → advisors-RWRTSJRR.js} +1 -1
- package/dist/{chunk-YUSDTJD6.js → chunk-D44U3IEA.js} +3 -3
- package/dist/{chunk-ND6T4UDY.js → chunk-LANM5XQW.js} +4 -4
- package/dist/index.js +813 -318
- package/dist/{prd-AIEY63YY.js → prd-RYITSL6Q.js} +1 -1
- package/package.json +1 -1
- package/src/commands/advisors.ts +3 -3
- package/src/commands/build.ts +13 -8
- package/src/commands/check.ts +1 -1
- package/src/commands/code.ts +3 -3
- package/src/commands/deploy.ts +5 -5
- package/src/commands/design.ts +1 -1
- package/src/commands/generate.ts +1 -1
- package/src/commands/init.ts +90 -40
- package/src/commands/integrate.ts +1 -1
- package/src/commands/migrate.ts +1 -1
- package/src/commands/prd-maker.ts +4 -3
- package/src/commands/prd.ts +4 -4
- package/src/commands/security.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/website.ts +176 -93
- package/src/index.ts +54 -42
- package/src/utils/display.ts +338 -0
- package/src/utils/files.ts +1 -1
- package/src/utils/ui.ts +441 -0
- package/src/utils/voice.ts +1 -1
package/src/commands/website.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import Anthropic from '@anthropic-ai/sdk';
|
|
6
6
|
import { execa } from 'execa';
|
|
7
7
|
import { Config } from '../utils/config.js';
|
|
8
8
|
import { textWithVoice } from '../utils/voice.js';
|
|
9
|
+
import { StepTracker, showError, showSuccess, showFileList, ERRORS } from '../utils/display.js';
|
|
10
|
+
import {
|
|
11
|
+
showMiniLogo,
|
|
12
|
+
box,
|
|
13
|
+
doubleBox,
|
|
14
|
+
sectionHeader,
|
|
15
|
+
showSuccessScreen,
|
|
16
|
+
showErrorScreen,
|
|
17
|
+
showFileTree,
|
|
18
|
+
colors
|
|
19
|
+
} from '../utils/ui.js';
|
|
9
20
|
|
|
10
21
|
// ============================================================================
|
|
11
22
|
// WEBSITE TEMPLATES
|
|
@@ -135,39 +146,37 @@ export async function websiteCommand(): Promise<void> {
|
|
|
135
146
|
const config = new Config();
|
|
136
147
|
|
|
137
148
|
if (!config.isConfigured()) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
`));
|
|
149
|
+
showErrorScreen({
|
|
150
|
+
title: 'CodeBakers not configured',
|
|
151
|
+
message: 'Run setup first to connect your API keys.',
|
|
152
|
+
command: 'codebakers setup',
|
|
153
|
+
});
|
|
144
154
|
return;
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
const anthropicCreds = config.getCredentials('anthropic');
|
|
148
158
|
if (!anthropicCreds?.apiKey) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
${chalk.dim('https://console.anthropic.com/settings/keys')}
|
|
159
|
-
`));
|
|
159
|
+
showErrorScreen({
|
|
160
|
+
title: 'Anthropic API key not configured',
|
|
161
|
+
message: 'The website builder needs Claude AI to generate code.',
|
|
162
|
+
fixes: [
|
|
163
|
+
'Run: codebakers setup',
|
|
164
|
+
'Get an API key at: console.anthropic.com',
|
|
165
|
+
],
|
|
166
|
+
command: 'codebakers setup',
|
|
167
|
+
});
|
|
160
168
|
return;
|
|
161
169
|
}
|
|
162
170
|
|
|
163
|
-
console.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
console.clear();
|
|
172
|
+
showMiniLogo();
|
|
173
|
+
|
|
174
|
+
doubleBox([
|
|
175
|
+
colors.primary('🌐 WEBSITE BUILDER'),
|
|
176
|
+
'',
|
|
177
|
+
colors.white('Describe your website in plain English.'),
|
|
178
|
+
colors.muted('AI builds it in minutes.'),
|
|
179
|
+
]);
|
|
171
180
|
|
|
172
181
|
// Step 1: Choose approach
|
|
173
182
|
const approach = await p.select({
|
|
@@ -254,7 +263,7 @@ async function describeWebsite(anthropic: Anthropic): Promise<WebsiteSpec | null
|
|
|
254
263
|
spinner.start('Understanding your vision...');
|
|
255
264
|
|
|
256
265
|
const response = await anthropic.messages.create({
|
|
257
|
-
model: 'claude-sonnet-4-
|
|
266
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
258
267
|
max_tokens: 2048,
|
|
259
268
|
messages: [{
|
|
260
269
|
role: 'user',
|
|
@@ -350,7 +359,7 @@ async function templateWebsite(anthropic: Anthropic): Promise<WebsiteSpec | null
|
|
|
350
359
|
spinner.start('Customizing template...');
|
|
351
360
|
|
|
352
361
|
const response = await anthropic.messages.create({
|
|
353
|
-
model: 'claude-sonnet-4-
|
|
362
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
354
363
|
max_tokens: 2048,
|
|
355
364
|
messages: [{
|
|
356
365
|
role: 'user',
|
|
@@ -429,7 +438,7 @@ async function cloneDesign(anthropic: Anthropic): Promise<WebsiteSpec | null> {
|
|
|
429
438
|
spinner.start('Analyzing design inspiration...');
|
|
430
439
|
|
|
431
440
|
const response = await anthropic.messages.create({
|
|
432
|
-
model: 'claude-sonnet-4-
|
|
441
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
433
442
|
max_tokens: 2048,
|
|
434
443
|
messages: [{
|
|
435
444
|
role: 'user',
|
|
@@ -497,48 +506,110 @@ async function buildWebsite(
|
|
|
497
506
|
await fs.remove(projectPath);
|
|
498
507
|
}
|
|
499
508
|
|
|
500
|
-
console.log(chalk.cyan(`\n 🏗️ Building ${spec.name}
|
|
509
|
+
console.log(chalk.cyan(`\n 🏗️ Building ${chalk.bold(spec.name)}\n`));
|
|
501
510
|
|
|
502
|
-
|
|
511
|
+
// Initialize step tracker
|
|
512
|
+
const steps = new StepTracker([
|
|
513
|
+
'Create project structure',
|
|
514
|
+
'Install dependencies',
|
|
515
|
+
'Set up UI components',
|
|
516
|
+
'Generate website code',
|
|
517
|
+
'Create additional pages',
|
|
518
|
+
'Initialize git repository',
|
|
519
|
+
]);
|
|
520
|
+
|
|
521
|
+
const createdFiles: string[] = [];
|
|
503
522
|
|
|
504
523
|
// Step 1: Create project
|
|
505
|
-
|
|
524
|
+
steps.start();
|
|
506
525
|
|
|
507
|
-
|
|
508
|
-
'
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
526
|
+
try {
|
|
527
|
+
await execa('npx', [
|
|
528
|
+
'create-next-app@latest',
|
|
529
|
+
spec.name,
|
|
530
|
+
'--typescript',
|
|
531
|
+
'--tailwind',
|
|
532
|
+
'--eslint',
|
|
533
|
+
'--app',
|
|
534
|
+
'--src-dir',
|
|
535
|
+
'--import-alias', '@/*',
|
|
536
|
+
'--no-git',
|
|
537
|
+
'--yes',
|
|
538
|
+
], {
|
|
539
|
+
cwd: process.cwd(),
|
|
540
|
+
stdio: 'pipe', // Hide output for cleaner display
|
|
541
|
+
});
|
|
542
|
+
steps.complete();
|
|
543
|
+
} catch (error) {
|
|
544
|
+
steps.error();
|
|
545
|
+
showError({
|
|
546
|
+
title: 'Project creation failed',
|
|
547
|
+
message: 'create-next-app could not complete',
|
|
548
|
+
fixes: [
|
|
549
|
+
'Make sure Node.js 18+ is installed',
|
|
550
|
+
'Check your internet connection',
|
|
551
|
+
'Try running manually: npx create-next-app@latest ' + spec.name,
|
|
552
|
+
],
|
|
553
|
+
command: 'npx create-next-app@latest ' + spec.name,
|
|
554
|
+
});
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Verify project was created
|
|
559
|
+
if (!(await fs.pathExists(projectPath))) {
|
|
560
|
+
steps.error();
|
|
561
|
+
showError({
|
|
562
|
+
title: 'Project folder not created',
|
|
563
|
+
message: 'The project directory was not found after creation',
|
|
564
|
+
fixes: [
|
|
565
|
+
'Check if you have write permissions in this folder',
|
|
566
|
+
'Try creating the project in a different location',
|
|
567
|
+
],
|
|
568
|
+
});
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Step 2: Install dependencies
|
|
573
|
+
steps.start();
|
|
523
574
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
575
|
+
try {
|
|
576
|
+
await execa('npm', ['install', 'lucide-react'], {
|
|
577
|
+
cwd: projectPath,
|
|
578
|
+
stdio: 'pipe',
|
|
579
|
+
});
|
|
580
|
+
steps.complete();
|
|
581
|
+
} catch (error) {
|
|
582
|
+
steps.error();
|
|
583
|
+
showError({
|
|
584
|
+
...ERRORS.npmFailed,
|
|
585
|
+
command: `cd ${spec.name} && npm install`,
|
|
586
|
+
});
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
528
589
|
|
|
529
|
-
// Install
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
590
|
+
// Step 3: Install shadcn
|
|
591
|
+
steps.start();
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
await execa('npx', ['shadcn@latest', 'init', '-y', '-d'], {
|
|
595
|
+
cwd: projectPath,
|
|
596
|
+
stdio: 'pipe',
|
|
597
|
+
});
|
|
534
598
|
|
|
535
|
-
|
|
599
|
+
await execa('npx', ['shadcn@latest', 'add', 'button', 'card', 'input', 'badge', '-y'], {
|
|
600
|
+
cwd: projectPath,
|
|
601
|
+
stdio: 'pipe',
|
|
602
|
+
});
|
|
603
|
+
steps.complete();
|
|
604
|
+
} catch (error) {
|
|
605
|
+
steps.skip(); // Not critical, continue anyway
|
|
606
|
+
}
|
|
536
607
|
|
|
537
|
-
// Step
|
|
538
|
-
|
|
608
|
+
// Step 4: Generate pages
|
|
609
|
+
steps.start();
|
|
539
610
|
|
|
540
611
|
const response = await anthropic.messages.create({
|
|
541
|
-
model: 'claude-sonnet-4-
|
|
612
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
542
613
|
max_tokens: 16000,
|
|
543
614
|
messages: [{
|
|
544
615
|
role: 'user',
|
|
@@ -596,7 +667,6 @@ Make it production-quality and visually impressive.`
|
|
|
596
667
|
// Write files
|
|
597
668
|
const fileRegex = /<<<FILE:\s*(.+?)>>>([\s\S]*?)<<<END_FILE>>>/g;
|
|
598
669
|
let match;
|
|
599
|
-
let fileCount = 0;
|
|
600
670
|
|
|
601
671
|
while ((match = fileRegex.exec(text)) !== null) {
|
|
602
672
|
const filePath = path.join(projectPath, match[1].trim());
|
|
@@ -604,38 +674,51 @@ Make it production-quality and visually impressive.`
|
|
|
604
674
|
|
|
605
675
|
await fs.ensureDir(path.dirname(filePath));
|
|
606
676
|
await fs.writeFile(filePath, content);
|
|
607
|
-
|
|
677
|
+
createdFiles.push(match[1].trim());
|
|
608
678
|
}
|
|
609
679
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
// Step
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
680
|
+
steps.complete();
|
|
681
|
+
|
|
682
|
+
// Step 5: Additional pages (placeholder for now)
|
|
683
|
+
steps.start();
|
|
684
|
+
steps.complete();
|
|
685
|
+
|
|
686
|
+
// Step 6: Git init
|
|
687
|
+
steps.start();
|
|
688
|
+
|
|
689
|
+
try {
|
|
690
|
+
await execa('git', ['init'], { cwd: projectPath, stdio: 'pipe' });
|
|
691
|
+
await execa('git', ['add', '.'], { cwd: projectPath, stdio: 'pipe' });
|
|
692
|
+
await execa('git', ['commit', '-m', 'Initial website build by CodeBakers'], { cwd: projectPath, stdio: 'pipe' });
|
|
693
|
+
steps.complete();
|
|
694
|
+
} catch (error) {
|
|
695
|
+
steps.skip();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Show created files
|
|
699
|
+
if (createdFiles.length > 0) {
|
|
700
|
+
showFileTree('Files created', createdFiles.slice(0, 10));
|
|
701
|
+
if (createdFiles.length > 10) {
|
|
702
|
+
console.log(colors.muted(` ... and ${createdFiles.length - 10} more files`));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Success message with beautiful screen
|
|
707
|
+
showSuccessScreen({
|
|
708
|
+
title: 'Website built successfully!',
|
|
709
|
+
message: spec.description,
|
|
710
|
+
stats: [
|
|
711
|
+
{ label: 'Project', value: spec.name },
|
|
712
|
+
{ label: 'Files', value: createdFiles.length.toString() + ' created' },
|
|
713
|
+
{ label: 'Sections', value: spec.sections.length.toString() },
|
|
714
|
+
{ label: 'Time', value: steps.getElapsedTime() },
|
|
715
|
+
],
|
|
716
|
+
nextSteps: [
|
|
717
|
+
`cd ${spec.name}`,
|
|
718
|
+
'npm run dev',
|
|
719
|
+
],
|
|
720
|
+
command: `cd ${spec.name} && npm run dev`,
|
|
721
|
+
});
|
|
639
722
|
|
|
640
723
|
// Offer to open in browser
|
|
641
724
|
const openDev = await p.confirm({
|
|
@@ -644,7 +727,7 @@ Make it production-quality and visually impressive.`
|
|
|
644
727
|
});
|
|
645
728
|
|
|
646
729
|
if (openDev && !p.isCancel(openDev)) {
|
|
647
|
-
console.log(
|
|
730
|
+
console.log(colors.muted('\n Starting dev server...\n'));
|
|
648
731
|
|
|
649
732
|
// Change to project directory and run dev
|
|
650
733
|
process.chdir(projectPath);
|
package/src/index.ts
CHANGED
|
@@ -29,18 +29,21 @@ import { buildCommand } from './commands/build.js';
|
|
|
29
29
|
import { integrateCommand, INTEGRATIONS } from './commands/integrate.js';
|
|
30
30
|
import { websiteCommand } from './commands/website.js';
|
|
31
31
|
import { parseNaturalLanguage } from './utils/nlp.js';
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
import {
|
|
33
|
+
showLogo,
|
|
34
|
+
showMiniLogo,
|
|
35
|
+
showMenuCards,
|
|
36
|
+
showSuccessScreen,
|
|
37
|
+
showErrorScreen,
|
|
38
|
+
box,
|
|
39
|
+
doubleBox,
|
|
40
|
+
divider,
|
|
41
|
+
sectionHeader,
|
|
42
|
+
colors,
|
|
43
|
+
headerGradient
|
|
44
|
+
} from './utils/ui.js';
|
|
45
|
+
|
|
46
|
+
const VERSION = '2.5.3';
|
|
44
47
|
|
|
45
48
|
// ============================================================================
|
|
46
49
|
// MAIN MENU
|
|
@@ -49,21 +52,23 @@ const logo = `
|
|
|
49
52
|
async function showMainMenu(): Promise<void> {
|
|
50
53
|
const config = new Config();
|
|
51
54
|
|
|
52
|
-
//
|
|
53
|
-
console.
|
|
54
|
-
|
|
55
|
+
// Clear screen and show beautiful logo
|
|
56
|
+
console.clear();
|
|
57
|
+
showLogo();
|
|
58
|
+
console.log(colors.muted(` v${VERSION}`));
|
|
59
|
+
console.log('');
|
|
55
60
|
|
|
56
61
|
// Check setup status
|
|
57
62
|
const hasAnthropic = !!config.getCredentials('anthropic')?.apiKey;
|
|
58
63
|
|
|
59
64
|
// STEP 1: First time? Run setup
|
|
60
65
|
if (!hasAnthropic) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
66
|
+
doubleBox([
|
|
67
|
+
colors.warning('👋 Welcome to CodeBakers!'),
|
|
68
|
+
'',
|
|
69
|
+
colors.white("Let's connect your Anthropic API key so the AI can work."),
|
|
70
|
+
colors.muted('(Takes about 1 minute)'),
|
|
71
|
+
], 'default');
|
|
67
72
|
|
|
68
73
|
await setupCommand();
|
|
69
74
|
|
|
@@ -81,14 +86,18 @@ async function showMainMenu(): Promise<void> {
|
|
|
81
86
|
// Show context
|
|
82
87
|
if (inProject && projectConfig) {
|
|
83
88
|
const framework = projectConfig.framework || 'detected';
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
box([
|
|
90
|
+
colors.secondary('📁 ') + colors.white(folderName),
|
|
91
|
+
colors.muted(` ${framework} project`),
|
|
92
|
+
]);
|
|
86
93
|
|
|
87
94
|
// SHOW PROJECT MENU
|
|
88
95
|
await showProjectMenu(config);
|
|
89
96
|
} else {
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
box([
|
|
98
|
+
colors.secondary('📁 ') + colors.white(cwd),
|
|
99
|
+
colors.muted(' Not a project folder'),
|
|
100
|
+
]);
|
|
92
101
|
|
|
93
102
|
// SHOW START MENU
|
|
94
103
|
await showStartMenu(config);
|
|
@@ -100,25 +109,28 @@ async function showMainMenu(): Promise<void> {
|
|
|
100
109
|
// ============================================================================
|
|
101
110
|
|
|
102
111
|
async function showStartMenu(config: Config): Promise<void> {
|
|
103
|
-
console.log(
|
|
112
|
+
console.log(colors.muted(' This folder doesn\'t have a project yet.'));
|
|
113
|
+
console.log('');
|
|
104
114
|
|
|
105
115
|
let keepRunning = true;
|
|
106
116
|
|
|
107
117
|
while (keepRunning) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
sectionHeader('What would you like to do?');
|
|
119
|
+
|
|
120
|
+
showMenuCards([
|
|
121
|
+
{ key: '1', icon: '🌐', label: 'Build a Website', description: 'Describe it, AI builds it' },
|
|
122
|
+
{ key: '2', icon: '🆕', label: 'Create New Project', description: 'Start with Next.js, React' },
|
|
123
|
+
{ key: '3', icon: '✏️', label: 'Plan My Project', description: 'Create a detailed plan' },
|
|
124
|
+
{ key: '4', icon: '🏗️', label: 'Build from Plan', description: 'I have a PRD file' },
|
|
125
|
+
{ key: '5', icon: '🌟', label: 'Get Expert Advice', description: 'AI consultants help' },
|
|
126
|
+
{ key: '6', icon: '🔌', label: 'Add a Service', description: 'Stripe, Supabase, Auth' },
|
|
127
|
+
{ key: '7', icon: '⚙️', label: 'Settings', description: 'API keys & preferences' },
|
|
128
|
+
{ key: '8', icon: '❓', label: 'Help', description: 'Learn how it works' },
|
|
129
|
+
{ key: '0', icon: '🚪', label: 'Exit', description: 'Return to terminal' },
|
|
130
|
+
]);
|
|
119
131
|
|
|
120
132
|
const choice = await p.text({
|
|
121
|
-
message: 'Enter a number (0-8):',
|
|
133
|
+
message: colors.primary('Enter a number (0-8):'),
|
|
122
134
|
placeholder: '1',
|
|
123
135
|
validate: (value) => {
|
|
124
136
|
if (!/^[0-8]$/.test(value) && value !== '') return 'Please enter a number 0-8';
|
|
@@ -172,11 +184,11 @@ async function showStartMenu(config: Config): Promise<void> {
|
|
|
172
184
|
|
|
173
185
|
function showExitMessage(): void {
|
|
174
186
|
console.log('');
|
|
175
|
-
|
|
176
|
-
console.log(
|
|
187
|
+
divider();
|
|
188
|
+
console.log(colors.white(' You\'re back in the terminal.'));
|
|
177
189
|
console.log('');
|
|
178
|
-
console.log(
|
|
179
|
-
console.log(
|
|
190
|
+
console.log(colors.muted(' To start CodeBakers again, type:'));
|
|
191
|
+
console.log(colors.success(' codebakers'));
|
|
180
192
|
console.log('');
|
|
181
193
|
console.log(chalk.dim(' Quick commands you can run directly:'));
|
|
182
194
|
console.log(chalk.dim(' codebakers website') + chalk.gray(' - Build a website'));
|