@codebakers/cli 1.1.5 → 1.1.6
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/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +218 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +772 -0
- package/dist/commands/install-hook.d.ts +12 -0
- package/dist/commands/install-hook.js +193 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +54 -0
- package/dist/commands/mcp-config.d.ts +6 -0
- package/dist/commands/mcp-config.js +209 -0
- package/dist/commands/serve.d.ts +1 -0
- package/dist/commands/serve.js +26 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +92 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +49 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +50 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +33 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +71 -1075
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +544 -0
- package/package.json +16 -38
- package/src/commands/doctor.ts +231 -0
- package/src/commands/init.ts +827 -0
- package/src/commands/install-hook.ts +207 -0
- package/src/commands/install.ts +94 -0
- package/src/commands/login.ts +56 -0
- package/src/commands/mcp-config.ts +235 -0
- package/src/commands/serve.ts +23 -0
- package/src/commands/setup.ts +104 -0
- package/src/commands/status.ts +48 -0
- package/src/commands/uninstall.ts +49 -0
- package/src/config.ts +34 -0
- package/src/index.ts +87 -0
- package/src/mcp/server.ts +617 -0
- package/tsconfig.json +16 -0
- package/README.md +0 -89
- package/dist/chunk-7CKLRE2H.js +0 -36
- package/dist/config-R2H6JKGW.js +0 -16
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.init = init;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const readline_1 = require("readline");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const config_js_1 = require("../config.js");
|
|
13
|
+
// Enhanced .cursorrules with pre-flight and self-review automation
|
|
14
|
+
const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
|
|
15
|
+
# Zero-friction AI assistance - everything is automatic
|
|
16
|
+
|
|
17
|
+
## ON EVERY MESSAGE - AUTOMATIC WORKFLOW
|
|
18
|
+
|
|
19
|
+
### PHASE 1: CONTEXT LOAD (automatic)
|
|
20
|
+
1. Read CLAUDE.md → Load router
|
|
21
|
+
2. Read PRD.md → Understand what we're building
|
|
22
|
+
3. Read PROJECT-CONTEXT.md → Understand codebase
|
|
23
|
+
4. Read PROJECT-STATE.md → Check what's in progress
|
|
24
|
+
5. Read DECISIONS.md → Know past decisions
|
|
25
|
+
|
|
26
|
+
### PHASE 2: PRE-FLIGHT CHECK (before writing code)
|
|
27
|
+
Ask yourself silently:
|
|
28
|
+
- [ ] What existing code does this touch? (check PROJECT-CONTEXT.md)
|
|
29
|
+
- [ ] Is similar code already in the codebase? (copy that pattern)
|
|
30
|
+
- [ ] What's the data model involved?
|
|
31
|
+
- [ ] What are the error cases?
|
|
32
|
+
- [ ] Is someone else working on this? (check PROJECT-STATE.md)
|
|
33
|
+
|
|
34
|
+
If PROJECT-CONTEXT.md is empty or stale (>7 days), SCAN THE PROJECT FIRST:
|
|
35
|
+
- Read package.json for dependencies
|
|
36
|
+
- Check src/ structure
|
|
37
|
+
- Note existing patterns
|
|
38
|
+
- Update PROJECT-CONTEXT.md
|
|
39
|
+
|
|
40
|
+
### PHASE 3: EXECUTE
|
|
41
|
+
- State: \`📋 CodeBakers | [Type] | Modules: [list]\`
|
|
42
|
+
- Load required modules from .claude/
|
|
43
|
+
- Follow patterns EXACTLY
|
|
44
|
+
|
|
45
|
+
### PHASE 4: SELF-REVIEW (before saying "done")
|
|
46
|
+
- [ ] TypeScript compiles? (npx tsc --noEmit)
|
|
47
|
+
- [ ] Imports resolve correctly?
|
|
48
|
+
- [ ] Error handling exists?
|
|
49
|
+
- [ ] Matches existing patterns in codebase?
|
|
50
|
+
- [ ] Tests written?
|
|
51
|
+
- [ ] PROJECT-STATE.md updated?
|
|
52
|
+
|
|
53
|
+
If ANY check fails, fix it before responding.
|
|
54
|
+
|
|
55
|
+
### PHASE 5: UPDATE STATE
|
|
56
|
+
- Update PROJECT-STATE.md with completed work
|
|
57
|
+
- Add to DECISIONS.md if architectural choice was made
|
|
58
|
+
|
|
59
|
+
## MULTI-AGENT AWARENESS
|
|
60
|
+
- ALWAYS check PROJECT-STATE.md "In Progress" section
|
|
61
|
+
- Do NOT duplicate work another agent is doing
|
|
62
|
+
- If conflict detected, STOP and ask user
|
|
63
|
+
|
|
64
|
+
## REMEMBER
|
|
65
|
+
- You are a full product team, not just a code assistant
|
|
66
|
+
- The modules contain production-tested patterns — USE THEM
|
|
67
|
+
- When in doubt, check existing code first
|
|
68
|
+
`;
|
|
69
|
+
const CURSORIGNORE_TEMPLATE = `# CodeBakers - Files to ignore in Cursor context
|
|
70
|
+
|
|
71
|
+
# Dependencies
|
|
72
|
+
node_modules/
|
|
73
|
+
.pnpm-store/
|
|
74
|
+
bower_components/
|
|
75
|
+
|
|
76
|
+
# Build outputs
|
|
77
|
+
dist/
|
|
78
|
+
build/
|
|
79
|
+
.next/
|
|
80
|
+
.nuxt/
|
|
81
|
+
.output/
|
|
82
|
+
out/
|
|
83
|
+
|
|
84
|
+
# Cache
|
|
85
|
+
.cache/
|
|
86
|
+
.turbo/
|
|
87
|
+
.eslintcache
|
|
88
|
+
.prettiercache
|
|
89
|
+
*.tsbuildinfo
|
|
90
|
+
|
|
91
|
+
# Logs
|
|
92
|
+
logs/
|
|
93
|
+
*.log
|
|
94
|
+
npm-debug.log*
|
|
95
|
+
yarn-debug.log*
|
|
96
|
+
yarn-error.log*
|
|
97
|
+
|
|
98
|
+
# Environment files (don't leak secrets)
|
|
99
|
+
.env
|
|
100
|
+
.env.local
|
|
101
|
+
.env.*.local
|
|
102
|
+
.env.production
|
|
103
|
+
|
|
104
|
+
# IDE
|
|
105
|
+
.idea/
|
|
106
|
+
*.swp
|
|
107
|
+
*.swo
|
|
108
|
+
|
|
109
|
+
# OS
|
|
110
|
+
.DS_Store
|
|
111
|
+
Thumbs.db
|
|
112
|
+
|
|
113
|
+
# Test coverage
|
|
114
|
+
coverage/
|
|
115
|
+
.nyc_output/
|
|
116
|
+
|
|
117
|
+
# Package locks (large files, not needed for context)
|
|
118
|
+
package-lock.json
|
|
119
|
+
yarn.lock
|
|
120
|
+
pnpm-lock.yaml
|
|
121
|
+
|
|
122
|
+
# Generated files
|
|
123
|
+
*.min.js
|
|
124
|
+
*.min.css
|
|
125
|
+
*.map
|
|
126
|
+
`;
|
|
127
|
+
const VSCODE_SETTINGS_TEMPLATE = {
|
|
128
|
+
"cursor.chat.defaultContext": [
|
|
129
|
+
"CLAUDE.md",
|
|
130
|
+
"PRD.md",
|
|
131
|
+
"PROJECT-CONTEXT.md",
|
|
132
|
+
"PROJECT-STATE.md",
|
|
133
|
+
"DECISIONS.md"
|
|
134
|
+
],
|
|
135
|
+
"cursor.chat.alwaysIncludeRules": true,
|
|
136
|
+
"cursor.composer.alwaysIncludeRules": true,
|
|
137
|
+
"cursor.general.enableAutoImport": true
|
|
138
|
+
};
|
|
139
|
+
function createPrdTemplate(projectName, projectType) {
|
|
140
|
+
const date = new Date().toISOString().split('T')[0];
|
|
141
|
+
const typeSpecificSections = projectType === 'client'
|
|
142
|
+
? `
|
|
143
|
+
## Client Info
|
|
144
|
+
- Client Name: [Who is this for?]
|
|
145
|
+
- Contact: [Primary contact]
|
|
146
|
+
- Deadline: [When is this due?]
|
|
147
|
+
- Budget: [If relevant]
|
|
148
|
+
`
|
|
149
|
+
: projectType === 'business'
|
|
150
|
+
? `
|
|
151
|
+
## Business Context
|
|
152
|
+
- Target Market: [Who are you selling to?]
|
|
153
|
+
- Revenue Model: [How does this make money?]
|
|
154
|
+
- Competition: [Who are you competing with?]
|
|
155
|
+
- MVP Deadline: [When do you need to launch?]
|
|
156
|
+
`
|
|
157
|
+
: `
|
|
158
|
+
## Personal Goals
|
|
159
|
+
- Why am I building this? [Your motivation]
|
|
160
|
+
- Learning goals: [What do you want to learn?]
|
|
161
|
+
- Time commitment: [Hours per week?]
|
|
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
|
+
async function prompt(question) {
|
|
245
|
+
const rl = (0, readline_1.createInterface)({
|
|
246
|
+
input: process.stdin,
|
|
247
|
+
output: process.stdout,
|
|
248
|
+
});
|
|
249
|
+
return new Promise((resolve) => {
|
|
250
|
+
rl.question(question, (answer) => {
|
|
251
|
+
rl.close();
|
|
252
|
+
resolve(answer);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async function confirm(question) {
|
|
257
|
+
const answer = await prompt(`${question} (Y/n): `);
|
|
258
|
+
return answer.toLowerCase() !== 'n';
|
|
259
|
+
}
|
|
260
|
+
async function selectProjectType() {
|
|
261
|
+
console.log(chalk_1.default.white('\n What kind of project is this?\n'));
|
|
262
|
+
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('PERSONAL') + chalk_1.default.gray(' - Just building for myself'));
|
|
263
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('CLIENT') + chalk_1.default.gray(' - Building for someone else'));
|
|
264
|
+
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('BUSINESS') + chalk_1.default.gray(' - My own product/startup\n'));
|
|
265
|
+
let typeChoice = '';
|
|
266
|
+
while (!['1', '2', '3'].includes(typeChoice)) {
|
|
267
|
+
typeChoice = await prompt(' Enter 1, 2, or 3: ');
|
|
268
|
+
}
|
|
269
|
+
const typeMap = {
|
|
270
|
+
'1': 'personal',
|
|
271
|
+
'2': 'client',
|
|
272
|
+
'3': 'business'
|
|
273
|
+
};
|
|
274
|
+
const cwd = process.cwd();
|
|
275
|
+
const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
|
|
276
|
+
const name = await prompt(` Project name (${defaultName}): `) || defaultName;
|
|
277
|
+
return {
|
|
278
|
+
type: typeMap[typeChoice],
|
|
279
|
+
name
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function createProjectState(projectName, projectType) {
|
|
283
|
+
const date = new Date().toISOString().split('T')[0];
|
|
284
|
+
return `# PROJECT STATE
|
|
285
|
+
# Last Updated: ${date}
|
|
286
|
+
# Auto-maintained by AI - update when starting/completing tasks
|
|
287
|
+
|
|
288
|
+
## Project Info
|
|
289
|
+
name: ${projectName}
|
|
290
|
+
type: ${projectType}
|
|
291
|
+
phase: planning
|
|
292
|
+
|
|
293
|
+
## Current Sprint
|
|
294
|
+
Goal: [AI will fill this based on conversation]
|
|
295
|
+
|
|
296
|
+
## In Progress
|
|
297
|
+
<!-- AI: Add tasks here when you START working on them -->
|
|
298
|
+
<!-- Format: - [task] (started: date, agent: cursor/claude) -->
|
|
299
|
+
|
|
300
|
+
## Completed
|
|
301
|
+
<!-- AI: Move tasks here when DONE -->
|
|
302
|
+
<!-- Format: - [task] (completed: date) -->
|
|
303
|
+
|
|
304
|
+
## Blockers
|
|
305
|
+
<!-- AI: List anything blocking progress -->
|
|
306
|
+
|
|
307
|
+
## Next Up
|
|
308
|
+
<!-- AI: Queue of upcoming tasks -->
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
function createProjectContext(projectName, projectType) {
|
|
312
|
+
const date = new Date().toISOString().split('T')[0];
|
|
313
|
+
return `# PROJECT CONTEXT
|
|
314
|
+
# Last Scanned: ${date}
|
|
315
|
+
# AI: Update this when you first analyze the project or when structure changes significantly
|
|
316
|
+
|
|
317
|
+
## Overview
|
|
318
|
+
name: ${projectName}
|
|
319
|
+
type: ${projectType}
|
|
320
|
+
description: [AI will fill after scanning]
|
|
321
|
+
|
|
322
|
+
## Tech Stack
|
|
323
|
+
<!-- AI: Fill this by reading package.json and checking file extensions -->
|
|
324
|
+
framework:
|
|
325
|
+
language:
|
|
326
|
+
database:
|
|
327
|
+
auth:
|
|
328
|
+
styling:
|
|
329
|
+
testing:
|
|
330
|
+
|
|
331
|
+
## Project Structure
|
|
332
|
+
<!-- AI: Fill this by scanning the directory structure -->
|
|
333
|
+
\`\`\`
|
|
334
|
+
[AI will map the project structure here]
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
## Key Files
|
|
338
|
+
<!-- AI: List the most important files for understanding the project -->
|
|
339
|
+
- Entry point:
|
|
340
|
+
- Config:
|
|
341
|
+
- Database schema:
|
|
342
|
+
- API routes:
|
|
343
|
+
- Components:
|
|
344
|
+
|
|
345
|
+
## Existing Patterns
|
|
346
|
+
<!-- AI: Document patterns you find so you can reuse them -->
|
|
347
|
+
|
|
348
|
+
### API Route Pattern
|
|
349
|
+
\`\`\`typescript
|
|
350
|
+
[AI: Copy an example API route pattern from this project]
|
|
351
|
+
\`\`\`
|
|
352
|
+
|
|
353
|
+
### Component Pattern
|
|
354
|
+
\`\`\`typescript
|
|
355
|
+
[AI: Copy an example component pattern from this project]
|
|
356
|
+
\`\`\`
|
|
357
|
+
|
|
358
|
+
### Database Query Pattern
|
|
359
|
+
\`\`\`typescript
|
|
360
|
+
[AI: Copy an example database query pattern from this project]
|
|
361
|
+
\`\`\`
|
|
362
|
+
|
|
363
|
+
## Environment Variables
|
|
364
|
+
<!-- AI: List required env vars (don't include values!) -->
|
|
365
|
+
- [ ] DATABASE_URL
|
|
366
|
+
- [ ] [others...]
|
|
367
|
+
|
|
368
|
+
## Notes
|
|
369
|
+
<!-- AI: Any important context about this specific project -->
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
function createDecisionsLog(projectName) {
|
|
373
|
+
const date = new Date().toISOString().split('T')[0];
|
|
374
|
+
return `# ARCHITECTURAL DECISIONS
|
|
375
|
+
# Project: ${projectName}
|
|
376
|
+
# AI: Add entries here when making significant technical choices
|
|
377
|
+
|
|
378
|
+
## How to Use This File
|
|
379
|
+
When you make a decision that affects architecture, add an entry:
|
|
380
|
+
- Date
|
|
381
|
+
- Decision
|
|
382
|
+
- Reason
|
|
383
|
+
- Alternatives considered
|
|
384
|
+
- Pattern location (if applicable)
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## ${date}: Project Initialized
|
|
389
|
+
**Decision:** Using CodeBakers pattern system
|
|
390
|
+
**Reason:** Ensure consistent, production-quality code
|
|
391
|
+
**Pattern:** See .claude/ folder for all patterns
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
<!-- AI: Add new decisions above this line -->
|
|
396
|
+
`;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Auto-scan project to detect tech stack
|
|
400
|
+
*/
|
|
401
|
+
function scanProject(cwd) {
|
|
402
|
+
const stack = {};
|
|
403
|
+
let structure = '';
|
|
404
|
+
// Check package.json
|
|
405
|
+
const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
|
|
406
|
+
if ((0, fs_1.existsSync)(packageJsonPath)) {
|
|
407
|
+
try {
|
|
408
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8'));
|
|
409
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
410
|
+
// Detect framework
|
|
411
|
+
if (deps['next'])
|
|
412
|
+
stack.framework = `Next.js ${deps['next']}`;
|
|
413
|
+
else if (deps['react'])
|
|
414
|
+
stack.framework = `React ${deps['react']}`;
|
|
415
|
+
else if (deps['vue'])
|
|
416
|
+
stack.framework = `Vue ${deps['vue']}`;
|
|
417
|
+
else if (deps['express'])
|
|
418
|
+
stack.framework = `Express ${deps['express']}`;
|
|
419
|
+
// Detect database
|
|
420
|
+
if (deps['drizzle-orm'])
|
|
421
|
+
stack.database = 'Drizzle ORM';
|
|
422
|
+
else if (deps['prisma'])
|
|
423
|
+
stack.database = 'Prisma';
|
|
424
|
+
else if (deps['mongoose'])
|
|
425
|
+
stack.database = 'MongoDB/Mongoose';
|
|
426
|
+
else if (deps['pg'])
|
|
427
|
+
stack.database = 'PostgreSQL';
|
|
428
|
+
// Detect auth
|
|
429
|
+
if (deps['@supabase/supabase-js'])
|
|
430
|
+
stack.auth = 'Supabase Auth';
|
|
431
|
+
else if (deps['next-auth'])
|
|
432
|
+
stack.auth = 'NextAuth.js';
|
|
433
|
+
else if (deps['@clerk/nextjs'])
|
|
434
|
+
stack.auth = 'Clerk';
|
|
435
|
+
// Detect styling
|
|
436
|
+
if (deps['tailwindcss'])
|
|
437
|
+
stack.styling = 'Tailwind CSS';
|
|
438
|
+
else if (deps['styled-components'])
|
|
439
|
+
stack.styling = 'Styled Components';
|
|
440
|
+
// Detect testing
|
|
441
|
+
if (deps['vitest'])
|
|
442
|
+
stack.testing = 'Vitest';
|
|
443
|
+
else if (deps['jest'])
|
|
444
|
+
stack.testing = 'Jest';
|
|
445
|
+
else if (deps['@playwright/test'])
|
|
446
|
+
stack.testing = 'Playwright';
|
|
447
|
+
// Detect language
|
|
448
|
+
if (deps['typescript'] || (0, fs_1.existsSync)((0, path_1.join)(cwd, 'tsconfig.json'))) {
|
|
449
|
+
stack.language = 'TypeScript';
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
stack.language = 'JavaScript';
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// Ignore parse errors
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Build simple structure
|
|
460
|
+
try {
|
|
461
|
+
const items = (0, fs_1.readdirSync)(cwd);
|
|
462
|
+
const dirs = [];
|
|
463
|
+
const files = [];
|
|
464
|
+
for (const item of items) {
|
|
465
|
+
if (item.startsWith('.') || item === 'node_modules')
|
|
466
|
+
continue;
|
|
467
|
+
const fullPath = (0, path_1.join)(cwd, item);
|
|
468
|
+
try {
|
|
469
|
+
if ((0, fs_1.statSync)(fullPath).isDirectory()) {
|
|
470
|
+
dirs.push(item + '/');
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
files.push(item);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// Skip inaccessible items
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
structure = [...dirs.sort(), ...files.sort()].join('\n');
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
structure = '[Could not scan structure]';
|
|
484
|
+
}
|
|
485
|
+
return { stack, structure };
|
|
486
|
+
}
|
|
487
|
+
function updateProjectContextWithScan(contextContent, stack, structure) {
|
|
488
|
+
let updated = contextContent;
|
|
489
|
+
// Update tech stack
|
|
490
|
+
if (stack.framework)
|
|
491
|
+
updated = updated.replace('framework: ', `framework: ${stack.framework}`);
|
|
492
|
+
if (stack.language)
|
|
493
|
+
updated = updated.replace('language: ', `language: ${stack.language}`);
|
|
494
|
+
if (stack.database)
|
|
495
|
+
updated = updated.replace('database: ', `database: ${stack.database}`);
|
|
496
|
+
if (stack.auth)
|
|
497
|
+
updated = updated.replace('auth: ', `auth: ${stack.auth}`);
|
|
498
|
+
if (stack.styling)
|
|
499
|
+
updated = updated.replace('styling: ', `styling: ${stack.styling}`);
|
|
500
|
+
if (stack.testing)
|
|
501
|
+
updated = updated.replace('testing: ', `testing: ${stack.testing}`);
|
|
502
|
+
// Update structure
|
|
503
|
+
updated = updated.replace('[AI will map the project structure here]', structure || '[Empty project]');
|
|
504
|
+
return updated;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Interactive init command - walks users through complete setup
|
|
508
|
+
*/
|
|
509
|
+
async function init() {
|
|
510
|
+
console.log(chalk_1.default.blue(`
|
|
511
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
512
|
+
║ ║
|
|
513
|
+
║ ${chalk_1.default.bold('Welcome to CodeBakers!')} ║
|
|
514
|
+
║ ║
|
|
515
|
+
║ Production-grade patterns for AI-assisted development ║
|
|
516
|
+
║ ║
|
|
517
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
518
|
+
`));
|
|
519
|
+
console.log(chalk_1.default.gray(' This wizard will set up CodeBakers in your project.\n'));
|
|
520
|
+
const cwd = process.cwd();
|
|
521
|
+
// Check if already initialized
|
|
522
|
+
const claudeMdPath = (0, path_1.join)(cwd, 'CLAUDE.md');
|
|
523
|
+
if ((0, fs_1.existsSync)(claudeMdPath)) {
|
|
524
|
+
const reinitialize = await confirm(' CLAUDE.md already exists. Reinitialize?');
|
|
525
|
+
if (!reinitialize) {
|
|
526
|
+
console.log(chalk_1.default.yellow('\n Skipping. Run with a fresh project or delete CLAUDE.md first.\n'));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// Step 1: Get project type
|
|
531
|
+
const { type: projectType, name: projectName } = await selectProjectType();
|
|
532
|
+
console.log(chalk_1.default.green(`\n ✓ Setting up ${projectName} as ${projectType.toUpperCase()} project\n`));
|
|
533
|
+
// Step 2: Check if already logged in
|
|
534
|
+
let apiKey = (0, config_js_1.getApiKey)();
|
|
535
|
+
if (apiKey) {
|
|
536
|
+
console.log(chalk_1.default.green(' ✓ Already logged in\n'));
|
|
537
|
+
const useExisting = await confirm(' Use existing API key?');
|
|
538
|
+
if (!useExisting) {
|
|
539
|
+
apiKey = null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Step 3: Login if needed
|
|
543
|
+
if (!apiKey) {
|
|
544
|
+
console.log(chalk_1.default.white('\n Step 1: Get your API key\n'));
|
|
545
|
+
console.log(chalk_1.default.gray(' Go to: ') + chalk_1.default.cyan('https://codebakers.ai/dashboard'));
|
|
546
|
+
console.log(chalk_1.default.gray(' Copy your API key (starts with cb_)\n'));
|
|
547
|
+
apiKey = await prompt(' Paste your API key: ');
|
|
548
|
+
if (!apiKey || !apiKey.startsWith('cb_')) {
|
|
549
|
+
console.log(chalk_1.default.red('\n ✗ Invalid API key. Keys start with "cb_"\n'));
|
|
550
|
+
console.log(chalk_1.default.gray(' Get your key at https://codebakers.ai/dashboard\n'));
|
|
551
|
+
process.exit(1);
|
|
552
|
+
}
|
|
553
|
+
const spinner = (0, ora_1.default)(' Validating API key...').start();
|
|
554
|
+
try {
|
|
555
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
556
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
557
|
+
method: 'GET',
|
|
558
|
+
headers: {
|
|
559
|
+
Authorization: `Bearer ${apiKey}`,
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
if (!response.ok) {
|
|
563
|
+
const error = await response.json().catch(() => ({}));
|
|
564
|
+
throw new Error(error.error || 'Invalid API key');
|
|
565
|
+
}
|
|
566
|
+
(0, config_js_1.setApiKey)(apiKey);
|
|
567
|
+
spinner.succeed('API key valid!');
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
spinner.fail('Invalid API key');
|
|
571
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
572
|
+
console.log(chalk_1.default.red(`\n Error: ${message}\n`));
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// Step 4: Install patterns from API
|
|
577
|
+
console.log(chalk_1.default.white('\n Step 2: Installing patterns\n'));
|
|
578
|
+
const spinner = (0, ora_1.default)(' Downloading patterns...').start();
|
|
579
|
+
try {
|
|
580
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
581
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
582
|
+
method: 'GET',
|
|
583
|
+
headers: {
|
|
584
|
+
Authorization: `Bearer ${apiKey}`,
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
if (!response.ok) {
|
|
588
|
+
const error = await response.json().catch(() => ({}));
|
|
589
|
+
throw new Error(error.error || 'Failed to fetch content');
|
|
590
|
+
}
|
|
591
|
+
const content = await response.json();
|
|
592
|
+
spinner.text = ' Installing patterns...';
|
|
593
|
+
// Write router file (CLAUDE.md)
|
|
594
|
+
if (content.router) {
|
|
595
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'CLAUDE.md'), content.router);
|
|
596
|
+
}
|
|
597
|
+
// Write modules to .claude/
|
|
598
|
+
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
599
|
+
const modulesDir = (0, path_1.join)(cwd, '.claude');
|
|
600
|
+
if (!(0, fs_1.existsSync)(modulesDir)) {
|
|
601
|
+
(0, fs_1.mkdirSync)(modulesDir, { recursive: true });
|
|
602
|
+
}
|
|
603
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
604
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(modulesDir, name), data);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
spinner.succeed('Patterns installed!');
|
|
608
|
+
console.log(chalk_1.default.gray(`\n Version: ${content.version}`));
|
|
609
|
+
console.log(chalk_1.default.gray(` Modules: ${Object.keys(content.modules || {}).length} pattern files`));
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
spinner.fail('Pattern installation failed');
|
|
613
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
614
|
+
console.log(chalk_1.default.red(`\n Error: ${message}\n`));
|
|
615
|
+
process.exit(1);
|
|
616
|
+
}
|
|
617
|
+
// Step 5: Auto-scan project
|
|
618
|
+
console.log(chalk_1.default.white('\n Step 3: Scanning project\n'));
|
|
619
|
+
const scanSpinner = (0, ora_1.default)(' Analyzing project structure...').start();
|
|
620
|
+
const { stack, structure } = scanProject(cwd);
|
|
621
|
+
const detectedItems = Object.entries(stack).filter(([_, v]) => v).map(([k, v]) => `${k}: ${v}`);
|
|
622
|
+
if (detectedItems.length > 0) {
|
|
623
|
+
scanSpinner.succeed('Project analyzed!');
|
|
624
|
+
console.log(chalk_1.default.gray('\n Detected:'));
|
|
625
|
+
for (const item of detectedItems) {
|
|
626
|
+
console.log(chalk_1.default.gray(` ${item}`));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
scanSpinner.succeed('Project scanned (new project detected)');
|
|
631
|
+
}
|
|
632
|
+
// Step 6: Create PROJECT-CONTEXT.md with scan results
|
|
633
|
+
console.log(chalk_1.default.white('\n Step 4: Setting up project files\n'));
|
|
634
|
+
const filesSpinner = (0, ora_1.default)(' Creating project files...').start();
|
|
635
|
+
try {
|
|
636
|
+
// PROJECT-CONTEXT.md
|
|
637
|
+
let contextContent = createProjectContext(projectName, projectType);
|
|
638
|
+
contextContent = updateProjectContextWithScan(contextContent, stack, structure);
|
|
639
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PROJECT-CONTEXT.md'), contextContent);
|
|
640
|
+
// PROJECT-STATE.md
|
|
641
|
+
const stateContent = createProjectState(projectName, projectType);
|
|
642
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'PROJECT-STATE.md'), stateContent);
|
|
643
|
+
// DECISIONS.md
|
|
644
|
+
const decisionsContent = createDecisionsLog(projectName);
|
|
645
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'DECISIONS.md'), decisionsContent);
|
|
646
|
+
filesSpinner.succeed('Project files created!');
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
filesSpinner.warn('Some project files could not be created');
|
|
650
|
+
}
|
|
651
|
+
// Step 7: Install Cursor files
|
|
652
|
+
console.log(chalk_1.default.white('\n Step 5: Setting up Cursor IDE\n'));
|
|
653
|
+
const cursorSpinner = (0, ora_1.default)(' Installing Cursor configuration...').start();
|
|
654
|
+
try {
|
|
655
|
+
// Write .cursorrules
|
|
656
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
|
|
657
|
+
// Write .cursorignore
|
|
658
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
|
|
659
|
+
// Create/merge .vscode/settings.json
|
|
660
|
+
const vscodeDir = (0, path_1.join)(cwd, '.vscode');
|
|
661
|
+
if (!(0, fs_1.existsSync)(vscodeDir)) {
|
|
662
|
+
(0, fs_1.mkdirSync)(vscodeDir, { recursive: true });
|
|
663
|
+
}
|
|
664
|
+
const existingSettingsPath = (0, path_1.join)(vscodeDir, 'settings.json');
|
|
665
|
+
if ((0, fs_1.existsSync)(existingSettingsPath)) {
|
|
666
|
+
const existing = JSON.parse((0, fs_1.readFileSync)(existingSettingsPath, 'utf-8'));
|
|
667
|
+
const merged = { ...existing, ...VSCODE_SETTINGS_TEMPLATE };
|
|
668
|
+
(0, fs_1.writeFileSync)(existingSettingsPath, JSON.stringify(merged, null, 2));
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
(0, fs_1.writeFileSync)(existingSettingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
|
|
672
|
+
}
|
|
673
|
+
cursorSpinner.succeed('Cursor configuration installed!');
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
cursorSpinner.warn('Could not install Cursor files (continuing anyway)');
|
|
677
|
+
}
|
|
678
|
+
// Step 8: Add to .gitignore if not present
|
|
679
|
+
const gitignorePath = (0, path_1.join)(cwd, '.gitignore');
|
|
680
|
+
if ((0, fs_1.existsSync)(gitignorePath)) {
|
|
681
|
+
const gitignore = (0, fs_1.readFileSync)(gitignorePath, 'utf-8');
|
|
682
|
+
if (!gitignore.includes('.cursorrules')) {
|
|
683
|
+
const additions = '\n# CodeBakers (encoded patterns)\n.cursorrules\n.claude/\n';
|
|
684
|
+
(0, fs_1.writeFileSync)(gitignorePath, gitignore + additions);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
// Step 9: PRD Setup
|
|
688
|
+
console.log(chalk_1.default.white('\n Step 6: Product Requirements\n'));
|
|
689
|
+
const prdPath = (0, path_1.join)(cwd, 'PRD.md');
|
|
690
|
+
let prdCreated = false;
|
|
691
|
+
if ((0, fs_1.existsSync)(prdPath)) {
|
|
692
|
+
console.log(chalk_1.default.green(' ✓ PRD.md already exists\n'));
|
|
693
|
+
prdCreated = true;
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
console.log(chalk_1.default.gray(' A PRD helps the AI understand what you\'re building.\n'));
|
|
697
|
+
console.log(chalk_1.default.white(' How would you like to set up your PRD?\n'));
|
|
698
|
+
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('CREATE TEMPLATE') + chalk_1.default.gray(' - I\'ll fill it out'));
|
|
699
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('PASTE CONTENT') + chalk_1.default.gray(' - I have requirements ready'));
|
|
700
|
+
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('SKIP FOR NOW') + chalk_1.default.gray(' - I\'ll add it later\n'));
|
|
701
|
+
let prdChoice = '';
|
|
702
|
+
while (!['1', '2', '3'].includes(prdChoice)) {
|
|
703
|
+
prdChoice = await prompt(' Enter 1, 2, or 3: ');
|
|
704
|
+
}
|
|
705
|
+
if (prdChoice === '1') {
|
|
706
|
+
// Create template
|
|
707
|
+
const prdSpinner = (0, ora_1.default)(' Creating PRD template...').start();
|
|
708
|
+
const prdContent = createPrdTemplate(projectName, projectType);
|
|
709
|
+
(0, fs_1.writeFileSync)(prdPath, prdContent);
|
|
710
|
+
prdSpinner.succeed('PRD template created!');
|
|
711
|
+
console.log(chalk_1.default.yellow('\n → Open PRD.md and fill in your requirements'));
|
|
712
|
+
console.log(chalk_1.default.gray(' The AI will use this to understand what to build.\n'));
|
|
713
|
+
prdCreated = true;
|
|
714
|
+
}
|
|
715
|
+
else if (prdChoice === '2') {
|
|
716
|
+
// Paste content
|
|
717
|
+
console.log(chalk_1.default.gray('\n Paste your PRD content below.'));
|
|
718
|
+
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'));
|
|
719
|
+
const lines = [];
|
|
720
|
+
let line = '';
|
|
721
|
+
while (true) {
|
|
722
|
+
line = await prompt(' ');
|
|
723
|
+
if (line.trim().toUpperCase() === 'END')
|
|
724
|
+
break;
|
|
725
|
+
lines.push(line);
|
|
726
|
+
}
|
|
727
|
+
if (lines.length > 0) {
|
|
728
|
+
const prdSpinner = (0, ora_1.default)(' Saving PRD...').start();
|
|
729
|
+
const header = `# Product Requirements Document\n# Project: ${projectName}\n# Created: ${new Date().toISOString().split('T')[0]}\n\n`;
|
|
730
|
+
(0, fs_1.writeFileSync)(prdPath, header + lines.join('\n'));
|
|
731
|
+
prdSpinner.succeed('PRD saved!');
|
|
732
|
+
prdCreated = true;
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
console.log(chalk_1.default.yellow(' No content provided, skipping PRD.\n'));
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
console.log(chalk_1.default.gray('\n You can add PRD.md anytime. The AI will use it automatically.\n'));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Success message
|
|
743
|
+
console.log(chalk_1.default.green(`
|
|
744
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
745
|
+
║ ║
|
|
746
|
+
║ ${chalk_1.default.bold('✓ Setup Complete!')} ║
|
|
747
|
+
║ ║
|
|
748
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
749
|
+
`));
|
|
750
|
+
console.log(chalk_1.default.white(' Files created:\n'));
|
|
751
|
+
console.log(chalk_1.default.cyan(' CLAUDE.md ') + chalk_1.default.gray('→ AI router'));
|
|
752
|
+
if (prdCreated) {
|
|
753
|
+
console.log(chalk_1.default.cyan(' PRD.md ') + chalk_1.default.gray('→ Product requirements (AI reads this!)'));
|
|
754
|
+
}
|
|
755
|
+
console.log(chalk_1.default.cyan(' PROJECT-CONTEXT.md ') + chalk_1.default.gray('→ Codebase knowledge (auto-updated)'));
|
|
756
|
+
console.log(chalk_1.default.cyan(' PROJECT-STATE.md ') + chalk_1.default.gray('→ Task tracking (auto-updated)'));
|
|
757
|
+
console.log(chalk_1.default.cyan(' DECISIONS.md ') + chalk_1.default.gray('→ Architecture log (auto-updated)'));
|
|
758
|
+
console.log(chalk_1.default.cyan(' .cursorrules ') + chalk_1.default.gray('→ Cursor AI instructions'));
|
|
759
|
+
console.log(chalk_1.default.cyan(' .cursorignore ') + chalk_1.default.gray('→ Context optimization'));
|
|
760
|
+
console.log(chalk_1.default.cyan(' .claude/ ') + chalk_1.default.gray('→ Pattern modules\n'));
|
|
761
|
+
console.log(chalk_1.default.white(' What happens automatically:\n'));
|
|
762
|
+
console.log(chalk_1.default.gray(' ✓ AI loads context before every response'));
|
|
763
|
+
console.log(chalk_1.default.gray(' ✓ AI checks for existing patterns to copy'));
|
|
764
|
+
console.log(chalk_1.default.gray(' ✓ AI validates code before outputting'));
|
|
765
|
+
console.log(chalk_1.default.gray(' ✓ AI updates project state after completing tasks'));
|
|
766
|
+
console.log(chalk_1.default.gray(' ✓ AI logs architectural decisions\n'));
|
|
767
|
+
console.log(chalk_1.default.white(' For Cursor users:\n'));
|
|
768
|
+
console.log(chalk_1.default.gray(' Just open the project and start chatting!\n'));
|
|
769
|
+
console.log(chalk_1.default.white(' For Claude Code users:\n'));
|
|
770
|
+
console.log(chalk_1.default.cyan(' codebakers install-hook') + chalk_1.default.gray(' (one-time setup)\n'));
|
|
771
|
+
console.log(chalk_1.default.blue(' Zero friction. Just build.\n'));
|
|
772
|
+
}
|