@codebakers/cli 3.8.2 → 3.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,33 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { createInterface } from 'readline';
4
- import { writeFileSync, mkdirSync, existsSync, readFileSync, readdirSync, statSync } from 'fs';
5
- import { join, extname } from 'path';
4
+ import { writeFileSync, mkdirSync, existsSync, readFileSync, readdirSync, statSync, rmSync } from 'fs';
5
+ import { join } from 'path';
6
6
  import { setApiKey, getApiKey, getApiUrl } from '../config.js';
7
+ import { audit } from './audit.js';
8
+ import { heal } from './heal.js';
9
+
10
+ // ============================================================================
11
+ // TEMPLATES
12
+ // ============================================================================
13
+
14
+ const CLAUDE_MD_BOOTSTRAP = `# CodeBakers
15
+
16
+ **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
17
+
18
+ \`\`\`
19
+ Tool: discover_patterns
20
+ Args: { task: "description of what you're building" }
21
+ \`\`\`
22
+
23
+ The server will return:
24
+ - All coding patterns and rules you must follow
25
+ - Test requirements
26
+ - Validation instructions
27
+
28
+ **You cannot write code without calling this tool first. The server tracks compliance.**
29
+ `;
7
30
 
8
- // Enhanced .cursorrules with pre-flight and self-review automation
9
31
  const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
10
32
  # Zero-friction AI assistance - everything is automatic
11
33
 
@@ -13,10 +35,9 @@ const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
13
35
 
14
36
  ### PHASE 1: CONTEXT LOAD (automatic)
15
37
  1. Read CLAUDE.md → Load router
16
- 2. Read PRD.md → Understand what we're building
17
- 3. Read PROJECT-CONTEXT.md → Understand codebase
18
- 4. Read PROJECT-STATE.md → Check what's in progress
19
- 5. Read DECISIONS.md → Know past decisions
38
+ 2. Read PROJECT-CONTEXT.md → Understand codebase
39
+ 3. Read PROJECT-STATE.md → Check what's in progress
40
+ 4. Read DECISIONS.md → Know past decisions
20
41
 
21
42
  ### PHASE 2: PRE-FLIGHT CHECK (before writing code)
22
43
  Ask yourself silently:
@@ -24,16 +45,9 @@ Ask yourself silently:
24
45
  - [ ] Is similar code already in the codebase? (copy that pattern)
25
46
  - [ ] What's the data model involved?
26
47
  - [ ] What are the error cases?
27
- - [ ] Is someone else working on this? (check PROJECT-STATE.md)
28
-
29
- If PROJECT-CONTEXT.md is empty or stale (>7 days), SCAN THE PROJECT FIRST:
30
- - Read package.json for dependencies
31
- - Check src/ structure
32
- - Note existing patterns
33
- - Update PROJECT-CONTEXT.md
34
48
 
35
49
  ### PHASE 3: EXECUTE
36
- - State: \`📋 CodeBakers | [Type] | v6.0 Server-Enforced\`
50
+ - State: \`📋 CodeBakers | [Type] | Server-Enforced\`
37
51
  - Call discover_patterns MCP tool first
38
52
  - Follow patterns from server EXACTLY
39
53
 
@@ -50,11 +64,7 @@ If ANY check fails, fix it before responding.
50
64
  ### PHASE 5: UPDATE STATE
51
65
  - Update PROJECT-STATE.md with completed work
52
66
  - Add to DECISIONS.md if architectural choice was made
53
-
54
- ## MULTI-AGENT AWARENESS
55
- - ALWAYS check PROJECT-STATE.md "In Progress" section
56
- - Do NOT duplicate work another agent is doing
57
- - If conflict detected, STOP and ask user
67
+ - Update .codebakers/DEVLOG.md with session summary
58
68
 
59
69
  ## REMEMBER
60
70
  - You are a full product team, not just a code assistant
@@ -67,40 +77,31 @@ const CURSORIGNORE_TEMPLATE = `# CodeBakers - Files to ignore in Cursor context
67
77
  # Dependencies
68
78
  node_modules/
69
79
  .pnpm-store/
70
- bower_components/
71
80
 
72
81
  # Build outputs
73
82
  dist/
74
83
  build/
75
84
  .next/
76
85
  .nuxt/
77
- .output/
78
86
  out/
79
87
 
80
88
  # Cache
81
89
  .cache/
82
90
  .turbo/
83
- .eslintcache
84
- .prettiercache
85
91
  *.tsbuildinfo
86
92
 
87
93
  # Logs
88
94
  logs/
89
95
  *.log
90
- npm-debug.log*
91
- yarn-debug.log*
92
- yarn-error.log*
93
96
 
94
97
  # Environment files (don't leak secrets)
95
98
  .env
96
99
  .env.local
97
100
  .env.*.local
98
- .env.production
99
101
 
100
102
  # IDE
101
103
  .idea/
102
104
  *.swp
103
- *.swo
104
105
 
105
106
  # OS
106
107
  .DS_Store
@@ -108,9 +109,8 @@ Thumbs.db
108
109
 
109
110
  # Test coverage
110
111
  coverage/
111
- .nyc_output/
112
112
 
113
- # Package locks (large files, not needed for context)
113
+ # Package locks
114
114
  package-lock.json
115
115
  yarn.lock
116
116
  pnpm-lock.yaml
