@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.
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.js +578 -550
- package/dist/commands/install.js +14 -15
- package/dist/commands/upgrade.d.ts +1 -1
- package/dist/commands/upgrade.js +57 -313
- package/dist/index.js +7 -151
- package/package.json +1 -1
- package/src/commands/init.ts +606 -559
- package/src/commands/install.ts +14 -15
- package/src/commands/upgrade.ts +60 -369
- package/src/index.ts +7 -185
package/src/commands/init.ts
CHANGED
|
@@ -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
|
|
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
|
|
17
|
-
3. Read PROJECT-
|
|
18
|
-
4. Read
|
|
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] |
|
|
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
|
|
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
|
-
|
|
138
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
};
|
|
297
|
-
}
|
|
199
|
+
if (depCount > 0) {
|
|
200
|
+
details.push(`package.json with ${depCount} dependencies`);
|
|
201
|
+
}
|
|
298
202
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
322
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
301
|
+
# Mode: ${isExisting ? 'Existing Project' : 'New Project'}
|
|
334
302
|
|
|
335
303
|
## Overview
|
|
336
304
|
name: ${projectName}
|
|
337
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
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
|
-
- [ ]
|
|
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}:
|
|
408
|
-
**Decision:** Using CodeBakers
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
436
|
+
---
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
452
439
|
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
|
495
|
-
}
|
|
463
|
+
return `# Product Requirements Document
|
|
464
|
+
# Project: ${projectName}
|
|
465
|
+
# Created: ${date}
|
|
466
|
+
# Type: ${projectType}
|
|
496
467
|
|
|
497
|
-
|
|
498
|
-
|
|
468
|
+
## Overview
|
|
469
|
+
**One-liner:** [Describe this project in one sentence]
|
|
499
470
|
|
|
500
|
-
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
|
|
515
|
-
|
|
479
|
+
## Core Features (MVP)
|
|
480
|
+
1. [ ] **Feature 1:** [Description]
|
|
481
|
+
2. [ ] **Feature 2:** [Description]
|
|
482
|
+
3. [ ] **Feature 3:** [Description]
|
|
516
483
|
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
488
|
+
## Technical Requirements
|
|
489
|
+
- **Must use:** [Required technologies, APIs, etc.]
|
|
490
|
+
- **Must avoid:** [Things you don't want]
|
|
532
491
|
|
|
533
|
-
|
|
492
|
+
## Success Metrics
|
|
493
|
+
- [ ] [How will you measure success?]
|
|
494
|
+
- [ ] [What does "done" look like?]
|
|
534
495
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
-
|
|
546
|
-
|
|
547
|
-
|
|
501
|
+
// ============================================================================
|
|
502
|
+
// SHARED SETUP FUNCTIONS
|
|
503
|
+
// ============================================================================
|
|
548
504
|
|
|
549
|
-
|
|
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 (
|
|
556
|
-
|
|
557
|
-
}
|
|
511
|
+
if (useExisting) return apiKey;
|
|
512
|
+
apiKey = null;
|
|
558
513
|
}
|
|
559
514
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
535
|
+
if (!response.ok) {
|
|
536
|
+
throw new Error('Invalid API key');
|
|
537
|
+
}
|
|
629
538
|
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
`;
|
|
548
|
+
function installBootstrapFiles(cwd: string): void {
|
|
549
|
+
const spinner = ora(' Installing bootstrap files...').start();
|
|
636
550
|
|
|
637
551
|
try {
|
|
638
|
-
|
|
639
|
-
writeFileSync(join(cwd, '
|
|
640
|
-
writeFileSync(join(cwd, '.
|
|
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
|
|
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('
|
|
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('
|
|
658
|
-
|
|
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
|
-
|
|
664
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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(
|
|
617
|
+
writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
|
|
761
618
|
}
|
|
762
619
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
775
|
-
|
|
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
|
-
|
|
780
|
-
|
|
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
|
-
|
|
783
|
-
|
|
703
|
+
// Login
|
|
704
|
+
await ensureLoggedIn();
|
|
784
705
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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('
|
|
790
|
-
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
-
|
|
808
|
-
|
|
809
|
-
|
|
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
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
|
804
|
+
console.log(chalk.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
|
|
841
805
|
}
|
|
842
806
|
}
|
|
843
807
|
|
|
844
|
-
//
|
|
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
|
|
855
|
-
console.log(chalk.cyan(' .cursorrules
|
|
856
|
-
if (prdCreated) {
|
|
857
|
-
console.log(chalk.cyan(' PRD.md
|
|
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('
|
|
865
|
-
console.log(chalk.gray('
|
|
866
|
-
console.log(chalk.gray('
|
|
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.
|
|
872
|
-
|
|
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
|
-
|
|
875
|
-
|
|
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
|
-
|
|
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
|
}
|