codebakers 2.5.4 → 3.1.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 (60) hide show
  1. package/README.md +54 -255
  2. package/dist/chunk-HOWR3YTF.js +146 -0
  3. package/dist/index.d.ts +0 -3
  4. package/dist/index.js +10505 -7997
  5. package/dist/terminal-6ZQVP6R7.js +10 -0
  6. package/package.json +26 -41
  7. package/AUDIT_REPORT.md +0 -138
  8. package/dist/advisors-RWRTSJRR.js +0 -7
  9. package/dist/chunk-ASIJIQYC.js +0 -320
  10. package/dist/chunk-D44U3IEA.js +0 -565
  11. package/dist/chunk-LANM5XQW.js +0 -326
  12. package/dist/prd-RYITSL6Q.js +0 -7
  13. package/install.bat +0 -9
  14. package/installers/CodeBakers-Install.bat +0 -207
  15. package/installers/CodeBakers-Install.command +0 -232
  16. package/installers/README.md +0 -157
  17. package/installers/mac/assets/README.txt +0 -31
  18. package/installers/mac/build-mac-installer.sh +0 -240
  19. package/installers/windows/CodeBakers.iss +0 -256
  20. package/installers/windows/assets/README.txt +0 -16
  21. package/installers/windows/scripts/post-install.bat +0 -15
  22. package/src/channels/discord.ts +0 -5
  23. package/src/channels/slack.ts +0 -5
  24. package/src/channels/sms.ts +0 -4
  25. package/src/channels/telegram.ts +0 -5
  26. package/src/channels/whatsapp.ts +0 -7
  27. package/src/commands/advisors.ts +0 -699
  28. package/src/commands/build.ts +0 -1025
  29. package/src/commands/check.ts +0 -365
  30. package/src/commands/code.ts +0 -806
  31. package/src/commands/connect.ts +0 -12
  32. package/src/commands/deploy.ts +0 -448
  33. package/src/commands/design.ts +0 -298
  34. package/src/commands/fix.ts +0 -20
  35. package/src/commands/gateway.ts +0 -604
  36. package/src/commands/generate.ts +0 -178
  37. package/src/commands/init.ts +0 -634
  38. package/src/commands/integrate.ts +0 -884
  39. package/src/commands/learn.ts +0 -36
  40. package/src/commands/migrate.ts +0 -419
  41. package/src/commands/prd-maker.ts +0 -588
  42. package/src/commands/prd.ts +0 -419
  43. package/src/commands/security.ts +0 -102
  44. package/src/commands/setup.ts +0 -600
  45. package/src/commands/status.ts +0 -56
  46. package/src/commands/website.ts +0 -741
  47. package/src/index.ts +0 -627
  48. package/src/patterns/loader.ts +0 -337
  49. package/src/services/github.ts +0 -61
  50. package/src/services/supabase.ts +0 -147
  51. package/src/services/vercel.ts +0 -61
  52. package/src/utils/claude-md.ts +0 -287
  53. package/src/utils/config.ts +0 -375
  54. package/src/utils/display.ts +0 -338
  55. package/src/utils/files.ts +0 -418
  56. package/src/utils/nlp.ts +0 -312
  57. package/src/utils/ui.ts +0 -441
  58. package/src/utils/updates.ts +0 -8
  59. package/src/utils/voice.ts +0 -323
  60. package/tsconfig.json +0 -26
