@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.
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.js +685 -548
- 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 +730 -545
- 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,306 +403,294 @@ 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
|
-
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const files: string[] = [];
|
|
463
|
+
return `# Product Requirements Document
|
|
464
|
+
# Project: ${projectName}
|
|
465
|
+
# Created: ${date}
|
|
466
|
+
# Type: ${projectType}
|
|
474
467
|
|
|
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
|
-
}
|
|
468
|
+
## Overview
|
|
469
|
+
**One-liner:** [Describe this project in one sentence]
|
|
488
470
|
|
|
489
|
-
|
|
490
|
-
} catch {
|
|
491
|
-
structure = '[Could not scan structure]';
|
|
492
|
-
}
|
|
471
|
+
**Problem:** [What problem does this solve?]
|
|
493
472
|
|
|
494
|
-
|
|
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
|
-
|
|
498
|
-
|
|
479
|
+
## Core Features (MVP)
|
|
480
|
+
1. [ ] **Feature 1:** [Description]
|
|
481
|
+
2. [ ] **Feature 2:** [Description]
|
|
482
|
+
3. [ ] **Feature 3:** [Description]
|
|
499
483
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
structure || '[Empty project]'
|
|
512
|
-
);
|
|
492
|
+
## Success Metrics
|
|
493
|
+
- [ ] [How will you measure success?]
|
|
494
|
+
- [ ] [What does "done" look like?]
|
|
513
495
|
|
|
514
|
-
|
|
496
|
+
---
|
|
497
|
+
<!-- AI reads this file to understand what to build -->
|
|
498
|
+
`;
|
|
515
499
|
}
|
|
516
500
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
}
|
|
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
|
-
//
|
|
546
|
-
|
|
547
|
-
console.log(chalk.
|
|
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
|
-
//
|
|
550
|
-
|
|
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
|
-
|
|
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
|
-
|
|
561
|
-
|
|
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
|
-
|
|
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
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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
|
-
|
|
591
|
-
|
|
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
|
-
|
|
601
|
-
console.log(chalk.white('\n Step 2: Installing CodeBakers v6.0\n'));
|
|
592
|
+
**Problem:** ${answers.problem || '[To be refined]'}
|
|
602
593
|
|
|
603
|
-
|
|
594
|
+
**Solution:** ${answers.oneLiner}
|
|
604
595
|
|
|
605
|
-
|
|
606
|
-
|
|
596
|
+
## Target Users
|
|
597
|
+
- **Primary:** ${answers.users}
|
|
607
598
|
|
|
608
|
-
|
|
599
|
+
## Core Features (MVP)
|
|
600
|
+
${featuresSection}
|
|
609
601
|
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
- Test requirements
|
|
618
|
-
- Validation instructions
|
|
605
|
+
## Timeline
|
|
606
|
+
${answers.deadline ? `- **Target:** ${answers.deadline}` : '- [No deadline specified]'}
|
|
619
607
|
|
|
620
|
-
|
|
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
|
-
|
|
613
|
+
<!-- Generated from guided questions - AI reads this to build your project -->
|
|
624
614
|
`;
|
|
615
|
+
}
|
|
625
616
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
617
|
+
// ============================================================================
|
|
618
|
+
// SHARED SETUP FUNCTIONS
|
|
619
|
+
// ============================================================================
|
|
629
620
|
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
654
|
-
console.log(chalk.gray('\n Patterns are server-enforced via MCP tools'));
|
|
635
|
+
apiKey = await prompt(' Paste your API key: ');
|
|
655
636
|
|
|
656
|
-
|
|
657
|
-
|
|
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
|
-
|
|
664
|
-
console.log(chalk.white('\n Step 3: Scanning project\n'));
|
|
642
|
+
const spinner = ora(' Validating API key...').start();
|
|
665
643
|
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
670
|
-
|
|
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
|
-
|
|
681
|
-
|
|
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
|
-
|
|
664
|
+
function installBootstrapFiles(cwd: string): void {
|
|
665
|
+
const spinner = ora(' Installing bootstrap files...').start();
|
|
684
666
|
|
|
685
667
|
try {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
//
|
|
696
|
-
const
|
|
697
|
-
|
|
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
|
-
|
|
682
|
+
spinner.succeed('Bootstrap files installed!');
|
|
700
683
|
} catch (error) {
|
|
701
|
-
|
|
684
|
+
spinner.fail('Failed to install bootstrap files');
|
|
685
|
+
throw error;
|
|
702
686
|
}
|
|
687
|
+
}
|
|
703
688
|
|
|
704
|
-
|
|
705
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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(
|
|
733
|
+
writeFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
|
|
761
734
|
}
|
|
762
735
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
780
|
-
|
|
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 (
|
|
786
|
-
|
|
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
|
-
|
|
790
|
-
console.log(chalk.
|
|
791
|
-
console.log(chalk.gray('
|
|
792
|
-
console.log(chalk.gray('
|
|
793
|
-
console.log(chalk.gray('
|
|
794
|
-
console.log(chalk.
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
|
942
|
+
console.log(chalk.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
|
|
841
943
|
}
|
|
842
944
|
}
|
|
843
945
|
|
|
844
|
-
//
|
|
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
|
|
855
|
-
console.log(chalk.cyan(' .cursorrules
|
|
856
|
-
if (prdCreated) {
|
|
857
|
-
console.log(chalk.cyan(' PRD.md
|
|
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
|
|
860
|
-
console.log(chalk.cyan(' PROJECT-STATE.md
|
|
861
|
-
console.log(chalk.cyan(' DECISIONS.md
|
|
862
|
-
console.log(chalk.cyan(' .
|
|
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
|
-
|
|
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
|
-
|
|
872
|
-
|
|
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
|
-
|
|
875
|
-
|
|
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
|
-
|
|
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
|
}
|