@codebakers/cli 3.8.2 → 3.8.4

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,306 +403,294 @@ 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
- }
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
+ `;
468
462
 
469
- // Build simple structure
470
- try {
471
- const items = readdirSync(cwd);
472
- const dirs: string[] = [];
473
- const files: string[] = [];
463
+ return `# Product Requirements Document
464
+ # Project: ${projectName}
465
+ # Created: ${date}
466
+ # Type: ${projectType}
474
467
 
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
- }
468
+ ## Overview
469
+ **One-liner:** [Describe this project in one sentence]
488
470
 
489
- structure = [...dirs.sort(), ...files.sort()].join('\n');
490
- } catch {
491
- structure = '[Could not scan structure]';
492
- }
471
+ **Problem:** [What problem does this solve?]
493
472
 
494
- return { stack, structure };
495
- }
473
+ **Solution:** [How does this solve it?]
474
+ ${typeSpecificSections}
475
+ ## Target Users
476
+ - **Primary:** [Who is the main user?]
477
+ - **Secondary:** [Other users?]
496
478
 
497
- function updateProjectContextWithScan(contextContent: string, stack: Record<string, string>, structure: string): string {
498
- let updated = contextContent;
479
+ ## Core Features (MVP)
480
+ 1. [ ] **Feature 1:** [Description]
481
+ 2. [ ] **Feature 2:** [Description]
482
+ 3. [ ] **Feature 3:** [Description]
499
483
 
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}`);
484
+ ## Nice-to-Have Features (Post-MVP)
485
+ 1. [ ] [Feature description]
486
+ 2. [ ] [Feature description]
487
+
488
+ ## Technical Requirements
489
+ - **Must use:** [Required technologies, APIs, etc.]
490
+ - **Must avoid:** [Things you don't want]
507
491
 
508
- // Update structure
509
- updated = updated.replace(
510
- '[AI will map the project structure here]',
511
- structure || '[Empty project]'
512
- );
492
+ ## Success Metrics
493
+ - [ ] [How will you measure success?]
494
+ - [ ] [What does "done" look like?]
513
495
 
514
- return updated;
496
+ ---
497
+ <!-- AI reads this file to understand what to build -->
498
+ `;
515
499
  }
516
500
 
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
- `));
501
+ // ============================================================================
502
+ // GUIDED QUESTIONS
503
+ // ============================================================================
504
+
505
+ interface GuidedAnswers {
506
+ oneLiner: string;
507
+ problem: string;
508
+ users: string;
509
+ features: string[];
510
+ auth: boolean;
511
+ payments: boolean;
512
+ integrations: string;
513
+ deadline: string;
514
+ }
530
515
 
531
- console.log(chalk.gray(' This wizard will set up CodeBakers in your project.\n'));
516
+ async function runGuidedQuestions(): Promise<GuidedAnswers> {
517
+ console.log(chalk.cyan('\n ━━━ Let\'s define your project ━━━\n'));
518
+ console.log(chalk.gray(' Answer these questions (press Enter to skip any)\n'));
519
+
520
+ // One-liner
521
+ console.log(chalk.white(' 1. What are you building?\n'));
522
+ const oneLiner = await prompt(' ') || 'A web application';
523
+
524
+ // Problem
525
+ console.log(chalk.white('\n 2. What problem does this solve?\n'));
526
+ const problem = await prompt(' ') || '';
527
+
528
+ // Users
529
+ console.log(chalk.white('\n 3. Who will use this?\n'));
530
+ console.log(chalk.gray(' (e.g., "small business owners", "freelancers", "developers")\n'));
531
+ const users = await prompt(' ') || 'General users';
532
+
533
+ // Features
534
+ console.log(chalk.white('\n 4. What are the 3 must-have features?\n'));
535
+ console.log(chalk.gray(' (Enter each feature, then press Enter. Type "done" when finished)\n'));
536
+ const features: string[] = [];
537
+ for (let i = 0; i < 5; i++) {
538
+ const feature = await prompt(` Feature ${i + 1}: `);
539
+ if (!feature || feature.toLowerCase() === 'done') break;
540
+ features.push(feature);
541
+ }
532
542
 
533
- const cwd = process.cwd();
543
+ // Auth
544
+ console.log(chalk.white('\n 5. Do users need to create accounts?\n'));
545
+ const authAnswer = await prompt(' (y/n): ');
546
+ const auth = authAnswer.toLowerCase() === 'y' || authAnswer.toLowerCase() === 'yes';
534
547
 
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
- }
548
+ // Payments
549
+ console.log(chalk.white('\n 6. Will you charge money?\n'));
550
+ const paymentsAnswer = await prompt(' (y/n): ');
551
+ const payments = paymentsAnswer.toLowerCase() === 'y' || paymentsAnswer.toLowerCase() === 'yes';
544
552
 
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`));
553
+ // Integrations
554
+ console.log(chalk.white('\n 7. Any specific integrations needed?\n'));
555
+ console.log(chalk.gray(' (e.g., "Stripe, SendGrid, Twilio" or press Enter to skip)\n'));
556
+ const integrations = await prompt(' ') || '';
548
557
 