@@ -124,7 +124,6 @@ pnpm-lock.yaml
124
124
  const VSCODE_SETTINGS_TEMPLATE = {
125
125
  "cursor.chat.defaultContext": [
126
126
  "CLAUDE.md",
127
- "PRD.md",
128
127
  "PROJECT-CONTEXT.md",
129
128
  "PROJECT-STATE.md",
130
129
  "DECISIONS.md"
@@ -134,119 +133,9 @@ const VSCODE_SETTINGS_TEMPLATE = {
134
133
  "cursor.general.enableAutoImport": true
135
134
  };
136
135
 
137
- function createPrdTemplate(projectName: string, projectType: string): string {
138
- const date = new Date().toISOString().split('T')[0];
139
-
140
- const typeSpecificSections = projectType === 'client'
141
- ? `
142
- ## Client Info
143
- - Client Name: [Who is this for?]
144
- - Contact: [Primary contact]
145
- - Deadline: [When is this due?]
146
- - Budget: [If relevant]
147
- `
148
- : projectType === 'business'
149
- ? `
150
- ## Business Context
151
- - Target Market: [Who are you selling to?]
152
- - Revenue Model: [How does this make money?]
153
- - Competition: [Who are you competing with?]
154
- - MVP Deadline: [When do you need to launch?]
155
- `
156
- : `
157
- ## Personal Goals
158
- - Why am I building this? [Your motivation]
159
- - Learning goals: [What do you want to learn?]
160
- - Time commitment: [Hours per week?]
161
- `;
162
-
163
- return `# Product Requirements Document
164
- # Project: ${projectName}
165
- # Created: ${date}
166
- # Type: ${projectType}
167
-
168
- ## Overview
169
- **One-liner:** [Describe this project in one sentence]
170
-
171
- **Problem:** [What problem does this solve?]
172
-
173
- **Solution:** [How does this solve it?]
174
- ${typeSpecificSections}
175
- ## Target Users
176
- - **Primary:** [Who is the main user?]
177
- - **Secondary:** [Other users?]
178
-
179
- ## Core Features (MVP)
180
- <!-- List the MINIMUM features needed to launch -->
181
- <!-- AI will build these first -->
182
-
183
- 1. [ ] **Feature 1:** [Description]
184
- - Acceptance criteria: [How do we know it's done?]
185
-
186
- 2. [ ] **Feature 2:** [Description]
187
- - Acceptance criteria: [How do we know it's done?]
188
-
189
- 3. [ ] **Feature 3:** [Description]
190
- - Acceptance criteria: [How do we know it's done?]
191
-
192
- ## Nice-to-Have Features (Post-MVP)
193
- <!-- Features to add after MVP is working -->
194
-
195
- 1. [ ] [Feature description]
196
- 2. [ ] [Feature description]
197
-
198
- ## Technical Requirements
199
- <!-- AI will use these to make architecture decisions -->
200
-
201
- - **Must use:** [Required technologies, APIs, etc.]
202
- - **Must avoid:** [Things you don't want]
203
- - **Performance:** [Any speed/scale requirements?]
204
- - **Security:** [Auth requirements, data sensitivity?]
205
-
206
- ## User Flows
207
- <!-- Describe the main user journeys -->
208
-
209
- ### Flow 1: [Name]
210
- 1. User does X
211
- 2. System responds with Y
212
- 3. User sees Z
213
-
214
- ### Flow 2: [Name]
215
- 1. User does X
216
- 2. System responds with Y
217
- 3. User sees Z
218
-
219
- ## Data Model (if known)
220
- <!-- Rough idea of main entities -->
221
-
222
- - **User:** [fields]
223
- - **[Entity 2]:** [fields]
224
- - **[Entity 3]:** [fields]
225
-
226
- ## Success Metrics
227
- - [ ] [How will you measure success?]
228
- - [ ] [What does "done" look like?]
229
-
230
- ## Open Questions
231
- <!-- Things you're unsure about - AI can help clarify -->
232
-
233
- 1. [Question?]
234
- 2. [Question?]
235
-
236
- ---
237
- <!-- AI INSTRUCTIONS -->
238
- <!-- When building features, reference this PRD -->
239
- <!-- Check off features as they're completed -->
240
- <!-- Add new questions to Open Questions -->
241
- <!-- Update this doc as requirements change -->
242
- `;
243
- }
244
-
245
- interface ContentResponse {
246
- version: string;
247
- router: string;
248
- modules: Record<string, string>;
249
- }
136
+ // ============================================================================
137
+ // HELPER FUNCTIONS
138
+ // ============================================================================
250
139
 
251
140
  type ProjectType = 'personal' | 'client' | 'business';
252
141
 
@@ -269,96 +158,171 @@ async function confirm(question: string): Promise<boolean> {
269
158
  return answer.toLowerCase() !== 'n';
270
159
  }
271
160
 
272
- async function selectProjectType(): Promise<{ type: ProjectType; name: string }> {
273
- console.log(chalk.white('\n What kind of project is this?\n'));
274
- console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
275
- console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
276
- console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
277
-
278
- let typeChoice = '';
279
- while (!['1', '2', '3'].includes(typeChoice)) {
280
- typeChoice = await prompt(' Enter 1, 2, or 3: ');
161
+ function countSourceFiles(dir: string): number {
162
+ let count = 0;
163
+ try {
164
+ const items = readdirSync(dir, { withFileTypes: true });
165
+ for (const item of items) {
166
+ if (item.name.startsWith('.') || item.name === 'node_modules') continue;
167
+
168
+ const fullPath = join(dir, item.name);
169
+ if (item.isDirectory()) {
170
+ count += countSourceFiles(fullPath);
171
+ } else if (
172
+ item.name.endsWith('.ts') ||
173
+ item.name.endsWith('.tsx') ||
174
+ item.name.endsWith('.js') ||
175
+ item.name.endsWith('.jsx')
176
+ ) {
177
+ count++;
178
+ }
179
+ }
180
+ } catch {
181
+ // Ignore access errors
281
182
  }
183
+ return count;
184
+ }
282
185
 
283
- const typeMap: Record<string, ProjectType> = {
284
- '1': 'personal',
285
- '2': 'client',
286
- '3': 'business'
287
- };
186
+ function detectExistingProject(cwd: string): { exists: boolean; files: number; details: string[]; stack: Record<string, string> } {
187
+ const details: string[] = [];
188
+ const stack: Record<string, string> = {};
189
+ let sourceFileCount = 0;
288
190
 
289
- const cwd = process.cwd();
290
- const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
291
- const name = await prompt(` Project name (${defaultName}): `) || defaultName;
191
+ // Check for package.json with dependencies
192
+ const packageJsonPath = join(cwd, 'package.json');
193
+ if (existsSync(packageJsonPath)) {
194
+ try {
195
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
196
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
197
+ const depCount = Object.keys(pkg.dependencies || {}).length;
292
198
 
293
- return {
294
- type: typeMap[typeChoice],
295
- name
296
- };
297
- }
199
+ if (depCount > 0) {
200
+ details.push(`package.json with ${depCount} dependencies`);
201
+ }
298
202
 
299
- function createProjectState(projectName: string, projectType: string): string {
300
- const date = new Date().toISOString().split('T')[0];
301
- return `# PROJECT STATE
302
- # Last Updated: ${date}
303
- # Auto-maintained by AI - update when starting/completing tasks
203
+ // Detect stack
204
+ if (deps['next']) stack.framework = `Next.js ${deps['next']}`;
205
+ else if (deps['react']) stack.framework = `React ${deps['react']}`;
206
+ else if (deps['vue']) stack.framework = `Vue ${deps['vue']}`;
207
+ else if (deps['express']) stack.framework = `Express ${deps['express']}`;
304
208
 
305
- ## Project Info
306
- name: ${projectName}
307
- type: ${projectType}
308
- phase: planning
209
+ if (deps['drizzle-orm']) stack.database = 'Drizzle ORM';
210
+ else if (deps['prisma']) stack.database = 'Prisma';
211
+ else if (deps['mongoose']) stack.database = 'MongoDB/Mongoose';
309
212
 
310
- ## Current Sprint
311
- Goal: [AI will fill this based on conversation]
213
+ if (deps['@supabase/supabase-js']) stack.auth = 'Supabase Auth';
214
+ else if (deps['next-auth']) stack.auth = 'NextAuth.js';
215
+ else if (deps['@clerk/nextjs']) stack.auth = 'Clerk';
312
216
 
313
- ## In Progress
314
- <!-- AI: Add tasks here when you START working on them -->
315
- <!-- Format: - [task] (started: date, agent: cursor/claude) -->
217
+ if (deps['tailwindcss']) stack.styling = 'Tailwind CSS';
218
+ if (deps['typescript'] || existsSync(join(cwd, 'tsconfig.json'))) {
219
+ stack.language = 'TypeScript';
220
+ } else {
221
+ stack.language = 'JavaScript';
222
+ }
316
223
 
317
- ## Completed
318
- <!-- AI: Move tasks here when DONE -->
319
- <!-- Format: - [task] (completed: date) -->
224
+ if (deps['vitest']) stack.testing = 'Vitest';
225
+ else if (deps['jest']) stack.testing = 'Jest';
226
+ else if (deps['@playwright/test']) stack.testing = 'Playwright';
227
+ } catch {
228
+ // Ignore parse errors
229
+ }
230
+ }
320
231
 
321
- ## Blockers
322
- <!-- AI: List anything blocking progress -->
232
+ // Check for source directories
233
+ const sourceDirs = ['src', 'app', 'pages', 'components', 'lib'];
234
+ for (const dir of sourceDirs) {
235
+ const dirPath = join(cwd, dir);
236
+ if (existsSync(dirPath)) {
237
+ try {
238
+ if (statSync(dirPath).isDirectory()) {
239
+ const files = countSourceFiles(dirPath);
240
+ if (files > 0) {
241
+ sourceFileCount += files;
242
+ details.push(`${dir}/ with ${files} source files`);
243
+ }
244
+ }
245
+ } catch {
246
+ // Ignore access errors
247
+ }
248
+ }
249
+ }
323
250
 
324
- ## Next Up
325
- <!-- AI: Queue of upcoming tasks -->
326
- `;
251
+ // Check for common config files
252
+ const configFiles = ['tsconfig.json', 'next.config.js', 'next.config.mjs', 'vite.config.ts', 'tailwind.config.js'];
253
+ for (const file of configFiles) {
254
+ if (existsSync(join(cwd, file))) {
255
+ details.push(file);
256
+ }
257
+ }
258
+
259
+ return {
260
+ exists: sourceFileCount > 5 || details.length >= 3,
261
+ files: sourceFileCount,
262
+ details,
263
+ stack
264
+ };
265
+ }
266
+
267
+ function buildStructureString(cwd: string): string {
268
+ try {
269
+ const items = readdirSync(cwd);
270
+ const dirs: string[] = [];
271
+ const files: string[] = [];
272
+
273
+ for (const item of items) {
274
+ if (item.startsWith('.') || item === 'node_modules') continue;
275
+ const fullPath = join(cwd, item);
276
+ try {
277
+ if (statSync(fullPath).isDirectory()) {
278
+ dirs.push(item + '/');
279
+ } else {
280
+ files.push(item);
281
+ }
282
+ } catch {
283
+ // Skip inaccessible items
284
+ }
285
+ }
286
+
287
+ return [...dirs.sort(), ...files.sort()].join('\n');
288
+ } catch {
289
+ return '[Could not scan structure]';
290
+ }
327
291
  }
328
292
 
329
- function createProjectContext(projectName: string, projectType: string): string {
293
+ // ============================================================================
294
+ // FILE CREATION FUNCTIONS
295
+ // ============================================================================
296
+
297
+ function createProjectContext(projectName: string, stack: Record<string, string>, structure: string, isExisting: boolean): string {
330
298
  const date = new Date().toISOString().split('T')[0];
331
299
  return `# PROJECT CONTEXT
332
300
  # Last Scanned: ${date}
333
- # AI: Update this when you first analyze the project or when structure changes significantly
301
+ # Mode: ${isExisting ? 'Existing Project' : 'New Project'}
334
302
 
335
303
  ## Overview
336
304
  name: ${projectName}
337
- type: ${projectType}
338
- description: [AI will fill after scanning]
305
+ description: ${isExisting ? '[Existing project - AI will analyze on first interaction]' : '[AI will fill after first feature]'}
339
306
 
340
307
  ## Tech Stack
341
- <!-- AI: Fill this by reading package.json and checking file extensions -->
342
- framework:
343
- language:
344
- database:
345
- auth:
346
- styling:
347
- testing:
308
+ framework: ${stack.framework || '[Not detected]'}
309
+ language: ${stack.language || '[Not detected]'}
310
+ database: ${stack.database || '[Not detected]'}
311
+ auth: ${stack.auth || '[Not detected]'}
312
+ styling: ${stack.styling || '[Not detected]'}
313
+ testing: ${stack.testing || '[Not detected]'}
348
314
 
349
315
  ## Project Structure
350
- <!-- AI: Fill this by scanning the directory structure -->
351
316
  \`\`\`
352
- [AI will map the project structure here]
317
+ ${structure || '[Empty project]'}
353
318
  \`\`\`
354
319
 
355
320
  ## Key Files
356
321
  <!-- AI: List the most important files for understanding the project -->
357
- - Entry point:
358
- - Config:
359
- - Database schema:
360
- - API routes:
361
- - Components:
322
+ - Entry point: ${stack.framework?.includes('Next') ? 'src/app/page.tsx or pages/index.tsx' : '[AI will identify]'}
323
+ - Config: ${existsSync(join(process.cwd(), 'tsconfig.json')) ? 'tsconfig.json' : '[AI will identify]'}
324
+ - Database schema: [AI will identify]
325
+ - API routes: ${stack.framework?.includes('Next') ? 'src/app/api/ or pages/api/' : '[AI will identify]'}
362
326
 
363
327
  ## Existing Patterns
364
328
  <!-- AI: Document patterns you find so you can reuse them -->
@@ -373,21 +337,46 @@ testing:
373
337
  [AI: Copy an example component pattern from this project]
374
338
  \`\`\`
375
339
 
376
- ### Database Query Pattern
377
- \`\`\`typescript
378
- [AI: Copy an example database query pattern from this project]
379
- \`\`\`
380
-
381
340
  ## Environment Variables
382
341
  <!-- AI: List required env vars (don't include values!) -->
383
- - [ ] DATABASE_URL
384
- - [ ] [others...]
342
+ ${existsSync(join(process.cwd(), '.env.example')) ? '[Check .env.example]' : '- [ ] [AI will identify required vars]'}
385
343
 
386
344
  ## Notes
387
345
  <!-- AI: Any important context about this specific project -->
388
346
  `;
389
347
  }
390
348
 
349
+ function createProjectState(projectName: string, isExisting: boolean): string {
350
+ const date = new Date().toISOString().split('T')[0];
351
+ return `# PROJECT STATE
352
+ # Last Updated: ${date}
353
+ # Auto-maintained by AI - update when starting/completing tasks
354
+
355
+ ## Project Info
356
+ name: ${projectName}
357
+ phase: ${isExisting ? 'active' : 'planning'}
358
+ mode: ${isExisting ? 'existing-project' : 'new-project'}
359
+
360
+ ## Current Sprint
361
+ Goal: ${isExisting ? '[AI will identify based on conversation]' : '[Define in first conversation]'}
362
+
363
+ ## In Progress
364
+ <!-- AI: Add tasks here when you START working on them -->
365
+ <!-- Format: - [task] (started: date, agent: cursor/claude) -->
366
+
367
+ ## Completed
368
+ <!-- AI: Move tasks here when DONE -->
369
+ <!-- Format: - [task] (completed: date) -->
370
+ ${isExisting ? `\n- CodeBakers integration (completed: ${date})` : ''}
371
+
372
+ ## Blockers
373
+ <!-- AI: List anything blocking progress -->
374
+
375
+ ## Next Up
376
+ <!-- AI: Queue of upcoming tasks -->
377
+ `;
378
+ }
379
+
391
380
  function createDecisionsLog(projectName: string): string {
392
381
  const date = new Date().toISOString().split('T')[0];
393
382
  return `# ARCHITECTURAL DECISIONS
@@ -400,12 +389,11 @@ When you make a decision that affects architecture, add an entry:
400
389
  - Decision
401
390
  - Reason
402
391
  - Alternatives considered
403
- - Pattern location (if applicable)
404
392
 
405
393
  ---
406
394
 
407
- ## ${date}: Project Initialized
408
- **Decision:** Using CodeBakers v6.0 pattern system
395
+ ## ${date}: CodeBakers Initialized
396
+ **Decision:** Using CodeBakers server-enforced pattern system
409
397
  **Reason:** Ensure consistent, production-quality code
410
398
  **Pattern:** Server-enforced via discover_patterns MCP tool
411
399
 
@@ -415,234 +403,159 @@ When you make a decision that affects architecture, add an entry:
415
403
  `;
416
404
  }
417
405
 
418
- /**
419
- * Auto-scan project to detect tech stack
420
- */
421
- function scanProject(cwd: string): { stack: Record<string, string>; structure: string } {
422
- const stack: Record<string, string> = {};
423
- let structure = '';
424
-
425
- // Check package.json
426
- const packageJsonPath = join(cwd, 'package.json');
427
- if (existsSync(packageJsonPath)) {
428
- try {
429
- const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
430
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
431
-
432
- // Detect framework
433
- if (deps['next']) stack.framework = `Next.js ${deps['next']}`;
434
- else if (deps['react']) stack.framework = `React ${deps['react']}`;
435
- else if (deps['vue']) stack.framework = `Vue ${deps['vue']}`;
436
- else if (deps['express']) stack.framework = `Express ${deps['express']}`;
406
+ function createDevlog(projectName: string, isExisting: boolean, auditScore?: number): string {
407
+ const date = new Date().toISOString().split('T')[0];
408
+ const timestamp = new Date().toISOString();
437
409
 
438
- // Detect database
439
- if (deps['drizzle-orm']) stack.database = 'Drizzle ORM';
440
- else if (deps['prisma']) stack.database = 'Prisma';
441
- else if (deps['mongoose']) stack.database = 'MongoDB/Mongoose';
442
- else if (deps['pg']) stack.database = 'PostgreSQL';
410
+ return `# Development Log
411
+ # Project: ${projectName}
443
412
 
444
- // Detect auth
445
- if (deps['@supabase/supabase-js']) stack.auth = 'Supabase Auth';
446
- else if (deps['next-auth']) stack.auth = 'NextAuth.js';
447
- else if (deps['@clerk/nextjs']) stack.auth = 'Clerk';
413
+ ## ${date} - CodeBakers Integration
414
+ **Session:** ${timestamp}
415
+ **Task Size:** MEDIUM
416
+ **Status:** Completed
417
+
418
+ ### What was done:
419
+ - Integrated CodeBakers into ${isExisting ? 'existing' : 'new'} project
420
+ - Created project tracking files
421
+ - Set up Cursor IDE configuration
422
+ ${isExisting && auditScore !== undefined ? `- Ran initial code audit (Score: ${auditScore}%)` : ''}
423
+
424
+ ### Files created:
425
+ - \`CLAUDE.md\` - AI bootstrap file
426
+ - \`.cursorrules\` - Cursor IDE rules
427
+ - \`PROJECT-CONTEXT.md\` - Project knowledge base
428
+ - \`PROJECT-STATE.md\` - Task tracking
429
+ - \`DECISIONS.md\` - Architecture log
430
+ - \`.codebakers/DEVLOG.md\` - This file
431
+
432
+ ### Next steps:
433
+ ${isExisting ? '- Start using AI assistance with existing codebase' : '- Define project requirements in first conversation'}
434
+ ${isExisting ? '- AI will analyze existing patterns on first interaction' : '- AI will help scaffold initial features'}
448
435
 
449
- // Detect styling
450
- if (deps['tailwindcss']) stack.styling = 'Tailwind CSS';
451
- else if (deps['styled-components']) stack.styling = 'Styled Components';
436
+ ---
437
+ `;
438
+ }
452
439
 
453
- // Detect testing
454
- if (deps['vitest']) stack.testing = 'Vitest';
455
- else if (deps['jest']) stack.testing = 'Jest';
456
- else if (deps['@playwright/test']) stack.testing = 'Playwright';
440
+ function createPrdTemplate(projectName: string, projectType: string): string {
441
+ const date = new Date().toISOString().split('T')[0];
457
442
 
458
- // Detect language
459
- if (deps['typescript'] || existsSync(join(cwd, 'tsconfig.json'))) {
460
- stack.language = 'TypeScript';
461
- } else {
462
- stack.language = 'JavaScript';
463
- }
464
- } catch {
465
- // Ignore parse errors
466
- }
467
- }
468
-
469
- // Build simple structure
470
- try {
471
- const items = readdirSync(cwd);
472
- const dirs: string[] = [];
473
- const files: string[] = [];
474
-
475
- for (const item of items) {
476
- if (item.startsWith('.') || item === 'node_modules') continue;
477
- const fullPath = join(cwd, item);
478
- try {
479
- if (statSync(fullPath).isDirectory()) {
480
- dirs.push(item + '/');
481
- } else {
482
- files.push(item);
483
- }
484
- } catch {
485
- // Skip inaccessible items
486
- }
487
- }
488
-
489
- structure = [...dirs.sort(), ...files.sort()].join('\n');
490
- } catch {
491
- structure = '[Could not scan structure]';
492
- }
443
+ const typeSpecificSections = projectType === 'client'
444
+ ? `
445
+ ## Client Info
446
+ - Client Name: [Who is this for?]
447
+ - Contact: [Primary contact]
448
+ - Deadline: [When is this due?]
449
+ `
450
+ : projectType === 'business'
451
+ ? `
452
+ ## Business Context
453
+ - Target Market: [Who are you selling to?]
454
+ - Revenue Model: [How does this make money?]
455
+ - MVP Deadline: [When do you need to launch?]
456
+ `
457
+ : `
458
+ ## Personal Goals
459
+ - Why am I building this? [Your motivation]
460
+ - Learning goals: [What do you want to learn?]
461
+ `;
493
462
 
494
- return { stack, structure };
495
- }
463
+ return `# Product Requirements Document
464
+ # Project: ${projectName}
465
+ # Created: ${date}
466
+ # Type: ${projectType}
496
467
 
497
- function updateProjectContextWithScan(contextContent: string, stack: Record<string, string>, structure: string): string {
498
- let updated = contextContent;
468
+ ## Overview
469
+ **One-liner:** [Describe this project in one sentence]
499
470
 
500
- // Update tech stack
501
- if (stack.framework) updated = updated.replace('framework: ', `framework: ${stack.framework}`);
502
- if (stack.language) updated = updated.replace('language: ', `language: ${stack.language}`);
503
- if (stack.database) updated = updated.replace('database: ', `database: ${stack.database}`);
504
- if (stack.auth) updated = updated.replace('auth: ', `auth: ${stack.auth}`);
505
- if (stack.styling) updated = updated.replace('styling: ', `styling: ${stack.styling}`);
506
- if (stack.testing) updated = updated.replace('testing: ', `testing: ${stack.testing}`);
471
+ **Problem:** [What problem does this solve?]
507
472
 
508
- // Update structure
509
- updated = updated.replace(
510
- '[AI will map the project structure here]',
511
- structure || '[Empty project]'
512
- );
473
+ **Solution:** [How does this solve it?]
474
+ ${typeSpecificSections}
475
+ ## Target Users
476
+ - **Primary:** [Who is the main user?]
477
+ - **Secondary:** [Other users?]
513
478
 
514
- return updated;
515
- }
479
+ ## Core Features (MVP)
480
+ 1. [ ] **Feature 1:** [Description]
481
+ 2. [ ] **Feature 2:** [Description]
482
+ 3. [ ] **Feature 3:** [Description]
516
483
 
517
- /**
518
- * Interactive init command - walks users through complete setup
519
- */
520
- export async function init(): Promise<void> {
521
- console.log(chalk.blue(`
522
- ╔═══════════════════════════════════════════════════════════╗
523
- ║ ║
524
- ║ ${chalk.bold('Welcome to CodeBakers!')} ║
525
- ║ ║
526
- ║ Production-grade patterns for AI-assisted development ║
527
- ║ ║
528
- ╚═══════════════════════════════════════════════════════════╝
529
- `));
484
+ ## Nice-to-Have Features (Post-MVP)
485
+ 1. [ ] [Feature description]
486
+ 2. [ ] [Feature description]
530
487
 
531
- console.log(chalk.gray(' This wizard will set up CodeBakers in your project.\n'));
488
+ ## Technical Requirements
489
+ - **Must use:** [Required technologies, APIs, etc.]
490
+ - **Must avoid:** [Things you don't want]
532
491
 
533
- const cwd = process.cwd();
492
+ ## Success Metrics
493
+ - [ ] [How will you measure success?]
494
+ - [ ] [What does "done" look like?]
534
495
 
535
- // Check if already initialized
536
- const claudeMdPath = join(cwd, 'CLAUDE.md');
537
- if (existsSync(claudeMdPath)) {
538
- const reinitialize = await confirm(' CLAUDE.md already exists. Reinitialize?');
539
- if (!reinitialize) {
540
- console.log(chalk.yellow('\n Skipping. Run with a fresh project or delete CLAUDE.md first.\n'));
541
- return;
542
- }
543
- }
496
+ ---
497
+ <!-- AI reads this file to understand what to build -->
498
+ `;
499
+ }
544
500
 
545
- // Step 1: Get project type
546
- const { type: projectType, name: projectName } = await selectProjectType();
547
- console.log(chalk.green(`\n ✓ Setting up ${projectName} as ${projectType.toUpperCase()} project\n`));
501
+ // ============================================================================
502
+ // SHARED SETUP FUNCTIONS
503
+ // ============================================================================
548
504
 
549
- // Step 2: Check if already logged in
505
+ async function ensureLoggedIn(): Promise<string> {
550
506
  let apiKey = getApiKey();
551
507
 
552
508
  if (apiKey) {
553
509
  console.log(chalk.green(' ✓ Already logged in\n'));
554
510
  const useExisting = await confirm(' Use existing API key?');
555
- if (!useExisting) {
556
- apiKey = null;
557
- }
511
+ if (useExisting) return apiKey;
512
+ apiKey = null;
558
513
  }
559
514
 
560
- // Step 3: Login if needed
561
- if (!apiKey) {
562
- console.log(chalk.white('\n Step 1: Get your API key\n'));
563
- console.log(chalk.gray(' Go to: ') + chalk.cyan('https://codebakers.ai/dashboard'));
564
- console.log(chalk.gray(' Copy your API key (starts with cb_)\n'));
515
+ console.log(chalk.white('\n Login to CodeBakers\n'));
516
+ console.log(chalk.gray(' Go to: ') + chalk.cyan('https://codebakers.ai/dashboard'));
517
+ console.log(chalk.gray(' Copy your API key (starts with cb_)\n'));
565
518
 
566
- apiKey = await prompt(' Paste your API key: ');
567
-
568
- if (!apiKey || !apiKey.startsWith('cb_')) {
569
- console.log(chalk.red('\n ✗ Invalid API key. Keys start with "cb_"\n'));
570
- console.log(chalk.gray(' Get your key at https://codebakers.ai/dashboard\n'));
571
- process.exit(1);
572
- }
519
+ apiKey = await prompt(' Paste your API key: ');
573
520
 
574
- const spinner = ora(' Validating API key...').start();
575
-
576
- try {
577
- const apiUrl = getApiUrl();
578
- const response = await fetch(`${apiUrl}/api/content`, {
579
- method: 'GET',
580
- headers: {
581
- Authorization: `Bearer ${apiKey}`,
582
- },
583
- });
584
-
585
- if (!response.ok) {
586
- const error = await response.json().catch(() => ({}));
587
- throw new Error(error.error || 'Invalid API key');
588
- }
589
-
590
- setApiKey(apiKey);
591
- spinner.succeed('API key valid!');
592
- } catch (error) {
593
- spinner.fail('Invalid API key');
594
- const message = error instanceof Error ? error.message : 'Unknown error';
595
- console.log(chalk.red(`\n Error: ${message}\n`));
596
- process.exit(1);
597
- }
521
+ if (!apiKey || !apiKey.startsWith('cb_')) {
522
+ console.log(chalk.red('\n ✗ Invalid API key. Keys start with "cb_"\n'));
523
+ process.exit(1);
598
524
  }
599
525
 
600
- // Step 4: Install v6.0 bootstrap files
601
- console.log(chalk.white('\n Step 2: Installing CodeBakers v6.0\n'));
602
-
603
- const spinner = ora(' Installing v6.0 bootstrap...').start();
604
-
605
- // v6.0 bootstrap content - minimal files, patterns from server
606
- const V6_CLAUDE_MD = `# CodeBakers v6.0
607
-
608
- **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
609
-
610
- \`\`\`
611
- Tool: discover_patterns
612
- Args: { task: "description of what you're building" }
613
- \`\`\`
614
-
615
- The server will return:
616
- - All coding patterns and rules you must follow
617
- - Test requirements
618
- - Validation instructions
619
-
620
- **You cannot write code without calling this tool first. The server tracks compliance.**
621
-
622
- ---
623
- *CodeBakers v6.0 - Server-Enforced*
624
- `;
526
+ const spinner = ora(' Validating API key...').start();
625
527
 
626
- const V6_CURSORRULES = `# CodeBakers v6.0
528
+ try {
529
+ const apiUrl = getApiUrl();
530
+ const response = await fetch(`${apiUrl}/api/content`, {
531
+ method: 'GET',
532
+ headers: { Authorization: `Bearer ${apiKey}` },
533
+ });
627
534
 
628
- MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
535
+ if (!response.ok) {
536
+ throw new Error('Invalid API key');
537
+ }
629
538
 
630
- Tool: discover_patterns
631
- Args: { task: "description of what you're building" }
539
+ setApiKey(apiKey);
540
+ spinner.succeed('API key valid!');
541
+ return apiKey;
542
+ } catch (error) {
543
+ spinner.fail('Invalid API key');
544
+ process.exit(1);
545
+ }
546
+ }
632
547
 
633
- The server returns all patterns, rules, and test requirements.
634
- You cannot write code without calling this tool first.
635
- `;
548
+ function installBootstrapFiles(cwd: string): void {
549
+ const spinner = ora(' Installing bootstrap files...').start();
636
550
 
637
551
  try {
638
- // Write v6.0 bootstrap files
639
- writeFileSync(join(cwd, 'CLAUDE.md'), V6_CLAUDE_MD);
640
- writeFileSync(join(cwd, '.cursorrules'), V6_CURSORRULES);
552
+ writeFileSync(join(cwd, 'CLAUDE.md'), CLAUDE_MD_BOOTSTRAP);
553
+ writeFileSync(join(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
554
+ writeFileSync(join(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
641
555
 
642
- // Remove old .claude folder if it exists (v5 → v6 migration)
556
+ // Remove old .claude folder if it exists
643
557
  const claudeDir = join(cwd, '.claude');
644
558
  if (existsSync(claudeDir)) {
645
- const { rmSync } = await import('fs');
646
559
  try {
647
560
  rmSync(claudeDir, { recursive: true, force: true });
648
561
  } catch {
@@ -650,71 +563,18 @@ You cannot write code without calling this tool first.
650
563
  }
651
564
  }
652
565
 
653
- spinner.succeed('CodeBakers v6.0 installed!');
654
- console.log(chalk.gray('\n Patterns are server-enforced via MCP tools'));
655
-
566
+ spinner.succeed('Bootstrap files installed!');
656
567
  } catch (error) {
657
- spinner.fail('Installation failed');
658
- const message = error instanceof Error ? error.message : 'Unknown error';
659
- console.log(chalk.red(`\n Error: ${message}\n`));
660
- process.exit(1);
568
+ spinner.fail('Failed to install bootstrap files');
569
+ throw error;
661
570
  }
571
+ }
662
572
 
663
- // Step 5: Auto-scan project
664
- console.log(chalk.white('\n Step 3: Scanning project\n'));
665
-
666
- const scanSpinner = ora(' Analyzing project structure...').start();
667
- const { stack, structure } = scanProject(cwd);
668
-
669
- const detectedItems = Object.entries(stack).filter(([_, v]) => v).map(([k, v]) => `${k}: ${v}`);
670
- if (detectedItems.length > 0) {
671
- scanSpinner.succeed('Project analyzed!');
672
- console.log(chalk.gray('\n Detected:'));
673
- for (const item of detectedItems) {
674
- console.log(chalk.gray(` ${item}`));
675
- }
676
- } else {
677
- scanSpinner.succeed('Project scanned (new project detected)');
678
- }
679
-
680
- // Step 6: Create PROJECT-CONTEXT.md with scan results
681
- console.log(chalk.white('\n Step 4: Setting up project files\n'));
682
-
683
- const filesSpinner = ora(' Creating project files...').start();
684
-
685
- try {
686
- // PROJECT-CONTEXT.md
687
- let contextContent = createProjectContext(projectName, projectType);
688
- contextContent = updateProjectContextWithScan(contextContent, stack, structure);
689
- writeFileSync(join(cwd, 'PROJECT-CONTEXT.md'), contextContent);
690
-
691
- // PROJECT-STATE.md
692
- const stateContent = createProjectState(projectName, projectType);
693
- writeFileSync(join(cwd, 'PROJECT-STATE.md'), stateContent);
694
-
695
- // DECISIONS.md
696
- const decisionsContent = createDecisionsLog(projectName);
697
- writeFileSync(join(cwd, 'DECISIONS.md'), decisionsContent);
698
-
699
- filesSpinner.succeed('Project files created!');
700
- } catch (error) {
701
- filesSpinner.warn('Some project files could not be created');
702
- }
703
-
704
- // Step 7: Install Cursor files
705
- console.log(chalk.white('\n Step 5: Setting up Cursor IDE\n'));
706
-
707
- const cursorSpinner = ora(' Installing Cursor configuration...').start();
573
+ function setupCursorIDE(cwd: string): void {
574
+ const spinner = ora(' Setting up Cursor IDE...').start();
708
575
 
709
576
  try {
710
- // Write .cursorrules
711
- writeFileSync(join(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
712
-
713
- // Write .cursorignore
714
- writeFileSync(join(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
715
-
716
- // Create GLOBAL ~/.cursor/mcp.json for MCP server configuration
717
- // Cursor reads MCP config from global location, not project-local
577
+ // Global MCP config
718
578
  const homeDir = process.env.USERPROFILE || process.env.HOME || '';
719
579
  const globalCursorDir = join(homeDir, '.cursor');
720
580
  if (!existsSync(globalCursorDir)) {
@@ -731,7 +591,6 @@ You cannot write code without calling this tool first.
731
591
  }
732
592
  };
733
593
 
734
- // Merge with existing MCP config if present
735
594
  if (existsSync(mcpConfigPath)) {
736
595
  try {
737
596
  const existing = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
@@ -744,135 +603,323 @@ You cannot write code without calling this tool first.
744
603
  writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
745
604
  }
746
605
 
747
- // Create/merge .vscode/settings.json
606
+ // VSCode settings
748
607
  const vscodeDir = join(cwd, '.vscode');
749
608
  if (!existsSync(vscodeDir)) {
750
609
  mkdirSync(vscodeDir, { recursive: true });
751
610
  }
752
611
 
753
- const existingSettingsPath = join(vscodeDir, 'settings.json');
754
-
755
- if (existsSync(existingSettingsPath)) {
756
- const existing = JSON.parse(readFileSync(existingSettingsPath, 'utf-8'));
757
- const merged = { ...existing, ...VSCODE_SETTINGS_TEMPLATE };
758
- writeFileSync(existingSettingsPath, JSON.stringify(merged, null, 2));
612
+ const settingsPath = join(vscodeDir, 'settings.json');
613
+ if (existsSync(settingsPath)) {
614
+ const existing = JSON.parse(readFileSync(settingsPath, 'utf-8'));
615
+ writeFileSync(settingsPath, JSON.stringify({ ...existing, ...VSCODE_SETTINGS_TEMPLATE }, null, 2));
759
616
  } else {
760
- writeFileSync(existingSettingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
617
+ writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
761
618
  }
762
619
 
763
- cursorSpinner.succeed('Cursor configuration installed!');
764
- console.log(chalk.green(' ✓ MCP server configured (~/.cursor/mcp.json)'));
765
- } catch (error) {
766
- cursorSpinner.warn('Could not install Cursor files (continuing anyway)');
620
+ spinner.succeed('Cursor IDE configured!');
621
+ } catch {
622
+ spinner.warn('Could not configure Cursor IDE (continuing anyway)');
767
623
  }
624
+ }
768
625
 
769
- // Step 8: Add to .gitignore if not present
626
+ function updateGitignore(cwd: string): void {
770
627
  const gitignorePath = join(cwd, '.gitignore');
771
628
  if (existsSync(gitignorePath)) {
772
629
  const gitignore = readFileSync(gitignorePath, 'utf-8');
773
630
  if (!gitignore.includes('.cursorrules')) {
774
- const additions = '\n# CodeBakers\n.cursorrules\n';
775
- writeFileSync(gitignorePath, gitignore + additions);
631
+ writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.cursorrules\n');
632
+ }
633
+ }
634
+ }
635
+
636
+ function createTrackingFiles(cwd: string, projectName: string, stack: Record<string, string>, structure: string, isExisting: boolean, auditScore?: number): void {
637
+ const spinner = ora(' Creating project tracking files...').start();
638
+
639
+ try {
640
+ // Create .codebakers directory
641
+ const codebakersDir = join(cwd, '.codebakers');
642
+ if (!existsSync(codebakersDir)) {
643
+ mkdirSync(codebakersDir, { recursive: true });
776
644
  }
645
+
646
+ // PROJECT-CONTEXT.md
647
+ writeFileSync(join(cwd, 'PROJECT-CONTEXT.md'), createProjectContext(projectName, stack, structure, isExisting));
648
+
649
+ // PROJECT-STATE.md
650
+ writeFileSync(join(cwd, 'PROJECT-STATE.md'), createProjectState(projectName, isExisting));
651
+
652
+ // DECISIONS.md
653
+ writeFileSync(join(cwd, 'DECISIONS.md'), createDecisionsLog(projectName));
654
+
655
+ // .codebakers/DEVLOG.md
656
+ writeFileSync(join(codebakersDir, 'DEVLOG.md'), createDevlog(projectName, isExisting, auditScore));
657
+
658
+ // .codebakers.json state file
659
+ const stateFile = join(cwd, '.codebakers.json');
660
+ const state = {
661
+ version: '1.0',
662
+ serverEnforced: true,
663
+ projectType: isExisting ? 'existing' : 'new',
664
+ projectName,
665
+ createdAt: new Date().toISOString(),
666
+ stack,
667
+ auditScore: auditScore
668
+ };
669
+ writeFileSync(stateFile, JSON.stringify(state, null, 2));
670
+
671
+ spinner.succeed('Project tracking files created!');
672
+ } catch (error) {
673
+ spinner.warn('Some tracking files could not be created');
674
+ }
675
+ }
676
+
677
+ // ============================================================================
678
+ // MODE: NEW PROJECT
679
+ // ============================================================================
680
+
681
+ async function initNewProject(cwd: string): Promise<void> {
682
+ console.log(chalk.cyan('\n ━━━ New Project Setup ━━━\n'));
683
+
684
+ // Get project info
685
+ console.log(chalk.white(' What kind of project is this?\n'));
686
+ console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
687
+ console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
688
+ console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
689
+
690
+ let typeChoice = '';
691
+ while (!['1', '2', '3'].includes(typeChoice)) {
692
+ typeChoice = await prompt(' Enter 1, 2, or 3: ');
777
693
  }
778
694
 
779
- // Step 9: PRD Setup
780
- console.log(chalk.white('\n Step 6: Product Requirements\n'));
695
+ const typeMap: Record<string, ProjectType> = { '1': 'personal', '2': 'client', '3': 'business' };
696
+ const projectType = typeMap[typeChoice];
697
+
698
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
699
+ const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
700
+
701
+ console.log(chalk.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
781
702
 
782
- const prdPath = join(cwd, 'PRD.md');
783
- let prdCreated = false;
703
+ // Login
704
+ await ensureLoggedIn();
784
705
 
785
- if (existsSync(prdPath)) {
786
- console.log(chalk.green(' PRD.md already exists\n'));
787
- prdCreated = true;
706
+ // Install files
707
+ console.log(chalk.white('\n Installing CodeBakers...\n'));
708
+ installBootstrapFiles(cwd);
709
+
710
+ // Create tracking files
711
+ const structure = buildStructureString(cwd);
712
+ createTrackingFiles(cwd, projectName, {}, structure, false);
713
+
714
+ // Setup Cursor
715
+ console.log('');
716
+ setupCursorIDE(cwd);
717
+
718
+ // Update .gitignore
719
+ updateGitignore(cwd);
720
+
721
+ // PRD Setup
722
+ console.log(chalk.white('\n Product Requirements Document\n'));
723
+ console.log(chalk.gray(' A PRD helps the AI understand what you\'re building.\n'));
724
+ console.log(chalk.gray(' 1. ') + chalk.cyan('CREATE TEMPLATE') + chalk.gray(' - I\'ll fill it out'));
725
+ console.log(chalk.gray(' 2. ') + chalk.cyan('SKIP FOR NOW') + chalk.gray(' - I\'ll describe it in chat\n'));
726
+
727
+ let prdChoice = '';
728
+ while (!['1', '2'].includes(prdChoice)) {
729
+ prdChoice = await prompt(' Enter 1 or 2: ');
730
+ }
731
+
732
+ if (prdChoice === '1') {
733
+ const prdSpinner = ora(' Creating PRD template...').start();
734
+ writeFileSync(join(cwd, 'PRD.md'), createPrdTemplate(projectName, projectType));
735
+ prdSpinner.succeed('PRD template created!');
736
+ console.log(chalk.yellow('\n → Open PRD.md and fill in your requirements\n'));
788
737
  } else {
789
- console.log(chalk.gray(' A PRD helps the AI understand what you\'re building.\n'));
790
- console.log(chalk.white(' How would you like to set up your PRD?\n'));
791
- console.log(chalk.gray(' 0. ') + chalk.magenta('You Decide') + chalk.gray(' - Let AI pick the best option'));
792
- console.log(chalk.gray(' 1. ') + chalk.cyan('CREATE TEMPLATE') + chalk.gray(' - I\'ll fill it out'));
793
- console.log(chalk.gray(' 2. ') + chalk.cyan('PASTE CONTENT') + chalk.gray(' - I have requirements ready'));
794
- console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP FOR NOW') + chalk.gray(' - I\'ll add it later\n'));
795
-
796
- let prdChoice = '';
797
- while (!['0', '1', '2', '3'].includes(prdChoice)) {
798
- prdChoice = await prompt(' Enter 0, 1, 2, or 3: ');
799
- }
738
+ console.log(chalk.gray('\n No problem! Just describe your project to the AI.\n'));
739
+ }
800
740
 
801
- // "You Decide" defaults to creating a template (most helpful)
802
- if (prdChoice === '0') {
803
- console.log(chalk.magenta(' → AI chose: Create Template (recommended)\n'));
804
- prdChoice = '1';
741
+ // Success
742
+ showSuccessMessage(projectName, false, prdChoice === '1');
743
+ }
744
+
745
+ // ============================================================================
746
+ // MODE: EXISTING PROJECT
747
+ // ============================================================================
748
+
749
+ async function initExistingProject(cwd: string, projectInfo: ReturnType<typeof detectExistingProject>): Promise<void> {
750
+ console.log(chalk.cyan('\n ━━━ Existing Project Setup ━━━\n'));
751
+
752
+ // Show what was detected
753
+ console.log(chalk.gray(' Detected:'));
754
+ for (const detail of projectInfo.details.slice(0, 5)) {
755
+ console.log(chalk.gray(` • ${detail}`));
756
+ }
757
+
758
+ const stackItems = Object.entries(projectInfo.stack).filter(([_, v]) => v);
759
+ if (stackItems.length > 0) {
760
+ console.log(chalk.gray('\n Tech Stack:'));
761
+ for (const [key, value] of stackItems) {
762
+ console.log(chalk.gray(` • ${key}: ${value}`));
805
763
  }
764
+ }
806
765
 
807
- if (prdChoice === '1') {
808
- // Create template
809
- const prdSpinner = ora(' Creating PRD template...').start();
810
- const prdContent = createPrdTemplate(projectName, projectType);
811
- writeFileSync(prdPath, prdContent);
812
- prdSpinner.succeed('PRD template created!');
813
- console.log(chalk.yellow('\n → Open PRD.md and fill in your requirements'));
814
- console.log(chalk.gray(' The AI will use this to understand what to build.\n'));
815
- prdCreated = true;
816
- } else if (prdChoice === '2') {
817
- // Paste content
818
- console.log(chalk.gray('\n Paste your PRD content below.'));
819
- console.log(chalk.gray(' When done, type ') + chalk.cyan('END') + chalk.gray(' on a new line and press Enter.\n'));
820
-
821
- const lines: string[] = [];
822
- let line = '';
823
-
824
- while (true) {
825
- line = await prompt(' ');
826
- if (line.trim().toUpperCase() === 'END') break;
827
- lines.push(line);
828
- }
766
+ // Get project name
767
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
768
+ const projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
829
769
 
830
- if (lines.length > 0) {
831
- const prdSpinner = ora(' Saving PRD...').start();
832
- const header = `# Product Requirements Document\n# Project: ${projectName}\n# Created: ${new Date().toISOString().split('T')[0]}\n\n`;
833
- writeFileSync(prdPath, header + lines.join('\n'));
834
- prdSpinner.succeed('PRD saved!');
835
- prdCreated = true;
836
- } else {
837
- console.log(chalk.yellow(' No content provided, skipping PRD.\n'));
770
+ // Code review offer
771
+ console.log(chalk.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
772
+ console.log(chalk.gray(' 1. ') + chalk.cyan('YES, REVIEW & FIX') + chalk.gray(' - Run audit, then auto-fix issues'));
773
+ console.log(chalk.gray(' 2. ') + chalk.cyan('REVIEW ONLY') + chalk.gray(' - Just show me the issues'));
774
+ console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP') + chalk.gray(' - Just install CodeBakers\n'));
775
+
776
+ let reviewChoice = '';
777
+ while (!['1', '2', '3'].includes(reviewChoice)) {
778
+ reviewChoice = await prompt(' Enter 1, 2, or 3: ');
779
+ }
780
+
781
+ let auditScore: number | undefined;
782
+
783
+ if (reviewChoice !== '3') {
784
+ console.log(chalk.blue('\n Running code audit...\n'));
785
+ const auditResult = await audit();
786
+ auditScore = auditResult.score;
787
+
788
+ if (auditResult.score >= 90) {
789
+ console.log(chalk.green('\n 🎉 Your code is already in great shape!\n'));
790
+ } else if (reviewChoice === '1') {
791
+ const fixableCount = auditResult.checks.filter(c => !c.passed && c.severity !== 'info').length;
792
+ if (fixableCount > 0) {
793
+ console.log(chalk.blue('\n 🔧 Attempting to auto-fix issues...\n'));
794
+ const healResult = await heal({ auto: true });
795
+
796
+ if (healResult.fixed > 0) {
797
+ console.log(chalk.green(`\n ✓ Fixed ${healResult.fixed} issue(s)!`));
798
+ if (healResult.remaining > 0) {
799
+ console.log(chalk.yellow(` ⚠ ${healResult.remaining} issue(s) need manual attention.`));
800
+ }
801
+ }
838
802
  }
839
803
  } else {
840
- console.log(chalk.gray('\n You can add PRD.md anytime. The AI will use it automatically.\n'));
804
+ console.log(chalk.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
841
805
  }
842
806
  }
843
807
 
844
- // Success message
808
+ // Login
809
+ console.log('');
810
+ await ensureLoggedIn();
811
+
812
+ // Install files
813
+ console.log(chalk.white('\n Installing CodeBakers...\n'));
814
+ installBootstrapFiles(cwd);
815
+
816
+ // Create tracking files with detected stack
817
+ const structure = buildStructureString(cwd);
818
+ createTrackingFiles(cwd, projectName, projectInfo.stack, structure, true, auditScore);
819
+
820
+ // Setup Cursor
821
+ console.log('');
822
+ setupCursorIDE(cwd);
823
+
824
+ // Update .gitignore
825
+ updateGitignore(cwd);
826
+
827
+ // Success
828
+ showSuccessMessage(projectName, true);
829
+ }
830
+
831
+ // ============================================================================
832
+ // SUCCESS MESSAGE
833
+ // ============================================================================
834
+
835
+ function showSuccessMessage(projectName: string, isExisting: boolean, prdCreated?: boolean): void {
845
836
  console.log(chalk.green(`
846
837
  ╔═══════════════════════════════════════════════════════════╗
847
838
  ║ ║
848
- ║ ${chalk.bold('✓ Setup Complete!')}
839
+ ║ ${chalk.bold('✓ CodeBakers Setup Complete!')}
849
840
  ║ ║
850
841
  ╚═══════════════════════════════════════════════════════════╝
851
842
  `));
852
843
 
853
844
  console.log(chalk.white(' Files created:\n'));
854
- console.log(chalk.cyan(' CLAUDE.md ') + chalk.gray('→ v6.0 bootstrap (patterns via MCP)'));
855
- console.log(chalk.cyan(' .cursorrules ') + chalk.gray('→ v6.0 bootstrap (patterns via MCP)'));
856
- if (prdCreated) {
857
- console.log(chalk.cyan(' PRD.md ') + chalk.gray('→ Product requirements (AI reads this!)'));
845
+ console.log(chalk.cyan(' CLAUDE.md ') + chalk.gray('→ AI bootstrap (patterns via MCP)'));
846
+ console.log(chalk.cyan(' .cursorrules ') + chalk.gray('→ Cursor IDE rules'));
847
+ if (!isExisting && prdCreated) {
848
+ console.log(chalk.cyan(' PRD.md ') + chalk.gray('→ Product requirements'));
849
+ }
850
+ console.log(chalk.cyan(' PROJECT-CONTEXT.md ') + chalk.gray('→ Codebase knowledge'));
851
+ console.log(chalk.cyan(' PROJECT-STATE.md ') + chalk.gray('→ Task tracking'));
852
+ console.log(chalk.cyan(' DECISIONS.md ') + chalk.gray('→ Architecture log'));
853
+ console.log(chalk.cyan(' .codebakers/DEVLOG.md') + chalk.gray('→ Development log\n'));
854
+
855
+ if (isExisting) {
856
+ console.log(chalk.white(' What happens next:\n'));
857
+ console.log(chalk.gray(' ✓ AI will analyze your existing code patterns'));
858
+ console.log(chalk.gray(' ✓ AI follows your existing conventions'));
859
+ console.log(chalk.gray(' ✓ AI updates tracking files as you work\n'));
860
+ } else {
861
+ console.log(chalk.white(' What happens next:\n'));
862
+ console.log(chalk.gray(' ✓ Describe what you want to build'));
863
+ console.log(chalk.gray(' ✓ AI fetches patterns and generates code'));
864
+ console.log(chalk.gray(' ✓ AI updates tracking files as you work\n'));
858
865
  }
859
- console.log(chalk.cyan(' PROJECT-CONTEXT.md ') + chalk.gray('→ Codebase knowledge (auto-updated)'));
860
- console.log(chalk.cyan(' PROJECT-STATE.md ') + chalk.gray('→ Task tracking (auto-updated)'));
861
- console.log(chalk.cyan(' DECISIONS.md ') + chalk.gray('→ Architecture log (auto-updated)'));
862
- console.log(chalk.cyan(' .cursorignore ') + chalk.gray('→ Context optimization\n'));
863
866
 
864
- console.log(chalk.white(' What happens automatically:\n'));
865
- console.log(chalk.gray(' AI loads context before every response'));
866
- console.log(chalk.gray(' AI checks for existing patterns to copy'));
867
- console.log(chalk.gray(' ✓ AI validates code before outputting'));
868
- console.log(chalk.gray(' ✓ AI updates project state after completing tasks'));
869
- console.log(chalk.gray(' ✓ AI logs architectural decisions\n'));
867
+ console.log(chalk.white(' Getting started:\n'));
868
+ console.log(chalk.gray(' For Cursor: Just open the project and start chatting'));
869
+ console.log(chalk.gray(' For Claude Code: Run ') + chalk.cyan('codebakers install-hook') + chalk.gray(' first\n'));
870
870
 
871
- console.log(chalk.white(' For Cursor users:\n'));
872
- console.log(chalk.gray(' Just open the project and start chatting!\n'));
871
+ console.log(chalk.blue(' Ready to build! 🚀\n'));
872
+ }
873
+
874
+ // ============================================================================
875
+ // MAIN INIT FUNCTION
876
+ // ============================================================================
877
+
878
+ export async function init(): Promise<void> {
879
+ console.log(chalk.blue(`
880
+ ╔═══════════════════════════════════════════════════════════╗
881
+ ║ ║
882
+ ║ ${chalk.bold('Welcome to CodeBakers!')} ║
883
+ ║ ║
884
+ ║ Production-grade patterns for AI-assisted development ║
885
+ ║ ║
886
+ ╚═══════════════════════════════════════════════════════════╝
887
+ `));
888
+
889
+ const cwd = process.cwd();
890
+
891
+ // Check if already initialized
892
+ const claudeMdPath = join(cwd, 'CLAUDE.md');
893
+ if (existsSync(claudeMdPath)) {
894
+ const reinitialize = await confirm(' CLAUDE.md already exists. Reinitialize?');
895
+ if (!reinitialize) {
896
+ console.log(chalk.yellow('\n Skipping. Run with a fresh project or delete CLAUDE.md first.\n'));
897
+ return;
898
+ }
899
+ }
900
+
901
+ // Detect existing project
902
+ const projectInfo = detectExistingProject(cwd);
873
903
 
874
- console.log(chalk.white(' For Claude Code users:\n'));
875
- console.log(chalk.cyan(' codebakers install-hook') + chalk.gray(' (one-time setup)\n'));
904
+ if (projectInfo.exists) {
905
+ // Existing project detected - ask what they want to do
906
+ console.log(chalk.yellow('\n 📁 Existing project detected!\n'));
907
+ console.log(chalk.white(' How would you like to proceed?\n'));
908
+ console.log(chalk.gray(' 1. ') + chalk.cyan('EXISTING PROJECT') + chalk.gray(' - Add CodeBakers to this codebase'));
909
+ console.log(chalk.gray(' 2. ') + chalk.cyan('NEW PROJECT') + chalk.gray(' - Start fresh (ignore existing code)\n'));
910
+
911
+ let modeChoice = '';
912
+ while (!['1', '2'].includes(modeChoice)) {
913
+ modeChoice = await prompt(' Enter 1 or 2: ');
914
+ }
876
915
 
877
- console.log(chalk.blue(' Zero friction. Just build.\n'));
916
+ if (modeChoice === '1') {
917
+ await initExistingProject(cwd, projectInfo);
918
+ } else {
919
+ await initNewProject(cwd);
920
+ }
921
+ } else {
922
+ // No existing project - go straight to new project flow
923
+ await initNewProject(cwd);
924
+ }
878
925
  }