@codebakers/cli 3.8.6 → 3.8.8

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.
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { execSync, spawn } from 'child_process';
4
- import { writeFileSync, existsSync, readFileSync } from 'fs';
4
+ import { writeFileSync, existsSync, readFileSync, mkdirSync, readdirSync, statSync, rmSync } from 'fs';
5
5
  import { join } from 'path';
6
6
  import { createInterface } from 'readline';
7
7
  import {
@@ -16,6 +16,8 @@ import {
16
16
  } from '../config.js';
17
17
  import { validateApiKey } from '../lib/api.js';
18
18
  import { getDeviceFingerprint } from '../lib/fingerprint.js';
19
+ import { audit } from './audit.js';
20
+ import { heal } from './heal.js';
19
21
 
20
22
  function prompt(question: string): Promise<string> {
21
23
  const rl = createInterface({
@@ -26,13 +28,754 @@ function prompt(question: string): Promise<string> {
26
28
  return new Promise((resolve) => {
27
29
  rl.question(question, (answer) => {
28
30
  rl.close();
29
- resolve(answer.trim().toLowerCase());
31
+ resolve(answer.trim());
30
32
  });
31
33
  });
32
34
  }
33
35
 
36
+ async function confirm(question: string): Promise<boolean> {
37
+ const answer = await prompt(`${question} (Y/n): `);
38
+ return answer.toLowerCase() !== 'n';
39
+ }
40
+
41
+ // ============================================================================
42
+ // PROJECT DETECTION
43
+ // ============================================================================
44
+
45
+ function countSourceFiles(dir: string): number {
46
+ let count = 0;
47
+ try {
48
+ const items = readdirSync(dir, { withFileTypes: true });
49
+ for (const item of items) {
50
+ if (item.name.startsWith('.') || item.name === 'node_modules') continue;
51
+ const fullPath = join(dir, item.name);
52
+ if (item.isDirectory()) {
53
+ count += countSourceFiles(fullPath);
54
+ } else if (
55
+ item.name.endsWith('.ts') ||
56
+ item.name.endsWith('.tsx') ||
57
+ item.name.endsWith('.js') ||
58
+ item.name.endsWith('.jsx')
59
+ ) {
60
+ count++;
61
+ }
62
+ }
63
+ } catch {
64
+ // Ignore access errors
65
+ }
66
+ return count;
67
+ }
68
+
69
+ interface ProjectInfo {
70
+ exists: boolean;
71
+ files: number;
72
+ details: string[];
73
+ stack: Record<string, string>;
74
+ }
75
+
76
+ function detectExistingProject(cwd: string): ProjectInfo {
77
+ const details: string[] = [];
78
+ const stack: Record<string, string> = {};
79
+ let sourceFileCount = 0;
80
+
81
+ const packageJsonPath = join(cwd, 'package.json');
82
+ if (existsSync(packageJsonPath)) {
83
+ try {
84
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
85
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
86
+ const depCount = Object.keys(pkg.dependencies || {}).length;
87
+
88
+ if (depCount > 0) {
89
+ details.push(`package.json with ${depCount} dependencies`);
90
+ }
91
+
92
+ if (deps['next']) stack.framework = `Next.js ${deps['next']}`;
93
+ else if (deps['react']) stack.framework = `React ${deps['react']}`;
94
+ else if (deps['vue']) stack.framework = `Vue ${deps['vue']}`;
95
+ else if (deps['express']) stack.framework = `Express ${deps['express']}`;
96
+
97
+ if (deps['drizzle-orm']) stack.database = 'Drizzle ORM';
98
+ else if (deps['prisma']) stack.database = 'Prisma';
99
+ else if (deps['mongoose']) stack.database = 'MongoDB/Mongoose';
100
+
101
+ if (deps['@supabase/supabase-js']) stack.auth = 'Supabase Auth';
102
+ else if (deps['next-auth']) stack.auth = 'NextAuth.js';
103
+ else if (deps['@clerk/nextjs']) stack.auth = 'Clerk';
104
+
105
+ if (deps['tailwindcss']) stack.styling = 'Tailwind CSS';
106
+ if (deps['typescript'] || existsSync(join(cwd, 'tsconfig.json'))) {
107
+ stack.language = 'TypeScript';
108
+ } else {
109
+ stack.language = 'JavaScript';
110
+ }
111
+
112
+ if (deps['vitest']) stack.testing = 'Vitest';
113
+ else if (deps['jest']) stack.testing = 'Jest';
114
+ else if (deps['@playwright/test']) stack.testing = 'Playwright';
115
+ } catch {
116
+ // Ignore parse errors
117
+ }
118
+ }
119
+
120
+ const sourceDirs = ['src', 'app', 'pages', 'components', 'lib'];
121
+ for (const dir of sourceDirs) {
122
+ const dirPath = join(cwd, dir);
123
+ if (existsSync(dirPath)) {
124
+ try {
125
+ if (statSync(dirPath).isDirectory()) {
126
+ const files = countSourceFiles(dirPath);
127
+ if (files > 0) {
128
+ sourceFileCount += files;
129
+ details.push(`${dir}/ with ${files} source files`);
130
+ }
131
+ }
132
+ } catch {
133
+ // Ignore access errors
134
+ }
135
+ }
136
+ }
137
+
138
+ const configFiles = ['tsconfig.json', 'next.config.js', 'next.config.mjs', 'vite.config.ts', 'tailwind.config.js'];
139
+ for (const file of configFiles) {
140
+ if (existsSync(join(cwd, file))) {
141
+ details.push(file);
142
+ }
143
+ }
144
+
145
+ return {
146
+ exists: sourceFileCount > 5 || details.length >= 3,
147
+ files: sourceFileCount,
148
+ details,
149
+ stack
150
+ };
151
+ }
152
+
153
+ function buildStructureString(cwd: string): string {
154
+ try {
155
+ const items = readdirSync(cwd);
156
+ const dirs: string[] = [];
157
+ const files: string[] = [];
158
+
159
+ for (const item of items) {
160
+ if (item.startsWith('.') || item === 'node_modules') continue;
161
+ const fullPath = join(cwd, item);
162
+ try {
163
+ if (statSync(fullPath).isDirectory()) {
164
+ dirs.push(item + '/');
165
+ } else {
166
+ files.push(item);
167
+ }
168
+ } catch {
169
+ // Skip inaccessible items
170
+ }
171
+ }
172
+
173
+ return [...dirs.sort(), ...files.sort()].join('\n');
174
+ } catch {
175
+ return '[Could not scan structure]';
176
+ }
177
+ }
178
+
179
+ // ============================================================================
180
+ // GUIDED QUESTIONS FOR NEW PROJECTS
181
+ // ============================================================================
182
+
183
+ interface GuidedAnswers {
184
+ oneLiner: string;
185
+ problem: string;
186
+ users: string;
187
+ features: string[];
188
+ auth: boolean;
189
+ payments: boolean;
190
+ integrations: string;
191
+ deadline: string;
192
+ }
193
+
194
+ async function runGuidedQuestions(): Promise<GuidedAnswers> {
195
+ console.log(chalk.cyan('\n ━━━ Let\'s define your project ━━━\n'));
196
+ console.log(chalk.gray(' Answer these questions (press Enter to skip any)\n'));
197
+
198
+ console.log(chalk.white(' 1. What are you building?\n'));
199
+ const oneLiner = await prompt(' ') || 'A web application';
200
+
201
+ console.log(chalk.white('\n 2. What problem does this solve?\n'));
202
+ const problem = await prompt(' ') || '';
203
+
204
+ console.log(chalk.white('\n 3. Who will use this?\n'));
205
+ console.log(chalk.gray(' (e.g., "small business owners", "freelancers", "developers")\n'));
206
+ const users = await prompt(' ') || 'General users';
207
+
208
+ console.log(chalk.white('\n 4. What are the 3 must-have features?\n'));
209
+ console.log(chalk.gray(' (Enter each feature, then press Enter. Type "done" when finished)\n'));
210
+ const features: string[] = [];
211
+ for (let i = 0; i < 5; i++) {
212
+ const feature = await prompt(` Feature ${i + 1}: `);
213
+ if (!feature || feature.toLowerCase() === 'done') break;
214
+ features.push(feature);
215
+ }
216
+
217
+ console.log(chalk.white('\n 5. Do users need to create accounts?\n'));
218
+ const authAnswer = await prompt(' (y/n): ');
219
+ const auth = authAnswer.toLowerCase() === 'y' || authAnswer.toLowerCase() === 'yes';
220
+
221
+ console.log(chalk.white('\n 6. Will you charge money?\n'));
222
+ const paymentsAnswer = await prompt(' (y/n): ');
223
+ const payments = paymentsAnswer.toLowerCase() === 'y' || paymentsAnswer.toLowerCase() === 'yes';
224
+
225
+ console.log(chalk.white('\n 7. Any specific integrations needed?\n'));
226
+ console.log(chalk.gray(' (e.g., "Stripe, SendGrid, Twilio" or press Enter to skip)\n'));
227
+ const integrations = await prompt(' ') || '';
228
+
229
+ console.log(chalk.white('\n 8. When do you need this done?\n'));
230
+ console.log(chalk.gray(' (e.g., "2 weeks", "end of month", or press Enter to skip)\n'));
231
+ const deadline = await prompt(' ') || '';
232
+
233
+ console.log(chalk.green('\n ✓ Got it! Creating your PRD...\n'));
234
+
235
+ return { oneLiner, problem, users, features, auth, payments, integrations, deadline };
236
+ }
237
+
238
+ function createPrdFromAnswers(projectName: string, projectType: string, answers: GuidedAnswers): string {
239
+ const date = new Date().toISOString().split('T')[0];
240
+
241
+ const featuresSection = answers.features.length > 0
242
+ ? answers.features.map((f, i) => `${i + 1}. [ ] **${f}**`).join('\n')
243
+ : '1. [ ] **Feature 1:** [To be defined]\n2. [ ] **Feature 2:** [To be defined]';
244
+
245
+ const techRequirements: string[] = [];
246
+ if (answers.auth) techRequirements.push('User authentication (Supabase Auth)');
247
+ if (answers.payments) techRequirements.push('Payment processing (Stripe)');
248
+ if (answers.integrations) techRequirements.push(answers.integrations);
249
+
250
+ return `# Product Requirements Document
251
+ # Project: ${projectName}
252
+ # Created: ${date}
253
+ # Type: ${projectType}
254
+
255
+ ## Overview
256
+ **One-liner:** ${answers.oneLiner}
257
+
258
+ **Problem:** ${answers.problem || '[To be refined]'}
259
+
260
+ **Solution:** ${answers.oneLiner}
261
+
262
+ ## Target Users
263
+ - **Primary:** ${answers.users}
264
+
265
+ ## Core Features (MVP)
266
+ ${featuresSection}
267
+
268
+ ## Technical Requirements
269
+ ${techRequirements.length > 0 ? techRequirements.map(t => `- ${t}`).join('\n') : '- [No specific requirements noted]'}
270
+
271
+ ## Timeline
272
+ ${answers.deadline ? `- **Target:** ${answers.deadline}` : '- [No deadline specified]'}
273
+
274
+ ## Notes
275
+ - Authentication: ${answers.auth ? 'Yes - users need accounts' : 'No - public access'}
276
+ - Payments: ${answers.payments ? 'Yes - will charge users' : 'No - free to use'}
277
+
278
+ ---
279
+ <!-- Generated from guided questions - AI reads this to build your project -->
280
+ `;
281
+ }
282
+
283
+ function createPrdTemplate(projectName: string, projectType: string): string {
284
+ const date = new Date().toISOString().split('T')[0];
285
+
286
+ const typeSpecificSections = projectType === 'client'
287
+ ? `
288
+ ## Client Info
289
+ - Client Name: [Who is this for?]
290
+ - Contact: [Primary contact]
291
+ - Deadline: [When is this due?]
292
+ `
293
+ : projectType === 'business'
294
+ ? `
295
+ ## Business Context
296
+ - Target Market: [Who are you selling to?]
297
+ - Revenue Model: [How does this make money?]
298
+ - MVP Deadline: [When do you need to launch?]
299
+ `
300
+ : `
301
+ ## Personal Goals
302
+ - Why am I building this? [Your motivation]
303
+ - Learning goals: [What do you want to learn?]
304
+ `;
305
+
306
+ return `# Product Requirements Document
307
+ # Project: ${projectName}
308
+ # Created: ${date}
309
+ # Type: ${projectType}
310
+
311
+ ## Overview
312
+ **One-liner:** [Describe this project in one sentence]
313
+
314
+ **Problem:** [What problem does this solve?]
315
+
316
+ **Solution:** [How does this solve it?]
317
+ ${typeSpecificSections}
318
+ ## Target Users
319
+ - **Primary:** [Who is the main user?]
320
+ - **Secondary:** [Other users?]
321
+
322
+ ## Core Features (MVP)
323
+ 1. [ ] **Feature 1:** [Description]
324
+ 2. [ ] **Feature 2:** [Description]
325
+ 3. [ ] **Feature 3:** [Description]
326
+
327
+ ## Nice-to-Have Features (Post-MVP)
328
+ 1. [ ] [Feature description]
329
+ 2. [ ] [Feature description]
330
+
331
+ ## Technical Requirements
332
+ - **Must use:** [Required technologies, APIs, etc.]
333
+ - **Must avoid:** [Things you don't want]
334
+
335
+ ## Success Metrics
336
+ - [ ] [How will you measure success?]
337
+ - [ ] [What does "done" look like?]
338
+
339
+ ---
340
+ <!-- AI reads this file to understand what to build -->
341
+ `;
342
+ }
343
+
344
+ // ============================================================================
345
+ // PROJECT FILE CREATION
346
+ // ============================================================================
347
+
348
+ function createProjectContext(projectName: string, stack: Record<string, string>, structure: string, isExisting: boolean): string {
349
+ const date = new Date().toISOString().split('T')[0];
350
+ return `# PROJECT CONTEXT
351
+ # Last Scanned: ${date}
352
+ # Mode: ${isExisting ? 'Existing Project' : 'New Project'}
353
+
354
+ ## Overview
355
+ name: ${projectName}
356
+ description: ${isExisting ? '[Existing project - AI will analyze on first interaction]' : '[AI will fill after first feature]'}
357
+
358
+ ## Tech Stack
359
+ framework: ${stack.framework || '[Not detected]'}
360
+ language: ${stack.language || '[Not detected]'}
361
+ database: ${stack.database || '[Not detected]'}
362
+ auth: ${stack.auth || '[Not detected]'}
363
+ styling: ${stack.styling || '[Not detected]'}
364
+ testing: ${stack.testing || '[Not detected]'}
365
+
366
+ ## Project Structure
367
+ \`\`\`
368
+ ${structure || '[Empty project]'}
369
+ \`\`\`
370
+
371
+ ## Key Files
372
+ <!-- AI: List the most important files for understanding the project -->
373
+ - Entry point: ${stack.framework?.includes('Next') ? 'src/app/page.tsx or pages/index.tsx' : '[AI will identify]'}
374
+ - Config: ${existsSync(join(process.cwd(), 'tsconfig.json')) ? 'tsconfig.json' : '[AI will identify]'}
375
+ - Database schema: [AI will identify]
376
+ - API routes: ${stack.framework?.includes('Next') ? 'src/app/api/ or pages/api/' : '[AI will identify]'}
377
+
378
+ ## Existing Patterns
379
+ <!-- AI: Document patterns you find so you can reuse them -->
380
+
381
+ ### API Route Pattern
382
+ \`\`\`typescript
383
+ [AI: Copy an example API route pattern from this project]
384
+ \`\`\`
385
+
386
+ ### Component Pattern
387
+ \`\`\`typescript
388
+ [AI: Copy an example component pattern from this project]
389
+ \`\`\`
390
+
391
+ ## Environment Variables
392
+ <!-- AI: List required env vars (don't include values!) -->
393
+ ${existsSync(join(process.cwd(), '.env.example')) ? '[Check .env.example]' : '- [ ] [AI will identify required vars]'}
394
+
395
+ ## Notes
396
+ <!-- AI: Any important context about this specific project -->
397
+ `;
398
+ }
399
+
400
+ function createProjectState(projectName: string, isExisting: boolean): string {
401
+ const date = new Date().toISOString().split('T')[0];
402
+ return `# PROJECT STATE
403
+ # Last Updated: ${date}
404
+ # Auto-maintained by AI - update when starting/completing tasks
405
+
406
+ ## Project Info
407
+ name: ${projectName}
408
+ phase: ${isExisting ? 'active' : 'planning'}
409
+ mode: ${isExisting ? 'existing-project' : 'new-project'}
410
+
411
+ ## Current Sprint
412
+ Goal: ${isExisting ? '[AI will identify based on conversation]' : '[Define in first conversation]'}
413
+
414
+ ## In Progress
415
+ <!-- AI: Add tasks here when you START working on them -->
416
+ <!-- Format: - [task] (started: date, agent: cursor/claude) -->
417
+
418
+ ## Completed
419
+ <!-- AI: Move tasks here when DONE -->
420
+ <!-- Format: - [task] (completed: date) -->
421
+ ${isExisting ? `\n- CodeBakers integration (completed: ${date})` : ''}
422
+
423
+ ## Blockers
424
+ <!-- AI: List anything blocking progress -->
425
+
426
+ ## Next Up
427
+ <!-- AI: Queue of upcoming tasks -->
428
+ `;
429
+ }
430
+
431
+ function createDecisionsLog(projectName: string): string {
432
+ const date = new Date().toISOString().split('T')[0];
433
+ return `# ARCHITECTURAL DECISIONS
434
+ # Project: ${projectName}
435
+ # AI: Add entries here when making significant technical choices
436
+
437
+ ## How to Use This File
438
+ When you make a decision that affects architecture, add an entry:
439
+ - Date
440
+ - Decision
441
+ - Reason
442
+ - Alternatives considered
443
+
444
+ ---
445
+
446
+ ## ${date}: CodeBakers Initialized
447
+ **Decision:** Using CodeBakers server-enforced pattern system
448
+ **Reason:** Ensure consistent, production-quality code
449
+ **Pattern:** Server-enforced via discover_patterns MCP tool
450
+
451
+ ---
452
+
453
+ <!-- AI: Add new decisions above this line -->
454
+ `;
455
+ }
456
+
457
+ function createDevlog(projectName: string, isExisting: boolean, auditScore?: number): string {
458
+ const date = new Date().toISOString().split('T')[0];
459
+ const timestamp = new Date().toISOString();
460
+
461
+ return `# Development Log
462
+ # Project: ${projectName}
463
+
464
+ ## ${date} - CodeBakers Integration
465
+ **Session:** ${timestamp}
466
+ **Task Size:** MEDIUM
467
+ **Status:** Completed
468
+
469
+ ### What was done:
470
+ - Integrated CodeBakers into ${isExisting ? 'existing' : 'new'} project
471
+ - Created project tracking files
472
+ - Configured AI assistants (Cursor + Claude Code)
473
+ ${isExisting && auditScore !== undefined ? `- Ran initial code audit (Score: ${auditScore}%)` : ''}
474
+
475
+ ### Files created:
476
+ - \`CLAUDE.md\` - AI bootstrap file
477
+ - \`.cursorrules\` - Cursor IDE rules
478
+ - \`PROJECT-CONTEXT.md\` - Project knowledge base
479
+ - \`PROJECT-STATE.md\` - Task tracking
480
+ - \`DECISIONS.md\` - Architecture log
481
+ - \`.codebakers/DEVLOG.md\` - This file
482
+
483
+ ### Next steps:
484
+ ${isExisting ? '- Start using AI assistance with existing codebase' : '- Define project requirements in first conversation'}
485
+ ${isExisting ? '- AI will analyze existing patterns on first interaction' : '- AI will help scaffold initial features'}
486
+
487
+ ---
488
+ `;
489
+ }
490
+
491
+ // ============================================================================
492
+ // IDE AND MCP SETUP
493
+ // ============================================================================
494
+
495
+ const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
496
+ # Zero-friction AI assistance - everything is automatic
497
+
498
+ ## ON EVERY MESSAGE - AUTOMATIC WORKFLOW
499
+
500
+ ### PHASE 1: CONTEXT LOAD (automatic)
501
+ 1. Read CLAUDE.md → Load router
502
+ 2. Read PROJECT-CONTEXT.md → Understand codebase
503
+ 3. Read PROJECT-STATE.md → Check what's in progress
504
+ 4. Read DECISIONS.md → Know past decisions
505
+
506
+ ### PHASE 2: PRE-FLIGHT CHECK (before writing code)
507
+ Ask yourself silently:
508
+ - [ ] What existing code does this touch? (check PROJECT-CONTEXT.md)
509
+ - [ ] Is similar code already in the codebase? (copy that pattern)
510
+ - [ ] What's the data model involved?
511
+ - [ ] What are the error cases?
512
+
513
+ ### PHASE 3: EXECUTE
514
+ - State: \`📋 CodeBakers | [Type] | Server-Enforced\`
515
+ - Call discover_patterns MCP tool first
516
+ - Follow patterns from server EXACTLY
517
+
518
+ ### PHASE 4: SELF-REVIEW (before saying "done")
519
+ - [ ] TypeScript compiles? (npx tsc --noEmit)
520
+ - [ ] Imports resolve correctly?
521
+ - [ ] Error handling exists?
522
+ - [ ] Matches existing patterns in codebase?
523
+ - [ ] Tests written?
524
+ - [ ] PROJECT-STATE.md updated?
525
+
526
+ If ANY check fails, fix it before responding.
527
+
528
+ ### PHASE 5: UPDATE STATE
529
+ - Update PROJECT-STATE.md with completed work
530
+ - Add to DECISIONS.md if architectural choice was made
531
+ - Update .codebakers/DEVLOG.md with session summary
532
+
533
+ ## REMEMBER
534
+ - You are a full product team, not just a code assistant
535
+ - The modules contain production-tested patterns — USE THEM
536
+ - When in doubt, check existing code first
537
+ `;
538
+
539
+ const CURSORIGNORE_TEMPLATE = `# CodeBakers - Files to ignore in Cursor context
540
+
541
+ # Dependencies
542
+ node_modules/
543
+ .pnpm-store/
544
+
545
+ # Build outputs
546
+ dist/
547
+ build/
548
+ .next/
549
+ .nuxt/
550
+ out/
551
+
552
+ # Cache
553
+ .cache/
554
+ .turbo/
555
+ *.tsbuildinfo
556
+
557
+ # Logs
558
+ logs/
559
+ *.log
560
+
561
+ # Environment files (don't leak secrets)
562
+ .env
563
+ .env.local
564
+ .env.*.local
565
+
566
+ # IDE
567
+ .idea/
568
+ *.swp
569
+
570
+ # OS
571
+ .DS_Store
572
+ Thumbs.db
573
+
574
+ # Test coverage
575
+ coverage/
576
+
577
+ # Package locks
578
+ package-lock.json
579
+ yarn.lock
580
+ pnpm-lock.yaml
581
+
582
+ # Generated files
583
+ *.min.js
584
+ *.min.css
585
+ *.map
586
+ `;
587
+
588
+ const VSCODE_SETTINGS_TEMPLATE = {
589
+ "cursor.chat.defaultContext": [
590
+ "CLAUDE.md",
591
+ "PROJECT-CONTEXT.md",
592
+ "PROJECT-STATE.md",
593
+ "DECISIONS.md"
594
+ ],
595
+ "cursor.chat.alwaysIncludeRules": true,
596
+ "cursor.composer.alwaysIncludeRules": true,
597
+ "cursor.general.enableAutoImport": true
598
+ };
599
+
600
+ function setupCursorIDE(cwd: string): void {
601
+ const spinner = ora(' Setting up Cursor IDE...').start();
602
+
603
+ try {
604
+ // Write .cursorrules and .cursorignore
605
+ writeFileSync(join(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
606
+ writeFileSync(join(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
607
+
608
+ // Global MCP config for Cursor
609
+ const homeDir = process.env.USERPROFILE || process.env.HOME || '';
610
+ const globalCursorDir = join(homeDir, '.cursor');
611
+ if (!existsSync(globalCursorDir)) {
612
+ mkdirSync(globalCursorDir, { recursive: true });
613
+ }
614
+
615
+ const mcpConfigPath = join(globalCursorDir, 'mcp.json');
616
+ const isWindows = process.platform === 'win32';
617
+ const mcpConfig = {
618
+ mcpServers: {
619
+ codebakers: isWindows
620
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
621
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
622
+ }
623
+ };
624
+
625
+ if (existsSync(mcpConfigPath)) {
626
+ try {
627
+ const existing = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
628
+ existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
629
+ writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2));
630
+ } catch {
631
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
632
+ }
633
+ } else {
634
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
635
+ }
636
+
637
+ // VSCode settings
638
+ const vscodeDir = join(cwd, '.vscode');
639
+ if (!existsSync(vscodeDir)) {
640
+ mkdirSync(vscodeDir, { recursive: true });
641
+ }
642
+
643
+ const settingsPath = join(vscodeDir, 'settings.json');
644
+ if (existsSync(settingsPath)) {
645
+ try {
646
+ const existing = JSON.parse(readFileSync(settingsPath, 'utf-8'));
647
+ writeFileSync(settingsPath, JSON.stringify({ ...existing, ...VSCODE_SETTINGS_TEMPLATE }, null, 2));
648
+ } catch {
649
+ writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
650
+ }
651
+ } else {
652
+ writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
653
+ }
654
+
655
+ spinner.succeed('Cursor IDE configured!');
656
+ } catch {
657
+ spinner.warn('Could not configure Cursor IDE (continuing anyway)');
658
+ }
659
+ }
660
+
661
+ function setupClaudeCodeMCP(): void {
662
+ const spinner = ora(' Setting up Claude Code MCP...').start();
663
+
664
+ try {
665
+ const homeDir = process.env.USERPROFILE || process.env.HOME || '';
666
+ let configPath: string;
667
+ const isWindows = process.platform === 'win32';
668
+
669
+ if (isWindows) {
670
+ configPath = join(homeDir, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
671
+ } else if (process.platform === 'darwin') {
672
+ configPath = join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
673
+ } else {
674
+ configPath = join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
675
+ }
676
+
677
+ const configDir = join(configPath, '..');
678
+ if (!existsSync(configDir)) {
679
+ mkdirSync(configDir, { recursive: true });
680
+ }
681
+
682
+ const mcpConfig = {
683
+ mcpServers: {
684
+ codebakers: isWindows
685
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
686
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
687
+ }
688
+ };
689
+
690
+ if (existsSync(configPath)) {
691
+ try {
692
+ const existing = JSON.parse(readFileSync(configPath, 'utf-8'));
693
+ if (!existing.mcpServers) {
694
+ existing.mcpServers = {};
695
+ }
696
+ existing.mcpServers.codebakers = mcpConfig.mcpServers.codebakers;
697
+ writeFileSync(configPath, JSON.stringify(existing, null, 2));
698
+ } catch {
699
+ writeFileSync(configPath, JSON.stringify(mcpConfig, null, 2));
700
+ }
701
+ } else {
702
+ writeFileSync(configPath, JSON.stringify(mcpConfig, null, 2));
703
+ }
704
+
705
+ spinner.succeed('Claude Code MCP configured!');
706
+ } catch {
707
+ spinner.warn('Could not configure Claude Code MCP (continuing anyway)');
708
+ }
709
+ }
710
+
711
+ function createTrackingFiles(cwd: string, projectName: string, stack: Record<string, string>, structure: string, isExisting: boolean, auditScore?: number): void {
712
+ const spinner = ora(' Creating project tracking files...').start();
713
+
714
+ try {
715
+ // Create .codebakers directory
716
+ const codebakersDir = join(cwd, '.codebakers');
717
+ if (!existsSync(codebakersDir)) {
718
+ mkdirSync(codebakersDir, { recursive: true });
719
+ }
720
+
721
+ // Remove old .claude folder if it exists
722
+ const claudeDir = join(cwd, '.claude');
723
+ if (existsSync(claudeDir)) {
724
+ try {
725
+ rmSync(claudeDir, { recursive: true, force: true });
726
+ } catch {
727
+ // Ignore errors
728
+ }
729
+ }
730
+
731
+ // PROJECT-CONTEXT.md
732
+ writeFileSync(join(cwd, 'PROJECT-CONTEXT.md'), createProjectContext(projectName, stack, structure, isExisting));
733
+
734
+ // PROJECT-STATE.md
735
+ writeFileSync(join(cwd, 'PROJECT-STATE.md'), createProjectState(projectName, isExisting));
736
+
737
+ // DECISIONS.md
738
+ writeFileSync(join(cwd, 'DECISIONS.md'), createDecisionsLog(projectName));
739
+
740
+ // .codebakers/DEVLOG.md
741
+ writeFileSync(join(codebakersDir, 'DEVLOG.md'), createDevlog(projectName, isExisting, auditScore));
742
+
743
+ // .codebakers.json state file
744
+ const stateFile = join(cwd, '.codebakers.json');
745
+ const state = {
746
+ version: '1.0',
747
+ serverEnforced: true,
748
+ projectType: isExisting ? 'existing' : 'new',
749
+ projectName,
750
+ createdAt: new Date().toISOString(),
751
+ stack,
752
+ auditScore: auditScore
753
+ };
754
+ writeFileSync(stateFile, JSON.stringify(state, null, 2));
755
+
756
+ spinner.succeed('Project tracking files created!');
757
+ } catch {
758
+ spinner.warn('Some tracking files could not be created');
759
+ }
760
+ }
761
+
762
+ function updateGitignore(cwd: string): void {
763
+ const gitignorePath = join(cwd, '.gitignore');
764
+ if (existsSync(gitignorePath)) {
765
+ const gitignore = readFileSync(gitignorePath, 'utf-8');
766
+ if (!gitignore.includes('.cursorrules')) {
767
+ writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.cursorrules\n');
768
+ }
769
+ }
770
+ }
771
+
34
772
  interface GoOptions {
35
773
  verbose?: boolean;
774
+ // Non-interactive flags for programmatic use (e.g., by AI assistants)
775
+ type?: 'personal' | 'client' | 'business';
776
+ name?: string;
777
+ describe?: 'guided' | 'template' | 'paste' | 'chat' | 'files';
778
+ skipReview?: boolean;
36
779
  }
37
780
 
38
781
  interface ConfirmData {
@@ -116,9 +859,8 @@ export async function go(options: GoOptions = {}): Promise<void> {
116
859
  log(`Found API key: ${existingApiKey.substring(0, 8)}...`, options);
117
860
  console.log(chalk.green(' ✓ You\'re already logged in!\n'));
118
861
 
119
- // Install patterns if not already installed
120
- await installPatternsWithApiKey(existingApiKey, options);
121
- await configureMCP(options);
862
+ // Run complete project setup
863
+ await setupProject(options, { apiKey: existingApiKey });
122
864
  await showSuccessAndRestart();
123
865
  return;
124
866
  }
@@ -136,9 +878,8 @@ export async function go(options: GoOptions = {}): Promise<void> {
136
878
  console.log(chalk.cyan(' codebakers extend\n'));
137
879
  }
138
880
 
139
- // Install patterns if not already installed
140
- await installPatterns(existingTrial.trialId, options);
141
- await configureMCP(options);
881
+ // Run complete project setup
882
+ await setupProject(options, { trialId: existingTrial.trialId });
142
883
  await showSuccessAndRestart();
143
884
  return;
144
885
  }
@@ -263,13 +1004,8 @@ async function startTrialWithGitHub(options: GoOptions = {}): Promise<void> {
263
1004
  spinner.succeed(`Trial started (${data.daysRemaining} days free)${username}`);
264
1005
  console.log('');
265
1006
 
266
- // Install v6.0 bootstrap files
267
- await installPatterns(data.trialId, options);
268
-
269
- // Configure MCP
270
- await configureMCP(options);
271
-
272
- // Show success and restart
1007
+ // Run complete project setup
1008
+ await setupProject(options, { trialId: data.trialId });
273
1009
  await showSuccessAndRestart();
274
1010
  return;
275
1011
  }
@@ -286,30 +1022,6 @@ async function startTrialWithGitHub(options: GoOptions = {}): Promise<void> {
286
1022
  }
287
1023
  }
288
1024
 
289
- async function configureMCP(options: GoOptions = {}): Promise<void> {
290
- log('Configuring MCP integration...', options);
291
- const spinner = ora('Configuring Claude Code integration...').start();
292
- const isWindows = process.platform === 'win32';
293
-
294
- const mcpCmd = isWindows
295
- ? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
296
- : 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
297
-
298
- try {
299
- execSync(mcpCmd, { stdio: 'pipe' });
300
- spinner.succeed('CodeBakers connected to Claude Code');
301
- } catch (error) {
302
- const errorMessage = error instanceof Error ? error.message : String(error);
303
- if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
304
- spinner.succeed('CodeBakers already connected to Claude Code');
305
- } else {
306
- spinner.warn('Could not auto-configure Claude Code');
307
- console.log(chalk.gray('\n Run this command manually:\n'));
308
- console.log(chalk.cyan(` ${mcpCmd}\n`));
309
- }
310
- }
311
- }
312
-
313
1025
  /**
314
1026
  * Handle API key login flow (for paid users)
315
1027
  */
@@ -334,13 +1046,8 @@ async function handleApiKeyLogin(options: GoOptions = {}): Promise<void> {
334
1046
  setApiKey(apiKey);
335
1047
  console.log(chalk.green(' ✓ Logged in successfully!\n'));
336
1048
 
337
- // Install patterns
338
- await installPatternsWithApiKey(apiKey, options);
339
-
340
- // Configure MCP
341
- await configureMCP(options);
342
-
343
- // Show success
1049
+ // Run complete project setup
1050
+ await setupProject(options, { apiKey });
344
1051
  await showSuccessAndRestart();
345
1052
 
346
1053
  } catch (error) {
@@ -513,69 +1220,303 @@ CodeBakers v6.0 - Server-Enforced Patterns
513
1220
  `;
514
1221
 
515
1222
  /**
516
- * Install v6.0 bootstrap files for API key users (paid users)
517
- * Only installs minimal CLAUDE.md and .cursorrules - no .claude/ folder
1223
+ * Complete project setup - handles everything:
1224
+ * - Detect new vs existing project
1225
+ * - Set up all tracking files
1226
+ * - Configure Cursor and Claude Code MCP
1227
+ * - Run guided questions for new projects
1228
+ * - Run code review for existing projects
518
1229
  */
519
- async function installPatternsWithApiKey(apiKey: string, options: GoOptions = {}): Promise<void> {
520
- log('Installing v6.0 bootstrap files (API key user)...', options);
521
- await installBootstrapFiles(options, { apiKey });
1230
+ async function setupProject(options: GoOptions = {}, auth?: AuthInfo): Promise<void> {
1231
+ const cwd = process.cwd();
1232
+
1233
+ // Detect if this is an existing project
1234
+ const projectInfo = detectExistingProject(cwd);
1235
+
1236
+ if (projectInfo.exists) {
1237
+ // Existing project detected
1238
+ await setupExistingProject(cwd, projectInfo, options, auth);
1239
+ } else {
1240
+ // New project
1241
+ await setupNewProject(cwd, options, auth);
1242
+ }
522
1243
  }
523
1244
 
524
- /**
525
- * Install v6.0 bootstrap files for trial users
526
- * Only installs minimal CLAUDE.md and .cursorrules - no .claude/ folder
527
- */
528
- async function installPatterns(trialId: string, options: GoOptions = {}): Promise<void> {
529
- log(`Installing v6.0 bootstrap files (trial: ${trialId.substring(0, 8)}...)`, options);
530
- await installBootstrapFiles(options, { trialId });
1245
+ async function setupNewProject(cwd: string, options: GoOptions = {}, auth?: AuthInfo): Promise<void> {
1246
+ console.log(chalk.cyan('\n ━━━ New Project Setup ━━━\n'));
1247
+
1248
+ let projectType: string;
1249
+ let projectName: string;
1250
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1251
+
1252
+ // Use flags if provided (non-interactive mode for AI)
1253
+ if (options.type) {
1254
+ projectType = options.type;
1255
+ projectName = options.name || defaultName;
1256
+ console.log(chalk.green(` Using: ${projectType.toUpperCase()} project named "${projectName}"\n`));
1257
+ } else {
1258
+ // Interactive mode - ask questions
1259
+ console.log(chalk.white(' What kind of project is this?\n'));
1260
+ console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
1261
+ console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
1262
+ console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
1263
+
1264
+ let typeChoice = '';
1265
+ while (!['1', '2', '3'].includes(typeChoice)) {
1266
+ typeChoice = await prompt(' Enter 1, 2, or 3: ');
1267
+ }
1268
+
1269
+ const typeMap: Record<string, string> = { '1': 'personal', '2': 'client', '3': 'business' };
1270
+ projectType = typeMap[typeChoice];
1271
+ projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
1272
+ }
1273
+
1274
+ console.log(chalk.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
1275
+
1276
+ // Install bootstrap files
1277
+ console.log(chalk.white(' Installing CodeBakers...\n'));
1278
+ installBootstrapFilesSync(cwd);
1279
+
1280
+ // Create tracking files
1281
+ const structure = buildStructureString(cwd);
1282
+ createTrackingFiles(cwd, projectName, {}, structure, false);
1283
+
1284
+ // Setup IDEs and MCP
1285
+ console.log('');
1286
+ setupCursorIDE(cwd);
1287
+ setupClaudeCodeMCP();
1288
+
1289
+ // Update .gitignore
1290
+ updateGitignore(cwd);
1291
+
1292
+ // How to describe project
1293
+ let describeChoice = '';
1294
+
1295
+ // Use flag if provided (non-interactive mode for AI)
1296
+ if (options.describe) {
1297
+ const describeMap: Record<string, string> = {
1298
+ 'guided': '1', 'template': '2', 'paste': '3', 'chat': '4', 'files': '5'
1299
+ };
1300
+ describeChoice = describeMap[options.describe] || '4';
1301
+ console.log(chalk.green(` Using: ${options.describe} mode for project description\n`));
1302
+ } else {
1303
+ // Interactive mode
1304
+ console.log(chalk.white('\n 📝 How would you like to describe your project?\n'));
1305
+ console.log(chalk.gray(' 1. ') + chalk.cyan('GUIDED QUESTIONS') + chalk.gray(' - I\'ll ask you step by step'));
1306
+ console.log(chalk.gray(' 2. ') + chalk.cyan('WRITE A PRD') + chalk.gray(' - Create a blank template to fill out'));
1307
+ console.log(chalk.gray(' 3. ') + chalk.cyan('PASTE/UPLOAD PRD') + chalk.gray(' - I already have requirements written'));
1308
+ console.log(chalk.gray(' 4. ') + chalk.cyan('DESCRIBE IN CHAT') + chalk.gray(' - Just tell the AI what you want'));
1309
+ console.log(chalk.gray(' 5. ') + chalk.cyan('SHARE FILES') + chalk.gray(' - I\'ll share docs/mockups/screenshots\n'));
1310
+
1311
+ while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
1312
+ describeChoice = await prompt(' Enter 1-5: ');
1313
+ }
1314
+ }
1315
+
1316
+ let prdCreated = false;
1317
+
1318
+ if (describeChoice === '1') {
1319
+ // Guided questions
1320
+ const answers = await runGuidedQuestions();
1321
+ const prdSpinner = ora(' Creating PRD from your answers...').start();
1322
+ writeFileSync(join(cwd, 'PRD.md'), createPrdFromAnswers(projectName, projectType, answers));
1323
+ prdSpinner.succeed('PRD created from your answers!');
1324
+ console.log(chalk.yellow('\n → Review PRD.md, then start building with the AI\n'));
1325
+ prdCreated = true;
1326
+ } else if (describeChoice === '2') {
1327
+ // Write PRD template
1328
+ const prdSpinner = ora(' Creating PRD template...').start();
1329
+ writeFileSync(join(cwd, 'PRD.md'), createPrdTemplate(projectName, projectType));
1330
+ prdSpinner.succeed('PRD template created!');
1331
+ console.log(chalk.yellow('\n → Open PRD.md and fill in your requirements\n'));
1332
+ prdCreated = true;
1333
+ } else if (describeChoice === '3') {
1334
+ // Paste/upload existing PRD
1335
+ console.log(chalk.cyan('\n ━━━ Paste Your Requirements ━━━\n'));
1336
+ console.log(chalk.gray(' Paste your PRD, requirements, or spec below.'));
1337
+ console.log(chalk.gray(' When done, type ') + chalk.cyan('END') + chalk.gray(' on a new line and press Enter.\n'));
1338
+
1339
+ const lines: string[] = [];
1340
+ let line = '';
1341
+ while (true) {
1342
+ line = await prompt(' ');
1343
+ if (line.toUpperCase() === 'END') break;
1344
+ lines.push(line);
1345
+ }
1346
+
1347
+ if (lines.length > 0) {
1348
+ const content = lines.join('\n');
1349
+ const prdContent = `# Product Requirements Document
1350
+ # Project: ${projectName}
1351
+ # Created: ${new Date().toISOString().split('T')[0]}
1352
+ # Type: ${projectType}
1353
+ # Source: Pasted by user
1354
+
1355
+ ${content}
1356
+
1357
+ ---
1358
+ <!-- User-provided requirements - AI reads this to build your project -->
1359
+ `;
1360
+ writeFileSync(join(cwd, 'PRD.md'), prdContent);
1361
+ console.log(chalk.green('\n ✓ Saved to PRD.md'));
1362
+ console.log(chalk.yellow(' → The AI will read this when you start building\n'));
1363
+ prdCreated = true;
1364
+ } else {
1365
+ console.log(chalk.gray('\n No content pasted. You can add PRD.md manually later.\n'));
1366
+ }
1367
+ } else if (describeChoice === '4') {
1368
+ // Describe in chat
1369
+ console.log(chalk.gray('\n Perfect! Just describe your project to the AI when you\'re ready.\n'));
1370
+ console.log(chalk.gray(' Example: "Build me a SaaS for invoice management with Stripe payments"\n'));
1371
+ } else {
1372
+ // Share files (option 5)
1373
+ console.log(chalk.gray('\n Great! When chatting with the AI:\n'));
1374
+ console.log(chalk.gray(' • Drag and drop your mockups or screenshots'));
1375
+ console.log(chalk.gray(' • Share links to Figma, design files, or websites'));
1376
+ console.log(chalk.gray(' • Reference existing apps: "Make it look like Linear"\n'));
1377
+ console.log(chalk.cyan(' The AI will analyze them and start building.\n'));
1378
+ }
1379
+
1380
+ // Confirm to server
1381
+ if (auth) {
1382
+ const apiUrl = getApiUrl();
1383
+ confirmDownload(apiUrl, auth, {
1384
+ version: '6.0',
1385
+ moduleCount: 0,
1386
+ cliVersion: getCliVersion(),
1387
+ command: 'go',
1388
+ projectName,
1389
+ }).catch(() => {});
1390
+ }
531
1391
  }
532
1392
 
533
- /**
534
- * Install v6.0 minimal bootstrap files
535
- * - CLAUDE.md: Instructions for Claude Code
536
- * - .cursorrules: Instructions for Cursor
537
- * - NO .claude/ folder - all patterns are server-side
538
- */
539
- async function installBootstrapFiles(options: GoOptions = {}, auth?: AuthInfo): Promise<void> {
540
- const spinner = ora('Installing CodeBakers v6.0...').start();
541
- const cwd = process.cwd();
1393
+ async function setupExistingProject(cwd: string, projectInfo: ProjectInfo, options: GoOptions = {}, auth?: AuthInfo): Promise<void> {
1394
+ console.log(chalk.cyan('\n ━━━ Existing Project Detected ━━━\n'));
542
1395
 
543
- try {
544
- const claudeMdPath = join(cwd, 'CLAUDE.md');
545
- const cursorRulesPath = join(cwd, '.cursorrules');
546
-
547
- // Check if already installed with v6
548
- if (existsSync(claudeMdPath)) {
549
- const content = readFileSync(claudeMdPath, 'utf-8');
550
- if (content.includes('v6.0') && content.includes('discover_patterns')) {
551
- spinner.succeed('CodeBakers v6.0 already installed');
552
- return;
1396
+ // Show what was detected
1397
+ console.log(chalk.gray(' Found:'));
1398
+ for (const detail of projectInfo.details.slice(0, 5)) {
1399
+ console.log(chalk.gray(` • ${detail}`));
1400
+ }
1401
+
1402
+ const stackItems = Object.entries(projectInfo.stack).filter(([_, v]) => v);
1403
+ if (stackItems.length > 0) {
1404
+ console.log(chalk.gray('\n Tech Stack:'));
1405
+ for (const [key, value] of stackItems) {
1406
+ console.log(chalk.gray(` • ${key}: ${value}`));
1407
+ }
1408
+ }
1409
+
1410
+ // Get project name
1411
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1412
+ let projectName: string;
1413
+ let reviewChoice: string;
1414
+
1415
+ // Use flags if provided (non-interactive mode for AI)
1416
+ if (options.name) {
1417
+ projectName = options.name;
1418
+ console.log(chalk.green(`\n Using project name: "${projectName}"\n`));
1419
+ } else {
1420
+ projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
1421
+ }
1422
+
1423
+ // Use skipReview flag or ask
1424
+ if (options.skipReview) {
1425
+ reviewChoice = '3';
1426
+ console.log(chalk.gray(' Skipping code review (--skip-review flag)\n'));
1427
+ } else if (options.type) {
1428
+ // If running in non-interactive mode, default to skip review
1429
+ reviewChoice = '3';
1430
+ console.log(chalk.gray(' Skipping code review (non-interactive mode)\n'));
1431
+ } else {
1432
+ // Interactive mode - ask about code review
1433
+ console.log(chalk.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
1434
+ console.log(chalk.gray(' 1. ') + chalk.cyan('YES, REVIEW & FIX') + chalk.gray(' - Run audit, then auto-fix issues'));
1435
+ console.log(chalk.gray(' 2. ') + chalk.cyan('REVIEW ONLY') + chalk.gray(' - Just show me the issues'));
1436
+ console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP') + chalk.gray(' - Just install CodeBakers\n'));
1437
+
1438
+ reviewChoice = '';
1439
+ while (!['1', '2', '3'].includes(reviewChoice)) {
1440
+ reviewChoice = await prompt(' Enter 1, 2, or 3: ');
1441
+ }
1442
+ }
1443
+
1444
+ let auditScore: number | undefined;
1445
+
1446
+ if (reviewChoice !== '3') {
1447
+ console.log(chalk.blue('\n Running code audit...\n'));
1448
+ const auditResult = await audit();
1449
+ auditScore = auditResult.score;
1450
+
1451
+ if (auditResult.score >= 90) {
1452
+ console.log(chalk.green('\n 🎉 Your code is already in great shape!\n'));
1453
+ } else if (reviewChoice === '1') {
1454
+ const fixableCount = auditResult.checks.filter(c => !c.passed && c.severity !== 'info').length;
1455
+ if (fixableCount > 0) {
1456
+ console.log(chalk.blue('\n 🔧 Attempting to auto-fix issues...\n'));
1457
+ const healResult = await heal({ auto: true });
1458
+
1459
+ if (healResult.fixed > 0) {
1460
+ console.log(chalk.green(`\n ✓ Fixed ${healResult.fixed} issue(s)!`));
1461
+ if (healResult.remaining > 0) {
1462
+ console.log(chalk.yellow(` ⚠ ${healResult.remaining} issue(s) need manual attention.`));
1463
+ }
1464
+ }
553
1465
  }
554
- // Upgrade from v5
555
- log('Upgrading from v5 to v6...', options);
1466
+ } else {
1467
+ console.log(chalk.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
556
1468
  }
1469
+ }
1470
+
1471
+ // Install files
1472
+ console.log(chalk.white('\n Installing CodeBakers...\n'));
1473
+ installBootstrapFilesSync(cwd);
1474
+
1475
+ // Create tracking files with detected stack
1476
+ const structure = buildStructureString(cwd);
1477
+ createTrackingFiles(cwd, projectName, projectInfo.stack, structure, true, auditScore);
1478
+
1479
+ // Setup IDEs and MCP
1480
+ console.log('');
1481
+ setupCursorIDE(cwd);
1482
+ setupClaudeCodeMCP();
1483
+
1484
+ // Update .gitignore
1485
+ updateGitignore(cwd);
1486
+
1487
+ // Confirm to server
1488
+ if (auth) {
1489
+ const apiUrl = getApiUrl();
1490
+ confirmDownload(apiUrl, auth, {
1491
+ version: '6.0',
1492
+ moduleCount: 0,
1493
+ cliVersion: getCliVersion(),
1494
+ command: 'go',
1495
+ projectName,
1496
+ }).catch(() => {});
1497
+ }
1498
+ }
1499
+
1500
+ function installBootstrapFilesSync(cwd: string): void {
1501
+ const spinner = ora(' Installing bootstrap files...').start();
557
1502
 
558
- // Write v6.0 bootstrap files
559
- writeFileSync(claudeMdPath, V6_CLAUDE_MD);
560
- writeFileSync(cursorRulesPath, V6_CURSORRULES);
561
-
562
- spinner.succeed('CodeBakers v6.0 installed');
563
- console.log(chalk.gray(' Patterns are server-enforced via MCP tools\n'));
564
-
565
- // Confirm install to server (non-blocking)
566
- if (auth) {
567
- const apiUrl = getApiUrl();
568
- confirmDownload(apiUrl, auth, {
569
- version: '6.0',
570
- moduleCount: 0, // No local modules in v6
571
- cliVersion: getCliVersion(),
572
- command: 'go',
573
- }).catch(() => {}); // Silently ignore
1503
+ try {
1504
+ writeFileSync(join(cwd, 'CLAUDE.md'), V6_CLAUDE_MD);
1505
+ // .cursorrules is written by setupCursorIDE
1506
+
1507
+ // Remove old .claude folder if it exists
1508
+ const claudeDir = join(cwd, '.claude');
1509
+ if (existsSync(claudeDir)) {
1510
+ try {
1511
+ rmSync(claudeDir, { recursive: true, force: true });
1512
+ } catch {
1513
+ // Ignore errors
1514
+ }
574
1515
  }
575
1516
 
1517
+ spinner.succeed('Bootstrap files installed!');
576
1518
  } catch (error) {
577
- log(`Error: ${error instanceof Error ? error.message : String(error)}`, options);
578
- spinner.warn('Could not install bootstrap files');
579
- console.log(chalk.gray(' MCP tools will still work without local files.\n'));
1519
+ spinner.fail('Failed to install bootstrap files');
1520
+ throw error;
580
1521
  }
581
1522
  }