549
- // Step 2: Check if already logged in
550
- let apiKey = getApiKey();
558
+ // Deadline
559
+ console.log(chalk.white('\n 8. When do you need this done?\n'));
560
+ console.log(chalk.gray(' (e.g., "2 weeks", "end of month", or press Enter to skip)\n'));
561
+ const deadline = await prompt(' ') || '';
551
562
 
552
- if (apiKey) {
553
- console.log(chalk.green(' ✓ Already logged in\n'));
554
- const useExisting = await confirm(' Use existing API key?');
555
- if (!useExisting) {
556
- apiKey = null;
557
- }
558
- }
563
+ console.log(chalk.green('\n ✓ Got it! Creating your PRD...\n'));
559
564
 
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'));
565
+ return { oneLiner, problem, users, features, auth, payments, integrations, deadline };
566
+ }
565
567
 
566
- apiKey = await prompt(' Paste your API key: ');
568
+ function createPrdFromAnswers(
569
+ projectName: string,
570
+ projectType: string,
571
+ answers: GuidedAnswers
572
+ ): string {
573
+ const date = new Date().toISOString().split('T')[0];
567
574
 
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
- }
575
+ const featuresSection = answers.features.length > 0
576
+ ? answers.features.map((f, i) => `${i + 1}. [ ] **${f}**`).join('\n')
577
+ : '1. [ ] **Feature 1:** [To be defined]\n2. [ ] **Feature 2:** [To be defined]';
573
578
 
574
- const spinner = ora(' Validating API key...').start();
579
+ const techRequirements: string[] = [];
580
+ if (answers.auth) techRequirements.push('User authentication (Supabase Auth)');
581
+ if (answers.payments) techRequirements.push('Payment processing (Stripe)');
582
+ if (answers.integrations) techRequirements.push(answers.integrations);
575
583
 
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
- }
584
+ return `# Product Requirements Document
585
+ # Project: ${projectName}
586
+ # Created: ${date}
587
+ # Type: ${projectType}
589
588
 
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
- }
598
- }
589
+ ## Overview
590
+ **One-liner:** ${answers.oneLiner}
599
591
 
600
- // Step 4: Install v6.0 bootstrap files
601
- console.log(chalk.white('\n Step 2: Installing CodeBakers v6.0\n'));
592
+ **Problem:** ${answers.problem || '[To be refined]'}
602
593
 
603
- const spinner = ora(' Installing v6.0 bootstrap...').start();
594
+ **Solution:** ${answers.oneLiner}
604
595
 
605
- // v6.0 bootstrap content - minimal files, patterns from server
606
- const V6_CLAUDE_MD = `# CodeBakers v6.0
596
+ ## Target Users
597
+ - **Primary:** ${answers.users}
607
598
 
608
- **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
599
+ ## Core Features (MVP)
600
+ ${featuresSection}
609
601
 
