@codebakers/cli 3.8.1 → 3.8.2
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/build.d.ts +27 -0
- package/dist/commands/build.js +325 -0
- package/dist/index.js +22 -5
- package/package.json +1 -1
- package/src/commands/build.ts +425 -0
- package/src/index.ts +24 -5
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CODEBAKERS BUILD COMMAND
|
|
3
|
+
*
|
|
4
|
+
* This is the zero-friction project builder.
|
|
5
|
+
* User describes what they want → AI builds actual files on their machine.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. User runs: codebakers build "SaaS for invoicing"
|
|
9
|
+
* 2. CLI creates engineering session on server
|
|
10
|
+
* 3. Server runs AI agents to generate PRD, specs, code
|
|
11
|
+
* 4. CLI receives file contents and writes them to disk
|
|
12
|
+
* 5. User has a runnable project
|
|
13
|
+
*/
|
|
14
|
+
interface BuildOptions {
|
|
15
|
+
description?: string;
|
|
16
|
+
output?: string;
|
|
17
|
+
verbose?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Main build command
|
|
21
|
+
*/
|
|
22
|
+
export declare function build(options?: BuildOptions): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Check build status
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildStatus(): Promise<void>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,325 @@
|
|
|
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.build = build;
|
|
7
|
+
exports.buildStatus = buildStatus;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const readline_1 = require("readline");
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const config_js_1 = require("../config.js");
|
|
14
|
+
async function prompt(question) {
|
|
15
|
+
const rl = (0, readline_1.createInterface)({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout,
|
|
18
|
+
});
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
rl.question(question, (answer) => {
|
|
21
|
+
rl.close();
|
|
22
|
+
resolve(answer.trim());
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Main build command
|
|
28
|
+
*/
|
|
29
|
+
async function build(options = {}) {
|
|
30
|
+
console.log(chalk_1.default.blue(`
|
|
31
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
32
|
+
║ ║
|
|
33
|
+
║ ${chalk_1.default.bold('CodeBakers Build')} ║
|
|
34
|
+
║ ║
|
|
35
|
+
║ Describe your project → Get working code ║
|
|
36
|
+
║ ║
|
|
37
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
38
|
+
`));
|
|
39
|
+
// Check authentication
|
|
40
|
+
if (!(0, config_js_1.hasValidAccess)()) {
|
|
41
|
+
console.log(chalk_1.default.red(' ✗ Not authenticated\n'));
|
|
42
|
+
console.log(chalk_1.default.gray(' Run: codebakers go (free trial) or codebakers setup (with API key)\n'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
// Get project description
|
|
46
|
+
let description = options.description;
|
|
47
|
+
if (!description) {
|
|
48
|
+
console.log(chalk_1.default.white(' What do you want to build?\n'));
|
|
49
|
+
console.log(chalk_1.default.gray(' Examples:'));
|
|
50
|
+
console.log(chalk_1.default.gray(' • "A SaaS for managing invoices with Stripe payments"'));
|
|
51
|
+
console.log(chalk_1.default.gray(' • "Todo app with user auth and real-time sync"'));
|
|
52
|
+
console.log(chalk_1.default.gray(' • "Blog platform with markdown support"\n'));
|
|
53
|
+
description = await prompt(' Describe your project: ');
|
|
54
|
+
if (!description.trim()) {
|
|
55
|
+
console.log(chalk_1.default.red('\n Please provide a project description.\n'));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const outputDir = options.output || process.cwd();
|
|
60
|
+
// Check if directory is empty
|
|
61
|
+
const files = (0, fs_1.existsSync)(outputDir) ?
|
|
62
|
+
require('fs').readdirSync(outputDir).filter((f) => !f.startsWith('.')) : [];
|
|
63
|
+
if (files.length > 0) {
|
|
64
|
+
console.log(chalk_1.default.yellow('\n ⚠️ This directory is not empty.'));
|
|
65
|
+
const proceed = await prompt(' Continue? (y/N): ');
|
|
66
|
+
if (proceed.toLowerCase() !== 'y') {
|
|
67
|
+
console.log(chalk_1.default.gray('\n Build cancelled.\n'));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
console.log(chalk_1.default.green(`\n Building: "${description}"\n`));
|
|
72
|
+
// Create engineering session
|
|
73
|
+
const spinner = (0, ora_1.default)(' Initializing build...').start();
|
|
74
|
+
try {
|
|
75
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
76
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
77
|
+
const trial = (0, config_js_1.getTrialState)();
|
|
78
|
+
let authHeader = '';
|
|
79
|
+
if (apiKey) {
|
|
80
|
+
authHeader = `Bearer ${apiKey}`;
|
|
81
|
+
}
|
|
82
|
+
else if (trial?.trialId) {
|
|
83
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
84
|
+
}
|
|
85
|
+
if (!authHeader) {
|
|
86
|
+
spinner.fail('Authentication required');
|
|
87
|
+
console.log(chalk_1.default.gray('\n Run: codebakers go\n'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Step 1: Create engineering session
|
|
91
|
+
spinner.text = ' Creating build session...';
|
|
92
|
+
const createResponse = await fetch(`${apiUrl}/api/engineering/sessions`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
'Authorization': authHeader,
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
projectName: description,
|
|
100
|
+
projectDescription: description,
|
|
101
|
+
source: 'cli',
|
|
102
|
+
}),
|
|
103
|
+
});
|
|
104
|
+
if (!createResponse.ok) {
|
|
105
|
+
const error = await createResponse.json().catch(() => ({}));
|
|
106
|
+
throw new Error(error.error || 'Failed to create build session');
|
|
107
|
+
}
|
|
108
|
+
const { data: sessionData } = await createResponse.json();
|
|
109
|
+
const sessionId = sessionData.sessionId;
|
|
110
|
+
spinner.text = ' Session created, starting build...';
|
|
111
|
+
// Step 2: Auto-complete scoping
|
|
112
|
+
await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/scope`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
'Authorization': authHeader,
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify({ stepId: 'auto', answer: description }),
|
|
119
|
+
});
|
|
120
|
+
// Step 3: Start the build with file generation enabled
|
|
121
|
+
const buildResponse = await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/build`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: {
|
|
124
|
+
'Content-Type': 'application/json',
|
|
125
|
+
'Authorization': authHeader,
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
generateFiles: true, // Tell server to generate actual file contents
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
if (!buildResponse.ok) {
|
|
132
|
+
const error = await buildResponse.json().catch(() => ({}));
|
|
133
|
+
throw new Error(error.error || 'Failed to start build');
|
|
134
|
+
}
|
|
135
|
+
spinner.succeed('Build started!');
|
|
136
|
+
console.log('');
|
|
137
|
+
// Step 4: Stream progress and receive files
|
|
138
|
+
await streamBuildProgress(apiUrl, authHeader, sessionId, outputDir, options.verbose);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
spinner.fail('Build failed');
|
|
142
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
143
|
+
console.log(chalk_1.default.red(`\n Error: ${message}\n`));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Stream build progress and write files as they're generated
|
|
149
|
+
*/
|
|
150
|
+
async function streamBuildProgress(apiUrl, authHeader, sessionId, outputDir, verbose) {
|
|
151
|
+
const phases = new Map();
|
|
152
|
+
let filesCreated = 0;
|
|
153
|
+
let currentSpinner = null;
|
|
154
|
+
// Display phase progress
|
|
155
|
+
function displayPhase(phase, status, displayName) {
|
|
156
|
+
const icon = status === 'completed' ? chalk_1.default.green('✓') :
|
|
157
|
+
status === 'in_progress' ? chalk_1.default.blue('●') :
|
|
158
|
+
status === 'failed' ? chalk_1.default.red('✗') : chalk_1.default.gray('○');
|
|
159
|
+
if (status === 'in_progress') {
|
|
160
|
+
if (currentSpinner)
|
|
161
|
+
currentSpinner.stop();
|
|
162
|
+
currentSpinner = (0, ora_1.default)(` ${displayName}...`).start();
|
|
163
|
+
}
|
|
164
|
+
else if (status === 'completed' && currentSpinner) {
|
|
165
|
+
currentSpinner.succeed(` ${displayName}`);
|
|
166
|
+
currentSpinner = null;
|
|
167
|
+
}
|
|
168
|
+
else if (status === 'failed' && currentSpinner) {
|
|
169
|
+
currentSpinner.fail(` ${displayName}`);
|
|
170
|
+
currentSpinner = null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Write a file to disk
|
|
174
|
+
function writeFile(file) {
|
|
175
|
+
const fullPath = (0, path_1.join)(outputDir, file.path);
|
|
176
|
+
const dir = (0, path_1.dirname)(fullPath);
|
|
177
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
178
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
(0, fs_1.writeFileSync)(fullPath, file.content);
|
|
181
|
+
filesCreated++;
|
|
182
|
+
if (verbose) {
|
|
183
|
+
console.log(chalk_1.default.gray(` + ${file.path}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Poll for updates (SSE would be better but this works for CLI)
|
|
187
|
+
let isComplete = false;
|
|
188
|
+
let pollCount = 0;
|
|
189
|
+
const maxPolls = 300; // 10 minutes max (2s intervals)
|
|
190
|
+
while (!isComplete && pollCount < maxPolls) {
|
|
191
|
+
try {
|
|
192
|
+
const response = await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/progress`, {
|
|
193
|
+
headers: {
|
|
194
|
+
'Authorization': authHeader,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new Error('Failed to get build progress');
|
|
199
|
+
}
|
|
200
|
+
const data = await response.json();
|
|
201
|
+
const progress = data.data || data;
|
|
202
|
+
// Update phase displays
|
|
203
|
+
if (progress.phases) {
|
|
204
|
+
for (const phase of progress.phases) {
|
|
205
|
+
const existing = phases.get(phase.phase);
|
|
206
|
+
if (!existing || existing.status !== phase.status) {
|
|
207
|
+
phases.set(phase.phase, phase);
|
|
208
|
+
displayPhase(phase.phase, phase.status, phase.displayName || phase.phase);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Write any new files
|
|
213
|
+
if (progress.newFiles && progress.newFiles.length > 0) {
|
|
214
|
+
for (const file of progress.newFiles) {
|
|
215
|
+
writeFile(file);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Check if build is complete
|
|
219
|
+
if (progress.status === 'completed' || progress.status === 'failed' || progress.status === 'abandoned') {
|
|
220
|
+
isComplete = true;
|
|
221
|
+
if (currentSpinner !== null) {
|
|
222
|
+
currentSpinner.stop();
|
|
223
|
+
currentSpinner = null;
|
|
224
|
+
}
|
|
225
|
+
if (progress.status === 'completed') {
|
|
226
|
+
// Write final files if any
|
|
227
|
+
if (progress.files && progress.files.length > 0) {
|
|
228
|
+
console.log(chalk_1.default.white('\n Writing project files...\n'));
|
|
229
|
+
const fileSpinner = (0, ora_1.default)(' Creating files...').start();
|
|
230
|
+
for (const file of progress.files) {
|
|
231
|
+
writeFile(file);
|
|
232
|
+
}
|
|
233
|
+
fileSpinner.succeed(` Created ${filesCreated} files`);
|
|
234
|
+
}
|
|
235
|
+
// Success message
|
|
236
|
+
console.log(chalk_1.default.green(`
|
|
237
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
238
|
+
║ ║
|
|
239
|
+
║ ${chalk_1.default.bold('✓ Build complete!')} ║
|
|
240
|
+
║ ║
|
|
241
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
242
|
+
`));
|
|
243
|
+
console.log(chalk_1.default.white(' Next steps:\n'));
|
|
244
|
+
console.log(chalk_1.default.cyan(' 1. ') + chalk_1.default.white('Install dependencies:'));
|
|
245
|
+
console.log(chalk_1.default.gray(' npm install\n'));
|
|
246
|
+
console.log(chalk_1.default.cyan(' 2. ') + chalk_1.default.white('Set up your database:'));
|
|
247
|
+
console.log(chalk_1.default.gray(' npx drizzle-kit db:push\n'));
|
|
248
|
+
console.log(chalk_1.default.cyan(' 3. ') + chalk_1.default.white('Start the dev server:'));
|
|
249
|
+
console.log(chalk_1.default.gray(' npm run dev\n'));
|
|
250
|
+
// Show summary
|
|
251
|
+
if (progress.summary) {
|
|
252
|
+
console.log(chalk_1.default.gray(` Summary: ${progress.summary.filesCreated || filesCreated} files, ${progress.summary.tokensUsed || 0} tokens used\n`));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log(chalk_1.default.red(`\n Build ${progress.status}: ${progress.lastError || 'Unknown error'}\n`));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (verbose) {
|
|
262
|
+
console.log(chalk_1.default.gray(` Poll error: ${error}`));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (!isComplete) {
|
|
266
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
267
|
+
pollCount++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!isComplete) {
|
|
271
|
+
if (currentSpinner !== null) {
|
|
272
|
+
currentSpinner.fail('Build timed out');
|
|
273
|
+
}
|
|
274
|
+
console.log(chalk_1.default.yellow('\n Build is taking longer than expected.'));
|
|
275
|
+
console.log(chalk_1.default.gray(' Check status: codebakers build-status\n'));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check build status
|
|
280
|
+
*/
|
|
281
|
+
async function buildStatus() {
|
|
282
|
+
console.log(chalk_1.default.blue('\n Checking recent builds...\n'));
|
|
283
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
284
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
285
|
+
const trial = (0, config_js_1.getTrialState)();
|
|
286
|
+
let authHeader = '';
|
|
287
|
+
if (apiKey) {
|
|
288
|
+
authHeader = `Bearer ${apiKey}`;
|
|
289
|
+
}
|
|
290
|
+
else if (trial?.trialId) {
|
|
291
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
292
|
+
}
|
|
293
|
+
if (!authHeader) {
|
|
294
|
+
console.log(chalk_1.default.red(' Not authenticated. Run: codebakers go\n'));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const response = await fetch(`${apiUrl}/api/engineering/sessions`, {
|
|
299
|
+
headers: {
|
|
300
|
+
'Authorization': authHeader,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
throw new Error('Failed to get builds');
|
|
305
|
+
}
|
|
306
|
+
const { data } = await response.json();
|
|
307
|
+
const sessions = data.sessions || [];
|
|
308
|
+
if (sessions.length === 0) {
|
|
309
|
+
console.log(chalk_1.default.gray(' No builds found. Run: codebakers build "your project"\n'));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
console.log(chalk_1.default.white(' Recent builds:\n'));
|
|
313
|
+
for (const session of sessions.slice(0, 5)) {
|
|
314
|
+
const statusIcon = session.status === 'completed' ? chalk_1.default.green('✓') :
|
|
315
|
+
session.status === 'active' ? chalk_1.default.blue('●') :
|
|
316
|
+
session.status === 'failed' ? chalk_1.default.red('✗') : chalk_1.default.gray('○');
|
|
317
|
+
console.log(` ${statusIcon} ${session.projectName}`);
|
|
318
|
+
console.log(chalk_1.default.gray(` Status: ${session.status} | Phase: ${session.currentPhase} | Progress: ${session.progress}%\n`));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
323
|
+
console.log(chalk_1.default.red(` Error: ${message}\n`));
|
|
324
|
+
}
|
|
325
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ const push_patterns_js_1 = require("./commands/push-patterns.js");
|
|
|
27
27
|
const go_js_1 = require("./commands/go.js");
|
|
28
28
|
const extend_js_1 = require("./commands/extend.js");
|
|
29
29
|
const billing_js_1 = require("./commands/billing.js");
|
|
30
|
+
const build_js_1 = require("./commands/build.js");
|
|
30
31
|
const config_js_2 = require("./config.js");
|
|
31
32
|
const child_process_1 = require("child_process");
|
|
32
33
|
const api_js_1 = require("./lib/api.js");
|
|
@@ -305,6 +306,7 @@ function showWelcome() {
|
|
|
305
306
|
`));
|
|
306
307
|
console.log(chalk_1.default.white(' Getting Started:\n'));
|
|
307
308
|
console.log(chalk_1.default.cyan(' codebakers go') + chalk_1.default.gray(' Start free trial instantly (no signup!)'));
|
|
309
|
+
console.log(chalk_1.default.cyan(' codebakers build') + chalk_1.default.gray(' Describe your project → Get working code'));
|
|
308
310
|
console.log(chalk_1.default.cyan(' codebakers scaffold') + chalk_1.default.gray(' Create a new project from scratch'));
|
|
309
311
|
console.log(chalk_1.default.cyan(' codebakers init') + chalk_1.default.gray(' Add patterns to existing project\n'));
|
|
310
312
|
console.log(chalk_1.default.white(' Development:\n'));
|
|
@@ -313,20 +315,20 @@ function showWelcome() {
|
|
|
313
315
|
console.log(chalk_1.default.cyan(' codebakers status') + chalk_1.default.gray(' Check what\'s installed'));
|
|
314
316
|
console.log(chalk_1.default.cyan(' codebakers config') + chalk_1.default.gray(' View or modify configuration\n'));
|
|
315
317
|
console.log(chalk_1.default.white(' Examples:\n'));
|
|
318
|
+
console.log(chalk_1.default.gray(' $ ') + chalk_1.default.cyan('codebakers build "SaaS for invoicing"'));
|
|
319
|
+
console.log(chalk_1.default.gray(' AI generates full project with auth, payments, dashboard\n'));
|
|
316
320
|
console.log(chalk_1.default.gray(' $ ') + chalk_1.default.cyan('codebakers scaffold'));
|
|
317
321
|
console.log(chalk_1.default.gray(' Create a new Next.js + Supabase + Drizzle project\n'));
|
|
318
322
|
console.log(chalk_1.default.gray(' $ ') + chalk_1.default.cyan('codebakers generate component Button'));
|
|
319
323
|
console.log(chalk_1.default.gray(' Generate a React component with TypeScript\n'));
|
|
320
|
-
console.log(chalk_1.default.gray(' $ ') + chalk_1.default.cyan('codebakers g api users'));
|
|
321
|
-
console.log(chalk_1.default.gray(' Generate a Next.js API route with validation\n'));
|
|
322
324
|
console.log(chalk_1.default.white(' Quality:\n'));
|
|
323
325
|
console.log(chalk_1.default.cyan(' codebakers audit') + chalk_1.default.gray(' Run automated code quality checks'));
|
|
324
326
|
console.log(chalk_1.default.cyan(' codebakers heal') + chalk_1.default.gray(' Auto-detect and fix common issues'));
|
|
325
327
|
console.log(chalk_1.default.cyan(' codebakers doctor') + chalk_1.default.gray(' Check CodeBakers setup\n'));
|
|
326
328
|
console.log(chalk_1.default.white(' All Commands:\n'));
|
|
327
|
-
console.log(chalk_1.default.gray(' go, extend, billing,
|
|
328
|
-
console.log(chalk_1.default.gray('
|
|
329
|
-
console.log(chalk_1.default.gray(' install
|
|
329
|
+
console.log(chalk_1.default.gray(' go, extend, billing, build, build-status, setup, scaffold, init'));
|
|
330
|
+
console.log(chalk_1.default.gray(' generate, upgrade, status, audit, heal, doctor, config'));
|
|
331
|
+
console.log(chalk_1.default.gray(' login, install, uninstall, serve, mcp-config, mcp-uninstall\n'));
|
|
330
332
|
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('codebakers <command> --help') + chalk_1.default.gray(' for more info\n'));
|
|
331
333
|
}
|
|
332
334
|
const program = new commander_1.Command();
|
|
@@ -350,6 +352,21 @@ program
|
|
|
350
352
|
.alias('subscribe')
|
|
351
353
|
.description('Manage subscription or upgrade to paid plan')
|
|
352
354
|
.action(billing_js_1.billing);
|
|
355
|
+
// AI Build command - describe what you want, get working code
|
|
356
|
+
program
|
|
357
|
+
.command('build [description]')
|
|
358
|
+
.description('Build a project from description - AI generates actual files')
|
|
359
|
+
.option('-o, --output <dir>', 'Output directory (default: current directory)')
|
|
360
|
+
.option('-v, --verbose', 'Show detailed progress')
|
|
361
|
+
.action((description, options) => (0, build_js_1.build)({
|
|
362
|
+
description,
|
|
363
|
+
output: options.output,
|
|
364
|
+
verbose: options.verbose,
|
|
365
|
+
}));
|
|
366
|
+
program
|
|
367
|
+
.command('build-status')
|
|
368
|
+
.description('Check status of recent builds')
|
|
369
|
+
.action(build_js_1.buildStatus);
|
|
353
370
|
// Primary command - one-time setup (for paid users)
|
|
354
371
|
program
|
|
355
372
|
.command('setup')
|
package/package.json
CHANGED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora, { type Ora } from 'ora';
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
6
|
+
import { getApiKey, getApiUrl, getTrialState, hasValidAccess } from '../config.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* CODEBAKERS BUILD COMMAND
|
|
10
|
+
*
|
|
11
|
+
* This is the zero-friction project builder.
|
|
12
|
+
* User describes what they want → AI builds actual files on their machine.
|
|
13
|
+
*
|
|
14
|
+
* Flow:
|
|
15
|
+
* 1. User runs: codebakers build "SaaS for invoicing"
|
|
16
|
+
* 2. CLI creates engineering session on server
|
|
17
|
+
* 3. Server runs AI agents to generate PRD, specs, code
|
|
18
|
+
* 4. CLI receives file contents and writes them to disk
|
|
19
|
+
* 5. User has a runnable project
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
interface BuildOptions {
|
|
23
|
+
description?: string;
|
|
24
|
+
output?: string;
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface FileToCreate {
|
|
29
|
+
path: string;
|
|
30
|
+
content: string;
|
|
31
|
+
type: 'code' | 'config' | 'doc';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface BuildPhase {
|
|
35
|
+
phase: string;
|
|
36
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
37
|
+
displayName: string;
|
|
38
|
+
files?: FileToCreate[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface BuildStreamEvent {
|
|
42
|
+
type: 'phase_start' | 'phase_complete' | 'file_create' | 'message' | 'error' | 'complete';
|
|
43
|
+
phase?: string;
|
|
44
|
+
displayName?: string;
|
|
45
|
+
message?: string;
|
|
46
|
+
file?: FileToCreate;
|
|
47
|
+
files?: FileToCreate[];
|
|
48
|
+
error?: string;
|
|
49
|
+
summary?: {
|
|
50
|
+
filesCreated: number;
|
|
51
|
+
phases: number;
|
|
52
|
+
tokensUsed: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function prompt(question: string): Promise<string> {
|
|
57
|
+
const rl = createInterface({
|
|
58
|
+
input: process.stdin,
|
|
59
|
+
output: process.stdout,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
rl.question(question, (answer) => {
|
|
64
|
+
rl.close();
|
|
65
|
+
resolve(answer.trim());
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Main build command
|
|
72
|
+
*/
|
|
73
|
+
export async function build(options: BuildOptions = {}): Promise<void> {
|
|
74
|
+
console.log(chalk.blue(`
|
|
75
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
76
|
+
║ ║
|
|
77
|
+
║ ${chalk.bold('CodeBakers Build')} ║
|
|
78
|
+
║ ║
|
|
79
|
+
║ Describe your project → Get working code ║
|
|
80
|
+
║ ║
|
|
81
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
82
|
+
`));
|
|
83
|
+
|
|
84
|
+
// Check authentication
|
|
85
|
+
if (!hasValidAccess()) {
|
|
86
|
+
console.log(chalk.red(' ✗ Not authenticated\n'));
|
|
87
|
+
console.log(chalk.gray(' Run: codebakers go (free trial) or codebakers setup (with API key)\n'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get project description
|
|
92
|
+
let description = options.description;
|
|
93
|
+
if (!description) {
|
|
94
|
+
console.log(chalk.white(' What do you want to build?\n'));
|
|
95
|
+
console.log(chalk.gray(' Examples:'));
|
|
96
|
+
console.log(chalk.gray(' • "A SaaS for managing invoices with Stripe payments"'));
|
|
97
|
+
console.log(chalk.gray(' • "Todo app with user auth and real-time sync"'));
|
|
98
|
+
console.log(chalk.gray(' • "Blog platform with markdown support"\n'));
|
|
99
|
+
|
|
100
|
+
description = await prompt(' Describe your project: ');
|
|
101
|
+
|
|
102
|
+
if (!description.trim()) {
|
|
103
|
+
console.log(chalk.red('\n Please provide a project description.\n'));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const outputDir = options.output || process.cwd();
|
|
109
|
+
|
|
110
|
+
// Check if directory is empty
|
|
111
|
+
const files = existsSync(outputDir) ?
|
|
112
|
+
require('fs').readdirSync(outputDir).filter((f: string) => !f.startsWith('.')) : [];
|
|
113
|
+
|
|
114
|
+
if (files.length > 0) {
|
|
115
|
+
console.log(chalk.yellow('\n ⚠️ This directory is not empty.'));
|
|
116
|
+
const proceed = await prompt(' Continue? (y/N): ');
|
|
117
|
+
if (proceed.toLowerCase() !== 'y') {
|
|
118
|
+
console.log(chalk.gray('\n Build cancelled.\n'));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(chalk.green(`\n Building: "${description}"\n`));
|
|
124
|
+
|
|
125
|
+
// Create engineering session
|
|
126
|
+
const spinner = ora(' Initializing build...').start();
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const apiUrl = getApiUrl();
|
|
130
|
+
const apiKey = getApiKey();
|
|
131
|
+
const trial = getTrialState();
|
|
132
|
+
|
|
133
|
+
let authHeader = '';
|
|
134
|
+
if (apiKey) {
|
|
135
|
+
authHeader = `Bearer ${apiKey}`;
|
|
136
|
+
} else if (trial?.trialId) {
|
|
137
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!authHeader) {
|
|
141
|
+
spinner.fail('Authentication required');
|
|
142
|
+
console.log(chalk.gray('\n Run: codebakers go\n'));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Step 1: Create engineering session
|
|
147
|
+
spinner.text = ' Creating build session...';
|
|
148
|
+
|
|
149
|
+
const createResponse = await fetch(`${apiUrl}/api/engineering/sessions`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: {
|
|
152
|
+
'Content-Type': 'application/json',
|
|
153
|
+
'Authorization': authHeader,
|
|
154
|
+
},
|
|
155
|
+
body: JSON.stringify({
|
|
156
|
+
projectName: description,
|
|
157
|
+
projectDescription: description,
|
|
158
|
+
source: 'cli',
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!createResponse.ok) {
|
|
163
|
+
const error = await createResponse.json().catch(() => ({}));
|
|
164
|
+
throw new Error(error.error || 'Failed to create build session');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { data: sessionData } = await createResponse.json();
|
|
168
|
+
const sessionId = sessionData.sessionId;
|
|
169
|
+
|
|
170
|
+
spinner.text = ' Session created, starting build...';
|
|
171
|
+
|
|
172
|
+
// Step 2: Auto-complete scoping
|
|
173
|
+
await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/scope`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: {
|
|
176
|
+
'Content-Type': 'application/json',
|
|
177
|
+
'Authorization': authHeader,
|
|
178
|
+
},
|
|
179
|
+
body: JSON.stringify({ stepId: 'auto', answer: description }),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Step 3: Start the build with file generation enabled
|
|
183
|
+
const buildResponse = await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/build`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: {
|
|
186
|
+
'Content-Type': 'application/json',
|
|
187
|
+
'Authorization': authHeader,
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
generateFiles: true, // Tell server to generate actual file contents
|
|
191
|
+
}),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (!buildResponse.ok) {
|
|
195
|
+
const error = await buildResponse.json().catch(() => ({}));
|
|
196
|
+
throw new Error(error.error || 'Failed to start build');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
spinner.succeed('Build started!');
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
// Step 4: Stream progress and receive files
|
|
203
|
+
await streamBuildProgress(apiUrl, authHeader, sessionId, outputDir, options.verbose);
|
|
204
|
+
|
|
205
|
+
} catch (error) {
|
|
206
|
+
spinner.fail('Build failed');
|
|
207
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
208
|
+
console.log(chalk.red(`\n Error: ${message}\n`));
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Stream build progress and write files as they're generated
|
|
215
|
+
*/
|
|
216
|
+
async function streamBuildProgress(
|
|
217
|
+
apiUrl: string,
|
|
218
|
+
authHeader: string,
|
|
219
|
+
sessionId: string,
|
|
220
|
+
outputDir: string,
|
|
221
|
+
verbose?: boolean
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
const phases: Map<string, BuildPhase> = new Map();
|
|
224
|
+
let filesCreated = 0;
|
|
225
|
+
let currentSpinner: Ora | null = null;
|
|
226
|
+
|
|
227
|
+
// Display phase progress
|
|
228
|
+
function displayPhase(phase: string, status: string, displayName: string): void {
|
|
229
|
+
const icon = status === 'completed' ? chalk.green('✓') :
|
|
230
|
+
status === 'in_progress' ? chalk.blue('●') :
|
|
231
|
+
status === 'failed' ? chalk.red('✗') : chalk.gray('○');
|
|
232
|
+
|
|
233
|
+
if (status === 'in_progress') {
|
|
234
|
+
if (currentSpinner) currentSpinner.stop();
|
|
235
|
+
currentSpinner = ora(` ${displayName}...`).start();
|
|
236
|
+
} else if (status === 'completed' && currentSpinner) {
|
|
237
|
+
currentSpinner.succeed(` ${displayName}`);
|
|
238
|
+
currentSpinner = null;
|
|
239
|
+
} else if (status === 'failed' && currentSpinner) {
|
|
240
|
+
currentSpinner.fail(` ${displayName}`);
|
|
241
|
+
currentSpinner = null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Write a file to disk
|
|
246
|
+
function writeFile(file: FileToCreate): void {
|
|
247
|
+
const fullPath = join(outputDir, file.path);
|
|
248
|
+
const dir = dirname(fullPath);
|
|
249
|
+
|
|
250
|
+
if (!existsSync(dir)) {
|
|
251
|
+
mkdirSync(dir, { recursive: true });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
writeFileSync(fullPath, file.content);
|
|
255
|
+
filesCreated++;
|
|
256
|
+
|
|
257
|
+
if (verbose) {
|
|
258
|
+
console.log(chalk.gray(` + ${file.path}`));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Poll for updates (SSE would be better but this works for CLI)
|
|
263
|
+
let isComplete = false;
|
|
264
|
+
let pollCount = 0;
|
|
265
|
+
const maxPolls = 300; // 10 minutes max (2s intervals)
|
|
266
|
+
|
|
267
|
+
while (!isComplete && pollCount < maxPolls) {
|
|
268
|
+
try {
|
|
269
|
+
const response = await fetch(`${apiUrl}/api/engineering/sessions/${sessionId}/progress`, {
|
|
270
|
+
headers: {
|
|
271
|
+
'Authorization': authHeader,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (!response.ok) {
|
|
276
|
+
throw new Error('Failed to get build progress');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const data = await response.json();
|
|
280
|
+
const progress = data.data || data;
|
|
281
|
+
|
|
282
|
+
// Update phase displays
|
|
283
|
+
if (progress.phases) {
|
|
284
|
+
for (const phase of progress.phases) {
|
|
285
|
+
const existing = phases.get(phase.phase);
|
|
286
|
+
if (!existing || existing.status !== phase.status) {
|
|
287
|
+
phases.set(phase.phase, phase);
|
|
288
|
+
displayPhase(phase.phase, phase.status, phase.displayName || phase.phase);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Write any new files
|
|
294
|
+
if (progress.newFiles && progress.newFiles.length > 0) {
|
|
295
|
+
for (const file of progress.newFiles) {
|
|
296
|
+
writeFile(file);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Check if build is complete
|
|
301
|
+
if (progress.status === 'completed' || progress.status === 'failed' || progress.status === 'abandoned') {
|
|
302
|
+
isComplete = true;
|
|
303
|
+
|
|
304
|
+
if (currentSpinner !== null) {
|
|
305
|
+
(currentSpinner as Ora).stop();
|
|
306
|
+
currentSpinner = null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (progress.status === 'completed') {
|
|
310
|
+
// Write final files if any
|
|
311
|
+
if (progress.files && progress.files.length > 0) {
|
|
312
|
+
console.log(chalk.white('\n Writing project files...\n'));
|
|
313
|
+
const fileSpinner = ora(' Creating files...').start();
|
|
314
|
+
|
|
315
|
+
for (const file of progress.files) {
|
|
316
|
+
writeFile(file);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
fileSpinner.succeed(` Created ${filesCreated} files`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Success message
|
|
323
|
+
console.log(chalk.green(`
|
|
324
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
325
|
+
║ ║
|
|
326
|
+
║ ${chalk.bold('✓ Build complete!')} ║
|
|
327
|
+
║ ║
|
|
328
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
329
|
+
`));
|
|
330
|
+
|
|
331
|
+
console.log(chalk.white(' Next steps:\n'));
|
|
332
|
+
console.log(chalk.cyan(' 1. ') + chalk.white('Install dependencies:'));
|
|
333
|
+
console.log(chalk.gray(' npm install\n'));
|
|
334
|
+
console.log(chalk.cyan(' 2. ') + chalk.white('Set up your database:'));
|
|
335
|
+
console.log(chalk.gray(' npx drizzle-kit db:push\n'));
|
|
336
|
+
console.log(chalk.cyan(' 3. ') + chalk.white('Start the dev server:'));
|
|
337
|
+
console.log(chalk.gray(' npm run dev\n'));
|
|
338
|
+
|
|
339
|
+
// Show summary
|
|
340
|
+
if (progress.summary) {
|
|
341
|
+
console.log(chalk.gray(` Summary: ${progress.summary.filesCreated || filesCreated} files, ${progress.summary.tokensUsed || 0} tokens used\n`));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
} else {
|
|
345
|
+
console.log(chalk.red(`\n Build ${progress.status}: ${progress.lastError || 'Unknown error'}\n`));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
} catch (error) {
|
|
350
|
+
if (verbose) {
|
|
351
|
+
console.log(chalk.gray(` Poll error: ${error}`));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!isComplete) {
|
|
356
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
357
|
+
pollCount++;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!isComplete) {
|
|
362
|
+
if (currentSpinner !== null) {
|
|
363
|
+
(currentSpinner as Ora).fail('Build timed out');
|
|
364
|
+
}
|
|
365
|
+
console.log(chalk.yellow('\n Build is taking longer than expected.'));
|
|
366
|
+
console.log(chalk.gray(' Check status: codebakers build-status\n'));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Check build status
|
|
372
|
+
*/
|
|
373
|
+
export async function buildStatus(): Promise<void> {
|
|
374
|
+
console.log(chalk.blue('\n Checking recent builds...\n'));
|
|
375
|
+
|
|
376
|
+
const apiUrl = getApiUrl();
|
|
377
|
+
const apiKey = getApiKey();
|
|
378
|
+
const trial = getTrialState();
|
|
379
|
+
|
|
380
|
+
let authHeader = '';
|
|
381
|
+
if (apiKey) {
|
|
382
|
+
authHeader = `Bearer ${apiKey}`;
|
|
383
|
+
} else if (trial?.trialId) {
|
|
384
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!authHeader) {
|
|
388
|
+
console.log(chalk.red(' Not authenticated. Run: codebakers go\n'));
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const response = await fetch(`${apiUrl}/api/engineering/sessions`, {
|
|
394
|
+
headers: {
|
|
395
|
+
'Authorization': authHeader,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
throw new Error('Failed to get builds');
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const { data } = await response.json();
|
|
404
|
+
const sessions = data.sessions || [];
|
|
405
|
+
|
|
406
|
+
if (sessions.length === 0) {
|
|
407
|
+
console.log(chalk.gray(' No builds found. Run: codebakers build "your project"\n'));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
console.log(chalk.white(' Recent builds:\n'));
|
|
412
|
+
for (const session of sessions.slice(0, 5)) {
|
|
413
|
+
const statusIcon = session.status === 'completed' ? chalk.green('✓') :
|
|
414
|
+
session.status === 'active' ? chalk.blue('●') :
|
|
415
|
+
session.status === 'failed' ? chalk.red('✗') : chalk.gray('○');
|
|
416
|
+
|
|
417
|
+
console.log(` ${statusIcon} ${session.projectName}`);
|
|
418
|
+
console.log(chalk.gray(` Status: ${session.status} | Phase: ${session.currentPhase} | Progress: ${session.progress}%\n`));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
} catch (error) {
|
|
422
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
423
|
+
console.log(chalk.red(` Error: ${message}\n`));
|
|
424
|
+
}
|
|
425
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { pushPatterns, pushPatternsInteractive } from './commands/push-patterns.
|
|
|
23
23
|
import { go } from './commands/go.js';
|
|
24
24
|
import { extend } from './commands/extend.js';
|
|
25
25
|
import { billing } from './commands/billing.js';
|
|
26
|
+
import { build, buildStatus } from './commands/build.js';
|
|
26
27
|
import { getCachedUpdateInfo, setCachedUpdateInfo, getCliVersion, getCachedPatternInfo, setCachedPatternInfo, getApiKey, getApiUrl, getTrialState, hasValidAccess, shouldAttemptCliUpdate, setCliUpdateAttempt, isCliAutoUpdateDisabled } from './config.js';
|
|
27
28
|
import { execSync } from 'child_process';
|
|
28
29
|
import { checkForUpdates } from './lib/api.js';
|
|
@@ -353,6 +354,7 @@ function showWelcome(): void {
|
|
|
353
354
|
|
|
354
355
|
console.log(chalk.white(' Getting Started:\n'));
|
|
355
356
|
console.log(chalk.cyan(' codebakers go') + chalk.gray(' Start free trial instantly (no signup!)'));
|
|
357
|
+
console.log(chalk.cyan(' codebakers build') + chalk.gray(' Describe your project → Get working code'));
|
|
356
358
|
console.log(chalk.cyan(' codebakers scaffold') + chalk.gray(' Create a new project from scratch'));
|
|
357
359
|
console.log(chalk.cyan(' codebakers init') + chalk.gray(' Add patterns to existing project\n'));
|
|
358
360
|
|
|
@@ -363,12 +365,12 @@ function showWelcome(): void {
|
|
|
363
365
|
console.log(chalk.cyan(' codebakers config') + chalk.gray(' View or modify configuration\n'));
|
|
364
366
|
|
|
365
367
|
console.log(chalk.white(' Examples:\n'));
|
|
368
|
+
console.log(chalk.gray(' $ ') + chalk.cyan('codebakers build "SaaS for invoicing"'));
|
|
369
|
+
console.log(chalk.gray(' AI generates full project with auth, payments, dashboard\n'));
|
|
366
370
|
console.log(chalk.gray(' $ ') + chalk.cyan('codebakers scaffold'));
|
|
367
371
|
console.log(chalk.gray(' Create a new Next.js + Supabase + Drizzle project\n'));
|
|
368
372
|
console.log(chalk.gray(' $ ') + chalk.cyan('codebakers generate component Button'));
|
|
369
373
|
console.log(chalk.gray(' Generate a React component with TypeScript\n'));
|
|
370
|
-
console.log(chalk.gray(' $ ') + chalk.cyan('codebakers g api users'));
|
|
371
|
-
console.log(chalk.gray(' Generate a Next.js API route with validation\n'));
|
|
372
374
|
|
|
373
375
|
console.log(chalk.white(' Quality:\n'));
|
|
374
376
|
console.log(chalk.cyan(' codebakers audit') + chalk.gray(' Run automated code quality checks'));
|
|
@@ -376,9 +378,9 @@ function showWelcome(): void {
|
|
|
376
378
|
console.log(chalk.cyan(' codebakers doctor') + chalk.gray(' Check CodeBakers setup\n'));
|
|
377
379
|
|
|
378
380
|
console.log(chalk.white(' All Commands:\n'));
|
|
379
|
-
console.log(chalk.gray(' go, extend, billing,
|
|
380
|
-
console.log(chalk.gray('
|
|
381
|
-
console.log(chalk.gray(' install
|
|
381
|
+
console.log(chalk.gray(' go, extend, billing, build, build-status, setup, scaffold, init'));
|
|
382
|
+
console.log(chalk.gray(' generate, upgrade, status, audit, heal, doctor, config'));
|
|
383
|
+
console.log(chalk.gray(' login, install, uninstall, serve, mcp-config, mcp-uninstall\n'));
|
|
382
384
|
|
|
383
385
|
console.log(chalk.gray(' Run ') + chalk.cyan('codebakers <command> --help') + chalk.gray(' for more info\n'));
|
|
384
386
|
}
|
|
@@ -409,6 +411,23 @@ program
|
|
|
409
411
|
.description('Manage subscription or upgrade to paid plan')
|
|
410
412
|
.action(billing);
|
|
411
413
|
|
|
414
|
+
// AI Build command - describe what you want, get working code
|
|
415
|
+
program
|
|
416
|
+
.command('build [description]')
|
|
417
|
+
.description('Build a project from description - AI generates actual files')
|
|
418
|
+
.option('-o, --output <dir>', 'Output directory (default: current directory)')
|
|
419
|
+
.option('-v, --verbose', 'Show detailed progress')
|
|
420
|
+
.action((description, options) => build({
|
|
421
|
+
description,
|
|
422
|
+
output: options.output,
|
|
423
|
+
verbose: options.verbose,
|
|
424
|
+
}));
|
|
425
|
+
|
|
426
|
+
program
|
|
427
|
+
.command('build-status')
|
|
428
|
+
.description('Check status of recent builds')
|
|
429
|
+
.action(buildStatus);
|
|
430
|
+
|
|
412
431
|
// Primary command - one-time setup (for paid users)
|
|
413
432
|
program
|
|
414
433
|
.command('setup')
|