codebakers 1.0.45 → 2.0.1

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 (82) hide show
  1. package/README.md +275 -60
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +4260 -0
  4. package/install.bat +9 -0
  5. package/package.json +71 -115
  6. package/src/channels/discord.ts +5 -0
  7. package/src/channels/slack.ts +5 -0
  8. package/src/channels/sms.ts +4 -0
  9. package/src/channels/telegram.ts +5 -0
  10. package/src/channels/whatsapp.ts +7 -0
  11. package/src/commands/check.ts +365 -0
  12. package/src/commands/code.ts +684 -0
  13. package/src/commands/connect.ts +12 -0
  14. package/src/commands/deploy.ts +414 -0
  15. package/src/commands/design.ts +298 -0
  16. package/src/commands/fix.ts +20 -0
  17. package/src/commands/gateway.ts +604 -0
  18. package/src/commands/generate.ts +178 -0
  19. package/src/commands/init.ts +574 -0
  20. package/src/commands/learn.ts +36 -0
  21. package/src/commands/security.ts +102 -0
  22. package/src/commands/setup.ts +448 -0
  23. package/src/commands/status.ts +56 -0
  24. package/src/index.ts +278 -0
  25. package/src/patterns/loader.ts +337 -0
  26. package/src/services/github.ts +61 -0
  27. package/src/services/supabase.ts +147 -0
  28. package/src/services/vercel.ts +61 -0
  29. package/src/utils/claude-md.ts +287 -0
  30. package/src/utils/config.ts +282 -0
  31. package/src/utils/updates.ts +27 -0
  32. package/tsconfig.json +17 -10
  33. package/.vscodeignore +0 -18
  34. package/LICENSE +0 -21
  35. package/codebakers-1.0.0.vsix +0 -0
  36. package/codebakers-1.0.10.vsix +0 -0
  37. package/codebakers-1.0.11.vsix +0 -0
  38. package/codebakers-1.0.12.vsix +0 -0
  39. package/codebakers-1.0.13.vsix +0 -0
  40. package/codebakers-1.0.14.vsix +0 -0
  41. package/codebakers-1.0.15.vsix +0 -0
  42. package/codebakers-1.0.16.vsix +0 -0
  43. package/codebakers-1.0.17.vsix +0 -0
  44. package/codebakers-1.0.18.vsix +0 -0
  45. package/codebakers-1.0.19.vsix +0 -0
  46. package/codebakers-1.0.20.vsix +0 -0
  47. package/codebakers-1.0.21.vsix +0 -0
  48. package/codebakers-1.0.22.vsix +0 -0
  49. package/codebakers-1.0.23.vsix +0 -0
  50. package/codebakers-1.0.24.vsix +0 -0
  51. package/codebakers-1.0.25.vsix +0 -0
  52. package/codebakers-1.0.26.vsix +0 -0
  53. package/codebakers-1.0.27.vsix +0 -0
  54. package/codebakers-1.0.28.vsix +0 -0
  55. package/codebakers-1.0.29.vsix +0 -0
  56. package/codebakers-1.0.30.vsix +0 -0
  57. package/codebakers-1.0.31.vsix +0 -0
  58. package/codebakers-1.0.32.vsix +0 -0
  59. package/codebakers-1.0.35.vsix +0 -0
  60. package/codebakers-1.0.36.vsix +0 -0
  61. package/codebakers-1.0.37.vsix +0 -0
  62. package/codebakers-1.0.38.vsix +0 -0
  63. package/codebakers-1.0.39.vsix +0 -0
  64. package/codebakers-1.0.40.vsix +0 -0
  65. package/codebakers-1.0.41.vsix +0 -0
  66. package/codebakers-1.0.42.vsix +0 -0
  67. package/codebakers-1.0.43.vsix +0 -0
  68. package/codebakers-1.0.44.vsix +0 -0
  69. package/codebakers-1.0.45.vsix +0 -0
  70. package/dist/extension.js +0 -1394
  71. package/esbuild.js +0 -63
  72. package/media/icon.png +0 -0
  73. package/media/icon.svg +0 -7
  74. package/nul +0 -1
  75. package/preview.html +0 -547
  76. package/src/ChatPanelProvider.ts +0 -1815
  77. package/src/ChatViewProvider.ts +0 -749
  78. package/src/CodeBakersClient.ts +0 -1146
  79. package/src/CodeValidator.ts +0 -645
  80. package/src/FileOperations.ts +0 -410
  81. package/src/ProjectContext.ts +0 -526
  82. package/src/extension.ts +0 -332