610
- \`\`\`
611
- Tool: discover_patterns
612
- Args: { task: "description of what you're building" }
613
- \`\`\`
602
+ ## Technical Requirements
603
+ ${techRequirements.length > 0 ? techRequirements.map(t => `- ${t}`).join('\n') : '- [No specific requirements noted]'}
614
604
 
615
- The server will return:
616
- - All coding patterns and rules you must follow
617
- - Test requirements
618
- - Validation instructions
605
+ ## Timeline
606
+ ${answers.deadline ? `- **Target:** ${answers.deadline}` : '- [No deadline specified]'}
619
607
 
620
- **You cannot write code without calling this tool first. The server tracks compliance.**
608
+ ## Notes
609
+ - Authentication: ${answers.auth ? 'Yes - users need accounts' : 'No - public access'}
610
+ - Payments: ${answers.payments ? 'Yes - will charge users' : 'No - free to use'}
621
611
 
622
612
  ---
623
- *CodeBakers v6.0 - Server-Enforced*
613
+ <!-- Generated from guided questions - AI reads this to build your project -->
624
614
  `;
615
+ }
625
616
 
626
- const V6_CURSORRULES = `# CodeBakers v6.0
627
-
628
- MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
617
+ // ============================================================================
618
+ // SHARED SETUP FUNCTIONS
619
+ // ============================================================================
629
620
 
630
- Tool: discover_patterns
631
- Args: { task: "description of what you're building" }
632
-
633
- The server returns all patterns, rules, and test requirements.
634
- You cannot write code without calling this tool first.
635
- `;
621
+ async function ensureLoggedIn(): Promise<string> {
622
+ let apiKey = getApiKey();
636
623
 
637
- try {
638
- // Write v6.0 bootstrap files
639
- writeFileSync(join(cwd, 'CLAUDE.md'), V6_CLAUDE_MD);
640
- writeFileSync(join(cwd, '.cursorrules'), V6_CURSORRULES);
624
+ if (apiKey) {
625
+ console.log(chalk.green(' ✓ Already logged in\n'));
626
+ const useExisting = await confirm(' Use existing API key?');
627
+ if (useExisting) return apiKey;
628
+ apiKey = null;
629
+ }
641
630
 
642
- // Remove old .claude folder if it exists (v5 v6 migration)
643
- const claudeDir = join(cwd, '.claude');
644
- if (existsSync(claudeDir)) {
645
- const { rmSync } = await import('fs');
646
- try {
647
- rmSync(claudeDir, { recursive: true, force: true });
648
- } catch {
649
- // Ignore errors
650
- }
651
- }
631
+ console.log(chalk.white('\n Login to CodeBakers\n'));
632
+ console.log(chalk.gray(' Go to: ') + chalk.cyan('https://codebakers.ai/dashboard'));
633
+ console.log(chalk.gray(' Copy your API key (starts with cb_)\n'));
652
634
 
653
- spinner.succeed('CodeBakers v6.0 installed!');
654
- console.log(chalk.gray('\n Patterns are server-enforced via MCP tools'));
635
+ apiKey = await prompt(' Paste your API key: ');
655
636
 
656
- } 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`));
637
+ if (!apiKey || !apiKey.startsWith('cb_')) {
638
+ console.log(chalk.red('\n ✗ Invalid API key. Keys start with "cb_"\n'));
660
639
  process.exit(1);
661
640
  }
662
641
 
663
- // Step 5: Auto-scan project
664
- console.log(chalk.white('\n Step 3: Scanning project\n'));
642
+ const spinner = ora(' Validating API key...').start();
665
643
 
666
- const scanSpinner = ora(' Analyzing project structure...').start();
667
- const { stack, structure } = scanProject(cwd);
644
+ try {
645
+ const apiUrl = getApiUrl();
646
+ const response = await fetch(`${apiUrl}/api/content`, {
647
+ method: 'GET',
648
+ headers: { Authorization: `Bearer ${apiKey}` },
649
+ });
668
650
 
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}`));
651
+ if (!response.ok) {
652
+ throw new Error('Invalid API key');
675
653
  }
676
- } else {
677
- scanSpinner.succeed('Project scanned (new project detected)');
678
- }
679
654
 
