@codebakers/cli 3.8.6 → 3.8.7
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/go.js +917 -89
- package/package.json +1 -1
- package/src/commands/go.ts +995 -98
package/dist/commands/go.js
CHANGED
|
@@ -13,6 +13,8 @@ const readline_1 = require("readline");
|
|
|
13
13
|
const config_js_1 = require("../config.js");
|
|
14
14
|
const api_js_1 = require("../lib/api.js");
|
|
15
15
|
const fingerprint_js_1 = require("../lib/fingerprint.js");
|
|
16
|
+
const audit_js_1 = require("./audit.js");
|
|
17
|
+
const heal_js_1 = require("./heal.js");
|
|
16
18
|
function prompt(question) {
|
|
17
19
|
const rl = (0, readline_1.createInterface)({
|
|
18
20
|
input: process.stdin,
|
|
@@ -21,10 +23,692 @@ function prompt(question) {
|
|
|
21
23
|
return new Promise((resolve) => {
|
|
22
24
|
rl.question(question, (answer) => {
|
|
23
25
|
rl.close();
|
|
24
|
-
resolve(answer.trim()
|
|
26
|
+
resolve(answer.trim());
|
|
25
27
|
});
|
|
26
28
|
});
|
|
27
29
|
}
|
|
30
|
+
async function confirm(question) {
|
|
31
|
+
const answer = await prompt(`${question} (Y/n): `);
|
|
32
|
+
return answer.toLowerCase() !== 'n';
|
|
33
|
+
}
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// PROJECT DETECTION
|
|
36
|
+
// ============================================================================
|
|
37
|
+
function countSourceFiles(dir) {
|
|
38
|
+
let count = 0;
|
|
39
|
+
try {
|
|
40
|
+
const items = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
41
|
+
for (const item of items) {
|
|
42
|
+
if (item.name.startsWith('.') || item.name === 'node_modules')
|
|
43
|
+
continue;
|
|
44
|
+
const fullPath = (0, path_1.join)(dir, item.name);
|
|
45
|
+
if (item.isDirectory()) {
|
|
46
|
+
count += countSourceFiles(fullPath);
|
|
47
|
+
}
|
|
48
|
+
else if (item.name.endsWith('.ts') ||
|
|
49
|
+
item.name.endsWith('.tsx') ||
|
|
50
|
+
item.name.endsWith('.js') ||
|
|
51
|
+
item.name.endsWith('.jsx')) {
|
|
52
|
+
count++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Ignore access errors
|
|
58
|
+
}
|
|
59
|
+
return count;
|
|
60
|
+
}
|
|
61
|
+
function detectExistingProject(cwd) {
|
|
62
|
+
const details = [];
|
|
63
|
+
const stack = {};
|
|
64
|
+
let sourceFileCount = 0;
|
|
65
|
+
const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
|
|
66
|
+
if ((0, fs_1.existsSync)(packageJsonPath)) {
|
|
67
|
+
try {
|
|
68
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8'));
|
|
69
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
70
|
+
const depCount = Object.keys(pkg.dependencies || {}).length;
|
|
71
|
+
if (depCount > 0) {
|
|
72
|
+
details.push(`package.json with ${depCount} dependencies`);
|
|
73
|
+
}
|
|
74
|
+
if (deps['next'])
|
|
75
|
+
stack.framework = `Next.js ${deps['next']}`;
|
|
76
|
+
else if (deps['react'])
|
|
77
|
+
stack.framework = `React ${deps['react']}`;
|
|
78
|
+
else if (deps['vue'])
|
|
79
|
+
stack.framework = `Vue ${deps['vue']}`;
|
|
80
|
+
else if (deps['express'])
|
|
81
|
+
stack.framework = `Express ${deps['express']}`;
|
|
82
|
+
if (deps['drizzle-orm'])
|
|
83
|
+
stack.database = 'Drizzle ORM';
|
|
84
|
+
else if (deps['prisma'])
|
|
85
|
+
stack.database = 'Prisma';
|
|
86
|
+
else if (deps['mongoose'])
|
|
87
|
+
stack.database = 'MongoDB/Mongoose';
|
|
88
|
+
if (deps['@supabase/supabase-js'])
|
|
89
|
+
stack.auth = 'Supabase Auth';
|
|
90
|
+
else if (deps['next-auth'])
|
|
91
|
+
stack.auth = 'NextAuth.js';
|
|
92
|
+
else if (deps['@clerk/nextjs'])
|
|
93
|
+
stack.auth = 'Clerk';
|
|
94
|
+
if (deps['tailwindcss'])
|
|
95
|
+
stack.styling = 'Tailwind CSS';
|
|
96
|
+
if (deps['typescript'] || (0, fs_1.existsSync)((0, path_1.join)(cwd, 'tsconfig.json'))) {
|
|
97
|
+
stack.language = 'TypeScript';
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
stack.language = 'JavaScript';
|
|
101
|
+
}
|
|
102
|
+
if (deps['vitest'])
|
|
103
|
+
stack.testing = 'Vitest';
|
|
104
|
+
else if (deps['jest'])
|
|
105
|
+
stack.testing = 'Jest';
|
|
106
|
+
else if (deps['@playwright/test'])
|
|
107
|
+
stack.testing = 'Playwright';
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Ignore parse errors
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const sourceDirs = ['src', 'app', 'pages', 'components', 'lib'];
|
|
114
|
+
for (const dir of sourceDirs) {
|
|
115
|
+
const dirPath = (0, path_1.join)(cwd, dir);
|
|
116
|
+
if ((0, fs_1.existsSync)(dirPath)) {
|
|
117
|
+
try {
|
|
118
|
+
if ((0, fs_1.statSync)(dirPath).isDirectory()) {
|
|
119
|
+
const files = countSourceFiles(dirPath);
|
|
120
|
+
if (files > 0) {
|
|
121
|
+
sourceFileCount += files;
|
|
122
|
+
details.push(`${dir}/ with ${files} source files`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore access errors
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const configFiles = ['tsconfig.json', 'next.config.js', 'next.config.mjs', 'vite.config.ts', 'tailwind.config.js'];
|
|
132
|
+
for (const file of configFiles) {
|
|
133
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(cwd, file))) {
|
|
134
|
+
details.push(file);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
exists: sourceFileCount > 5 || details.length >= 3,
|
|
139
|
+
files: sourceFileCount,
|
|
140
|
+
details,
|
|
141
|
+
stack
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function buildStructureString(cwd) {
|
|
145
|
+
try {
|
|
146
|
+
const items = (0, fs_1.readdirSync)(cwd);
|
|
147
|
+
const dirs = [];
|
|
148
|
+
const files = [];
|
|
149
|
+
for (const item of items) {
|
|
150
|
+
if (item.startsWith('.') || item === 'node_modules')
|
|
151
|
+
continue;
|
|
152
|
+
const fullPath = (0, path_1.join)(cwd, item);
|
|
153
|
+
try {
|
|
154
|
+
if ((0, fs_1.statSync)(fullPath).isDirectory()) {
|
|
155
|
+
dirs.push(item + '/');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
files.push(item);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Skip inaccessible items
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [...dirs.sort(), ...files.sort()].join('\n');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return '[Could not scan structure]';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function runGuidedQuestions() {
|
|
172
|
+
console.log(chalk_1.default.cyan('\n ━━━ Let\'s define your project ━━━\n'));
|
|
173
|
+
console.log(chalk_1.default.gray(' Answer these questions (press Enter to skip any)\n'));
|
|
174
|
+
console.log(chalk_1.default.white(' 1. What are you building?\n'));
|
|
175
|
+
const oneLiner = await prompt(' ') || 'A web application';
|
|
176
|
+
console.log(chalk_1.default.white('\n 2. What problem does this solve?\n'));
|
|
177
|
+
const problem = await prompt(' ') || '';
|
|
178
|
+
console.log(chalk_1.default.white('\n 3. Who will use this?\n'));
|
|
179
|
+
console.log(chalk_1.default.gray(' (e.g., "small business owners", "freelancers", "developers")\n'));
|
|
180
|
+
const users = await prompt(' ') || 'General users';
|
|
181
|
+
console.log(chalk_1.default.white('\n 4. What are the 3 must-have features?\n'));
|
|
182
|
+
console.log(chalk_1.default.gray(' (Enter each feature, then press Enter. Type "done" when finished)\n'));
|
|
183
|
+
const features = [];
|
|
184
|
+
for (let i = 0; i < 5; i++) {
|
|
185
|
+
const feature = await prompt(` Feature ${i + 1}: `);
|
|
186
|
+
if (!feature || feature.toLowerCase() === 'done')
|
|
187
|
+
break;
|
|
188
|
+
features.push(feature);
|
|
189
|
+
}
|
|
190
|
+
console.log(chalk_1.default.white('\n 5. Do users need to create accounts?\n'));
|
|
191
|
+
const authAnswer = await prompt(' (y/n): ');
|
|
192
|
+
const auth = authAnswer.toLowerCase() === 'y' || authAnswer.toLowerCase() === 'yes';
|
|
193
|
+
console.log(chalk_1.default.white('\n 6. Will you charge money?\n'));
|
|
194
|
+
const paymentsAnswer = await prompt(' (y/n): ');
|
|
195
|
+
const payments = paymentsAnswer.toLowerCase() === 'y' || paymentsAnswer.toLowerCase() === 'yes';
|
|
196
|
+
console.log(chalk_1.default.white('\n 7. Any specific integrations needed?\n'));
|
|
197
|
+
console.log(chalk_1.default.gray(' (e.g., "Stripe, SendGrid, Twilio" or press Enter to skip)\n'));
|
|
198
|
+
const integrations = await prompt(' ') || '';
|
|
199
|
+
console.log(chalk_1.default.white('\n 8. When do you need this done?\n'));
|
|
200
|
+
console.log(chalk_1.default.gray(' (e.g., "2 weeks", "end of month", or press Enter to skip)\n'));
|
|
201
|
+
const deadline = await prompt(' ') || '';
|
|
202
|
+
console.log(chalk_1.default.green('\n ✓ Got it! Creating your PRD...\n'));
|
|
203
|
+
return { oneLiner, problem, users, features, auth, payments, integrations, deadline };
|
|
204
|
+
}
|
|
205
|
+
function createPrdFromAnswers(projectName, projectType, answers) {
|
|
206
|
+
const date = new Date().toISOString().split('T')[0];
|
|
207
|
+
const featuresSection = answers.features.length > 0
|
|
208
|
+
? answers.features.map((f, i) => `${i + 1}. [ ] **${f}**`).join('\n')
|
|
209
|
+
: '1. [ ] **Feature 1:** [To be defined]\n2. [ ] **Feature 2:** [To be defined]';
|
|
210
|
+
const techRequirements = [];
|
|
211
|
+
if (answers.auth)
|
|
212
|
+
techRequirements.push('User authentication (Supabase Auth)');
|
|
213
|
+
if (answers.payments)
|
|
214
|
+
techRequirements.push('Payment processing (Stripe)');
|
|
215
|
+
if (answers.integrations)
|
|
216
|
+
techRequirements.push(answers.integrations);
|
|
217
|
+
return `# Product Requirements Document
|
|
218
|
+
# Project: ${projectName}
|
|
219
|
+
# Created: ${date}
|
|
220
|
+
# Type: ${projectType}
|
|
221
|
+
|
|
222
|
+
## Overview
|
|
223
|
+
**One-liner:** ${answers.oneLiner}
|
|
224
|
+
|
|
225
|
+
**Problem:** ${answers.problem || '[To be refined]'}
|
|
226
|
+
|
|
227
|
+
**Solution:** ${answers.oneLiner}
|
|
228
|
+
|
|
229
|
+
## Target Users
|
|
230
|
+
- **Primary:** ${answers.users}
|
|
231
|
+
|
|
232
|
+
## Core Features (MVP)
|
|
233
|
+
${featuresSection}
|
|
234
|
+
|
|
235
|
+
## Technical Requirements
|
|
236
|
+
${techRequirements.length > 0 ? techRequirements.map(t => `- ${t}`).join('\n') : '- [No specific requirements noted]'}
|
|
237
|
+
|
|
238
|
+
## Timeline
|
|
239
|
+
${answers.deadline ? `- **Target:** ${answers.deadline}` : '- [No deadline specified]'}
|
|
240
|
+
|
|
241
|
+
## Notes
|
|
242
|
+
- Authentication: ${answers.auth ? 'Yes - users need accounts' : 'No - public access'}
|
|
243
|
+
- Payments: ${answers.payments ? 'Yes - will charge users' : 'No - free to use'}
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
<!-- Generated from guided questions - AI reads this to build your project -->
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
function createPrdTemplate(projectName, projectType) {
|
|
250
|
+
const date = new Date().toISOString().split('T')[0];
|
|
251
|
+
const typeSpecificSections = projectType === 'client'
|
|
252
|
+
? `
|
|
253
|
+
## Client Info
|
|
254
|
+
- Client Name: [Who is this for?]
|
|
255
|
+
- Contact: [Primary contact]
|
|
256
|
+
- Deadline: [When is this due?]
|
|
257
|
+
`
|
|
258
|
+
: projectType === 'business'
|
|
259
|
+
? `
|
|
260
|
+
## Business Context
|
|
261
|
+
- Target Market: [Who are you selling to?]
|
|
262
|
+
- Revenue Model: [How does this make money?]
|
|
263
|
+
- MVP Deadline: [When do you need to launch?]
|
|
264
|
+
`
|
|
265
|
+
: `
|
|
266
|
+
## Personal Goals
|
|
267
|
+
- Why am I building this? [Your motivation]
|
|
268
|
+
- Learning goals: [What do you want to learn?]
|
|
269
|
+
`;
|
|
270
|
+
return `# Product Requirements Document
|
|
271
|
+
# Project: ${projectName}
|
|
272
|
+
# Created: ${date}
|
|
273
|
+
# Type: ${projectType}
|
|
274
|
+
|
|
275
|
+
## Overview
|
|
276
|
+
**One-liner:** [Describe this project in one sentence]
|
|
277
|
+
|
|
278
|
+
**Problem:** [What problem does this solve?]
|
|
279
|
+
|
|
280
|
+
**Solution:** [How does this solve it?]
|
|
281
|
+
${typeSpecificSections}
|
|
282
|
+
## Target Users
|
|
283
|
+
- **Primary:** [Who is the main user?]
|
|
284
|
+
- **Secondary:** [Other users?]
|
|
285
|
+
|
|
286
|
+
## Core Features (MVP)
|
|
287
|
+
1. [ ] **Feature 1:** [Description]
|
|
288
|
+
2. [ ] **Feature 2:** [Description]
|
|
289
|
+
3. [ ] **Feature 3:** [Description]
|
|
290
|
+
|
|
291
|
+
## Nice-to-Have Features (Post-MVP)
|
|
292
|
+
1. [ ] [Feature description]
|
|
293
|
+
2. [ ] [Feature description]
|
|
294
|
+
|
|
295
|
+
## Technical Requirements
|
|
296
|
+
- **Must use:** [Required technologies, APIs, etc.]
|
|
297
|
+
- **Must avoid:** [Things you don't want]
|
|
298
|
+
|
|
299
|
+
## Success Metrics
|
|
300
|
+
- [ ] [How will you measure success?]
|
|
301
|
+
- [ ] [What does "done" look like?]
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
<!-- AI reads this file to understand what to build -->
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// PROJECT FILE CREATION
|
|
309
|
+
// ============================================================================
|
|
310
|
+
function createProjectContext(projectName, stack, structure, isExisting) {
|
|
311
|
+
const date = new Date().toISOString().split('T')[0];
|
|
312
|
+
return `# PROJECT CONTEXT
|
|
313
|
+
# Last Scanned: ${date}
|
|
314
|
+
# Mode: ${isExisting ? 'Existing Project' : 'New Project'}
|
|
315
|
+
|
|
316
|
+
## Overview
|
|
317
|
+
name: ${projectName}
|
|
318
|
+
description: ${isExisting ? '[Existing project - AI will analyze on first interaction]' : '[AI will fill after first feature]'}
|
|
319
|
+
|
|
320
|
+
## Tech Stack
|
|
321
|
+
framework: ${stack.framework || '[Not detected]'}
|
|
322
|
+
language: ${stack.language || '[Not detected]'}
|
|
323
|
+
database: ${stack.database || '[Not detected]'}
|
|
324
|
+
auth: ${stack.auth || '[Not detected]'}
|
|
325
|
+
styling: ${stack.styling || '[Not detected]'}
|
|
326
|
+
testing: ${stack.testing || '[Not detected]'}
|
|
327
|
+
|
|
328
|
+
## Project Structure
|
|
329
|
+
\`\`\`
|
|
330
|
+
${structure || '[Empty project]'}
|
|
331
|
+
\`\`\`
|
|
332
|
+
|
|
333
|
+
## Key Files
|
|
334
|
+
<!-- AI: List the most important files for understanding the project -->
|
|
335
|
+
- Entry point: ${stack.framework?.includes('Next') ? 'src/app/page.tsx or pages/index.tsx' : '[AI will identify]'}
|
|
336
|
+
- Config: ${(0, fs_1.existsSync)((0, path_1.join)(process.cwd(), 'tsconfig.json')) ? 'tsconfig.json' : '[AI will identify]'}
|
|
337
|
+
- Database schema: [AI will identify]
|
|
338
|
+
- API routes: ${stack.framework?.includes('Next') ? 'src/app/api/ or pages/api/' : '[AI will identify]'}
|
|
339
|
+
|
|
340
|
+
## Existing Patterns
|
|
341
|
+
<!-- AI: Document patterns you find so you can reuse them -->
|
|
342
|
+
|
|
343
|
+
### API Route Pattern
|
|
344
|
+
\`\`\`typescript
|
|
345
|
+
[AI: Copy an example API route pattern from this project]
|
|
346
|
+
\`\`\`
|
|
347
|
+
|
|
348
|
+
### Component Pattern
|
|
349
|
+
\`\`\`typescript
|
|
350
|
+
[AI: Copy an example component pattern from this project]
|
|
351
|
+
\`\`\`
|
|
352
|
+
|
|
353
|
+
## Environment Variables
|
|
354
|
+
<!-- AI: List required env vars (don't include values!) -->
|
|
355
|
+
${(0, fs_1.existsSync)((0, path_1.join)(process.cwd(), '.env.example')) ? '[Check .env.example]' : '- [ ] [AI will identify required vars]'}
|
|
356
|
+
|
|
357
|
+
## Notes
|
|
358
|
+
<!-- AI: Any important context about this specific project -->
|
|
359
|
+
`;
|
|
360
|
+
}
|
|
361
|
+
function createProjectState(projectName, isExisting) {
|
|
362
|
+
const date = new Date().toISOString().split('T')[0];
|
|
363
|
+
return `# PROJECT STATE
|
|
364
|
+
# Last Updated: ${date}
|
|
365
|
+
# Auto-maintained by AI - update when starting/completing tasks
|
|
366
|
+
|
|
367
|
+
## Project Info
|
|
368
|
+
name: ${projectName}
|
|
369
|
+
phase: ${isExisting ? 'active' : 'planning'}
|
|
370
|
+
mode: ${isExisting ? 'existing-project' : 'new-project'}
|
|
371
|
+
|
|
372
|
+
## Current Sprint
|
|
373
|
+
Goal: ${isExisting ? '[AI will identify based on conversation]' : '[Define in first conversation]'}
|
|
374
|
+
|
|
375
|
+
## In Progress
|
|
376
|
+
<!-- AI: Add tasks here when you START working on them -->
|
|
377
|
+
<!-- Format: - [task] (started: date, agent: cursor/claude) -->
|
|
378
|
+
|
|
379
|
+
## Completed
|
|
380
|
+
<!-- AI: Move tasks here when DONE -->
|
|
381
|
+
<!-- Format: - [task] (completed: date) -->
|
|
382
|
+
${isExisting ? `\n- CodeBakers integration (completed: ${date})` : ''}
|
|
383
|
+
|
|
384
|
+
## Blockers
|
|
385
|
+
<!-- AI: List anything blocking progress -->
|
|
386
|
+
|
|
387
|
+
## Next Up
|
|
388
|
+
<!-- AI: Queue of upcoming tasks -->
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
391
|
+
function createDecisionsLog(projectName) {
|
|
392
|
+
const date = new Date().toISOString().split('T')[0];
|
|
393
|
+
return `# ARCHITECTURAL DECISIONS
|
|
394
|
+
# Project: ${projectName}
|
|
395
|
+
# AI: Add entries here when making significant technical choices
|
|
396
|
+
|
|
397
|
+
## How to Use This File
|
|
398
|
+
When you make a decision that affects architecture, add an entry:
|
|
399
|
+
- Date
|
|
400
|
+
- Decision
|
|
401
|
+
- Reason
|
|
402
|
+
- Alternatives considered
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## ${date}: CodeBakers Initialized
|
|
407
|
+
**Decision:** Using CodeBakers server-enforced pattern system
|
|
408
|
+
**Reason:** Ensure consistent, production-quality code
|
|
409
|
+
**Pattern:** Server-enforced via discover_patterns MCP tool
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
<!-- AI: Add new decisions above this line -->
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
function createDevlog(projectName, isExisting, auditScore) {
|
|
417
|
+
const date = new Date().toISOString().split('T')[0];
|
|
418
|
+
const timestamp = new Date().toISOString();
|
|
419
|
+
return `# Development Log
|
|
420
|
+
# Project: ${projectName}
|
|
421
|
+
|
|
422
|
+
## ${date} - CodeBakers Integration
|
|
423
|
+
**Session:** ${timestamp}
|
|
424
|
+
**Task Size:** MEDIUM
|
|
425
|
+
**Status:** Completed
|
|
426
|
+
|
|
427
|
+
### What was done:
|
|
428
|
+
- Integrated CodeBakers into ${isExisting ? 'existing' : 'new'} project
|
|
429
|
+
- Created project tracking files
|
|
430
|
+
- Configured AI assistants (Cursor + Claude Code)
|
|
431
|
+
${isExisting && auditScore !== undefined ? `- Ran initial code audit (Score: ${auditScore}%)` : ''}
|
|
432
|
+
|
|
433
|
+
### Files created:
|
|
434
|
+
- \`CLAUDE.md\` - AI bootstrap file
|
|
435
|
+
- \`.cursorrules\` - Cursor IDE rules
|
|
436
|
+
- \`PROJECT-CONTEXT.md\` - Project knowledge base
|
|
437
|
+
- \`PROJECT-STATE.md\` - Task tracking
|
|
438
|
+
- \`DECISIONS.md\` - Architecture log
|
|
439
|
+
- \`.codebakers/DEVLOG.md\` - This file
|
|
440
|
+
|
|
441
|
+
### Next steps:
|
|
442
|
+
${isExisting ? '- Start using AI assistance with existing codebase' : '- Define project requirements in first conversation'}
|
|
443
|
+
${isExisting ? '- AI will analyze existing patterns on first interaction' : '- AI will help scaffold initial features'}
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
`;
|
|
447
|
+
}
|
|
448
|
+
// ============================================================================
|
|
449
|
+
// IDE AND MCP SETUP
|
|
450
|
+
// ============================================================================
|
|
451
|
+
const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
|
|
452
|
+
# Zero-friction AI assistance - everything is automatic
|
|
453
|
+
|
|
454
|
+
## ON EVERY MESSAGE - AUTOMATIC WORKFLOW
|
|
455
|
+
|
|
456
|
+
### PHASE 1: CONTEXT LOAD (automatic)
|
|
457
|
+
1. Read CLAUDE.md → Load router
|
|
458
|
+
2. Read PROJECT-CONTEXT.md → Understand codebase
|
|
459
|
+
3. Read PROJECT-STATE.md → Check what's in progress
|
|
460
|
+
4. Read DECISIONS.md → Know past decisions
|
|
461
|
+
|
|
462
|
+
### PHASE 2: PRE-FLIGHT CHECK (before writing code)
|
|
463
|
+
Ask yourself silently:
|
|
464
|
+
- [ ] What existing code does this touch? (check PROJECT-CONTEXT.md)
|
|
465
|
+
- [ ] Is similar code already in the codebase? (copy that pattern)
|
|
466
|
+
- [ ] What's the data model involved?
|
|
467
|
+
- [ ] What are the error cases?
|
|
468
|
+
|
|
469
|
+
### PHASE 3: EXECUTE
|
|
470
|
+
- State: \`📋 CodeBakers | [Type] | Server-Enforced\`
|
|
471
|
+
- Call discover_patterns MCP tool first
|
|
472
|
+
- Follow patterns from server EXACTLY
|
|
473
|
+
|
|
474
|
+
### PHASE 4: SELF-REVIEW (before saying "done")
|
|
475
|
+
- [ ] TypeScript compiles? (npx tsc --noEmit)
|
|
476
|
+
- [ ] Imports resolve correctly?
|
|
477
|
+
- [ ] Error handling exists?
|
|
478
|
+
- [ ] Matches existing patterns in codebase?
|
|
479
|
+
- [ ] Tests written?
|
|
480
|
+
- [ ] PROJECT-STATE.md updated?
|
|
481
|
+
|
|
482
|
+
If ANY check fails, fix it before responding.
|
|
483
|
+
|
|
484
|
+
### PHASE 5: UPDATE STATE
|
|
485
|
+
- Update PROJECT-STATE.md with completed work
|
|
486
|
+
- Add to DECISIONS.md if architectural choice was made
|
|
487
|
+
- Update .codebakers/DEVLOG.md with session summary
|
|
488
|
+
|
|
489
|
+
## REMEMBER
|
|
490
|
+
- You are a full product team, not just a code assistant
|
|
491
|
+
- The modules contain production-tested patterns — USE THEM
|
|
492
|
+
- When in doubt, check existing code first
|
|
493
|
+
`;
|
|
494
|
+
const CURSORIGNORE_TEMPLATE = `# CodeBakers - Files to ignore in Cursor context
|
|
495
|
+
|
|
496
|
+
# Dependencies
|
|
497
|
+
node_modules/
|
|
498
|
+
.pnpm-store/
|
|
499
|
+
|
|
500
|
+
# Build outputs
|
|
501
|
+
dist/
|
|
502
|
+
build/
|
|
503
|
+
.next/
|
|
504
|
+
.nuxt/
|
|
505
|
+
out/
|
|
506
|
+
|
|
507
|
+
# Cache
|
|
508
|
+
.cache/
|
|
509
|
+
.turbo/
|
|
510
|
+
*.tsbuildinfo
|
|
511
|
+
|
|
512
|
+
# Logs
|
|
513
|
+
logs/
|
|
514
|
+
*.log
|
|
515
|
+
|
|
516
|
+
# Environment files (don't leak secrets)
|
|
517
|
+
.env
|
|
518
|
+
.env.local
|
|
519
|
+
.env.*.local
|
|
520
|
+
|
|
521
|
+
# IDE
|
|
522
|
+
.idea/
|
|
523
|
+
*.swp
|
|
524
|
+
|
|
525
|
+
# OS
|
|
526
|
+
.DS_Store
|
|
527
|
+
Thumbs.db
|
|
528
|
+
|
|
529
|
+
# Test coverage
|
|
530
|
+
coverage/
|
|
531
|
+
|
|
532
|
+
# Package locks
|
|
533
|
+
package-lock.json
|
|
534
|
+
yarn.lock
|
|
535
|
+
pnpm-lock.yaml
|
|
536
|
+
|
|
537
|
+
# Generated files
|
|
538
|
+
*.min.js
|
|
539
|
+
*.min.css
|
|
540
|
+
*.map
|
|
541
|
+
`;
|
|
542
|
+
const VSCODE_SETTINGS_TEMPLATE = {
|
|
543
|
+
"cursor.chat.defaultContext": [
|
|
544
|
+
"CLAUDE.md",
|
|
545
|
+
"PROJECT-CONTEXT.md",
|
|
546
|
+
"PROJECT-STATE.md",
|
|
547
|
+
"DECISIONS.md"
|
|
548
|
+
],
|
|
549
|
+
"cursor.chat.alwaysIncludeRules": true,
|
|
550
|
+
"cursor.composer.alwaysIncludeRules": true,
|
|
551
|
+
"cursor.general.enableAutoImport": true
|
|
552
|
+
};
|
|
553
|
+
function setupCursorIDE(cwd) {
|
|
554
|
+
const spinner = (0, ora_1.default)(' Setting up Cursor IDE...').start();
|
|
555
|
+
try {
|
|
556
|
+
// Write .cursorrules and .cursorignore
|
|
557
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
|
|
558
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
|
|
559
|
+
// Global MCP config for Cursor
|
|
560
|
+
const homeDir = process.env.USERPROFILE || process.env.HOME || '';
|
|
561
|
+
const globalCursorDir = (0, path_1.join)(homeDir, '.cursor');
|
|
562
|
+
if (!(0, fs_1.existsSync)(globalCursorDir)) {
|
|
563
|
+
(0, fs_1.mkdirSync)(globalCursorDir, { recursive: true });
|
|
564
|
+
}
|
|
565
|
+
const mcpConfigPath = (0, path_1.join)(globalCursorDir, 'mcp.json');
|
|
566
|
+
const isWindows = process.platform === 'win32';
|
|
567
|
+
const mcpConfig = {
|
|
568
|
+
mcpServers: {
|
|
569
|
+
codebakers: isWindows
|
|
570
|
+
? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
|
|
571
|
+
: { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
if ((0, fs_1.existsSync)(mcpConfigPath)) {
|
|
575
|
+
try {
|
|
576
|
+
const existing = JSON.parse((0, fs_1.readFileSync)(mcpConfigPath, 'utf-8'));
|
|
577
|
+
existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
|
|
578
|
+
(0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(existing, null, 2));
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
(0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
(0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
|
|
586
|
+
}
|
|
587
|
+
// VSCode settings
|
|
588
|
+
const vscodeDir = (0, path_1.join)(cwd, '.vscode');
|
|
589
|
+
if (!(0, fs_1.existsSync)(vscodeDir)) {
|
|
590
|
+
(0, fs_1.mkdirSync)(vscodeDir, { recursive: true });
|
|
591
|
+
}
|
|
592
|
+
const settingsPath = (0, path_1.join)(vscodeDir, 'settings.json');
|
|
593
|
+
if ((0, fs_1.existsSync)(settingsPath)) {
|
|
594
|
+
try {
|
|
595
|
+
const existing = JSON.parse((0, fs_1.readFileSync)(settingsPath, 'utf-8'));
|
|
596
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify({ ...existing, ...VSCODE_SETTINGS_TEMPLATE }, null, 2));
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
|
|
604
|
+
}
|
|
605
|
+
spinner.succeed('Cursor IDE configured!');
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
spinner.warn('Could not configure Cursor IDE (continuing anyway)');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function setupClaudeCodeMCP() {
|
|
612
|
+
const spinner = (0, ora_1.default)(' Setting up Claude Code MCP...').start();
|
|
613
|
+
try {
|
|
614
|
+
const homeDir = process.env.USERPROFILE || process.env.HOME || '';
|
|
615
|
+
let configPath;
|
|
616
|
+
const isWindows = process.platform === 'win32';
|
|
617
|
+
if (isWindows) {
|
|
618
|
+
configPath = (0, path_1.join)(homeDir, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
619
|
+
}
|
|
620
|
+
else if (process.platform === 'darwin') {
|
|
621
|
+
configPath = (0, path_1.join)(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
configPath = (0, path_1.join)(homeDir, '.config', 'claude', 'claude_desktop_config.json');
|
|
625
|
+
}
|
|
626
|
+
const configDir = (0, path_1.join)(configPath, '..');
|
|
627
|
+
if (!(0, fs_1.existsSync)(configDir)) {
|
|
628
|
+
(0, fs_1.mkdirSync)(configDir, { recursive: true });
|
|
629
|
+
}
|
|
630
|
+
const mcpConfig = {
|
|
631
|
+
mcpServers: {
|
|
632
|
+
codebakers: isWindows
|
|
633
|
+
? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
|
|
634
|
+
: { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
638
|
+
try {
|
|
639
|
+
const existing = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
|
|
640
|
+
if (!existing.mcpServers) {
|
|
641
|
+
existing.mcpServers = {};
|
|
642
|
+
}
|
|
643
|
+
existing.mcpServers.codebakers = mcpConfig.mcpServers.codebakers;
|
|
644
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(existing, null, 2));
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(mcpConfig, null, 2));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(mcpConfig, null, 2));
|
|
652
|
+
}
|
|
653
|
+
spinner.succeed('Claude Code MCP configured!');
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
spinner.warn('Could not configure Claude Code MCP (continuing anyway)');
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function createTrackingFiles(cwd, projectName, stack, structure, isExisting, auditScore) {
|
|
660
|
+
const spinner = (0, ora_1.default)(' Creating project tracking files...').start();
|
|
661
|
+
try {
|
|
662
|
+
// Create .codebakers directory
|
|
663
|
+
const codebakersDir = (0, path_1.join)(cwd, '.codebakers');
|
|
664
|
+
if (!(0, fs_1.existsSync)(codebakersDir)) {
|
|
665
|
+
(0, fs_1.mkdirSync)(codebakersDir, { recursive: true });
|
|
666
|
+
}
|
|
667
|
+
// Remove old .claude folder if it exists
|
|
668
|
+
const claudeDir = (0, path_1.join)(cwd, '.claude');
|
|
669
|
+
if ((0, fs_1.existsSync)(claudeDir)) {
|
|
670
|
+
try {
|
|
671
|
+
(0, fs_1.rmSync)(claudeDir, { recursive: true, force: true });
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
// Ignore errors
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// PROJECT-CONTEXT.md
|
|
678
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PROJECT-CONTEXT.md'), createProjectContext(projectName, stack, structure, isExisting));
|
|
679
|
+
// PROJECT-STATE.md
|
|
680
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PROJECT-STATE.md'), createProjectState(projectName, isExisting));
|
|
681
|
+
// DECISIONS.md
|
|
682
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'DECISIONS.md'), createDecisionsLog(projectName));
|
|
683
|
+
// .codebakers/DEVLOG.md
|
|
684
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(codebakersDir, 'DEVLOG.md'), createDevlog(projectName, isExisting, auditScore));
|
|
685
|
+
// .codebakers.json state file
|
|
686
|
+
const stateFile = (0, path_1.join)(cwd, '.codebakers.json');
|
|
687
|
+
const state = {
|
|
688
|
+
version: '1.0',
|
|
689
|
+
serverEnforced: true,
|
|
690
|
+
projectType: isExisting ? 'existing' : 'new',
|
|
691
|
+
projectName,
|
|
692
|
+
createdAt: new Date().toISOString(),
|
|
693
|
+
stack,
|
|
694
|
+
auditScore: auditScore
|
|
695
|
+
};
|
|
696
|
+
(0, fs_1.writeFileSync)(stateFile, JSON.stringify(state, null, 2));
|
|
697
|
+
spinner.succeed('Project tracking files created!');
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
spinner.warn('Some tracking files could not be created');
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function updateGitignore(cwd) {
|
|
704
|
+
const gitignorePath = (0, path_1.join)(cwd, '.gitignore');
|
|
705
|
+
if ((0, fs_1.existsSync)(gitignorePath)) {
|
|
706
|
+
const gitignore = (0, fs_1.readFileSync)(gitignorePath, 'utf-8');
|
|
707
|
+
if (!gitignore.includes('.cursorrules')) {
|
|
708
|
+
(0, fs_1.writeFileSync)(gitignorePath, gitignore + '\n# CodeBakers\n.cursorrules\n');
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
28
712
|
/**
|
|
29
713
|
* Get CLI version from package.json
|
|
30
714
|
*/
|
|
@@ -88,9 +772,8 @@ async function go(options = {}) {
|
|
|
88
772
|
if (existingApiKey) {
|
|
89
773
|
log(`Found API key: ${existingApiKey.substring(0, 8)}...`, options);
|
|
90
774
|
console.log(chalk_1.default.green(' ✓ You\'re already logged in!\n'));
|
|
91
|
-
//
|
|
92
|
-
await
|
|
93
|
-
await configureMCP(options);
|
|
775
|
+
// Run complete project setup
|
|
776
|
+
await setupProject(options, { apiKey: existingApiKey });
|
|
94
777
|
await showSuccessAndRestart();
|
|
95
778
|
return;
|
|
96
779
|
}
|
|
@@ -104,9 +787,8 @@ async function go(options = {}) {
|
|
|
104
787
|
console.log(chalk_1.default.yellow(' ⚠️ Trial expiring soon! Extend with GitHub:\n'));
|
|
105
788
|
console.log(chalk_1.default.cyan(' codebakers extend\n'));
|
|
106
789
|
}
|
|
107
|
-
//
|
|
108
|
-
await
|
|
109
|
-
await configureMCP(options);
|
|
790
|
+
// Run complete project setup
|
|
791
|
+
await setupProject(options, { trialId: existingTrial.trialId });
|
|
110
792
|
await showSuccessAndRestart();
|
|
111
793
|
return;
|
|
112
794
|
}
|
|
@@ -212,11 +894,8 @@ async function startTrialWithGitHub(options = {}) {
|
|
|
212
894
|
const username = data.githubUsername ? ` Welcome, @${data.githubUsername}!` : '';
|
|
213
895
|
spinner.succeed(`Trial started (${data.daysRemaining} days free)${username}`);
|
|
214
896
|
console.log('');
|
|
215
|
-
//
|
|
216
|
-
await
|
|
217
|
-
// Configure MCP
|
|
218
|
-
await configureMCP(options);
|
|
219
|
-
// Show success and restart
|
|
897
|
+
// Run complete project setup
|
|
898
|
+
await setupProject(options, { trialId: data.trialId });
|
|
220
899
|
await showSuccessAndRestart();
|
|
221
900
|
return;
|
|
222
901
|
}
|
|
@@ -232,29 +911,6 @@ async function startTrialWithGitHub(options = {}) {
|
|
|
232
911
|
console.log(chalk_1.default.cyan(` ${authUrl}\n`));
|
|
233
912
|
}
|
|
234
913
|
}
|
|
235
|
-
async function configureMCP(options = {}) {
|
|
236
|
-
log('Configuring MCP integration...', options);
|
|
237
|
-
const spinner = (0, ora_1.default)('Configuring Claude Code integration...').start();
|
|
238
|
-
const isWindows = process.platform === 'win32';
|
|
239
|
-
const mcpCmd = isWindows
|
|
240
|
-
? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
|
|
241
|
-
: 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
|
|
242
|
-
try {
|
|
243
|
-
(0, child_process_1.execSync)(mcpCmd, { stdio: 'pipe' });
|
|
244
|
-
spinner.succeed('CodeBakers connected to Claude Code');
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
248
|
-
if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
|
|
249
|
-
spinner.succeed('CodeBakers already connected to Claude Code');
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
spinner.warn('Could not auto-configure Claude Code');
|
|
253
|
-
console.log(chalk_1.default.gray('\n Run this command manually:\n'));
|
|
254
|
-
console.log(chalk_1.default.cyan(` ${mcpCmd}\n`));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
914
|
/**
|
|
259
915
|
* Handle API key login flow (for paid users)
|
|
260
916
|
*/
|
|
@@ -273,11 +929,8 @@ async function handleApiKeyLogin(options = {}) {
|
|
|
273
929
|
// Save API key
|
|
274
930
|
(0, config_js_1.setApiKey)(apiKey);
|
|
275
931
|
console.log(chalk_1.default.green(' ✓ Logged in successfully!\n'));
|
|
276
|
-
//
|
|
277
|
-
await
|
|
278
|
-
// Configure MCP
|
|
279
|
-
await configureMCP(options);
|
|
280
|
-
// Show success
|
|
932
|
+
// Run complete project setup
|
|
933
|
+
await setupProject(options, { apiKey });
|
|
281
934
|
await showSuccessAndRestart();
|
|
282
935
|
}
|
|
283
936
|
catch (error) {
|
|
@@ -438,62 +1091,237 @@ The tools will show "OFFLINE MODE" if the server can't be reached. In this case:
|
|
|
438
1091
|
CodeBakers v6.0 - Server-Enforced Patterns
|
|
439
1092
|
`;
|
|
440
1093
|
/**
|
|
441
|
-
*
|
|
442
|
-
*
|
|
1094
|
+
* Complete project setup - handles everything:
|
|
1095
|
+
* - Detect new vs existing project
|
|
1096
|
+
* - Set up all tracking files
|
|
1097
|
+
* - Configure Cursor and Claude Code MCP
|
|
1098
|
+
* - Run guided questions for new projects
|
|
1099
|
+
* - Run code review for existing projects
|
|
443
1100
|
*/
|
|
444
|
-
async function
|
|
445
|
-
|
|
446
|
-
|
|
1101
|
+
async function setupProject(options = {}, auth) {
|
|
1102
|
+
const cwd = process.cwd();
|
|
1103
|
+
// Detect if this is an existing project
|
|
1104
|
+
const projectInfo = detectExistingProject(cwd);
|
|
1105
|
+
if (projectInfo.exists) {
|
|
1106
|
+
// Existing project detected
|
|
1107
|
+
await setupExistingProject(cwd, projectInfo, options, auth);
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
// New project
|
|
1111
|
+
await setupNewProject(cwd, options, auth);
|
|
1112
|
+
}
|
|
447
1113
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
log(
|
|
454
|
-
|
|
1114
|
+
async function setupNewProject(cwd, options = {}, auth) {
|
|
1115
|
+
console.log(chalk_1.default.cyan('\n ━━━ New Project Setup ━━━\n'));
|
|
1116
|
+
// Get project info
|
|
1117
|
+
console.log(chalk_1.default.white(' What kind of project is this?\n'));
|
|
1118
|
+
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('PERSONAL') + chalk_1.default.gray(' - Just building for myself'));
|
|
1119
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('CLIENT') + chalk_1.default.gray(' - Building for someone else'));
|
|
1120
|
+
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('BUSINESS') + chalk_1.default.gray(' - My own product/startup\n'));
|
|
1121
|
+
let typeChoice = '';
|
|
1122
|
+
while (!['1', '2', '3'].includes(typeChoice)) {
|
|
1123
|
+
typeChoice = await prompt(' Enter 1, 2, or 3: ');
|
|
1124
|
+
}
|
|
1125
|
+
const typeMap = { '1': 'personal', '2': 'client', '3': 'business' };
|
|
1126
|
+
const projectType = typeMap[typeChoice];
|
|
1127
|
+
const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
|
|
1128
|
+
const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
|
|
1129
|
+
console.log(chalk_1.default.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
|
|
1130
|
+
// Install bootstrap files
|
|
1131
|
+
console.log(chalk_1.default.white(' Installing CodeBakers...\n'));
|
|
1132
|
+
installBootstrapFilesSync(cwd);
|
|
1133
|
+
// Create tracking files
|
|
1134
|
+
const structure = buildStructureString(cwd);
|
|
1135
|
+
createTrackingFiles(cwd, projectName, {}, structure, false);
|
|
1136
|
+
// Setup IDEs and MCP
|
|
1137
|
+
console.log('');
|
|
1138
|
+
setupCursorIDE(cwd);
|
|
1139
|
+
setupClaudeCodeMCP();
|
|
1140
|
+
// Update .gitignore
|
|
1141
|
+
updateGitignore(cwd);
|
|
1142
|
+
// How to describe project
|
|
1143
|
+
console.log(chalk_1.default.white('\n 📝 How would you like to describe your project?\n'));
|
|
1144
|
+
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('GUIDED QUESTIONS') + chalk_1.default.gray(' - I\'ll ask you step by step'));
|
|
1145
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('WRITE A PRD') + chalk_1.default.gray(' - Create a blank template to fill out'));
|
|
1146
|
+
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('PASTE/UPLOAD PRD') + chalk_1.default.gray(' - I already have requirements written'));
|
|
1147
|
+
console.log(chalk_1.default.gray(' 4. ') + chalk_1.default.cyan('DESCRIBE IN CHAT') + chalk_1.default.gray(' - Just tell the AI what you want'));
|
|
1148
|
+
console.log(chalk_1.default.gray(' 5. ') + chalk_1.default.cyan('SHARE FILES') + chalk_1.default.gray(' - I\'ll share docs/mockups/screenshots\n'));
|
|
1149
|
+
let describeChoice = '';
|
|
1150
|
+
while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
|
|
1151
|
+
describeChoice = await prompt(' Enter 1-5: ');
|
|
1152
|
+
}
|
|
1153
|
+
let prdCreated = false;
|
|
1154
|
+
if (describeChoice === '1') {
|
|
1155
|
+
// Guided questions
|
|
1156
|
+
const answers = await runGuidedQuestions();
|
|
1157
|
+
const prdSpinner = (0, ora_1.default)(' Creating PRD from your answers...').start();
|
|
1158
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PRD.md'), createPrdFromAnswers(projectName, projectType, answers));
|
|
1159
|
+
prdSpinner.succeed('PRD created from your answers!');
|
|
1160
|
+
console.log(chalk_1.default.yellow('\n → Review PRD.md, then start building with the AI\n'));
|
|
1161
|
+
prdCreated = true;
|
|
1162
|
+
}
|
|
1163
|
+
else if (describeChoice === '2') {
|
|
1164
|
+
// Write PRD template
|
|
1165
|
+
const prdSpinner = (0, ora_1.default)(' Creating PRD template...').start();
|
|
1166
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PRD.md'), createPrdTemplate(projectName, projectType));
|
|
1167
|
+
prdSpinner.succeed('PRD template created!');
|
|
1168
|
+
console.log(chalk_1.default.yellow('\n → Open PRD.md and fill in your requirements\n'));
|
|
1169
|
+
prdCreated = true;
|
|
1170
|
+
}
|
|
1171
|
+
else if (describeChoice === '3') {
|
|
1172
|
+
// Paste/upload existing PRD
|
|
1173
|
+
console.log(chalk_1.default.cyan('\n ━━━ Paste Your Requirements ━━━\n'));
|
|
1174
|
+
console.log(chalk_1.default.gray(' Paste your PRD, requirements, or spec below.'));
|
|
1175
|
+
console.log(chalk_1.default.gray(' When done, type ') + chalk_1.default.cyan('END') + chalk_1.default.gray(' on a new line and press Enter.\n'));
|
|
1176
|
+
const lines = [];
|
|
1177
|
+
let line = '';
|
|
1178
|
+
while (true) {
|
|
1179
|
+
line = await prompt(' ');
|
|
1180
|
+
if (line.toUpperCase() === 'END')
|
|
1181
|
+
break;
|
|
1182
|
+
lines.push(line);
|
|
1183
|
+
}
|
|
1184
|
+
if (lines.length > 0) {
|
|
1185
|
+
const content = lines.join('\n');
|
|
1186
|
+
const prdContent = `# Product Requirements Document
|
|
1187
|
+
# Project: ${projectName}
|
|
1188
|
+
# Created: ${new Date().toISOString().split('T')[0]}
|
|
1189
|
+
# Type: ${projectType}
|
|
1190
|
+
# Source: Pasted by user
|
|
1191
|
+
|
|
1192
|
+
${content}
|
|
1193
|
+
|
|
1194
|
+
---
|
|
1195
|
+
<!-- User-provided requirements - AI reads this to build your project -->
|
|
1196
|
+
`;
|
|
1197
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PRD.md'), prdContent);
|
|
1198
|
+
console.log(chalk_1.default.green('\n ✓ Saved to PRD.md'));
|
|
1199
|
+
console.log(chalk_1.default.yellow(' → The AI will read this when you start building\n'));
|
|
1200
|
+
prdCreated = true;
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
console.log(chalk_1.default.gray('\n No content pasted. You can add PRD.md manually later.\n'));
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
else if (describeChoice === '4') {
|
|
1207
|
+
// Describe in chat
|
|
1208
|
+
console.log(chalk_1.default.gray('\n Perfect! Just describe your project to the AI when you\'re ready.\n'));
|
|
1209
|
+
console.log(chalk_1.default.gray(' Example: "Build me a SaaS for invoice management with Stripe payments"\n'));
|
|
1210
|
+
}
|
|
1211
|
+
else {
|
|
1212
|
+
// Share files (option 5)
|
|
1213
|
+
console.log(chalk_1.default.gray('\n Great! When chatting with the AI:\n'));
|
|
1214
|
+
console.log(chalk_1.default.gray(' • Drag and drop your mockups or screenshots'));
|
|
1215
|
+
console.log(chalk_1.default.gray(' • Share links to Figma, design files, or websites'));
|
|
1216
|
+
console.log(chalk_1.default.gray(' • Reference existing apps: "Make it look like Linear"\n'));
|
|
1217
|
+
console.log(chalk_1.default.cyan(' The AI will analyze them and start building.\n'));
|
|
1218
|
+
}
|
|
1219
|
+
// Confirm to server
|
|
1220
|
+
if (auth) {
|
|
1221
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
1222
|
+
confirmDownload(apiUrl, auth, {
|
|
1223
|
+
version: '6.0',
|
|
1224
|
+
moduleCount: 0,
|
|
1225
|
+
cliVersion: getCliVersion(),
|
|
1226
|
+
command: 'go',
|
|
1227
|
+
projectName,
|
|
1228
|
+
}).catch(() => { });
|
|
1229
|
+
}
|
|
455
1230
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
1231
|
+
async function setupExistingProject(cwd, projectInfo, options = {}, auth) {
|
|
1232
|
+
console.log(chalk_1.default.cyan('\n ━━━ Existing Project Detected ━━━\n'));
|
|
1233
|
+
// Show what was detected
|
|
1234
|
+
console.log(chalk_1.default.gray(' Found:'));
|
|
1235
|
+
for (const detail of projectInfo.details.slice(0, 5)) {
|
|
1236
|
+
console.log(chalk_1.default.gray(` • ${detail}`));
|
|
1237
|
+
}
|
|
1238
|
+
const stackItems = Object.entries(projectInfo.stack).filter(([_, v]) => v);
|
|
1239
|
+
if (stackItems.length > 0) {
|
|
1240
|
+
console.log(chalk_1.default.gray('\n Tech Stack:'));
|
|
1241
|
+
for (const [key, value] of stackItems) {
|
|
1242
|
+
console.log(chalk_1.default.gray(` • ${key}: ${value}`));
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// Get project name
|
|
1246
|
+
const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
|
|
1247
|
+
const projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
|
|
1248
|
+
// Code review offer
|
|
1249
|
+
console.log(chalk_1.default.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
|
|
1250
|
+
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('YES, REVIEW & FIX') + chalk_1.default.gray(' - Run audit, then auto-fix issues'));
|
|
1251
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('REVIEW ONLY') + chalk_1.default.gray(' - Just show me the issues'));
|
|
1252
|
+
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('SKIP') + chalk_1.default.gray(' - Just install CodeBakers\n'));
|
|
1253
|
+
let reviewChoice = '';
|
|
1254
|
+
while (!['1', '2', '3'].includes(reviewChoice)) {
|
|
1255
|
+
reviewChoice = await prompt(' Enter 1, 2, or 3: ');
|
|
1256
|
+
}
|
|
1257
|
+
let auditScore;
|
|
1258
|
+
if (reviewChoice !== '3') {
|
|
1259
|
+
console.log(chalk_1.default.blue('\n Running code audit...\n'));
|
|
1260
|
+
const auditResult = await (0, audit_js_1.audit)();
|
|
1261
|
+
auditScore = auditResult.score;
|
|
1262
|
+
if (auditResult.score >= 90) {
|
|
1263
|
+
console.log(chalk_1.default.green('\n 🎉 Your code is already in great shape!\n'));
|
|
1264
|
+
}
|
|
1265
|
+
else if (reviewChoice === '1') {
|
|
1266
|
+
const fixableCount = auditResult.checks.filter(c => !c.passed && c.severity !== 'info').length;
|
|
1267
|
+
if (fixableCount > 0) {
|
|
1268
|
+
console.log(chalk_1.default.blue('\n 🔧 Attempting to auto-fix issues...\n'));
|
|
1269
|
+
const healResult = await (0, heal_js_1.heal)({ auto: true });
|
|
1270
|
+
if (healResult.fixed > 0) {
|
|
1271
|
+
console.log(chalk_1.default.green(`\n ✓ Fixed ${healResult.fixed} issue(s)!`));
|
|
1272
|
+
if (healResult.remaining > 0) {
|
|
1273
|
+
console.log(chalk_1.default.yellow(` ⚠ ${healResult.remaining} issue(s) need manual attention.`));
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
console.log(chalk_1.default.gray('\n Run `codebakers heal --auto` later to fix issues.\n'));
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
// Install files
|
|
1283
|
+
console.log(chalk_1.default.white('\n Installing CodeBakers...\n'));
|
|
1284
|
+
installBootstrapFilesSync(cwd);
|
|
1285
|
+
// Create tracking files with detected stack
|
|
1286
|
+
const structure = buildStructureString(cwd);
|
|
1287
|
+
createTrackingFiles(cwd, projectName, projectInfo.stack, structure, true, auditScore);
|
|
1288
|
+
// Setup IDEs and MCP
|
|
1289
|
+
console.log('');
|
|
1290
|
+
setupCursorIDE(cwd);
|
|
1291
|
+
setupClaudeCodeMCP();
|
|
1292
|
+
// Update .gitignore
|
|
1293
|
+
updateGitignore(cwd);
|
|
1294
|
+
// Confirm to server
|
|
1295
|
+
if (auth) {
|
|
1296
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
1297
|
+
confirmDownload(apiUrl, auth, {
|
|
1298
|
+
version: '6.0',
|
|
1299
|
+
moduleCount: 0,
|
|
1300
|
+
cliVersion: getCliVersion(),
|
|
1301
|
+
command: 'go',
|
|
1302
|
+
projectName,
|
|
1303
|
+
}).catch(() => { });
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
function installBootstrapFilesSync(cwd) {
|
|
1307
|
+
const spinner = (0, ora_1.default)(' Installing bootstrap files...').start();
|
|
465
1308
|
try {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
1309
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'CLAUDE.md'), V6_CLAUDE_MD);
|
|
1310
|
+
// .cursorrules is written by setupCursorIDE
|
|
1311
|
+
// Remove old .claude folder if it exists
|
|
1312
|
+
const claudeDir = (0, path_1.join)(cwd, '.claude');
|
|
1313
|
+
if ((0, fs_1.existsSync)(claudeDir)) {
|
|
1314
|
+
try {
|
|
1315
|
+
(0, fs_1.rmSync)(claudeDir, { recursive: true, force: true });
|
|
1316
|
+
}
|
|
1317
|
+
catch {
|
|
1318
|
+
// Ignore errors
|
|
474
1319
|
}
|
|
475
|
-
// Upgrade from v5
|
|
476
|
-
log('Upgrading from v5 to v6...', options);
|
|
477
|
-
}
|
|
478
|
-
// Write v6.0 bootstrap files
|
|
479
|
-
(0, fs_1.writeFileSync)(claudeMdPath, V6_CLAUDE_MD);
|
|
480
|
-
(0, fs_1.writeFileSync)(cursorRulesPath, V6_CURSORRULES);
|
|
481
|
-
spinner.succeed('CodeBakers v6.0 installed');
|
|
482
|
-
console.log(chalk_1.default.gray(' Patterns are server-enforced via MCP tools\n'));
|
|
483
|
-
// Confirm install to server (non-blocking)
|
|
484
|
-
if (auth) {
|
|
485
|
-
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
486
|
-
confirmDownload(apiUrl, auth, {
|
|
487
|
-
version: '6.0',
|
|
488
|
-
moduleCount: 0, // No local modules in v6
|
|
489
|
-
cliVersion: getCliVersion(),
|
|
490
|
-
command: 'go',
|
|
491
|
-
}).catch(() => { }); // Silently ignore
|
|
492
1320
|
}
|
|
1321
|
+
spinner.succeed('Bootstrap files installed!');
|
|
493
1322
|
}
|
|
494
1323
|
catch (error) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
console.log(chalk_1.default.gray(' MCP tools will still work without local files.\n'));
|
|
1324
|
+
spinner.fail('Failed to install bootstrap files');
|
|
1325
|
+
throw error;
|
|
498
1326
|
}
|
|
499
1327
|
}
|