@@ -0,0 +1,684 @@
1
+ import * as p from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import * as 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
+
11
+ interface CodeOptions {
12
+ watch?: boolean;
13
+ }
14
+
15
+ interface Message {
16
+ role: 'user' | 'assistant';
17
+ content: string;
18
+ }
19
+
20
+ interface ProjectContext {
21
+ name: string;
22
+ framework: string;
23
+ ui: string;
24
+ packages: string[];
25
+ existingFiles: string[];
26
+ recentChanges: string[];
27
+ }
28
+
29
+ export async function codeCommand(prompt?: string, options: CodeOptions = {}): Promise<void> {
30
+ const config = new Config();
31
+
32
+ // Check if configured
33
+ if (!config.isConfigured()) {
34
+ p.log.error('Please run `codebakers setup` first.');
35
+ return;
36
+ }
37
+
38
+ // Check if in project
39
+ if (!config.isInProject()) {
40
+ p.log.error('Not in a CodeBakers project. Run `codebakers init` first.');
41
+ return;
42
+ }
43
+
44
+ // Get API key
45
+ const anthropicCreds = config.getCredentials('anthropic');
46
+ if (!anthropicCreds?.apiKey) {
47
+ p.log.error('Anthropic API key not configured. Run `codebakers setup`.');
48
+ return;
49
+ }
50
+
51
+ // Initialize Anthropic
52
+ const anthropic = new Anthropic({
53
+ apiKey: anthropicCreds.apiKey,
54
+ });
55
+
56
+ // Load project context
57
+ const projectContext = await loadProjectContext();
58
+
59
+ // Load patterns
60
+ const patterns = await loadPatterns(config);
61
+
62
+ // Build system prompt
63
+ const systemPrompt = buildSystemPrompt(projectContext, patterns);
64
+
65
+ console.log(chalk.cyan(`
66
+ ╭─────────────────────────────────────────────────────╮
67
+ │ šŸ¤– CodeBakers AI │
68
+ │ │
69
+ │ Project: ${projectContext.name.padEnd(38)}│
70
+ │ Stack: ${`${projectContext.framework} + ${projectContext.ui}`.padEnd(40)}│
71
+ │ │
72
+ │ Type your request or "?" for help │
73
+ │ Type "exit" or Ctrl+C to quit │
74
+ ╰─────────────────────────────────────────────────────╯
75
+ `));
76
+
77
+ // Conversation history
78
+ const messages: Message[] = [];
79
+
80
+ // If prompt provided via command line
81
+ if (prompt) {
82
+ await processUserInput(prompt, messages, anthropic, systemPrompt, projectContext, config);
83
+ }
84
+
85
+ // Interactive loop
86
+ while (true) {
87
+ const input = await p.text({
88
+ message: '',
89
+ placeholder: 'What do you want to build?',
90
+ });
91
+
92
+ if (p.isCancel(input)) {
93
+ p.outro('Goodbye!');
94
+ break;
95
+ }
96
+
97
+ const userInput = (input as string).trim();
98
+
99
+ if (!userInput) continue;
100
+
101
+ if (userInput.toLowerCase() === 'exit') {
102
+ p.outro('Goodbye!');
103
+ break;
104
+ }
105
+
106
+ if (userInput === '?') {
107
+ showHelp();
108
+ continue;
109
+ }
110
+
111
+ // Handle slash commands
112
+ if (userInput.startsWith('/')) {
113
+ await handleSlashCommand(userInput, projectContext, config);
114
+ continue;
115
+ }
116
+
117
+ // Process input with AI
118
+ await processUserInput(userInput, messages, anthropic, systemPrompt, projectContext, config);
119
+ }
120
+ }
121
+
122
+ async function processUserInput(
123
+ userInput: string,
124
+ messages: Message[],
125
+ anthropic: Anthropic,
126
+ systemPrompt: string,
127
+ projectContext: ProjectContext,
128
+ config: Config
129
+ ): Promise<void> {
130
+ const spinner = p.spinner();
131
+
132
+ // Add user message
133
+ messages.push({ role: 'user', content: userInput });
134
+
135
+ // Check for wizard triggers
136
+ const wizardResult = await checkForWizard(userInput);
137
+ if (wizardResult) {
138
+ // If wizard ran, use its enhanced prompt
139
+ messages[messages.length - 1].content = wizardResult;
140
+ }
141
+
142
+ try {
143
+ spinner.start('Thinking...');
144
+
145
+ // Call Claude
146
+ const response = await anthropic.messages.create({
147
+ model: 'claude-sonnet-4-20250514',
148
+ max_tokens: 8192,
149
+ system: systemPrompt,
150
+ messages: messages.map(m => ({
151
+ role: m.role,
152
+ content: m.content,
153
+ })),
154
+ });
155
+
156
+ spinner.stop('');
157
+
158
+ // Extract response
159
+ const assistantMessage = response.content[0].type === 'text'
160
+ ? response.content[0].text
161
+ : '';
162
+
163
+ // Add to history
164
+ messages.push({ role: 'assistant', content: assistantMessage });
165
+
166
+ // Parse and execute any code actions
167
+ const actions = parseActions(assistantMessage);
168
+
169
+ if (actions.length > 0) {
170
+ // Show plan
171
+ console.log(chalk.cyan('\nšŸ“‹ Plan:'));
172
+ actions.forEach((action, i) => {
173
+ console.log(chalk.dim(` ${i + 1}. ${action.description}`));
174
+ });
175
+ console.log('');
176
+
177
+ const proceed = await p.confirm({
178
+ message: 'Execute this plan?',
179
+ initialValue: true,
180
+ });
181
+
182
+ if (proceed && !p.isCancel(proceed)) {
183
+ spinner.start('Building...');
184
+
185
+ for (const action of actions) {
186
+ await executeAction(action, spinner);
187
+ }
188
+
189
+ spinner.stop('Build complete');
190
+
191
+ // Run pattern check
192
+ console.log(chalk.dim('\nšŸ” Running CodeBakers check...'));
193
+ const checkResult = await runPatternCheck(false);
194
+
195
+ if (checkResult.violations.length > 0) {
196
+ console.log(chalk.yellow(`\nāš ļø ${checkResult.violations.length} pattern violations found`));
197
+
198
+ const autoFix = await p.confirm({
199
+ message: 'Auto-fix violations?',
200
+ initialValue: true,
201
+ });
202
+
203
+ if (autoFix && !p.isCancel(autoFix)) {
204
+ spinner.start('Auto-fixing...');
205
+ await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
206
+ spinner.stop('Violations fixed');
207
+ }
208
+ } else {
209
+ console.log(chalk.green('āœ“ All patterns satisfied'));
210
+ }
211
+
212
+ // Update project context
213
+ projectContext.recentChanges.push(...actions.map(a => a.description));
214
+ }
215
+ } else {
216
+ // Just print the response
217
+ console.log('\n' + assistantMessage + '\n');
218
+ }
219
+
220
+ } catch (error) {
221
+ spinner.stop('Error');
222
+ console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
223
+ }
224
+ }
225
+
226
+ function buildSystemPrompt(context: ProjectContext, patterns: string): string {
227
+ return `You are CodeBakers, an AI coding assistant that ALWAYS follows the user's patterns and rules.
228
+
229
+ PROJECT CONTEXT:
230
+ - Name: ${context.name}
231
+ - Framework: ${context.framework}
232
+ - UI Library: ${context.ui}
233
+ - Packages: ${context.packages.join(', ')}
234
+
235
+ EXISTING FILES:
236
+ ${context.existingFiles.slice(0, 50).join('\n')}
237
+
238
+ RECENT CHANGES:
239
+ ${context.recentChanges.slice(-10).join('\n')}
240
+
241
+ CODEBAKERS PATTERNS (MUST FOLLOW):
242
+ ${patterns}
243
+
244
+ INSTRUCTIONS:
245
+ 1. ALWAYS follow the patterns above - they are non-negotiable
246
+ 2. When creating code, output it in this format:
247
+
248
+ <<<ACTION:CREATE_FILE>>>
249
+ PATH: src/path/to/file.tsx
250
+ DESCRIPTION: Brief description
251
+ <<<CONTENT>>>
252
+ // file content here
253
+ <<<END_CONTENT>>>
254
+ <<<END_ACTION>>>
255
+
256
+ 3. For editing files:
257
+
258
+ <<<ACTION:EDIT_FILE>>>
259
+ PATH: src/path/to/file.tsx
260
+ DESCRIPTION: What you're changing
261
+ <<<FIND>>>
262
+ // code to find
263
+ <<<REPLACE>>>
264
+ // replacement code
265
+ <<<END_ACTION>>>
266
+
267
+ 4. For running commands:
268
+
269
+ <<<ACTION:RUN_COMMAND>>>
270
+ COMMAND: pnpm add some-package
271
+ DESCRIPTION: Why running this
272
+ <<<END_ACTION>>>
273
+
274
+ 5. Every button MUST have a working onClick handler
275
+ 6. Every form MUST have proper validation with Zod
276
+ 7. Every async operation MUST have loading, error, and success states
277
+ 8. Every list MUST have empty state handling
278
+ 9. Use Zustand for state management, not prop drilling
279
+ 10. Put files in the correct folders as specified in patterns
280
+
281
+ If you're unsure about requirements, ask clarifying questions before coding.
282
+ `;
283
+ }
284
+
285
+ async function loadProjectContext(): Promise<ProjectContext> {
286
+ const cwd = process.cwd();
287
+ const codebakersConfig = path.join(cwd, '.codebakers', 'config.json');
288
+
289
+ let projectConfig: Record<string, unknown> = {};
290
+ if (await fs.pathExists(codebakersConfig)) {
291
+ projectConfig = await fs.readJson(codebakersConfig);
292
+ }
293
+
294
+ // Get list of files
295
+ const glob = (await import('fast-glob')).default;
296
+ const files = await glob(['src/**/*.{ts,tsx,js,jsx}'], { cwd });
297
+
298
+ return {
299
+ name: path.basename(cwd),
300
+ framework: (projectConfig.framework as string) || 'nextjs',
301
+ ui: (projectConfig.ui as string) || 'shadcn',
302
+ packages: (projectConfig.packages as string[]) || [],
303
+ existingFiles: files,
304
+ recentChanges: [],
305
+ };
306
+ }
307
+
308
+ interface CodeAction {
309
+ type: 'CREATE_FILE' | 'EDIT_FILE' | 'RUN_COMMAND' | 'DELETE_FILE';
310
+ path?: string;
311
+ content?: string;
312
+ command?: string;
313
+ description: string;
314
+ find?: string;
315
+ replace?: string;
316
+ }
317
+
318
+ function parseActions(response: string): CodeAction[] {
319
+ const actions: CodeAction[] = [];
320
+
321
+ // Parse CREATE_FILE actions
322
+ const createFileRegex = /<<<ACTION:CREATE_FILE>>>\s*PATH:\s*(.+?)\s*DESCRIPTION:\s*(.+?)\s*<<<CONTENT>>>\s*([\s\S]+?)\s*<<<END_CONTENT>>>\s*<<<END_ACTION>>>/g;
323
+ let match;
324
+
325
+ while ((match = createFileRegex.exec(response)) !== null) {
326
+ actions.push({
327
+ type: 'CREATE_FILE',
328
+ path: match[1].trim(),
329
+ description: match[2].trim(),
330
+ content: match[3].trim(),
331
+ });
332
+ }
333
+
334
+ // Parse EDIT_FILE actions
335
+ 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;
336
+
337
+ while ((match = editFileRegex.exec(response)) !== null) {
338
+ actions.push({
339
+ type: 'EDIT_FILE',
340
+ path: match[1].trim(),
341
+ description: match[2].trim(),
342
+ find: match[3].trim(),
343
+ replace: match[4].trim(),
344
+ });
345
+ }
346
+
347
+ // Parse RUN_COMMAND actions
348
+ const runCommandRegex = /<<<ACTION:RUN_COMMAND>>>\s*COMMAND:\s*(.+?)\s*DESCRIPTION:\s*(.+?)\s*<<<END_ACTION>>>/g;
349
+
350
+ while ((match = runCommandRegex.exec(response)) !== null) {
351
+ actions.push({
352
+ type: 'RUN_COMMAND',
353
+ command: match[1].trim(),
354
+ description: match[2].trim(),
355
+ });
356
+ }
357
+
358
+ return actions;
359
+ }
360
+
361
+ async function executeAction(action: CodeAction, spinner: ReturnType<typeof p.spinner>): Promise<void> {
362
+ const cwd = process.cwd();
363
+
364
+ switch (action.type) {
365
+ case 'CREATE_FILE': {
366
+ const filePath = path.join(cwd, action.path!);
367
+ await fs.ensureDir(path.dirname(filePath));
368
+ await fs.writeFile(filePath, action.content!);
369
+ spinner.message(`Created ${action.path}`);
370
+ break;
371
+ }
372
+
373
+ case 'EDIT_FILE': {
374
+ const filePath = path.join(cwd, action.path!);
375
+ if (await fs.pathExists(filePath)) {
376
+ let content = await fs.readFile(filePath, 'utf-8');
377
+ if (action.find && content.includes(action.find)) {
378
+ content = content.replace(action.find, action.replace || '');
379
+ await fs.writeFile(filePath, content);
380
+ spinner.message(`Edited ${action.path}`);
381
+ }
382
+ }
383
+ break;
384
+ }
385
+
386
+ case 'RUN_COMMAND': {
387
+ spinner.message(`Running: ${action.command}`);
388
+ const [cmd, ...args] = action.command!.split(' ');
389
+ await execa(cmd, args, { cwd, stdio: 'pipe' });
390
+ break;
391
+ }
392
+
393
+ case 'DELETE_FILE': {
394
+ const filePath = path.join(cwd, action.path!);
395
+ if (await fs.pathExists(filePath)) {
396
+ await fs.remove(filePath);
397
+ spinner.message(`Deleted ${action.path}`);
398
+ }
399
+ break;
400
+ }
401
+ }
402
+ }
403
+
404
+ async function checkForWizard(input: string): Promise<string | null> {
405
+ const lower = input.toLowerCase();
406
+
407
+ // Check for feature-building triggers
408
+ if (lower.includes('add') || lower.includes('create') || lower.includes('build')) {
409
+ // Check for specific feature types
410
+ if (lower.includes('auth') || lower.includes('login') || lower.includes('signup')) {
411
+ return await runAuthWizard(input);
412
+ }
413
+ if (lower.includes('payment') || lower.includes('checkout') || lower.includes('stripe')) {
414
+ return await runPaymentWizard(input);
415
+ }
416
+ if (lower.includes('form')) {
417
+ return await runFormWizard(input);
418
+ }
419
+ if (lower.includes('dashboard')) {
420
+ return await runDashboardWizard(input);
421
+ }
422
+ if (lower.includes('chat') || lower.includes('ai')) {
423
+ return await runAIWizard(input);
424
+ }
425
+ }
426
+
427
+ return null;
428
+ }
429
+
430
+ async function runAuthWizard(originalInput: string): Promise<string> {
431
+ console.log(chalk.cyan('\nšŸ” Auth Wizard'));
432
+
433
+ const authType = await p.select({
434
+ message: 'What type of authentication?',
435
+ options: [
436
+ { value: 'magic-link', label: 'šŸ”— Magic link (email)' },
437
+ { value: 'password', label: 'šŸ”‘ Password (email + password)' },
438
+ { value: 'social', label: '🌐 Social login (Google, GitHub)' },
439
+ { value: 'phone', label: 'šŸ“± Phone (SMS code)' },
440
+ ],
441
+ });
442
+
443
+ if (p.isCancel(authType)) return originalInput;
444
+
445
+ const pages = await p.multiselect({
446
+ message: 'What pages do you need?',
447
+ options: [
448
+ { value: 'login', label: 'Login page' },
449
+ { value: 'signup', label: 'Signup page' },
450
+ { value: 'forgot', label: 'Forgot password' },
451
+ { value: 'reset', label: 'Reset password' },
452
+ { value: 'verify', label: 'Email verification' },
453
+ ],
454
+ initialValues: ['login', 'signup'],
455
+ });
456
+
457
+ if (p.isCancel(pages)) return originalInput;
458
+
459
+ const redirect = await p.text({
460
+ message: 'Where should users go after login?',
461
+ placeholder: '/dashboard',
462
+ initialValue: '/dashboard',
463
+ });
464
+
465
+ return `Create a complete authentication system with:
466
+ - Auth type: ${authType}
467
+ - Pages: ${(pages as string[]).join(', ')}
468
+ - Redirect after login: ${redirect}
469
+ - Use Supabase Auth
470
+ - Include loading states, error handling, form validation
471
+ - Follow all CodeBakers patterns`;
472
+ }
473
+
474
+ async function runPaymentWizard(originalInput: string): Promise<string> {
475
+ console.log(chalk.cyan('\nšŸ’³ Payment Wizard'));
476
+
477
+ const paymentType = await p.select({
478
+ message: 'What type of payment flow?',
479
+ options: [
480
+ { value: 'one-time', label: 'šŸ’µ One-time purchase' },
481
+ { value: 'subscription', label: 'šŸ”„ Subscription (recurring)' },
482
+ { value: 'both', label: 'šŸ“¦ Both' },
483
+ ],
484
+ });
485
+
486
+ if (p.isCancel(paymentType)) return originalInput;
487
+
488
+ const features = await p.multiselect({
489
+ message: 'What features?',
490
+ options: [
491
+ { value: 'checkout', label: 'Checkout page' },
492
+ { value: 'webhook', label: 'Webhook handler' },
493
+ { value: 'portal', label: 'Customer portal' },
494
+ { value: 'invoices', label: 'Invoice history' },
495
+ ],
496
+ initialValues: ['checkout', 'webhook'],
497
+ });
498
+
499
+ if (p.isCancel(features)) return originalInput;
500
+
501
+ return `Create a complete Stripe payment system with:
502
+ - Payment type: ${paymentType}
503
+ - Features: ${(features as string[]).join(', ')}
504
+ - Use Stripe SDK
505
+ - Include webhook signature verification
506
+ - Handle all error cases
507
+ - Follow all CodeBakers patterns`;
508
+ }
509
+
510
+ async function runFormWizard(originalInput: string): Promise<string> {
511
+ console.log(chalk.cyan('\nšŸ“ Form Wizard'));
512
+
513
+ const formName = await p.text({
514
+ message: 'What is this form for?',
515
+ placeholder: 'Contact form',
516
+ });
517
+
518
+ if (p.isCancel(formName)) return originalInput;
519
+
520
+ return `Create a ${formName} form with:
521
+ - React Hook Form for form handling
522
+ - Zod validation schema
523
+ - Loading state during submission
524
+ - Error messages for each field
525
+ - Success toast on completion
526
+ - Proper accessibility (aria labels)
527
+ - Follow all CodeBakers patterns`;
528
+ }
529
+
530
+ async function runDashboardWizard(originalInput: string): Promise<string> {
531
+ console.log(chalk.cyan('\nšŸ“Š Dashboard Wizard'));
532
+
533
+ const components = await p.multiselect({
534
+ message: 'What components?',
535
+ options: [
536
+ { value: 'stats', label: 'Stats cards' },
537
+ { value: 'chart', label: 'Charts/graphs' },
538
+ { value: 'table', label: 'Data table' },
539
+ { value: 'activity', label: 'Activity feed' },
540
+ { value: 'sidebar', label: 'Sidebar navigation' },
541
+ ],
542
+ initialValues: ['stats', 'sidebar'],
543
+ });
544
+
545
+ if (p.isCancel(components)) return originalInput;
546
+
547
+ return `Create a dashboard with:
548
+ - Components: ${(components as string[]).join(', ')}
549
+ - Responsive layout
550
+ - Loading skeletons
551
+ - Empty states
552
+ - Error boundaries
553
+ - Follow all CodeBakers patterns`;
554
+ }
555
+
556
+ async function runAIWizard(originalInput: string): Promise<string> {
557
+ console.log(chalk.cyan('\nšŸ¤– AI Feature Wizard'));
558
+
559
+ const aiType = await p.select({
560
+ message: 'What AI feature?',
561
+ options: [
562
+ { value: 'chat', label: 'šŸ’¬ Chat interface' },
563
+ { value: 'generate', label: 'šŸ“ Content generation' },
564
+ { value: 'analyze', label: 'šŸ” Data analysis' },
565
+ ],
566
+ });
567
+
568
+ if (p.isCancel(aiType)) return originalInput;
569
+
570
+ const streaming = await p.confirm({
571
+ message: 'Use streaming responses?',
572
+ initialValue: true,
573
+ });
574
+
575
+ return `Create an AI ${aiType} feature with:
576
+ - Streaming: ${streaming ? 'Yes' : 'No'}
577
+ - Use Anthropic Claude API
578
+ - Include conversation history
579
+ - Loading indicators
580
+ - Error handling
581
+ - Stop generation button
582
+ - Follow all CodeBakers patterns`;
583
+ }
584
+
585
+ async function handleSlashCommand(
586
+ command: string,
587
+ context: ProjectContext,
588
+ config: Config
589
+ ): Promise<void> {
590
+ const [cmd, ...args] = command.slice(1).split(' ');
591
+
592
+ switch (cmd.toLowerCase()) {
593
+ case 'deploy':
594
+ console.log(chalk.dim('Running deploy...'));
595
+ // Import and run deploy command
596
+ break;
597
+ case 'check':
598
+ console.log(chalk.dim('Running pattern check...'));
599
+ await runPatternCheck(false);
600
+ break;
601
+ case 'status':
602
+ console.log(chalk.cyan(`
603
+ Project: ${context.name}
604
+ Framework: ${context.framework}
605
+ UI: ${context.ui}
606
+ Files: ${context.existingFiles.length}
607
+ `));
608
+ break;
609
+ case 'undo':
610
+ console.log(chalk.yellow('Undo not yet implemented'));
611
+ break;
612
+ case 'help':
613
+ showHelp();
614
+ break;
615
+ default:
616
+ console.log(chalk.dim(`Unknown command: ${cmd}`));
617
+ }
618
+ }
619
+
620
+ function showHelp(): void {
621
+ console.log(chalk.cyan(`
622
+ Available commands:
623
+
624
+ /deploy Deploy to production
625
+ /check Run pattern check
626
+ /status Show project status
627
+ /undo Undo last change
628
+ /help Show this help
629
+
630
+ Tips:
631
+ • Just describe what you want to build
632
+ • I'll ask clarifying questions if needed
633
+ • All code follows your CodeBakers patterns
634
+ • Patterns are auto-checked after every change
635
+ `));
636
+ }
637
+
638
+ async function autoFixViolations(
639
+ violations: Array<{ file: string; rule: string; message: string }>,
640
+ anthropic: Anthropic,
641
+ systemPrompt: string
642
+ ): Promise<void> {
643
+ // Group violations by file
644
+ const byFile = new Map<string, typeof violations>();
645
+
646
+ for (const v of violations) {
647
+ if (!byFile.has(v.file)) {
648
+ byFile.set(v.file, []);
649
+ }
650
+ byFile.get(v.file)!.push(v);
651
+ }
652
+
653
+ for (const [file, fileViolations] of byFile) {
654
+ const content = await fs.readFile(path.join(process.cwd(), file), 'utf-8');
655
+
656
+ const fixPrompt = `Fix these violations in ${file}:
657
+
658
+ ${fileViolations.map(v => `- ${v.rule}: ${v.message}`).join('\n')}
659
+
660
+ Current file content:
661
+ \`\`\`
662
+ ${content}
663
+ \`\`\`
664
+
665
+ Output only the fixed file content, no explanation.`;
666
+
667
+ const response = await anthropic.messages.create({
668
+ model: 'claude-sonnet-4-20250514',
669
+ max_tokens: 8192,
670
+ system: systemPrompt,
671
+ messages: [{ role: 'user', content: fixPrompt }],
672
+ });
673
+
674
+ const fixedContent = response.content[0].type === 'text'
675
+ ? response.content[0].text
676
+ : '';
677
+
678
+ // Extract code from markdown if present
679
+ const codeMatch = fixedContent.match(/```(?:tsx?|jsx?)?\n([\s\S]+?)\n```/);
680
+ const finalContent = codeMatch ? codeMatch[1] : fixedContent;
681
+
682
+ await fs.writeFile(path.join(process.cwd(), file), finalContent);
683
+ }
684
+ }
@@ -0,0 +1,12 @@
1
+ // src/commands/connect.ts
2
+ import * as p from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import { Config } from '../utils/config.js';
5
+ import { setupCommand } from './setup.js';
6
+
7
+ export async function connectCommand(service?: string): Promise<void> {
8
+ const config = new Config();
9
+
10
+ // Delegate to setup for now
11
+ await setupCommand();
12
+ }