codebakers 2.5.4 → 3.0.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.
- package/README.md +54 -255
- package/dist/chunk-HOWR3YTF.js +146 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +10488 -7993
- package/dist/terminal-6ZQVP6R7.js +10 -0
- package/package.json +26 -41
- package/AUDIT_REPORT.md +0 -138
- package/dist/advisors-RWRTSJRR.js +0 -7
- package/dist/chunk-ASIJIQYC.js +0 -320
- package/dist/chunk-D44U3IEA.js +0 -565
- package/dist/chunk-LANM5XQW.js +0 -326
- package/dist/prd-RYITSL6Q.js +0 -7
- package/install.bat +0 -9
- package/installers/CodeBakers-Install.bat +0 -207
- package/installers/CodeBakers-Install.command +0 -232
- package/installers/README.md +0 -157
- package/installers/mac/assets/README.txt +0 -31
- package/installers/mac/build-mac-installer.sh +0 -240
- package/installers/windows/CodeBakers.iss +0 -256
- package/installers/windows/assets/README.txt +0 -16
- package/installers/windows/scripts/post-install.bat +0 -15
- package/src/channels/discord.ts +0 -5
- package/src/channels/slack.ts +0 -5
- package/src/channels/sms.ts +0 -4
- package/src/channels/telegram.ts +0 -5
- package/src/channels/whatsapp.ts +0 -7
- package/src/commands/advisors.ts +0 -699
- package/src/commands/build.ts +0 -1025
- package/src/commands/check.ts +0 -365
- package/src/commands/code.ts +0 -806
- package/src/commands/connect.ts +0 -12
- package/src/commands/deploy.ts +0 -448
- package/src/commands/design.ts +0 -298
- package/src/commands/fix.ts +0 -20
- package/src/commands/gateway.ts +0 -604
- package/src/commands/generate.ts +0 -178
- package/src/commands/init.ts +0 -634
- package/src/commands/integrate.ts +0 -884
- package/src/commands/learn.ts +0 -36
- package/src/commands/migrate.ts +0 -419
- package/src/commands/prd-maker.ts +0 -588
- package/src/commands/prd.ts +0 -419
- package/src/commands/security.ts +0 -102
- package/src/commands/setup.ts +0 -600
- package/src/commands/status.ts +0 -56
- package/src/commands/website.ts +0 -741
- package/src/index.ts +0 -627
- package/src/patterns/loader.ts +0 -337
- package/src/services/github.ts +0 -61
- package/src/services/supabase.ts +0 -147
- package/src/services/vercel.ts +0 -61
- package/src/utils/claude-md.ts +0 -287
- package/src/utils/config.ts +0 -375
- package/src/utils/display.ts +0 -338
- package/src/utils/files.ts +0 -418
- package/src/utils/nlp.ts +0 -312
- package/src/utils/ui.ts +0 -441
- package/src/utils/updates.ts +0 -8
- package/src/utils/voice.ts +0 -323
- package/tsconfig.json +0 -26
package/src/commands/website.ts
DELETED
|
@@ -1,741 +0,0 @@
|
|
|
1
|
-
import * as p from '@clack/prompts';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
6
|
-
import { execa } from 'execa';
|
|
7
|
-
import { Config } from '../utils/config.js';
|
|
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';
|
|
20
|
-
|
|
21
|
-
// ============================================================================
|
|
22
|
-
// WEBSITE TEMPLATES
|
|
23
|
-
// ============================================================================
|
|
24
|
-
|
|
25
|
-
interface WebsiteTemplate {
|
|
26
|
-
id: string;
|
|
27
|
-
name: string;
|
|
28
|
-
description: string;
|
|
29
|
-
icon: string;
|
|
30
|
-
sections: string[];
|
|
31
|
-
style: 'minimal' | 'bold' | 'elegant' | 'playful' | 'corporate';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const TEMPLATES: WebsiteTemplate[] = [
|
|
35
|
-
{
|
|
36
|
-
id: 'landing',
|
|
37
|
-
name: 'Landing Page',
|
|
38
|
-
description: 'Convert visitors into customers',
|
|
39
|
-
icon: '🚀',
|
|
40
|
-
sections: ['hero', 'features', 'testimonials', 'pricing', 'cta', 'footer'],
|
|
41
|
-
style: 'bold',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
id: 'saas',
|
|
45
|
-
name: 'SaaS Website',
|
|
46
|
-
description: 'Software product marketing site',
|
|
47
|
-
icon: '💻',
|
|
48
|
-
sections: ['hero', 'features', 'how-it-works', 'pricing', 'faq', 'testimonials', 'cta', 'footer'],
|
|
49
|
-
style: 'minimal',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: 'portfolio',
|
|
53
|
-
name: 'Portfolio',
|
|
54
|
-
description: 'Showcase your work',
|
|
55
|
-
icon: '🎨',
|
|
56
|
-
sections: ['hero', 'about', 'projects', 'skills', 'contact', 'footer'],
|
|
57
|
-
style: 'elegant',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: 'agency',
|
|
61
|
-
name: 'Agency Website',
|
|
62
|
-
description: 'Professional services company',
|
|
63
|
-
icon: '🏢',
|
|
64
|
-
sections: ['hero', 'services', 'portfolio', 'team', 'testimonials', 'contact', 'footer'],
|
|
65
|
-
style: 'corporate',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
id: 'startup',
|
|
69
|
-
name: 'Startup Page',
|
|
70
|
-
description: 'Early stage company',
|
|
71
|
-
icon: '⚡',
|
|
72
|
-
sections: ['hero', 'problem', 'solution', 'features', 'team', 'waitlist', 'footer'],
|
|
73
|
-
style: 'bold',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'restaurant',
|
|
77
|
-
name: 'Restaurant',
|
|
78
|
-
description: 'Food & dining establishment',
|
|
79
|
-
icon: '🍽️',
|
|
80
|
-
sections: ['hero', 'menu', 'about', 'gallery', 'reservations', 'location', 'footer'],
|
|
81
|
-
style: 'elegant',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
id: 'ecommerce',
|
|
85
|
-
name: 'E-commerce',
|
|
86
|
-
description: 'Online store',
|
|
87
|
-
icon: '🛒',
|
|
88
|
-
sections: ['hero', 'featured-products', 'categories', 'bestsellers', 'newsletter', 'footer'],
|
|
89
|
-
style: 'minimal',
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
id: 'blog',
|
|
93
|
-
name: 'Blog',
|
|
94
|
-
description: 'Content & articles',
|
|
95
|
-
icon: '📝',
|
|
96
|
-
sections: ['hero', 'featured-posts', 'categories', 'newsletter', 'about', 'footer'],
|
|
97
|
-
style: 'minimal',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
id: 'event',
|
|
101
|
-
name: 'Event Page',
|
|
102
|
-
description: 'Conference or meetup',
|
|
103
|
-
icon: '🎉',
|
|
104
|
-
sections: ['hero', 'speakers', 'schedule', 'venue', 'sponsors', 'tickets', 'footer'],
|
|
105
|
-
style: 'bold',
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
id: 'nonprofit',
|
|
109
|
-
name: 'Nonprofit',
|
|
110
|
-
description: 'Charity or cause',
|
|
111
|
-
icon: '💚',
|
|
112
|
-
sections: ['hero', 'mission', 'impact', 'programs', 'donate', 'volunteer', 'footer'],
|
|
113
|
-
style: 'elegant',
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
id: 'personal',
|
|
117
|
-
name: 'Personal Website',
|
|
118
|
-
description: 'Your online presence',
|
|
119
|
-
icon: '👤',
|
|
120
|
-
sections: ['hero', 'about', 'experience', 'projects', 'blog', 'contact', 'footer'],
|
|
121
|
-
style: 'minimal',
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
id: 'coming-soon',
|
|
125
|
-
name: 'Coming Soon',
|
|
126
|
-
description: 'Pre-launch teaser',
|
|
127
|
-
icon: '⏳',
|
|
128
|
-
sections: ['hero', 'countdown', 'features-preview', 'waitlist', 'footer'],
|
|
129
|
-
style: 'bold',
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
id: 'custom',
|
|
133
|
-
name: 'Custom',
|
|
134
|
-
description: 'Build from scratch with AI',
|
|
135
|
-
icon: '✨',
|
|
136
|
-
sections: [],
|
|
137
|
-
style: 'minimal',
|
|
138
|
-
},
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
// ============================================================================
|
|
142
|
-
// MAIN COMMAND
|
|
143
|
-
// ============================================================================
|
|
144
|
-
|
|
145
|
-
export async function websiteCommand(): Promise<void> {
|
|
146
|
-
const config = new Config();
|
|
147
|
-
|
|
148
|
-
if (!config.isConfigured()) {
|
|
149
|
-
showErrorScreen({
|
|
150
|
-
title: 'CodeBakers not configured',
|
|
151
|
-
message: 'Run setup first to connect your API keys.',
|
|
152
|
-
command: 'codebakers setup',
|
|
153
|
-
});
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const anthropicCreds = config.getCredentials('anthropic');
|
|
158
|
-
if (!anthropicCreds?.apiKey) {
|
|
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
|
-
});
|
|
168
|
-
return;
|
|
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
|
-
]);
|
|
180
|
-
|
|
181
|
-
// Step 1: Choose approach
|
|
182
|
-
const approach = await p.select({
|
|
183
|
-
message: 'How would you like to build your website?',
|
|
184
|
-
options: [
|
|
185
|
-
{ value: 'describe', label: '💬 Describe it', hint: 'Tell me what you want in plain English' },
|
|
186
|
-
{ value: 'template', label: '📋 Start from template', hint: 'Choose a pre-made structure' },
|
|
187
|
-
{ value: 'url', label: '🔗 Clone a design', hint: 'Describe a site you like' },
|
|
188
|
-
],
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
if (p.isCancel(approach)) return;
|
|
192
|
-
|
|
193
|
-
const anthropic = new Anthropic({ apiKey: anthropicCreds.apiKey });
|
|
194
|
-
let websiteSpec: WebsiteSpec;
|
|
195
|
-
|
|
196
|
-
if (approach === 'describe') {
|
|
197
|
-
websiteSpec = await describeWebsite(anthropic);
|
|
198
|
-
} else if (approach === 'template') {
|
|
199
|
-
websiteSpec = await templateWebsite(anthropic);
|
|
200
|
-
} else {
|
|
201
|
-
websiteSpec = await cloneDesign(anthropic);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (!websiteSpec) return;
|
|
205
|
-
|
|
206
|
-
// Step 2: Confirm spec
|
|
207
|
-
console.log(chalk.cyan(`\n 📋 Website Plan:\n`));
|
|
208
|
-
console.log(chalk.bold(` ${websiteSpec.name}`));
|
|
209
|
-
console.log(chalk.dim(` ${websiteSpec.description}\n`));
|
|
210
|
-
console.log(chalk.dim(` Style: ${websiteSpec.style}`));
|
|
211
|
-
console.log(chalk.dim(` Sections: ${websiteSpec.sections.join(', ')}\n`));
|
|
212
|
-
|
|
213
|
-
const confirm = await p.confirm({
|
|
214
|
-
message: 'Build this website?',
|
|
215
|
-
initialValue: true,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
if (!confirm || p.isCancel(confirm)) return;
|
|
219
|
-
|
|
220
|
-
// Step 3: Build it
|
|
221
|
-
await buildWebsite(anthropic, websiteSpec, config);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ============================================================================
|
|
225
|
-
// TYPES
|
|
226
|
-
// ============================================================================
|
|
227
|
-
|
|
228
|
-
interface WebsiteSpec {
|
|
229
|
-
name: string;
|
|
230
|
-
description: string;
|
|
231
|
-
style: string;
|
|
232
|
-
colorScheme: {
|
|
233
|
-
primary: string;
|
|
234
|
-
secondary: string;
|
|
235
|
-
accent: string;
|
|
236
|
-
background: string;
|
|
237
|
-
text: string;
|
|
238
|
-
};
|
|
239
|
-
sections: string[];
|
|
240
|
-
content: Record<string, any>;
|
|
241
|
-
features: string[];
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// ============================================================================
|
|
245
|
-
// DESCRIBE WEBSITE
|
|
246
|
-
// ============================================================================
|
|
247
|
-
|
|
248
|
-
async function describeWebsite(anthropic: Anthropic): Promise<WebsiteSpec | null> {
|
|
249
|
-
console.log(chalk.dim('\n Describe your website. Be as detailed as you want.\n'));
|
|
250
|
-
console.log(chalk.dim(' Examples:'));
|
|
251
|
-
console.log(chalk.dim(' • "A landing page for my AI writing tool called WriteBot"'));
|
|
252
|
-
console.log(chalk.dim(' • "Portfolio site for a photographer, dark theme, minimal"'));
|
|
253
|
-
console.log(chalk.dim(' • "Coffee shop website with menu, location, and online ordering"\n'));
|
|
254
|
-
|
|
255
|
-
const description = await textWithVoice({
|
|
256
|
-
message: 'Describe your website:',
|
|
257
|
-
placeholder: 'A landing page for...',
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
if (p.isCancel(description)) return null;
|
|
261
|
-
|
|
262
|
-
const spinner = p.spinner();
|
|
263
|
-
spinner.start('Understanding your vision...');
|
|
264
|
-
|
|
265
|
-
const response = await anthropic.messages.create({
|
|
266
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
267
|
-
max_tokens: 2048,
|
|
268
|
-
messages: [{
|
|
269
|
-
role: 'user',
|
|
270
|
-
content: `Based on this website description, create a detailed specification.
|
|
271
|
-
|
|
272
|
-
Description: "${description}"
|
|
273
|
-
|
|
274
|
-
Return JSON only:
|
|
275
|
-
{
|
|
276
|
-
"name": "Project name (kebab-case)",
|
|
277
|
-
"description": "One line description",
|
|
278
|
-
"style": "minimal | bold | elegant | playful | corporate",
|
|
279
|
-
"colorScheme": {
|
|
280
|
-
"primary": "#hex",
|
|
281
|
-
"secondary": "#hex",
|
|
282
|
-
"accent": "#hex",
|
|
283
|
-
"background": "#hex",
|
|
284
|
-
"text": "#hex"
|
|
285
|
-
},
|
|
286
|
-
"sections": ["hero", "features", ...],
|
|
287
|
-
"content": {
|
|
288
|
-
"hero": {
|
|
289
|
-
"headline": "...",
|
|
290
|
-
"subheadline": "...",
|
|
291
|
-
"cta": "..."
|
|
292
|
-
},
|
|
293
|
-
"features": [
|
|
294
|
-
{ "title": "...", "description": "...", "icon": "..." }
|
|
295
|
-
]
|
|
296
|
-
// etc for each section
|
|
297
|
-
},
|
|
298
|
-
"features": ["responsive", "dark-mode", "animations", etc]
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
Make the content compelling and professional. Use appropriate sections for the type of website.`
|
|
302
|
-
}],
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
spinner.stop('Got it!');
|
|
306
|
-
|
|
307
|
-
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
308
|
-
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
309
|
-
|
|
310
|
-
if (!jsonMatch) {
|
|
311
|
-
p.log.error('Failed to understand website description');
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return JSON.parse(jsonMatch[0]);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// ============================================================================
|
|
319
|
-
// TEMPLATE WEBSITE
|
|
320
|
-
// ============================================================================
|
|
321
|
-
|
|
322
|
-
async function templateWebsite(anthropic: Anthropic): Promise<WebsiteSpec | null> {
|
|
323
|
-
const template = await p.select({
|
|
324
|
-
message: 'Choose a template:',
|
|
325
|
-
options: TEMPLATES.map(t => ({
|
|
326
|
-
value: t.id,
|
|
327
|
-
label: `${t.icon} ${t.name}`,
|
|
328
|
-
hint: t.description,
|
|
329
|
-
})),
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (p.isCancel(template)) return null;
|
|
333
|
-
|
|
334
|
-
const selectedTemplate = TEMPLATES.find(t => t.id === template)!;
|
|
335
|
-
|
|
336
|
-
// Get customization details
|
|
337
|
-
const name = await p.text({
|
|
338
|
-
message: 'What is your business/project name?',
|
|
339
|
-
placeholder: 'My Awesome Project',
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
if (p.isCancel(name)) return null;
|
|
343
|
-
|
|
344
|
-
const tagline = await p.text({
|
|
345
|
-
message: 'Tagline or one-line description:',
|
|
346
|
-
placeholder: 'The best solution for...',
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
if (p.isCancel(tagline)) return null;
|
|
350
|
-
|
|
351
|
-
const details = await textWithVoice({
|
|
352
|
-
message: 'Any other details? (or press enter to skip)',
|
|
353
|
-
placeholder: 'We help startups with..., Our colors are blue and white...',
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
if (p.isCancel(details)) return null;
|
|
357
|
-
|
|
358
|
-
const spinner = p.spinner();
|
|
359
|
-
spinner.start('Customizing template...');
|
|
360
|
-
|
|
361
|
-
const response = await anthropic.messages.create({
|
|
362
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
363
|
-
max_tokens: 2048,
|
|
364
|
-
messages: [{
|
|
365
|
-
role: 'user',
|
|
366
|
-
content: `Create a website spec based on this template and customization.
|
|
367
|
-
|
|
368
|
-
Template: ${selectedTemplate.name}
|
|
369
|
-
Sections: ${selectedTemplate.sections.join(', ')}
|
|
370
|
-
Default Style: ${selectedTemplate.style}
|
|
371
|
-
|
|
372
|
-
Business Name: ${name}
|
|
373
|
-
Tagline: ${tagline}
|
|
374
|
-
Additional Details: ${details || 'None provided'}
|
|
375
|
-
|
|
376
|
-
Return JSON only:
|
|
377
|
-
{
|
|
378
|
-
"name": "project-name-kebab",
|
|
379
|
-
"description": "${tagline}",
|
|
380
|
-
"style": "${selectedTemplate.style}",
|
|
381
|
-
"colorScheme": {
|
|
382
|
-
"primary": "#hex",
|
|
383
|
-
"secondary": "#hex",
|
|
384
|
-
"accent": "#hex",
|
|
385
|
-
"background": "#hex",
|
|
386
|
-
"text": "#hex"
|
|
387
|
-
},
|
|
388
|
-
"sections": ${JSON.stringify(selectedTemplate.sections)},
|
|
389
|
-
"content": {
|
|
390
|
-
// Content for each section, tailored to the business
|
|
391
|
-
},
|
|
392
|
-
"features": ["responsive", "dark-mode", etc]
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
Make the content specific and compelling for this business.`
|
|
396
|
-
}],
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
spinner.stop('Template customized!');
|
|
400
|
-
|
|
401
|
-
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
402
|
-
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
403
|
-
|
|
404
|
-
if (!jsonMatch) {
|
|
405
|
-
p.log.error('Failed to customize template');
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return JSON.parse(jsonMatch[0]);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// ============================================================================
|
|
413
|
-
// CLONE DESIGN
|
|
414
|
-
// ============================================================================
|
|
415
|
-
|
|
416
|
-
async function cloneDesign(anthropic: Anthropic): Promise<WebsiteSpec | null> {
|
|
417
|
-
console.log(chalk.dim('\n Describe a website design you like.\n'));
|
|
418
|
-
console.log(chalk.dim(' Examples:'));
|
|
419
|
-
console.log(chalk.dim(' • "Like Linear.app - minimal, clean, dark mode"'));
|
|
420
|
-
console.log(chalk.dim(' • "Like Stripe - professional, lots of gradients"'));
|
|
421
|
-
console.log(chalk.dim(' • "Like Notion - simple, friendly, illustrated"\n'));
|
|
422
|
-
|
|
423
|
-
const inspiration = await textWithVoice({
|
|
424
|
-
message: 'What site do you want to be inspired by?',
|
|
425
|
-
placeholder: 'Like Linear.app but for...',
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
if (p.isCancel(inspiration)) return null;
|
|
429
|
-
|
|
430
|
-
const purpose = await p.text({
|
|
431
|
-
message: 'What is YOUR website for?',
|
|
432
|
-
placeholder: 'A project management tool for designers',
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
if (p.isCancel(purpose)) return null;
|
|
436
|
-
|
|
437
|
-
const spinner = p.spinner();
|
|
438
|
-
spinner.start('Analyzing design inspiration...');
|
|
439
|
-
|
|
440
|
-
const response = await anthropic.messages.create({
|
|
441
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
442
|
-
max_tokens: 2048,
|
|
443
|
-
messages: [{
|
|
444
|
-
role: 'user',
|
|
445
|
-
content: `Create a website spec inspired by another site's design.
|
|
446
|
-
|
|
447
|
-
Inspiration: "${inspiration}"
|
|
448
|
-
Purpose: "${purpose}"
|
|
449
|
-
|
|
450
|
-
Analyze the design style of the inspiration (based on your knowledge of popular websites) and create a spec that captures that aesthetic for this new purpose.
|
|
451
|
-
|
|
452
|
-
Return JSON only:
|
|
453
|
-
{
|
|
454
|
-
"name": "project-name-kebab",
|
|
455
|
-
"description": "One line description",
|
|
456
|
-
"style": "minimal | bold | elegant | playful | corporate",
|
|
457
|
-
"colorScheme": {
|
|
458
|
-
"primary": "#hex - inspired by the reference",
|
|
459
|
-
"secondary": "#hex",
|
|
460
|
-
"accent": "#hex",
|
|
461
|
-
"background": "#hex",
|
|
462
|
-
"text": "#hex"
|
|
463
|
-
},
|
|
464
|
-
"sections": ["appropriate sections for the purpose"],
|
|
465
|
-
"content": {
|
|
466
|
-
// Compelling content for each section
|
|
467
|
-
},
|
|
468
|
-
"features": ["responsive", "dark-mode", "animations", "gradients", etc - based on inspiration]
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
Capture the FEEL of the inspiration but make it original.`
|
|
472
|
-
}],
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
spinner.stop('Design analyzed!');
|
|
476
|
-
|
|
477
|
-
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
478
|
-
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
479
|
-
|
|
480
|
-
if (!jsonMatch) {
|
|
481
|
-
p.log.error('Failed to analyze design');
|
|
482
|
-
return null;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
return JSON.parse(jsonMatch[0]);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// ============================================================================
|
|
489
|
-
// BUILD WEBSITE
|
|
490
|
-
// ============================================================================
|
|
491
|
-
|
|
492
|
-
async function buildWebsite(
|
|
493
|
-
anthropic: Anthropic,
|
|
494
|
-
spec: WebsiteSpec,
|
|
495
|
-
config: Config
|
|
496
|
-
): Promise<void> {
|
|
497
|
-
const projectPath = path.join(process.cwd(), spec.name);
|
|
498
|
-
|
|
499
|
-
// Check if exists
|
|
500
|
-
if (await fs.pathExists(projectPath)) {
|
|
501
|
-
const overwrite = await p.confirm({
|
|
502
|
-
message: `${spec.name} already exists. Overwrite?`,
|
|
503
|
-
initialValue: false,
|
|
504
|
-
});
|
|
505
|
-
if (!overwrite || p.isCancel(overwrite)) return;
|
|
506
|
-
await fs.remove(projectPath);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
console.log(chalk.cyan(`\n 🏗️ Building ${chalk.bold(spec.name)}\n`));
|
|
510
|
-
|
|
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[] = [];
|
|
522
|
-
|
|
523
|
-
// Step 1: Create project
|
|
524
|
-
steps.start();
|
|
525
|
-
|
|
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();
|
|
574
|
-
|
|
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
|
-
}
|
|
589
|
-
|
|
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
|
-
});
|
|
598
|
-
|
|
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
|
-
}
|
|
607
|
-
|
|
608
|
-
// Step 4: Generate pages
|
|
609
|
-
steps.start();
|
|
610
|
-
|
|
611
|
-
const response = await anthropic.messages.create({
|
|
612
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
613
|
-
max_tokens: 16000,
|
|
614
|
-
messages: [{
|
|
615
|
-
role: 'user',
|
|
616
|
-
content: `Generate a complete Next.js website based on this specification.
|
|
617
|
-
|
|
618
|
-
Website Spec:
|
|
619
|
-
${JSON.stringify(spec, null, 2)}
|
|
620
|
-
|
|
621
|
-
Generate these files:
|
|
622
|
-
|
|
623
|
-
1. src/app/page.tsx - Main landing page with ALL sections
|
|
624
|
-
2. src/app/layout.tsx - Root layout with fonts, metadata
|
|
625
|
-
3. src/app/globals.css - Tailwind config with custom colors
|
|
626
|
-
4. src/components/sections/Hero.tsx
|
|
627
|
-
5. src/components/sections/[OtherSections].tsx - One for each section
|
|
628
|
-
6. src/components/ui/Navbar.tsx
|
|
629
|
-
7. src/components/ui/Footer.tsx
|
|
630
|
-
8. tailwind.config.ts - With custom colors from colorScheme
|
|
631
|
-
|
|
632
|
-
Requirements:
|
|
633
|
-
- Use TypeScript
|
|
634
|
-
- Use Tailwind CSS for styling
|
|
635
|
-
- Use shadcn/ui components (Button, Card, Input, Badge)
|
|
636
|
-
- Make it responsive (mobile-first)
|
|
637
|
-
- Add smooth scroll behavior
|
|
638
|
-
- Use modern design patterns
|
|
639
|
-
- Include hover effects and transitions
|
|
640
|
-
- Use Lucide icons where appropriate
|
|
641
|
-
|
|
642
|
-
Color scheme to use:
|
|
643
|
-
- Primary: ${spec.colorScheme.primary}
|
|
644
|
-
- Secondary: ${spec.colorScheme.secondary}
|
|
645
|
-
- Accent: ${spec.colorScheme.accent}
|
|
646
|
-
- Background: ${spec.colorScheme.background}
|
|
647
|
-
- Text: ${spec.colorScheme.text}
|
|
648
|
-
|
|
649
|
-
Style: ${spec.style}
|
|
650
|
-
${spec.style === 'minimal' ? 'Clean, lots of whitespace, simple' : ''}
|
|
651
|
-
${spec.style === 'bold' ? 'Strong colors, big typography, impactful' : ''}
|
|
652
|
-
${spec.style === 'elegant' ? 'Refined, sophisticated, subtle animations' : ''}
|
|
653
|
-
${spec.style === 'playful' ? 'Fun, colorful, friendly illustrations' : ''}
|
|
654
|
-
${spec.style === 'corporate' ? 'Professional, trustworthy, structured' : ''}
|
|
655
|
-
|
|
656
|
-
Output format:
|
|
657
|
-
<<<FILE: path/to/file.tsx>>>
|
|
658
|
-
content
|
|
659
|
-
<<<END_FILE>>>
|
|
660
|
-
|
|
661
|
-
Make it production-quality and visually impressive.`
|
|
662
|
-
}],
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
666
|
-
|
|
667
|
-
// Write files
|
|
668
|
-
const fileRegex = /<<<FILE:\s*(.+?)>>>([\s\S]*?)<<<END_FILE>>>/g;
|
|
669
|
-
let match;
|
|
670
|
-
|
|
671
|
-
while ((match = fileRegex.exec(text)) !== null) {
|
|
672
|
-
const filePath = path.join(projectPath, match[1].trim());
|
|
673
|
-
const content = match[2].trim();
|
|
674
|
-
|
|
675
|
-
await fs.ensureDir(path.dirname(filePath));
|
|
676
|
-
await fs.writeFile(filePath, content);
|
|
677
|
-
createdFiles.push(match[1].trim());
|
|
678
|
-
}
|
|
679
|
-
|
|
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
|
-
});
|
|
722
|
-
|
|
723
|
-
// Offer to open in browser
|
|
724
|
-
const openDev = await p.confirm({
|
|
725
|
-
message: 'Start development server now?',
|
|
726
|
-
initialValue: true,
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
if (openDev && !p.isCancel(openDev)) {
|
|
730
|
-
console.log(colors.muted('\n Starting dev server...\n'));
|
|
731
|
-
|
|
732
|
-
// Change to project directory and run dev
|
|
733
|
-
process.chdir(projectPath);
|
|
734
|
-
await execa('npm', ['run', 'dev'], {
|
|
735
|
-
stdio: 'inherit',
|
|
736
|
-
reject: false,
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
export { TEMPLATES };
|