@@ -1,806 +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 { loadPatterns } from '../patterns/loader.js';
9
- import { runPatternCheck } from './check.js';
10
- import { textWithVoice, checkVoiceAvailability } from '../utils/voice.js';
11
- import { readClipboard, readFile, formatFilesForContext, looksLikePaste, handlePastedContent } from '../utils/files.js';
12
-
13
- interface CodeOptions {
14
- watch?: boolean;
15
- }
16
-
17
- interface Message {
18
- role: 'user' | 'assistant';
19
- content: string;
20
- }
21
-
22
- interface ProjectContext {
23
- name: string;
24
- framework: string;
25
- ui: string;
26
- packages: string[];
27
- existingFiles: string[];
28
- recentChanges: string[];
29
- }
30
-
31
- export async function codeCommand(prompt?: string, options: CodeOptions = {}): Promise<void> {
32
- const config = new Config();
33
-
34
- // Check if configured
35
- if (!config.isConfigured()) {
36
- console.log(chalk.yellow(`
37
- ⚠️ CodeBakers isn't set up yet.
38
-
39
- Run this first:
40
- ${chalk.cyan('codebakers setup')}
41
-
42
- This connects your API keys (Anthropic, GitHub, etc.)
43
- so CodeBakers can generate code and deploy for you.
44
- `));
45
- return;
46
- }
47
-
48
- // Check if in project
49
- if (!config.isInProject()) {
50
- console.log(chalk.yellow(`
51
- ⚠️ You're not in a CodeBakers project.
52
-
53
- Options:
54
- ${chalk.cyan('codebakers init')} Create a new project here
55
- ${chalk.cyan('codebakers website')} Build a website by describing it
56
- ${chalk.cyan('cd my-project')} Navigate to an existing project
57
- `));
58
- return;
59
- }
60
-
61
- // Get API key
62
- const anthropicCreds = config.getCredentials('anthropic');
63
- if (!anthropicCreds?.apiKey) {
64
- console.log(chalk.yellow(`
65
- ⚠️ Anthropic API key not configured.
66
-
67
- The AI coding agent needs Claude to work.
68
-
69
- Run this to add your API key:
70
- ${chalk.cyan('codebakers setup')}
71
-
72
- Get an API key at:
73
- ${chalk.dim('https://console.anthropic.com/settings/keys')}
74
- `));
75
- return;
76
- }
77
-
78
- // Initialize Anthropic
79
- const anthropic = new Anthropic({
80
- apiKey: anthropicCreds.apiKey,
81
- });
82
-
83
- // Load project context
84
- const projectContext = await loadProjectContext();
85
-
86
- // Load patterns
87
- const patterns = await loadPatterns(config);
88
-
89
- // Build system prompt
90
- const systemPrompt = buildSystemPrompt(projectContext, patterns);
91
-
92
- console.log(chalk.cyan(`
93
- ╭─────────────────────────────────────────────────────╮
94
- │ 🤖 CodeBakers AI │
95
- │ │
96
- │ Project: ${projectContext.name.padEnd(38)}│
97
- │ Stack: ${`${projectContext.framework} + ${projectContext.ui}`.padEnd(40)}│
98
- │ │
99
- │ Type your request or "?" for help │
100
- │ Type "exit" or Ctrl+C to quit │
101
- ╰─────────────────────────────────────────────────────╯
102
- `));
103
-
104
- // Conversation history
105
- const messages: Message[] = [];
106
-
107
- // If prompt provided via command line
108
- if (prompt) {
109
- await processUserInput(prompt, messages, anthropic, systemPrompt, projectContext, config);
110
- }
111
-
112
- // Check for voice support
113
- const hasVoice = await checkVoiceAvailability();
114
-
115
- console.log(chalk.dim(' 💡 Tips:'));
116
- if (hasVoice) {
117
- console.log(chalk.dim(' • Type "v" for voice input'));
118
- }
119
- console.log(chalk.dim(' • Type "clip" to read from clipboard'));
120
- console.log(chalk.dim(' • Drag & drop files or paste file paths'));
121
- console.log(chalk.dim(' • Paste code directly and I\'ll detect it\n'));
122
-
123
- // Interactive loop
124
- while (true) {
125
- const input = await textWithVoice({
126
- message: '',
127
- placeholder: 'What do you want to build? (or paste code/file path)',
128
- });
129
-
130
- if (p.isCancel(input)) {
131
- p.outro('Goodbye!');
132
- break;
133
- }
134
-
135
- let userInput = (input as string).trim();
136
- let fileContext = '';
137
-
138
- if (!userInput) continue;
139
-
140
- if (userInput.toLowerCase() === 'exit') {
141
- p.outro('Goodbye!');
142
- break;
143
- }
144
-
145
- if (userInput === '?') {
146
- showHelp();
147
- continue;
148
- }
149
-
150
- // Handle slash commands
151
- if (userInput.startsWith('/')) {
152
- await handleSlashCommand(userInput, projectContext, config);
153
- continue;
154
- }
155
-
156
- // Check for clipboard command
157
- if (userInput.toLowerCase() === 'clip' || userInput.toLowerCase() === 'clipboard' || userInput.toLowerCase() === 'paste') {
158
- const clipContent = await readClipboard();
159
- if (clipContent) {
160
- console.log(chalk.green(`\n📋 Read ${clipContent.length} characters from clipboard\n`));
161
-
162
- // Check if clipboard is a file path
163
- if (await fs.pathExists(clipContent.trim())) {
164
- const file = await readFile(clipContent.trim());
165
- if (file) {
166
- fileContext = formatFilesForContext([file]);
167
- console.log(chalk.green(`📄 Loaded file: ${file.name}\n`));
168
-
169
- const instruction = await p.text({
170
- message: `What should I do with ${file.name}?`,
171
- placeholder: 'Fix any issues in this code',
172
- });
173
- if (p.isCancel(instruction)) continue;
174
- userInput = instruction as string;
175
- }
176
- } else {
177
- // Clipboard contains code/text
178
- fileContext = `\n\n--- CLIPBOARD CONTENT ---\n\`\`\`\n${clipContent}\n\`\`\`\n--- END ---\n\n`;
179
-
180
- const action = await p.select({
181
- message: 'What should I do with this?',
182
- options: [
183
- { value: 'analyze', label: '🔍 Analyze this' },
184
- { value: 'fix', label: '🔧 Fix issues' },
185
- { value: 'explain', label: '📖 Explain' },
186
- { value: 'improve', label: '✨ Improve' },
187
- { value: 'custom', label: '✏️ Custom' },
188
- ],
189
- });
190
-
191
- if (p.isCancel(action)) continue;
192
-
193
- if (action === 'custom') {
194
- const custom = await p.text({ message: 'What should I do?', placeholder: 'Add TypeScript types' });
195
- if (p.isCancel(custom)) continue;
196
- userInput = custom as string;
197
- } else {
198
- const actions: Record<string, string> = {
199
- analyze: 'Analyze this code and explain what it does',
200
- fix: 'Fix any bugs or issues in this code',
201
- explain: 'Explain this code step by step',
202
- improve: 'Improve and refactor this code',
203
- };
204
- userInput = actions[action as string];
205
- }
206
- }
207
- } else {
208
- console.log(chalk.yellow('Clipboard is empty'));
209
- continue;
210
- }
211
- }
212
-
213
- // Check if input is a file path
214
- else if (await fs.pathExists(userInput)) {
215
- const file = await readFile(userInput);
216
- if (file) {
217
- fileContext = formatFilesForContext([file]);
218
- console.log(chalk.green(`\n📄 Loaded file: ${file.name}\n`));
219
-
220
- const instruction = await p.text({
221
- message: `What should I do with ${file.name}?`,
222
- placeholder: 'Analyze and improve this code',
223
- });
224
- if (p.isCancel(instruction)) continue;
225
- userInput = instruction as string;
226
- }
227
- }
228
-
229
- // Check if input looks like pasted code
230
- else if (looksLikePaste(userInput)) {
231
- const result = await handlePastedContent(userInput);
232
- if (result) {
233
- userInput = result.prompt;
234
- fileContext = result.context;
235
- }
236
- }
237
-
238
- // Process input with AI (include file context if any)
239
- const fullInput = fileContext ? userInput + fileContext : userInput;
240
- await processUserInput(fullInput, messages, anthropic, systemPrompt, projectContext, config);
241
- }
242
- }
243
-
244
- async function processUserInput(
245
- userInput: string,
246
- messages: Message[],
247
- anthropic: Anthropic,
248
- systemPrompt: string,
249
- projectContext: ProjectContext,
250
- config: Config
251
- ): Promise<void> {
252
- const spinner = p.spinner();
253
-
254
- // Add user message
255
- messages.push({ role: 'user', content: userInput });
256
-
257
- // Check for wizard triggers
258
- const wizardResult = await checkForWizard(userInput);
259
- if (wizardResult) {
260
- // If wizard ran, use its enhanced prompt
261
- messages[messages.length - 1].content = wizardResult;
262
- }
263
-
264
- try {
265
- spinner.start('Thinking...');
266
-
267
- // Call Claude
268
- const response = await anthropic.messages.create({
269
- model: 'claude-sonnet-4-5-20250929',
270
- max_tokens: 8192,
271
- system: systemPrompt,
272
- messages: messages.map(m => ({
273
- role: m.role,
274
- content: m.content,
275
- })),
276
- });
277
-
278
- spinner.stop('');
279
-
280
- // Extract response
281
- const assistantMessage = response.content[0].type === 'text'
282
- ? response.content[0].text
283
- : '';
284
-
285
- // Add to history
286
- messages.push({ role: 'assistant', content: assistantMessage });
287
-
288
- // Parse and execute any code actions
289
- const actions = parseActions(assistantMessage);
290
-
291
- if (actions.length > 0) {
292
- // Show plan
293
- console.log(chalk.cyan('\n📋 Plan:'));
294
- actions.forEach((action, i) => {
295
- console.log(chalk.dim(` ${i + 1}. ${action.description}`));
296
- });
297
- console.log('');
298
-
299
- const proceed = await p.confirm({
300
- message: 'Execute this plan?',
301
- initialValue: true,
302
- });
303
-
304
- if (proceed && !p.isCancel(proceed)) {
305
- spinner.start('Building...');
306
-
307
- for (const action of actions) {
308
- await executeAction(action, spinner);
309
- }
310
-
311
- spinner.stop('Build complete');
312
-
313
- // Run pattern check
314
- console.log(chalk.dim('\n🔍 Running CodeBakers check...'));
315
- const checkResult = await runPatternCheck(false);
316
-
317
- if (checkResult.violations.length > 0) {
318
- console.log(chalk.yellow(`\n⚠️ ${checkResult.violations.length} pattern violations found`));
319
-
320
- const autoFix = await p.confirm({
321
- message: 'Auto-fix violations?',
322
- initialValue: true,
323
- });
324
-
325
- if (autoFix && !p.isCancel(autoFix)) {
326
- spinner.start('Auto-fixing...');
327
- await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
328
- spinner.stop('Violations fixed');
329
- }
330
- } else {
331
- console.log(chalk.green('✓ All patterns satisfied'));
332
- }
333
-
334
- // Update project context
335
- projectContext.recentChanges.push(...actions.map(a => a.description));
336
- }
337
- } else {
338
- // Just print the response
339
- console.log('\n' + assistantMessage + '\n');
340
- }
341
-
342
- } catch (error) {
343
- spinner.stop('Error');
344
- console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
345
- }
346
- }
347
-
348
- function buildSystemPrompt(context: ProjectContext, patterns: string): string {
349
- return `You are CodeBakers, an AI coding assistant that ALWAYS follows the user's patterns and rules.
350
-
351
- PROJECT CONTEXT:
352
- - Name: ${context.name}
353
- - Framework: ${context.framework}
354
- - UI Library: ${context.ui}
355
- - Packages: ${context.packages.join(', ')}
356
-
357
- EXISTING FILES:
358
- ${context.existingFiles.slice(0, 50).join('\n')}
359
-
360
- RECENT CHANGES:
361
- ${context.recentChanges.slice(-10).join('\n')}
362
-
363
- CODEBAKERS PATTERNS (MUST FOLLOW):
364
- ${patterns}
365
-
366
- INSTRUCTIONS:
367
- 1. ALWAYS follow the patterns above - they are non-negotiable
368
- 2. When creating code, output it in this format:
369
-
370
- <<<ACTION:CREATE_FILE>>>
371
- PATH: src/path/to/file.tsx
372
- DESCRIPTION: Brief description
373
- <<<CONTENT>>>
374
- // file content here
375
- <<<END_CONTENT>>>
376
- <<<END_ACTION>>>
377
-
378
- 3. For editing files:
379
-
380
- <<<ACTION:EDIT_FILE>>>
381
- PATH: src/path/to/file.tsx
382
- DESCRIPTION: What you're changing
383
- <<<FIND>>>
384
- // code to find
385
- <<<REPLACE>>>
386
- // replacement code
387
- <<<END_ACTION>>>
388
-
389
- 4. For running commands:
390
-
391
- <<<ACTION:RUN_COMMAND>>>
392
- COMMAND: pnpm add some-package
393
- DESCRIPTION: Why running this
394
- <<<END_ACTION>>>
395
-
396
- 5. Every button MUST have a working onClick handler
397
- 6. Every form MUST have proper validation with Zod
398
- 7. Every async operation MUST have loading, error, and success states
399
- 8. Every list MUST have empty state handling
400
- 9. Use Zustand for state management, not prop drilling
401
- 10. Put files in the correct folders as specified in patterns
402
-
403
- If you're unsure about requirements, ask clarifying questions before coding.
404
- `;
405
- }
406
-
407
- async function loadProjectContext(): Promise<ProjectContext> {
408
- const cwd = process.cwd();
409
- const codebakersConfig = path.join(cwd, '.codebakers', 'config.json');
410
-
411
- let projectConfig: Record<string, unknown> = {};
412
- if (await fs.pathExists(codebakersConfig)) {
413
- projectConfig = await fs.readJson(codebakersConfig);
414
- }
415
-
416
- // Get list of files
417
- const glob = (await import('fast-glob')).default;
418
- const files = await glob(['src/**/*.{ts,tsx,js,jsx}'], { cwd });
419
-
420
- return {
421
- name: path.basename(cwd),
422
- framework: (projectConfig.framework as string) || 'nextjs',
423
- ui: (projectConfig.ui as string) || 'shadcn',
424
- packages: (projectConfig.packages as string[]) || [],
425
- existingFiles: files,
426
- recentChanges: [],
427
- };
428
- }
429
-
430
- interface CodeAction {
431
- type: 'CREATE_FILE' | 'EDIT_FILE' | 'RUN_COMMAND' | 'DELETE_FILE';
432
- path?: string;
433
- content?: string;
434
- command?: string;
435
- description: string;
436
- find?: string;
437
- replace?: string;
438
- }
439
-
440
- function parseActions(response: string): CodeAction[] {
441
- const actions: CodeAction[] = [];
442
-
443
- // Parse CREATE_FILE actions
444
- const createFileRegex = /<<<ACTION:CREATE_FILE>>>\s*PATH:\s*(.+?)\s*DESCRIPTION:\s*(.+?)\s*<<<CONTENT>>>\s*([\s\S]+?)\s*<<<END_CONTENT>>>\s*<<<END_ACTION>>>/g;
445
- let match;
446
-
447
- while ((match = createFileRegex.exec(response)) !== null) {
448
- actions.push({
449
- type: 'CREATE_FILE',
450
- path: match[1].trim(),
451
- description: match[2].trim(),
452
- content: match[3].trim(),
453
- });
454
- }
455
-
456
- // Parse EDIT_FILE actions
457
- const editFileRegex = /<<<ACTION:EDIT_FILE>>>\s*PATH:\s*(.+?)\s*DESCRIPTION:\s*(.+?)\s*<<<FIND>>>\s*([\s\S]+?)\s*<<<REPLACE>>>\s*([\s\S]+?)\s*<<<END_ACTION>>>/g;
458
-
459
- while ((match = editFileRegex.exec(response)) !== null) {
460
- actions.push({
461
- type: 'EDIT_FILE',
462
- path: match[1].trim(),
463
- description: match[2].trim(),
464
- find: match[3].trim(),
465
- replace: match[4].trim(),
466
- });
467
- }
468
-
469
- // Parse RUN_COMMAND actions
470
- const runCommandRegex = /<<<ACTION:RUN_COMMAND>>>\s*COMMAND:\s*(.+?)\s*DESCRIPTION:\s*(.+?)\s*<<<END_ACTION>>>/g;
471
-
472
- while ((match = runCommandRegex.exec(response)) !== null) {
473
- actions.push({
474
- type: 'RUN_COMMAND',
475
- command: match[1].trim(),
476
- description: match[2].trim(),
477
- });
478
- }
479
-
480
- return actions;
481
- }
482
-
483
- async function executeAction(action: CodeAction, spinner: ReturnType<typeof p.spinner>): Promise<void> {
484
- const cwd = process.cwd();
485
-
486
- switch (action.type) {
487
- case 'CREATE_FILE': {
488
- const filePath = path.join(cwd, action.path!);
489
- await fs.ensureDir(path.dirname(filePath));
490
- await fs.writeFile(filePath, action.content!);
491
- spinner.message(`Created ${action.path}`);
492
- break;
493
- }
494
-
495
- case 'EDIT_FILE': {
496
- const filePath = path.join(cwd, action.path!);
497
- if (await fs.pathExists(filePath)) {
498
- let content = await fs.readFile(filePath, 'utf-8');
499
- if (action.find && content.includes(action.find)) {
500
- content = content.replace(action.find, action.replace || '');
501
- await fs.writeFile(filePath, content);
502
- spinner.message(`Edited ${action.path}`);
503
- }
504
- }
505
- break;
506
- }
507
-
508
- case 'RUN_COMMAND': {
509
- spinner.message(`Running: ${action.command}`);
510
- const [cmd, ...args] = action.command!.split(' ');
511
- await execa(cmd, args, { cwd, stdio: 'pipe' });
512
- break;
513
- }
514
-
515
- case 'DELETE_FILE': {
516
- const filePath = path.join(cwd, action.path!);
517
- if (await fs.pathExists(filePath)) {
518
- await fs.remove(filePath);
519
- spinner.message(`Deleted ${action.path}`);
520
- }
521
- break;
522
- }
523
- }
524
- }
525
-
526
- async function checkForWizard(input: string): Promise<string | null> {
527
- const lower = input.toLowerCase();
528
-
529
- // Check for feature-building triggers
530
- if (lower.includes('add') || lower.includes('create') || lower.includes('build')) {
531
- // Check for specific feature types
532
- if (lower.includes('auth') || lower.includes('login') || lower.includes('signup')) {
533
- return await runAuthWizard(input);
534
- }
535
- if (lower.includes('payment') || lower.includes('checkout') || lower.includes('stripe')) {
536
- return await runPaymentWizard(input);
537
- }
538
- if (lower.includes('form')) {
539
- return await runFormWizard(input);
540
- }
541
- if (lower.includes('dashboard')) {
542
- return await runDashboardWizard(input);
543
- }
544
- if (lower.includes('chat') || lower.includes('ai')) {
545
- return await runAIWizard(input);
546
- }
547
- }
548
-
549
- return null;
550
- }
551
-
552
- async function runAuthWizard(originalInput: string): Promise<string> {
553
- console.log(chalk.cyan('\n🔐 Auth Wizard'));
554
-
555
- const authType = await p.select({
556
- message: 'What type of authentication?',
557
- options: [
558
- { value: 'magic-link', label: '🔗 Magic link (email)' },
559
- { value: 'password', label: '🔑 Password (email + password)' },
560
- { value: 'social', label: '🌐 Social login (Google, GitHub)' },
561
- { value: 'phone', label: '📱 Phone (SMS code)' },
562
- ],
563
- });
564
-
565
- if (p.isCancel(authType)) return originalInput;
566
-
567
- const pages = await p.multiselect({
568
- message: 'What pages do you need?',
569
- options: [
570
- { value: 'login', label: 'Login page' },
571
- { value: 'signup', label: 'Signup page' },
572
- { value: 'forgot', label: 'Forgot password' },
573
- { value: 'reset', label: 'Reset password' },
574
- { value: 'verify', label: 'Email verification' },
575
- ],
576
- initialValues: ['login', 'signup'],
577
- });
578
-
579
- if (p.isCancel(pages)) return originalInput;
580
-
581
- const redirect = await p.text({
582
- message: 'Where should users go after login?',
583
- placeholder: '/dashboard',
584
- initialValue: '/dashboard',
585
- });
586
-
587
- return `Create a complete authentication system with:
588
- - Auth type: ${authType}
589
- - Pages: ${(pages as string[]).join(', ')}
590
- - Redirect after login: ${redirect}
591
- - Use Supabase Auth
592
- - Include loading states, error handling, form validation
593
- - Follow all CodeBakers patterns`;
594
- }
595
-
596
- async function runPaymentWizard(originalInput: string): Promise<string> {
597
- console.log(chalk.cyan('\n💳 Payment Wizard'));
598
-
599
- const paymentType = await p.select({
600
- message: 'What type of payment flow?',
601
- options: [
602
- { value: 'one-time', label: '💵 One-time purchase' },
603
- { value: 'subscription', label: '🔄 Subscription (recurring)' },
604
- { value: 'both', label: '📦 Both' },
605
- ],
606
- });
607
-
608
- if (p.isCancel(paymentType)) return originalInput;
609
-
610
- const features = await p.multiselect({
611
- message: 'What features?',
612
- options: [
613
- { value: 'checkout', label: 'Checkout page' },
614
- { value: 'webhook', label: 'Webhook handler' },
615
- { value: 'portal', label: 'Customer portal' },
616
- { value: 'invoices', label: 'Invoice history' },
617
- ],
618
- initialValues: ['checkout', 'webhook'],
619
- });
620
-
621
- if (p.isCancel(features)) return originalInput;
622
-
623
- return `Create a complete Stripe payment system with:
624
- - Payment type: ${paymentType}
625
- - Features: ${(features as string[]).join(', ')}
626
- - Use Stripe SDK
627
- - Include webhook signature verification
628
- - Handle all error cases
629
- - Follow all CodeBakers patterns`;
630
- }
631
-
632
- async function runFormWizard(originalInput: string): Promise<string> {
633
- console.log(chalk.cyan('\n📝 Form Wizard'));
634
-
635
- const formName = await p.text({
636
- message: 'What is this form for?',
637
- placeholder: 'Contact form',
638
- });
639
-
640
- if (p.isCancel(formName)) return originalInput;
641
-
642
- return `Create a ${formName} form with:
643
- - React Hook Form for form handling
644
- - Zod validation schema
645
- - Loading state during submission
646
- - Error messages for each field
647
- - Success toast on completion
648
- - Proper accessibility (aria labels)
649
- - Follow all CodeBakers patterns`;
650
- }
651
-
652
- async function runDashboardWizard(originalInput: string): Promise<string> {
653
- console.log(chalk.cyan('\n📊 Dashboard Wizard'));
654
-
655
- const components = await p.multiselect({
656
- message: 'What components?',
657
- options: [
658
- { value: 'stats', label: 'Stats cards' },
659
- { value: 'chart', label: 'Charts/graphs' },
660
- { value: 'table', label: 'Data table' },
661
- { value: 'activity', label: 'Activity feed' },
662
- { value: 'sidebar', label: 'Sidebar navigation' },
663
- ],
664
- initialValues: ['stats', 'sidebar'],
665
- });
666
-
667
- if (p.isCancel(components)) return originalInput;
668
-
669
- return `Create a dashboard with:
670
- - Components: ${(components as string[]).join(', ')}
671
- - Responsive layout
672
- - Loading skeletons
673
- - Empty states
674
- - Error boundaries
675
- - Follow all CodeBakers patterns`;
676
- }
677
-
678
- async function runAIWizard(originalInput: string): Promise<string> {
679
- console.log(chalk.cyan('\n🤖 AI Feature Wizard'));
680
-
681
- const aiType = await p.select({
682
- message: 'What AI feature?',
683
- options: [
684
- { value: 'chat', label: '💬 Chat interface' },
685
- { value: 'generate', label: '📝 Content generation' },
686
- { value: 'analyze', label: '🔍 Data analysis' },
687
- ],
688
- });
689
-
690
- if (p.isCancel(aiType)) return originalInput;
691
-
692
- const streaming = await p.confirm({
693
- message: 'Use streaming responses?',
694
- initialValue: true,
695
- });
696
-
697
- return `Create an AI ${aiType} feature with:
698
- - Streaming: ${streaming ? 'Yes' : 'No'}
699
- - Use Anthropic Claude API
700
- - Include conversation history
701
- - Loading indicators
702
- - Error handling
703
- - Stop generation button
704
- - Follow all CodeBakers patterns`;
705
- }
706
-
707
- async function handleSlashCommand(
708
- command: string,
709
- context: ProjectContext,
710
- config: Config
711
- ): Promise<void> {
712
- const [cmd, ...args] = command.slice(1).split(' ');
713
-
714
- switch (cmd.toLowerCase()) {
715
- case 'deploy':
716
- console.log(chalk.dim('Running deploy...'));
717
- // Import and run deploy command
718
- break;
719
- case 'check':
720
- console.log(chalk.dim('Running pattern check...'));
721
- await runPatternCheck(false);
722
- break;
723
- case 'status':
724
- console.log(chalk.cyan(`
725
- Project: ${context.name}
726
- Framework: ${context.framework}
727
- UI: ${context.ui}
728
- Files: ${context.existingFiles.length}
729
- `));
730
- break;
731
- case 'undo':
732
- console.log(chalk.yellow('Undo not yet implemented'));
733
- break;
734
- case 'help':
735
- showHelp();
736
- break;
737
- default:
738
- console.log(chalk.dim(`Unknown command: ${cmd}`));
739
- }
740
- }
741
-
742
- function showHelp(): void {
743
- console.log(chalk.cyan(`
744
- Available commands:
745
-
746
- /deploy Deploy to production
747
- /check Run pattern check
748
- /status Show project status
749
- /undo Undo last change
750
- /help Show this help
751
-
752
- Tips:
753
- • Just describe what you want to build
754
- • I'll ask clarifying questions if needed
755
- • All code follows your CodeBakers patterns
756
- • Patterns are auto-checked after every change
757
- `));
758
- }
759
-
760
- async function autoFixViolations(
761
- violations: Array<{ file: string; rule: string; message: string }>,
762
- anthropic: Anthropic,
763
- systemPrompt: string
764
- ): Promise<void> {
765
- // Group violations by file
766
- const byFile = new Map<string, typeof violations>();
767
-
768
- for (const v of violations) {
769
- if (!byFile.has(v.file)) {
770
- byFile.set(v.file, []);
771
- }
772
- byFile.get(v.file)!.push(v);
773
- }
774
-
775
- for (const [file, fileViolations] of byFile) {
776
- const content = await fs.readFile(path.join(process.cwd(), file), 'utf-8');
777
-
778
- const fixPrompt = `Fix these violations in ${file}:
779
-
780
- ${fileViolations.map(v => `- ${v.rule}: ${v.message}`).join('\n')}
781
-
782
- Current file content:
783
- \`\`\`
784
- ${content}
785
- \`\`\`
786
-
787
- Output only the fixed file content, no explanation.`;
788
-
789
- const response = await anthropic.messages.create({
790
- model: 'claude-sonnet-4-5-20250929',
791
- max_tokens: 8192,
792
- system: systemPrompt,
793
- messages: [{ role: 'user', content: fixPrompt }],
794
- });
795
-
796
- const fixedContent = response.content[0].type === 'text'
797
- ? response.content[0].text
798
- : '';
799
-
800
- // Extract code from markdown if present
801
- const codeMatch = fixedContent.match(/```(?:tsx?|jsx?)?\n([\s\S]+?)\n```/);
802
- const finalContent = codeMatch ? codeMatch[1] : fixedContent;
803
-
804
- await fs.writeFile(path.join(process.cwd(), file), finalContent);
805
- }
806
- }