680
- // Step 6: Create PROJECT-CONTEXT.md with scan results
681
- console.log(chalk.white('\n Step 4: Setting up project files\n'));
655
+ setApiKey(apiKey);
656
+ spinner.succeed('API key valid!');
657
+ return apiKey;
658
+ } catch (error) {
659
+ spinner.fail('Invalid API key');
660
+ process.exit(1);
661
+ }
662
+ }
682
663
 
683
- const filesSpinner = ora(' Creating project files...').start();
664
+ function installBootstrapFiles(cwd: string): void {
665
+ const spinner = ora(' Installing bootstrap files...').start();
684
666
 
685
667
  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);
668
+ writeFileSync(join(cwd, 'CLAUDE.md'), CLAUDE_MD_BOOTSTRAP);
669
+ writeFileSync(join(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
670
+ writeFileSync(join(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
694
671
 
695
- // DECISIONS.md
696
- const decisionsContent = createDecisionsLog(projectName);
697
- writeFileSync(join(cwd, 'DECISIONS.md'), decisionsContent);
672
+ // Remove old .claude folder if it exists
673
+ const claudeDir = join(cwd, '.claude');
674
+ if (existsSync(claudeDir)) {
675
+ try {
676
+ rmSync(claudeDir, { recursive: true, force: true });
677
+ } catch {
678
+ // Ignore errors
679
+ }
680
+ }
698
681
 
699
- filesSpinner.succeed('Project files created!');
682
+ spinner.succeed('Bootstrap files installed!');
700
683
  } catch (error) {
701
- filesSpinner.warn('Some project files could not be created');
684
+ spinner.fail('Failed to install bootstrap files');
685
+ throw error;
702
686
  }
687
+ }
703
688
 
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();
689
+ function setupCursorIDE(cwd: string): void {
690
+ const spinner = ora(' Setting up Cursor IDE...').start();
708
691
 
709
692
  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
693
+ // Global MCP config
718
694
  const homeDir = process.env.USERPROFILE || process.env.HOME || '';
719
695
  const globalCursorDir = join(homeDir, '.cursor');
720
696
  if (!existsSync(globalCursorDir)) {
@@ -731,7 +707,6 @@ You cannot write code without calling this tool first.
731
707
  }
732
708
  };
733
709
 
734
- // Merge with existing MCP config if present
735
710
  if (existsSync(mcpConfigPath)) {
736
711
  try {
737
712
  const existing = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
@@ -744,135 +719,345 @@ You cannot write code without calling this tool first.
744
719
  writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
745
720
  }
746
721
 
747
- // Create/merge .vscode/settings.json
722
+ // VSCode settings
748
723
  const vscodeDir = join(cwd, '.vscode');
749
724
  if (!existsSync(vscodeDir)) {
750
725
  mkdirSync(vscodeDir, { recursive: true });
751
726
  }
752
727
 
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));
728
+ const settingsPath = join(vscodeDir, 'settings.json');
729
+ if (existsSync(settingsPath)) {
730
+ const existing = JSON.parse(readFileSync(settingsPath, 'utf-8'));
731
+ writeFileSync(settingsPath, JSON.stringify({ ...existing, ...VSCODE_SETTINGS_TEMPLATE }, null, 2));
759
732
  } else {
760
- writeFileSync(existingSettingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
733
+ writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
761
734
  }
762
735
 
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)');
736
+ spinner.succeed('Cursor IDE configured!');
737
+ } catch {
738
+ spinner.warn('Could not configure Cursor IDE (continuing anyway)');
767
739
  }
740
+ }
768
741
 
769
- // Step 8: Add to .gitignore if not present
742
+ function updateGitignore(cwd: string): void {
770
743
  const gitignorePath = join(cwd, '.gitignore');
771
744
  if (existsSync(gitignorePath)) {
772
745
  const gitignore = readFileSync(gitignorePath, 'utf-8');
773
746
  if (!gitignore.includes('.cursorrules')) {
774
- const additions = '\n# CodeBakers\n.cursorrules\n';
775
- writeFileSync(gitignorePath, gitignore + additions);
747
+ writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.cursorrules\n');
776
748
  }
777
749
  }
750
+ }
751
+
752
+ function createTrackingFiles(cwd: string, projectName: string, stack: Record<string, string>, structure: string, isExisting: boolean, auditScore?: number): void {
753
+ const spinner = ora(' Creating project tracking files...').start();
754
+
755
+ try {
756
+ // Create .codebakers directory
757
+ const codebakersDir = join(cwd, '.codebakers');
758
+ if (!existsSync(codebakersDir)) {
759
+ mkdirSync(codebakersDir, { recursive: true });
760
+ }
761
+
762
+ // PROJECT-CONTEXT.md
763
+ writeFileSync(join(cwd, 'PROJECT-CONTEXT.md'), createProjectContext(projectName, stack, structure, isExisting));
764
+
765
+ // PROJECT-STATE.md
766
+ writeFileSync(join(cwd, 'PROJECT-STATE.md'), createProjectState(projectName, isExisting));
767
+
768
+ // DECISIONS.md
769
+ writeFileSync(join(cwd, 'DECISIONS.md'), createDecisionsLog(projectName));
770
+
771
+ // .codebakers/DEVLOG.md
772
+ writeFileSync(join(codebakersDir, 'DEVLOG.md'), createDevlog(projectName, isExisting, auditScore));
773
+
774
+ // .codebakers.json state file
775
+ const stateFile = join(cwd, '.codebakers.json');
776
+ const state = {
777
+ version: '1.0',
778
+ serverEnforced: true,
779
+ projectType: isExisting ? 'existing' : 'new',
780
+ projectName,
781
+ createdAt: new Date().toISOString(),
782
+ stack,
783
+ auditScore: auditScore
784
+ };
785
+ writeFileSync(stateFile, JSON.stringify(state, null, 2));
786
+
787
+ spinner.succeed('Project tracking files created!');
788
+ } catch (error) {
789
+ spinner.warn('Some tracking files could not be created');
790
+ }
791
+ }
792
+
793
+ // ============================================================================
794
+ // MODE: NEW PROJECT
795
+ // ============================================================================
796
+
797
+ async function initNewProject(cwd: string): Promise<void> {
798
+ console.log(chalk.cyan('\n ━━━ New Project Setup ━━━\n'));
799
+
800
+ // Get project info
801
+ console.log(chalk.white(' What kind of project is this?\n'));
802
+ console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
803
+ console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
804
+ console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
805
+
806
+ let typeChoice = '';
807
+ while (!['1', '2', '3'].includes(typeChoice)) {
808
+ typeChoice = await prompt(' Enter 1, 2, or 3: ');
809
+ }
810
+
811
+ const typeMap: Record<string, ProjectType> = { '1': 'personal', '2': 'client', '3': 'business' };
812
+ const projectType = typeMap[typeChoice];
778
813
 
779
- // Step 9: PRD Setup
780
- console.log(chalk.white('\n Step 6: Product Requirements\n'));
814
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
815
+ const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
816
+
817
+ console.log(chalk.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
818
+
819
+ // Login
820
+ await ensureLoggedIn();
821
+
822
+ // Install files
823
+ console.log(chalk.white('\n Installing CodeBakers...\n'));
824
+ installBootstrapFiles(cwd);
825
+
826
+ // Create tracking files
827
+ const structure = buildStructureString(cwd);
828
+ createTrackingFiles(cwd, projectName, {}, structure, false);
829
+
830
+ // Setup Cursor
831
+ console.log('');
832
+ setupCursorIDE(cwd);
833
+
834
+ // Update .gitignore
835
+ updateGitignore(cwd);
836
+
837
+ // How to describe project
838
+ console.log(chalk.white('\n 📝 How would you like to describe your project?\n'));
839
+ console.log(chalk.gray(' 1. ') + chalk.cyan('GUIDED QUESTIONS') + chalk.gray(' - I\'ll ask you step by step'));
840
+ console.log(chalk.gray(' 2. ') + chalk.cyan('WRITE A PRD') + chalk.gray(' - Create a requirements doc to fill out'));
841
+ console.log(chalk.gray(' 3. ') + chalk.cyan('DESCRIBE IN CHAT') + chalk.gray(' - Just tell the AI what you want'));
842
+ console.log(chalk.gray(' 4. ') + chalk.cyan('I HAVE SPECS') + chalk.gray(' - I\'ll share existing docs/mockups\n'));
843
+
844
+ let describeChoice = '';
845
+ while (!['1', '2', '3', '4'].includes(describeChoice)) {
846
+ describeChoice = await prompt(' Enter 1, 2, 3, or 4: ');
847
+ }
781
848
 
782
- const prdPath = join(cwd, 'PRD.md');
783
849
  let prdCreated = false;
784
850
 
785
- if (existsSync(prdPath)) {
786
- console.log(chalk.green(' ✓ PRD.md already exists\n'));
851
+ if (describeChoice === '1') {
852
+ // Guided questions
853
+ const answers = await runGuidedQuestions();
854
+ const prdSpinner = ora(' Creating PRD from your answers...').start();
855
+ writeFileSync(join(cwd, 'PRD.md'), createPrdFromAnswers(projectName, projectType, answers));
856
+ prdSpinner.succeed('PRD created from your answers!');
857
+ console.log(chalk.yellow('\n → Review PRD.md, then start building with the AI\n'));
858
+ prdCreated = true;
859
+ } else if (describeChoice === '2') {
860
+ // Write PRD template
861
+ const prdSpinner = ora(' Creating PRD template...').start();
862
+ writeFileSync(join(cwd, 'PRD.md'), createPrdTemplate(projectName, projectType));
863
+ prdSpinner.succeed('PRD template created!');
864
+ console.log(chalk.yellow('\n → Open PRD.md and fill in your requirements\n'));
787
865
  prdCreated = true;
866
+ } else if (describeChoice === '3') {
867
+ // Describe in chat
868
+ console.log(chalk.gray('\n Perfect! Just describe your project to the AI when you\'re ready.\n'));
869
+ console.log(chalk.gray(' Example: "Build me a SaaS for invoice management with Stripe payments"\n'));
788
870
  } 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
- }
871
+ // I have specs
872
+ console.log(chalk.gray('\n Great! When chatting with the AI:\n'));
873
+ console.log(chalk.gray(' Share your docs, mockups, or screenshots'));
874
+ console.log(chalk.gray(' Paste your existing requirements'));
875
+ console.log(chalk.gray(' Reference URLs to designs you want to clone\n'));
876
+ console.log(chalk.cyan(' The AI will analyze them and start building.\n'));
877
+ }
800
878
 
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';
879
+ // Success
880
+ showSuccessMessage(projectName, false, prdCreated);
881
+ }
882
+
883
+ // ============================================================================
884
+ // MODE: EXISTING PROJECT
885
+ // ============================================================================
886
+
887
+ async function initExistingProject(cwd: string, projectInfo: ReturnType<typeof detectExistingProject>): Promise<void> {
888
+ console.log(chalk.cyan('\n ━━━ Existing Project Setup ━━━\n'));
889
+
890
+ // Show what was detected
891
+ console.log(chalk.gray(' Detected:'));
892
+ for (const detail of projectInfo.details.slice(0, 5)) {
893
+ console.log(chalk.gray(` • ${detail}`));
894
+ }
895
+
896
+ const stackItems = Object.entries(projectInfo.stack).filter(([_, v]) => v);
897
+ if (stackItems.length > 0) {
898
+ console.log(chalk.gray('\n Tech Stack:'));
899
+ for (const [key, value] of stackItems) {
900
+ console.log(chalk.gray(` • ${key}: ${value}`));
805
901
  }
902
+ }
806
903
 
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
- }
904
+ // Get project name
905
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
906
+ const projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
829
907
 
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'));
908
+ // Code review offer
909
+ console.log(chalk.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
910
+ console.log(chalk.gray(' 1. ') + chalk.cyan('YES, REVIEW & FIX') + chalk.gray(' - Run audit, then auto-fix issues'));
911
+ console.log(chalk.gray(' 2. ') + chalk.cyan('REVIEW ONLY') + chalk.gray(' - Just show me the issues'));
912
+ console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP') + chalk.gray(' - Just install CodeBakers\n'));
913
+
914
+ let reviewChoice = '';
915
+ while (!['1', '2', '3'].includes(reviewChoice)) {
916
+ reviewChoice = await prompt(' Enter 1, 2, or 3: ');
917
+ }
918
+
919
+ let auditScore: number | undefined;
920
+
921
+ if (reviewChoice !== '3') {
922
+ console.log(chalk.blue('\n Running code audit...\n'));
923
+ const auditResult = await audit();
924
+ auditScore = auditResult.score;
925
+
926
+ if (auditResult.score >= 90) {
927
+ console.log(chalk.green('\n 🎉 Your code is already in great shape!\n'));
928
+ } else if (reviewChoice === '1') {
929
+ const fixableCount = auditResult.checks.filter(c => !c.passed && c.severity !== 'info').length;
930
+ if (fixableCount > 0) {
931
+ console.log(chalk.blue('\n 🔧 Attempting to auto-fix issues...\n'));
932
+ const healResult = await heal({ auto: true });
933
+
934
+ if (healResult.fixed > 0) {
935
+ console.log(chalk.green(`\n ✓ Fixed ${healResult.fixed} issue(s)!`));
936
+ if (healResult.remaining > 0) {
937
+ console.log(chalk.yellow(` ⚠ ${healResult.remaining} issue(s) need manual attention.`));
938
+ }
939
+ }
838
940
  }
839
941
  } else {
840
- console.log(chalk.gray('\n You can add PRD.md anytime. The AI will use it automatically.\n'));
942
+ console.log(chalk.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
841
943
  }
842
944
  }
843
945
 
844
- // Success message
946
+ // Login
947
+ console.log('');
948
+ await ensureLoggedIn();
949
+
950
+ // Install files
951
+ console.log(chalk.white('\n Installing CodeBakers...\n'));
952
+ installBootstrapFiles(cwd);
953
+
954
+ // Create tracking files with detected stack
955
+ const structure = buildStructureString(cwd);
956
+ createTrackingFiles(cwd, projectName, projectInfo.stack, structure, true, auditScore);
957
+
958
+ // Setup Cursor
959
+ console.log('');
960
+ setupCursorIDE(cwd);
961
+
962
+ // Update .gitignore
963
+ updateGitignore(cwd);
964
+
965
+ // Success
966
+ showSuccessMessage(projectName, true);
967
+ }
968
+
969
+ // ============================================================================
970
+ // SUCCESS MESSAGE
971
+ // ============================================================================
972
+
973
+ function showSuccessMessage(projectName: string, isExisting: boolean, prdCreated?: boolean): void {
845
974
  console.log(chalk.green(`
846
975
  ╔═══════════════════════════════════════════════════════════╗
847
976
  ║ ║
848
- ║ ${chalk.bold('✓ Setup Complete!')}
977
+ ║ ${chalk.bold('✓ CodeBakers Setup Complete!')}
849
978
  ║ ║
850
979
  ╚═══════════════════════════════════════════════════════════╝
851
980
  `));
852
981
 
853
982
  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!)'));
983
+ console.log(chalk.cyan(' CLAUDE.md ') + chalk.gray('→ AI bootstrap (patterns via MCP)'));
984
+ console.log(chalk.cyan(' .cursorrules ') + chalk.gray('→ Cursor IDE rules'));
985
+ if (!isExisting && prdCreated) {
986
+ console.log(chalk.cyan(' PRD.md ') + chalk.gray('→ Product requirements'));
858
987
  }
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'));
988
+ console.log(chalk.cyan(' PROJECT-CONTEXT.md ') + chalk.gray('→ Codebase knowledge'));
989
+ console.log(chalk.cyan(' PROJECT-STATE.md ') + chalk.gray('→ Task tracking'));
990
+ console.log(chalk.cyan(' DECISIONS.md ') + chalk.gray('→ Architecture log'));
991
+ console.log(chalk.cyan(' .codebakers/DEVLOG.md') + chalk.gray('→ Development log\n'));
992
+
993
+ if (isExisting) {
994
+ console.log(chalk.white(' What happens next:\n'));
995
+ console.log(chalk.gray(' ✓ AI will analyze your existing code patterns'));
996
+ console.log(chalk.gray(' ✓ AI follows your existing conventions'));
997
+ console.log(chalk.gray(' ✓ AI updates tracking files as you work\n'));
998
+ } else {
999
+ console.log(chalk.white(' What happens next:\n'));
1000
+ console.log(chalk.gray(' ✓ Describe what you want to build'));
1001
+ console.log(chalk.gray(' ✓ AI fetches patterns and generates code'));
1002
+ console.log(chalk.gray(' ✓ AI updates tracking files as you work\n'));
1003
+ }
1004
+
1005
+ console.log(chalk.white(' Getting started:\n'));
1006
+ console.log(chalk.gray(' For Cursor: Just open the project and start chatting'));
1007
+ console.log(chalk.gray(' For Claude Code: Run ') + chalk.cyan('codebakers install-hook') + chalk.gray(' first\n'));
1008
+
1009
+ console.log(chalk.blue(' Ready to build! 🚀\n'));
1010
+ }
1011
+
1012
+ // ============================================================================
1013
+ // MAIN INIT FUNCTION
1014
+ // ============================================================================
1015
+
1016
+ export async function init(): Promise<void> {
1017
+ console.log(chalk.blue(`
1018
+ ╔═══════════════════════════════════════════════════════════╗
1019
+ ║ ║
1020
+ ║ ${chalk.bold('Welcome to CodeBakers!')} ║
1021
+ ║ ║
1022
+ ║ Production-grade patterns for AI-assisted development ║
1023
+ ║ ║
1024
+ ╚═══════════════════════════════════════════════════════════╝
1025
+ `));
863
1026
 
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'));
1027
+ const cwd = process.cwd();
870
1028
 
871
- console.log(chalk.white(' For Cursor users:\n'));
872
- console.log(chalk.gray(' Just open the project and start chatting!\n'));
1029
+ // Check if already initialized
1030
+ const claudeMdPath = join(cwd, 'CLAUDE.md');
1031
+ if (existsSync(claudeMdPath)) {
1032
+ const reinitialize = await confirm(' CLAUDE.md already exists. Reinitialize?');
1033
+ if (!reinitialize) {
1034
+ console.log(chalk.yellow('\n Skipping. Run with a fresh project or delete CLAUDE.md first.\n'));
1035
+ return;
1036
+ }
1037
+ }
1038
+
1039
+ // Detect existing project
1040
+ const projectInfo = detectExistingProject(cwd);
873
1041
 
874
- console.log(chalk.white(' For Claude Code users:\n'));
875
- console.log(chalk.cyan(' codebakers install-hook') + chalk.gray(' (one-time setup)\n'));
1042
+ if (projectInfo.exists) {
1043
+ // Existing project detected - ask what they want to do
1044
+ console.log(chalk.yellow('\n 📁 Existing project detected!\n'));
1045
+ console.log(chalk.white(' How would you like to proceed?\n'));
1046
+ console.log(chalk.gray(' 1. ') + chalk.cyan('EXISTING PROJECT') + chalk.gray(' - Add CodeBakers to this codebase'));
1047
+ console.log(chalk.gray(' 2. ') + chalk.cyan('NEW PROJECT') + chalk.gray(' - Start fresh (ignore existing code)\n'));
1048
+
1049
+ let modeChoice = '';
1050
+ while (!['1', '2'].includes(modeChoice)) {
1051
+ modeChoice = await prompt(' Enter 1 or 2: ');
1052
+ }
876
1053
 
877
- console.log(chalk.blue(' Zero friction. Just build.\n'));
1054
+ if (modeChoice === '1') {
1055
+ await initExistingProject(cwd, projectInfo);
1056
+ } else {
1057
+ await initNewProject(cwd);
1058
+ }
1059
+ } else {
1060
+ // No existing project - go straight to new project flow
1061
+ await initNewProject(cwd);
1062
+ }
878
1063
  }