codebakers 2.0.0 → 2.0.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/index.js +1277 -127
- package/package.json +1 -1
- package/src/commands/advisors.ts +699 -0
- package/src/commands/design.ts +298 -0
- package/src/commands/prd.ts +419 -0
- package/src/index.ts +33 -0
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
5
|
+
import * as p15 from "@clack/prompts";
|
|
6
|
+
import chalk16 from "chalk";
|
|
7
7
|
import boxen from "boxen";
|
|
8
8
|
import gradient from "gradient-string";
|
|
9
9
|
|
|
@@ -304,10 +304,10 @@ async function setupCommand() {
|
|
|
304
304
|
return;
|
|
305
305
|
}
|
|
306
306
|
if (action === "reset") {
|
|
307
|
-
const
|
|
307
|
+
const confirm8 = await p.confirm({
|
|
308
308
|
message: "Are you sure? This will remove all credentials and settings."
|
|
309
309
|
});
|
|
310
|
-
if (
|
|
310
|
+
if (confirm8) {
|
|
311
311
|
p.outro(chalk2.yellow("Configuration reset. Run `codebakers setup` again."));
|
|
312
312
|
}
|
|
313
313
|
return;
|
|
@@ -371,7 +371,7 @@ ${chalk2.dim("and are never sent to our servers.")}`,
|
|
|
371
371
|
p.outro(chalk2.green("\u2713 Setup complete! Run `codebakers init` to create your first project."));
|
|
372
372
|
}
|
|
373
373
|
async function connectService(config, serviceKey, serviceName, required) {
|
|
374
|
-
const
|
|
374
|
+
const spinner13 = p.spinner();
|
|
375
375
|
switch (serviceKey) {
|
|
376
376
|
case "github": {
|
|
377
377
|
p.log.info(`${chalk2.bold("GitHub")} - Opens browser for OAuth authorization`);
|
|
@@ -845,10 +845,10 @@ var SupabaseService = class {
|
|
|
845
845
|
throw new Error("Failed to list projects");
|
|
846
846
|
}
|
|
847
847
|
const projects = await response.json();
|
|
848
|
-
return projects.map((
|
|
849
|
-
id:
|
|
850
|
-
name:
|
|
851
|
-
region:
|
|
848
|
+
return projects.map((p16) => ({
|
|
849
|
+
id: p16.id,
|
|
850
|
+
name: p16.name,
|
|
851
|
+
region: p16.region
|
|
852
852
|
}));
|
|
853
853
|
}
|
|
854
854
|
};
|
|
@@ -1268,20 +1268,20 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1268
1268
|
p2.cancel("Project creation cancelled.");
|
|
1269
1269
|
return;
|
|
1270
1270
|
}
|
|
1271
|
-
const
|
|
1271
|
+
const spinner13 = p2.spinner();
|
|
1272
1272
|
const projectPath = path2.join(process.cwd(), projectName);
|
|
1273
1273
|
try {
|
|
1274
|
-
|
|
1274
|
+
spinner13.start("Creating local project...");
|
|
1275
1275
|
await createLocalProject(projectPath, projectConfig);
|
|
1276
|
-
|
|
1277
|
-
|
|
1276
|
+
spinner13.stop("Local project created");
|
|
1277
|
+
spinner13.start("Installing dependencies...");
|
|
1278
1278
|
await execa2("pnpm", ["install"], { cwd: projectPath });
|
|
1279
|
-
|
|
1279
|
+
spinner13.stop("Dependencies installed");
|
|
1280
1280
|
if (services.includes("github")) {
|
|
1281
|
-
|
|
1281
|
+
spinner13.start("Creating GitHub repository...");
|
|
1282
1282
|
const github = new GitHubService(config);
|
|
1283
1283
|
const repo = await github.createRepo(projectName, { private: true });
|
|
1284
|
-
|
|
1284
|
+
spinner13.stop(`GitHub repo created: ${repo.html_url}`);
|
|
1285
1285
|
await execa2("git", ["init"], { cwd: projectPath });
|
|
1286
1286
|
await execa2("git", ["add", "."], { cwd: projectPath });
|
|
1287
1287
|
await execa2("git", ["commit", "-m", "Initial commit by CodeBakers"], { cwd: projectPath });
|
|
@@ -1289,10 +1289,10 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1289
1289
|
await execa2("git", ["push", "-u", "origin", "main"], { cwd: projectPath });
|
|
1290
1290
|
}
|
|
1291
1291
|
if (services.includes("supabase")) {
|
|
1292
|
-
|
|
1292
|
+
spinner13.start("Creating Supabase project...");
|
|
1293
1293
|
const supabase = new SupabaseService(config);
|
|
1294
1294
|
const project = await supabase.createProject(projectName);
|
|
1295
|
-
|
|
1295
|
+
spinner13.stop(`Supabase project created: ${project.name}`);
|
|
1296
1296
|
await fs2.writeJson(
|
|
1297
1297
|
path2.join(projectPath, ".codebakers", "supabase.json"),
|
|
1298
1298
|
{ projectId: project.id, projectUrl: project.api_url },
|
|
@@ -1300,26 +1300,26 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1300
1300
|
);
|
|
1301
1301
|
}
|
|
1302
1302
|
if (services.includes("vercel")) {
|
|
1303
|
-
|
|
1303
|
+
spinner13.start("Creating Vercel project...");
|
|
1304
1304
|
const vercel = new VercelService(config);
|
|
1305
1305
|
const project = await vercel.createProject(projectName);
|
|
1306
|
-
|
|
1306
|
+
spinner13.stop(`Vercel project created`);
|
|
1307
1307
|
if (domain) {
|
|
1308
|
-
|
|
1308
|
+
spinner13.start(`Configuring domain: ${domain}...`);
|
|
1309
1309
|
await vercel.addDomain(projectName, domain);
|
|
1310
|
-
|
|
1310
|
+
spinner13.stop("Domain configured");
|
|
1311
1311
|
}
|
|
1312
|
-
|
|
1312
|
+
spinner13.start("Deploying to Vercel...");
|
|
1313
1313
|
const deployment = await vercel.deploy(projectPath);
|
|
1314
|
-
|
|
1314
|
+
spinner13.stop(`Deployed: ${deployment.url}`);
|
|
1315
1315
|
}
|
|
1316
|
-
|
|
1316
|
+
spinner13.start("Generating CLAUDE.md...");
|
|
1317
1317
|
const claudeMd = generateClaudeMd(projectConfig);
|
|
1318
1318
|
await fs2.writeFile(path2.join(projectPath, "CLAUDE.md"), claudeMd);
|
|
1319
|
-
|
|
1320
|
-
|
|
1319
|
+
spinner13.stop("CLAUDE.md generated");
|
|
1320
|
+
spinner13.start("Setting up CodeBakers enforcement...");
|
|
1321
1321
|
await setupGitHooks(projectPath);
|
|
1322
|
-
|
|
1322
|
+
spinner13.stop("CodeBakers enforcement configured");
|
|
1323
1323
|
config.addProject({
|
|
1324
1324
|
name: projectName,
|
|
1325
1325
|
path: projectPath,
|
|
@@ -1339,7 +1339,7 @@ ${chalk3.dim("Shortcuts:")}
|
|
|
1339
1339
|
${chalk3.cyan("codebakers deploy")} \u2014 Deploy to production
|
|
1340
1340
|
`));
|
|
1341
1341
|
} catch (error) {
|
|
1342
|
-
|
|
1342
|
+
spinner13.stop("Error occurred");
|
|
1343
1343
|
p2.log.error(`Failed to create project: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1344
1344
|
const cleanup = await p2.confirm({
|
|
1345
1345
|
message: "Clean up partially created project?"
|
|
@@ -2065,17 +2065,17 @@ async function checkCommand(options = {}) {
|
|
|
2065
2065
|
return;
|
|
2066
2066
|
}
|
|
2067
2067
|
p3.intro(chalk4.bgCyan.black(" CodeBakers Pattern Check "));
|
|
2068
|
-
const
|
|
2069
|
-
|
|
2068
|
+
const spinner13 = p3.spinner();
|
|
2069
|
+
spinner13.start("Analyzing code...");
|
|
2070
2070
|
const result = await runPatternCheck(options.fix || false);
|
|
2071
|
-
|
|
2071
|
+
spinner13.stop("Analysis complete");
|
|
2072
2072
|
displayResults(result);
|
|
2073
2073
|
if (result.violations.length > 0 && options.fix) {
|
|
2074
2074
|
const fixable = result.violations.filter((v) => v.autoFixable);
|
|
2075
2075
|
if (fixable.length > 0) {
|
|
2076
|
-
|
|
2076
|
+
spinner13.start(`Auto-fixing ${fixable.length} violations...`);
|
|
2077
2077
|
await autoFix(fixable);
|
|
2078
|
-
|
|
2078
|
+
spinner13.stop("Auto-fix complete");
|
|
2079
2079
|
}
|
|
2080
2080
|
}
|
|
2081
2081
|
const errors = result.violations.filter((v) => v.severity === "error");
|
|
@@ -2267,14 +2267,14 @@ async function codeCommand(prompt, options = {}) {
|
|
|
2267
2267
|
}
|
|
2268
2268
|
}
|
|
2269
2269
|
async function processUserInput(userInput, messages, anthropic, systemPrompt, projectContext, config) {
|
|
2270
|
-
const
|
|
2270
|
+
const spinner13 = p4.spinner();
|
|
2271
2271
|
messages.push({ role: "user", content: userInput });
|
|
2272
2272
|
const wizardResult = await checkForWizard(userInput);
|
|
2273
2273
|
if (wizardResult) {
|
|
2274
2274
|
messages[messages.length - 1].content = wizardResult;
|
|
2275
2275
|
}
|
|
2276
2276
|
try {
|
|
2277
|
-
|
|
2277
|
+
spinner13.start("Thinking...");
|
|
2278
2278
|
const response = await anthropic.messages.create({
|
|
2279
2279
|
model: "claude-sonnet-4-20250514",
|
|
2280
2280
|
max_tokens: 8192,
|
|
@@ -2284,7 +2284,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2284
2284
|
content: m.content
|
|
2285
2285
|
}))
|
|
2286
2286
|
});
|
|
2287
|
-
|
|
2287
|
+
spinner13.stop("");
|
|
2288
2288
|
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
|
|
2289
2289
|
messages.push({ role: "assistant", content: assistantMessage });
|
|
2290
2290
|
const actions = parseActions(assistantMessage);
|
|
@@ -2299,11 +2299,11 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2299
2299
|
initialValue: true
|
|
2300
2300
|
});
|
|
2301
2301
|
if (proceed && !p4.isCancel(proceed)) {
|
|
2302
|
-
|
|
2302
|
+
spinner13.start("Building...");
|
|
2303
2303
|
for (const action of actions) {
|
|
2304
|
-
await executeAction(action,
|
|
2304
|
+
await executeAction(action, spinner13);
|
|
2305
2305
|
}
|
|
2306
|
-
|
|
2306
|
+
spinner13.stop("Build complete");
|
|
2307
2307
|
console.log(chalk5.dim("\n\u{1F50D} Running CodeBakers check..."));
|
|
2308
2308
|
const checkResult = await runPatternCheck(false);
|
|
2309
2309
|
if (checkResult.violations.length > 0) {
|
|
@@ -2314,9 +2314,9 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2314
2314
|
initialValue: true
|
|
2315
2315
|
});
|
|
2316
2316
|
if (autoFix2 && !p4.isCancel(autoFix2)) {
|
|
2317
|
-
|
|
2317
|
+
spinner13.start("Auto-fixing...");
|
|
2318
2318
|
await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
|
|
2319
|
-
|
|
2319
|
+
spinner13.stop("Violations fixed");
|
|
2320
2320
|
}
|
|
2321
2321
|
} else {
|
|
2322
2322
|
console.log(chalk5.green("\u2713 All patterns satisfied"));
|
|
@@ -2327,7 +2327,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2327
2327
|
console.log("\n" + assistantMessage + "\n");
|
|
2328
2328
|
}
|
|
2329
2329
|
} catch (error) {
|
|
2330
|
-
|
|
2330
|
+
spinner13.stop("Error");
|
|
2331
2331
|
console.log(chalk5.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2332
2332
|
}
|
|
2333
2333
|
}
|
|
@@ -2439,14 +2439,14 @@ function parseActions(response) {
|
|
|
2439
2439
|
}
|
|
2440
2440
|
return actions;
|
|
2441
2441
|
}
|
|
2442
|
-
async function executeAction(action,
|
|
2442
|
+
async function executeAction(action, spinner13) {
|
|
2443
2443
|
const cwd = process.cwd();
|
|
2444
2444
|
switch (action.type) {
|
|
2445
2445
|
case "CREATE_FILE": {
|
|
2446
2446
|
const filePath = path5.join(cwd, action.path);
|
|
2447
2447
|
await fs5.ensureDir(path5.dirname(filePath));
|
|
2448
2448
|
await fs5.writeFile(filePath, action.content);
|
|
2449
|
-
|
|
2449
|
+
spinner13.message(`Created ${action.path}`);
|
|
2450
2450
|
break;
|
|
2451
2451
|
}
|
|
2452
2452
|
case "EDIT_FILE": {
|
|
@@ -2456,13 +2456,13 @@ async function executeAction(action, spinner10) {
|
|
|
2456
2456
|
if (action.find && content.includes(action.find)) {
|
|
2457
2457
|
content = content.replace(action.find, action.replace || "");
|
|
2458
2458
|
await fs5.writeFile(filePath, content);
|
|
2459
|
-
|
|
2459
|
+
spinner13.message(`Edited ${action.path}`);
|
|
2460
2460
|
}
|
|
2461
2461
|
}
|
|
2462
2462
|
break;
|
|
2463
2463
|
}
|
|
2464
2464
|
case "RUN_COMMAND": {
|
|
2465
|
-
|
|
2465
|
+
spinner13.message(`Running: ${action.command}`);
|
|
2466
2466
|
const [cmd, ...args2] = action.command.split(" ");
|
|
2467
2467
|
await execa3(cmd, args2, { cwd, stdio: "pipe" });
|
|
2468
2468
|
break;
|
|
@@ -2471,7 +2471,7 @@ async function executeAction(action, spinner10) {
|
|
|
2471
2471
|
const filePath = path5.join(cwd, action.path);
|
|
2472
2472
|
if (await fs5.pathExists(filePath)) {
|
|
2473
2473
|
await fs5.remove(filePath);
|
|
2474
|
-
|
|
2474
|
+
spinner13.message(`Deleted ${action.path}`);
|
|
2475
2475
|
}
|
|
2476
2476
|
break;
|
|
2477
2477
|
}
|
|
@@ -2719,12 +2719,12 @@ async function deployCommand(options = {}) {
|
|
|
2719
2719
|
return;
|
|
2720
2720
|
}
|
|
2721
2721
|
p5.intro(chalk6.bgCyan.black(" Deploy to Production "));
|
|
2722
|
-
const
|
|
2722
|
+
const spinner13 = p5.spinner();
|
|
2723
2723
|
if (options.check !== false) {
|
|
2724
|
-
|
|
2724
|
+
spinner13.start("Running CodeBakers check...");
|
|
2725
2725
|
const checkResult = await runPatternCheck(false);
|
|
2726
2726
|
if (!checkResult.passed) {
|
|
2727
|
-
|
|
2727
|
+
spinner13.stop("");
|
|
2728
2728
|
const errors = checkResult.violations.filter((v) => v.severity === "error");
|
|
2729
2729
|
if (errors.length > 0) {
|
|
2730
2730
|
p5.log.error(`${errors.length} pattern violations found. Fix before deploying.`);
|
|
@@ -2745,9 +2745,9 @@ async function deployCommand(options = {}) {
|
|
|
2745
2745
|
p5.outro(chalk6.red("Deploy cancelled."));
|
|
2746
2746
|
return;
|
|
2747
2747
|
}
|
|
2748
|
-
|
|
2748
|
+
spinner13.start("Auto-fixing with AI...");
|
|
2749
2749
|
await autoFixWithAI(config, errors);
|
|
2750
|
-
|
|
2750
|
+
spinner13.stop("Auto-fix complete");
|
|
2751
2751
|
const recheck = await runPatternCheck(false);
|
|
2752
2752
|
if (!recheck.passed) {
|
|
2753
2753
|
p5.log.error("Some violations remain. Manual fix required.");
|
|
@@ -2756,23 +2756,23 @@ async function deployCommand(options = {}) {
|
|
|
2756
2756
|
}
|
|
2757
2757
|
}
|
|
2758
2758
|
}
|
|
2759
|
-
|
|
2759
|
+
spinner13.stop("Pattern check passed");
|
|
2760
2760
|
}
|
|
2761
|
-
|
|
2761
|
+
spinner13.start("Running TypeScript check...");
|
|
2762
2762
|
try {
|
|
2763
2763
|
await execa4("npx", ["tsc", "--noEmit"], { cwd: process.cwd() });
|
|
2764
|
-
|
|
2764
|
+
spinner13.stop("TypeScript check passed");
|
|
2765
2765
|
} catch (error) {
|
|
2766
|
-
|
|
2766
|
+
spinner13.stop("");
|
|
2767
2767
|
p5.log.error("TypeScript errors found.");
|
|
2768
2768
|
const fix = await p5.confirm({
|
|
2769
2769
|
message: "Attempt to fix TypeScript errors?",
|
|
2770
2770
|
initialValue: true
|
|
2771
2771
|
});
|
|
2772
2772
|
if (fix && !p5.isCancel(fix)) {
|
|
2773
|
-
|
|
2773
|
+
spinner13.start("Fixing TypeScript errors...");
|
|
2774
2774
|
const fixed = await fixTypeScriptErrors(config);
|
|
2775
|
-
|
|
2775
|
+
spinner13.stop(fixed ? "TypeScript errors fixed" : "Could not auto-fix all errors");
|
|
2776
2776
|
if (!fixed) {
|
|
2777
2777
|
p5.outro(chalk6.red("Deploy cancelled."));
|
|
2778
2778
|
return;
|
|
@@ -2782,12 +2782,12 @@ async function deployCommand(options = {}) {
|
|
|
2782
2782
|
return;
|
|
2783
2783
|
}
|
|
2784
2784
|
}
|
|
2785
|
-
|
|
2785
|
+
spinner13.start("Building project...");
|
|
2786
2786
|
try {
|
|
2787
2787
|
await execa4("pnpm", ["build"], { cwd: process.cwd() });
|
|
2788
|
-
|
|
2788
|
+
spinner13.stop("Build successful");
|
|
2789
2789
|
} catch (error) {
|
|
2790
|
-
|
|
2790
|
+
spinner13.stop("");
|
|
2791
2791
|
p5.log.error("Build failed.");
|
|
2792
2792
|
const errorOutput = error instanceof Error ? error.message : "Unknown error";
|
|
2793
2793
|
console.log(chalk6.dim(errorOutput));
|
|
@@ -2796,16 +2796,16 @@ async function deployCommand(options = {}) {
|
|
|
2796
2796
|
initialValue: true
|
|
2797
2797
|
});
|
|
2798
2798
|
if (fix && !p5.isCancel(fix)) {
|
|
2799
|
-
|
|
2799
|
+
spinner13.start("Fixing build errors...");
|
|
2800
2800
|
const fixed = await fixBuildErrors(config, errorOutput);
|
|
2801
|
-
|
|
2801
|
+
spinner13.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
|
|
2802
2802
|
if (fixed) {
|
|
2803
|
-
|
|
2803
|
+
spinner13.start("Retrying build...");
|
|
2804
2804
|
try {
|
|
2805
2805
|
await execa4("pnpm", ["build"], { cwd: process.cwd() });
|
|
2806
|
-
|
|
2806
|
+
spinner13.stop("Build successful");
|
|
2807
2807
|
} catch {
|
|
2808
|
-
|
|
2808
|
+
spinner13.stop("Build still failing");
|
|
2809
2809
|
p5.outro(chalk6.red("Deploy cancelled."));
|
|
2810
2810
|
return;
|
|
2811
2811
|
}
|
|
@@ -2818,10 +2818,10 @@ async function deployCommand(options = {}) {
|
|
|
2818
2818
|
return;
|
|
2819
2819
|
}
|
|
2820
2820
|
}
|
|
2821
|
-
|
|
2821
|
+
spinner13.start("Checking for uncommitted changes...");
|
|
2822
2822
|
const { stdout: gitStatus } = await execa4("git", ["status", "--porcelain"], { cwd: process.cwd() });
|
|
2823
2823
|
if (gitStatus.trim()) {
|
|
2824
|
-
|
|
2824
|
+
spinner13.stop("");
|
|
2825
2825
|
const commit = await p5.confirm({
|
|
2826
2826
|
message: "You have uncommitted changes. Commit before deploying?",
|
|
2827
2827
|
initialValue: true
|
|
@@ -2835,20 +2835,20 @@ async function deployCommand(options = {}) {
|
|
|
2835
2835
|
if (!p5.isCancel(message)) {
|
|
2836
2836
|
await execa4("git", ["add", "."], { cwd: process.cwd() });
|
|
2837
2837
|
await execa4("git", ["commit", "-m", message], { cwd: process.cwd() });
|
|
2838
|
-
|
|
2838
|
+
spinner13.start("Pushing to GitHub...");
|
|
2839
2839
|
await execa4("git", ["push"], { cwd: process.cwd() });
|
|
2840
|
-
|
|
2840
|
+
spinner13.stop("Pushed to GitHub");
|
|
2841
2841
|
}
|
|
2842
2842
|
}
|
|
2843
2843
|
} else {
|
|
2844
|
-
|
|
2844
|
+
spinner13.stop("No uncommitted changes");
|
|
2845
2845
|
}
|
|
2846
2846
|
const deployType = options.preview ? "preview" : "production";
|
|
2847
|
-
|
|
2847
|
+
spinner13.start(`Deploying to ${deployType}...`);
|
|
2848
2848
|
try {
|
|
2849
2849
|
const vercel = new VercelService(config);
|
|
2850
2850
|
const deployment = await vercel.deploy(process.cwd(), !options.preview);
|
|
2851
|
-
|
|
2851
|
+
spinner13.stop("Deployment complete!");
|
|
2852
2852
|
console.log(boxedOutput(`
|
|
2853
2853
|
${chalk6.green("\u2713")} Deployed successfully!
|
|
2854
2854
|
|
|
@@ -2860,7 +2860,7 @@ ${chalk6.dim("View in Vercel Dashboard:")}
|
|
|
2860
2860
|
${chalk6.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
2861
2861
|
`));
|
|
2862
2862
|
} catch (error) {
|
|
2863
|
-
|
|
2863
|
+
spinner13.stop("");
|
|
2864
2864
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2865
2865
|
p5.log.error(`Deployment failed: ${errorMsg}`);
|
|
2866
2866
|
if (errorMsg.includes("Build failed")) {
|
|
@@ -2869,8 +2869,8 @@ ${chalk6.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
|
2869
2869
|
initialValue: true
|
|
2870
2870
|
});
|
|
2871
2871
|
if (retry && !p5.isCancel(retry)) {
|
|
2872
|
-
|
|
2873
|
-
|
|
2872
|
+
spinner13.start("Analyzing Vercel build error...");
|
|
2873
|
+
spinner13.stop("Fix attempted");
|
|
2874
2874
|
}
|
|
2875
2875
|
}
|
|
2876
2876
|
p5.outro(chalk6.red("Deploy failed."));
|
|
@@ -3236,10 +3236,10 @@ you'll need a Meta Business account.
|
|
|
3236
3236
|
initialValue: true
|
|
3237
3237
|
});
|
|
3238
3238
|
if (!proceed || p7.isCancel(proceed)) return;
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3239
|
+
const spinner13 = p7.spinner();
|
|
3240
|
+
spinner13.start("Generating QR code...");
|
|
3241
3241
|
try {
|
|
3242
|
-
|
|
3242
|
+
spinner13.stop("");
|
|
3243
3243
|
console.log(chalk8.cyan(`
|
|
3244
3244
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
3245
3245
|
\u2551 \u2551
|
|
@@ -3271,7 +3271,7 @@ Scan this QR code with WhatsApp:
|
|
|
3271
3271
|
p7.log.success("WhatsApp connected!");
|
|
3272
3272
|
}
|
|
3273
3273
|
} catch (error) {
|
|
3274
|
-
|
|
3274
|
+
spinner13.stop("Error connecting WhatsApp");
|
|
3275
3275
|
p7.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
3276
3276
|
}
|
|
3277
3277
|
}
|
|
@@ -3302,15 +3302,15 @@ To create a Telegram bot:
|
|
|
3302
3302
|
}
|
|
3303
3303
|
});
|
|
3304
3304
|
if (p7.isCancel(token)) return;
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3305
|
+
const spinner13 = p7.spinner();
|
|
3306
|
+
spinner13.start("Verifying bot token...");
|
|
3307
3307
|
try {
|
|
3308
3308
|
const response = await fetch(`https://api.telegram.org/bot${token}/getMe`);
|
|
3309
3309
|
const data = await response.json();
|
|
3310
3310
|
if (!data.ok) {
|
|
3311
3311
|
throw new Error(data.description || "Invalid token");
|
|
3312
3312
|
}
|
|
3313
|
-
|
|
3313
|
+
spinner13.stop("Bot verified!");
|
|
3314
3314
|
config.setChannelConfig("telegram", {
|
|
3315
3315
|
enabled: true,
|
|
3316
3316
|
botToken: token,
|
|
@@ -3318,7 +3318,7 @@ To create a Telegram bot:
|
|
|
3318
3318
|
});
|
|
3319
3319
|
p7.log.success(`Connected to @${data.result.username}`);
|
|
3320
3320
|
} catch (error) {
|
|
3321
|
-
|
|
3321
|
+
spinner13.stop("Verification failed");
|
|
3322
3322
|
p7.log.error(error instanceof Error ? error.message : "Invalid token");
|
|
3323
3323
|
}
|
|
3324
3324
|
}
|
|
@@ -3456,10 +3456,10 @@ This requires:
|
|
|
3456
3456
|
p7.log.info("iMessage support coming soon.");
|
|
3457
3457
|
}
|
|
3458
3458
|
async function startAllChannels(config) {
|
|
3459
|
-
const
|
|
3460
|
-
|
|
3459
|
+
const spinner13 = p7.spinner();
|
|
3460
|
+
spinner13.start("Starting channel gateway...");
|
|
3461
3461
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3462
|
-
|
|
3462
|
+
spinner13.stop("Gateway started");
|
|
3463
3463
|
console.log(chalk8.green(`
|
|
3464
3464
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
3465
3465
|
\u2551 Gateway is running! \u2551
|
|
@@ -3473,10 +3473,10 @@ async function startAllChannels(config) {
|
|
|
3473
3473
|
`));
|
|
3474
3474
|
}
|
|
3475
3475
|
async function stopAllChannels(config) {
|
|
3476
|
-
const
|
|
3477
|
-
|
|
3476
|
+
const spinner13 = p7.spinner();
|
|
3477
|
+
spinner13.start("Stopping gateway...");
|
|
3478
3478
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3479
|
-
|
|
3479
|
+
spinner13.stop("Gateway stopped");
|
|
3480
3480
|
}
|
|
3481
3481
|
async function deployGatewayWizard(config) {
|
|
3482
3482
|
p7.log.info(chalk8.bold("Deploy Gateway to Cloud"));
|
|
@@ -3591,10 +3591,10 @@ import glob2 from "fast-glob";
|
|
|
3591
3591
|
import * as path8 from "path";
|
|
3592
3592
|
async function securityCommand() {
|
|
3593
3593
|
p9.intro(chalk10.bgCyan.black(" Security Audit "));
|
|
3594
|
-
const
|
|
3595
|
-
|
|
3594
|
+
const spinner13 = p9.spinner();
|
|
3595
|
+
spinner13.start("Scanning for security issues...");
|
|
3596
3596
|
const issues = await runSecurityScan();
|
|
3597
|
-
|
|
3597
|
+
spinner13.stop("Scan complete");
|
|
3598
3598
|
if (issues.length === 0) {
|
|
3599
3599
|
console.log(chalk10.green("\n\u2713 No security issues found!\n"));
|
|
3600
3600
|
displaySecurityScore(100);
|
|
@@ -3688,10 +3688,10 @@ async function generateCommand(type) {
|
|
|
3688
3688
|
validate: (v) => !v ? "Name is required" : void 0
|
|
3689
3689
|
});
|
|
3690
3690
|
if (p10.isCancel(name)) return;
|
|
3691
|
-
const
|
|
3692
|
-
|
|
3691
|
+
const spinner13 = p10.spinner();
|
|
3692
|
+
spinner13.start("Generating...");
|
|
3693
3693
|
await generateFile(generateType, name);
|
|
3694
|
-
|
|
3694
|
+
spinner13.stop(`Generated ${name}`);
|
|
3695
3695
|
p10.outro("");
|
|
3696
3696
|
}
|
|
3697
3697
|
async function generateFile(type, name) {
|
|
@@ -3836,17 +3836,1150 @@ import * as p11 from "@clack/prompts";
|
|
|
3836
3836
|
import chalk12 from "chalk";
|
|
3837
3837
|
async function fixCommand() {
|
|
3838
3838
|
p11.intro(chalk12.bgCyan.black(" Auto-Fix "));
|
|
3839
|
-
const
|
|
3840
|
-
|
|
3839
|
+
const spinner13 = p11.spinner();
|
|
3840
|
+
spinner13.start("Analyzing code...");
|
|
3841
3841
|
const result = await runPatternCheck(true);
|
|
3842
3842
|
if (result.passed) {
|
|
3843
|
-
|
|
3843
|
+
spinner13.stop("No issues found!");
|
|
3844
3844
|
} else {
|
|
3845
|
-
|
|
3845
|
+
spinner13.stop(`Fixed ${result.violations.length} issues`);
|
|
3846
3846
|
}
|
|
3847
3847
|
p11.outro(chalk12.green("Done!"));
|
|
3848
3848
|
}
|
|
3849
3849
|
|
|
3850
|
+
// src/commands/design.ts
|
|
3851
|
+
import * as p12 from "@clack/prompts";
|
|
3852
|
+
import chalk13 from "chalk";
|
|
3853
|
+
import * as fs10 from "fs-extra";
|
|
3854
|
+
import * as path10 from "path";
|
|
3855
|
+
var DESIGN_PROFILES = {
|
|
3856
|
+
minimal: {
|
|
3857
|
+
name: "Minimal",
|
|
3858
|
+
inspiration: "Linear, Notion, Vercel",
|
|
3859
|
+
fonts: { heading: "Inter", body: "Inter" },
|
|
3860
|
+
corners: "rounded-md",
|
|
3861
|
+
shadows: "none",
|
|
3862
|
+
spacing: "generous"
|
|
3863
|
+
},
|
|
3864
|
+
bold: {
|
|
3865
|
+
name: "Bold",
|
|
3866
|
+
inspiration: "Stripe, Ramp, Mercury",
|
|
3867
|
+
fonts: { heading: "Plus Jakarta Sans", body: "Inter" },
|
|
3868
|
+
corners: "rounded-2xl",
|
|
3869
|
+
shadows: "elevated",
|
|
3870
|
+
spacing: "generous"
|
|
3871
|
+
},
|
|
3872
|
+
editorial: {
|
|
3873
|
+
name: "Editorial",
|
|
3874
|
+
inspiration: "Medium, Substack, NY Times",
|
|
3875
|
+
fonts: { heading: "Playfair Display", body: "Source Serif Pro" },
|
|
3876
|
+
corners: "rounded-none",
|
|
3877
|
+
shadows: "none",
|
|
3878
|
+
spacing: "reading"
|
|
3879
|
+
},
|
|
3880
|
+
playful: {
|
|
3881
|
+
name: "Playful",
|
|
3882
|
+
inspiration: "Figma, Slack, Notion",
|
|
3883
|
+
fonts: { heading: "Nunito", body: "Nunito" },
|
|
3884
|
+
corners: "rounded-full",
|
|
3885
|
+
shadows: "soft",
|
|
3886
|
+
spacing: "comfortable"
|
|
3887
|
+
},
|
|
3888
|
+
premium: {
|
|
3889
|
+
name: "Premium",
|
|
3890
|
+
inspiration: "Apple, Porsche, Amex",
|
|
3891
|
+
fonts: { heading: "Cormorant Garamond", body: "Lato" },
|
|
3892
|
+
corners: "rounded-lg",
|
|
3893
|
+
shadows: "subtle",
|
|
3894
|
+
spacing: "luxurious"
|
|
3895
|
+
},
|
|
3896
|
+
dashboard: {
|
|
3897
|
+
name: "Dashboard",
|
|
3898
|
+
inspiration: "Datadog, Grafana, Linear",
|
|
3899
|
+
fonts: { heading: "Inter", body: "Inter" },
|
|
3900
|
+
corners: "rounded-md",
|
|
3901
|
+
shadows: "card",
|
|
3902
|
+
spacing: "compact"
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
async function designCommand(subcommand) {
|
|
3906
|
+
const config = new Config();
|
|
3907
|
+
if (!config.isInProject()) {
|
|
3908
|
+
p12.log.error("Not in a CodeBakers project. Run `codebakers init` first.");
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
p12.intro(chalk13.bgCyan.black(" Design System "));
|
|
3912
|
+
const action = subcommand || await p12.select({
|
|
3913
|
+
message: "What do you want to do?",
|
|
3914
|
+
options: [
|
|
3915
|
+
{ value: "profile", label: "\u{1F3A8} Set design profile" },
|
|
3916
|
+
{ value: "palette", label: "\u{1F308} Generate color palette" },
|
|
3917
|
+
{ value: "check", label: "\u2705 Check design quality" },
|
|
3918
|
+
{ value: "view", label: "\u{1F440} View current settings" }
|
|
3919
|
+
]
|
|
3920
|
+
});
|
|
3921
|
+
if (p12.isCancel(action)) return;
|
|
3922
|
+
switch (action) {
|
|
3923
|
+
case "profile":
|
|
3924
|
+
await setProfile();
|
|
3925
|
+
break;
|
|
3926
|
+
case "palette":
|
|
3927
|
+
await generatePalette();
|
|
3928
|
+
break;
|
|
3929
|
+
case "check":
|
|
3930
|
+
await checkDesign();
|
|
3931
|
+
break;
|
|
3932
|
+
case "view":
|
|
3933
|
+
await viewSettings();
|
|
3934
|
+
break;
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
async function setProfile() {
|
|
3938
|
+
const profile = await p12.select({
|
|
3939
|
+
message: "Choose your design profile:",
|
|
3940
|
+
options: Object.entries(DESIGN_PROFILES).map(([key, value]) => ({
|
|
3941
|
+
value: key,
|
|
3942
|
+
label: `${value.name}`,
|
|
3943
|
+
hint: value.inspiration
|
|
3944
|
+
}))
|
|
3945
|
+
});
|
|
3946
|
+
if (p12.isCancel(profile)) return;
|
|
3947
|
+
const selected = DESIGN_PROFILES[profile];
|
|
3948
|
+
const configPath = path10.join(process.cwd(), ".codebakers", "design.json");
|
|
3949
|
+
await fs10.ensureDir(path10.dirname(configPath));
|
|
3950
|
+
await fs10.writeJson(configPath, {
|
|
3951
|
+
profile,
|
|
3952
|
+
...selected,
|
|
3953
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3954
|
+
}, { spaces: 2 });
|
|
3955
|
+
p12.log.success(`Design profile set to ${selected.name}`);
|
|
3956
|
+
console.log(chalk13.dim(`
|
|
3957
|
+
Fonts: ${selected.fonts.heading} / ${selected.fonts.body}
|
|
3958
|
+
Corners: ${selected.corners}
|
|
3959
|
+
Shadows: ${selected.shadows}
|
|
3960
|
+
Spacing: ${selected.spacing}
|
|
3961
|
+
`));
|
|
3962
|
+
}
|
|
3963
|
+
async function generatePalette() {
|
|
3964
|
+
const brandColor = await p12.text({
|
|
3965
|
+
message: "Enter your brand color (hex):",
|
|
3966
|
+
placeholder: "#6366F1",
|
|
3967
|
+
validate: (v) => {
|
|
3968
|
+
if (!v.match(/^#[0-9A-Fa-f]{6}$/)) return "Enter a valid hex color (#RRGGBB)";
|
|
3969
|
+
return void 0;
|
|
3970
|
+
}
|
|
3971
|
+
});
|
|
3972
|
+
if (p12.isCancel(brandColor)) return;
|
|
3973
|
+
const palette = generateColorPalette(brandColor);
|
|
3974
|
+
const configPath = path10.join(process.cwd(), ".codebakers", "design.json");
|
|
3975
|
+
let config = {};
|
|
3976
|
+
if (await fs10.pathExists(configPath)) {
|
|
3977
|
+
config = await fs10.readJson(configPath);
|
|
3978
|
+
}
|
|
3979
|
+
await fs10.writeJson(configPath, {
|
|
3980
|
+
...config,
|
|
3981
|
+
colors: palette,
|
|
3982
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3983
|
+
}, { spaces: 2 });
|
|
3984
|
+
p12.log.success("Color palette generated!");
|
|
3985
|
+
console.log(`
|
|
3986
|
+
${chalk13.bgHex(palette.brand[500]).black(" Brand ")} ${palette.brand[500]}
|
|
3987
|
+
${chalk13.bgHex(palette.brand[100]).black(" Light ")} ${palette.brand[100]}
|
|
3988
|
+
${chalk13.bgHex(palette.brand[900]).white(" Dark ")} ${palette.brand[900]}
|
|
3989
|
+
${chalk13.bgHex(palette.accent).black(" Accent ")} ${palette.accent}
|
|
3990
|
+
`);
|
|
3991
|
+
}
|
|
3992
|
+
function generateColorPalette(hex) {
|
|
3993
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
3994
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
3995
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
3996
|
+
const max = Math.max(r, g, b);
|
|
3997
|
+
const min = Math.min(r, g, b);
|
|
3998
|
+
let h = 0, s = 0, l = (max + min) / 2;
|
|
3999
|
+
if (max !== min) {
|
|
4000
|
+
const d = max - min;
|
|
4001
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
4002
|
+
switch (max) {
|
|
4003
|
+
case r:
|
|
4004
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
4005
|
+
break;
|
|
4006
|
+
case g:
|
|
4007
|
+
h = ((b - r) / d + 2) / 6;
|
|
4008
|
+
break;
|
|
4009
|
+
case b:
|
|
4010
|
+
h = ((r - g) / d + 4) / 6;
|
|
4011
|
+
break;
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
h = Math.round(h * 360);
|
|
4015
|
+
s = Math.round(s * 100);
|
|
4016
|
+
l = Math.round(l * 100);
|
|
4017
|
+
const hslToHex = (h2, s2, l2) => {
|
|
4018
|
+
l2 /= 100;
|
|
4019
|
+
const a = s2 * Math.min(l2, 1 - l2) / 100;
|
|
4020
|
+
const f = (n) => {
|
|
4021
|
+
const k = (n + h2 / 30) % 12;
|
|
4022
|
+
const color = l2 - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
4023
|
+
return Math.round(255 * color).toString(16).padStart(2, "0");
|
|
4024
|
+
};
|
|
4025
|
+
return `#${f(0)}${f(8)}${f(4)}`;
|
|
4026
|
+
};
|
|
4027
|
+
return {
|
|
4028
|
+
brand: {
|
|
4029
|
+
50: hslToHex(h, s * 0.3, 97),
|
|
4030
|
+
100: hslToHex(h, s * 0.4, 94),
|
|
4031
|
+
200: hslToHex(h, s * 0.5, 86),
|
|
4032
|
+
300: hslToHex(h, s * 0.6, 74),
|
|
4033
|
+
400: hslToHex(h, s * 0.8, 62),
|
|
4034
|
+
500: hex,
|
|
4035
|
+
// Original
|
|
4036
|
+
600: hslToHex(h, s, l * 0.85),
|
|
4037
|
+
700: hslToHex(h, s, l * 0.7),
|
|
4038
|
+
800: hslToHex(h, s, l * 0.55),
|
|
4039
|
+
900: hslToHex(h, s, l * 0.4)
|
|
4040
|
+
},
|
|
4041
|
+
accent: hslToHex((h + 180) % 360, s, l)
|
|
4042
|
+
// Complementary
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
async function checkDesign() {
|
|
4046
|
+
const spinner13 = p12.spinner();
|
|
4047
|
+
spinner13.start("Checking design quality...");
|
|
4048
|
+
const cwd = process.cwd();
|
|
4049
|
+
const issues = [];
|
|
4050
|
+
const glob3 = (await import("fast-glob")).default;
|
|
4051
|
+
const files = await glob3(["src/**/*.{tsx,jsx}"], { cwd });
|
|
4052
|
+
for (const file of files) {
|
|
4053
|
+
const content = await fs10.readFile(path10.join(cwd, file), "utf-8");
|
|
4054
|
+
if (content.includes("bg-gradient-to-r from-blue-500") || content.includes("bg-gradient-to-r from-purple-500")) {
|
|
4055
|
+
issues.push(`${file}: Generic gradient hero detected`);
|
|
4056
|
+
}
|
|
4057
|
+
const iconMatches = content.match(/<(Rocket|Shield|Zap|Star|Check|Lightning)/g);
|
|
4058
|
+
if (iconMatches && iconMatches.length > 3) {
|
|
4059
|
+
issues.push(`${file}: Too many generic icons (${iconMatches.length})`);
|
|
4060
|
+
}
|
|
4061
|
+
if (content.includes(">Get Started<") || content.includes(">Learn More<")) {
|
|
4062
|
+
issues.push(`${file}: Generic CTA text - be more specific`);
|
|
4063
|
+
}
|
|
4064
|
+
if (content.includes("No data") || content.includes("Nothing here")) {
|
|
4065
|
+
issues.push(`${file}: Lazy empty state - add helpful message`);
|
|
4066
|
+
}
|
|
4067
|
+
if (content.includes("py-4") || content.includes("py-6") || content.includes("py-8")) {
|
|
4068
|
+
if (content.includes("<section")) {
|
|
4069
|
+
issues.push(`${file}: Section padding too small - use py-16 or larger`);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
spinner13.stop("Check complete");
|
|
4074
|
+
if (issues.length === 0) {
|
|
4075
|
+
console.log(chalk13.green("\n\u2713 No design issues found!\n"));
|
|
4076
|
+
} else {
|
|
4077
|
+
console.log(chalk13.yellow(`
|
|
4078
|
+
\u26A0\uFE0F ${issues.length} design issues found:
|
|
4079
|
+
`));
|
|
4080
|
+
for (const issue of issues) {
|
|
4081
|
+
console.log(chalk13.dim(` \u2022 ${issue}`));
|
|
4082
|
+
}
|
|
4083
|
+
console.log("");
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
async function viewSettings() {
|
|
4087
|
+
const configPath = path10.join(process.cwd(), ".codebakers", "design.json");
|
|
4088
|
+
if (!await fs10.pathExists(configPath)) {
|
|
4089
|
+
p12.log.info("No design settings configured. Run `codebakers design profile` to set up.");
|
|
4090
|
+
return;
|
|
4091
|
+
}
|
|
4092
|
+
const config = await fs10.readJson(configPath);
|
|
4093
|
+
console.log(chalk13.bold("\nCurrent Design Settings:\n"));
|
|
4094
|
+
console.log(` Profile: ${config.name || config.profile}`);
|
|
4095
|
+
console.log(` Fonts: ${config.fonts?.heading} / ${config.fonts?.body}`);
|
|
4096
|
+
console.log(` Corners: ${config.corners}`);
|
|
4097
|
+
console.log(` Shadows: ${config.shadows}`);
|
|
4098
|
+
console.log(` Spacing: ${config.spacing}`);
|
|
4099
|
+
if (config.colors) {
|
|
4100
|
+
console.log(` Brand: ${config.colors.brand?.[500]}`);
|
|
4101
|
+
console.log(` Accent: ${config.colors.accent}`);
|
|
4102
|
+
}
|
|
4103
|
+
console.log("");
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
// src/commands/prd.ts
|
|
4107
|
+
import * as p13 from "@clack/prompts";
|
|
4108
|
+
import chalk14 from "chalk";
|
|
4109
|
+
import * as fs11 from "fs-extra";
|
|
4110
|
+
import * as path11 from "path";
|
|
4111
|
+
import Anthropic3 from "@anthropic-ai/sdk";
|
|
4112
|
+
async function prdCommand(filePath) {
|
|
4113
|
+
const config = new Config();
|
|
4114
|
+
if (!config.isConfigured()) {
|
|
4115
|
+
p13.log.error("Please run `codebakers setup` first.");
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
4118
|
+
p13.intro(chalk14.bgCyan.black(" Build from PRD "));
|
|
4119
|
+
let prdPath = filePath;
|
|
4120
|
+
if (!prdPath) {
|
|
4121
|
+
const file = await p13.text({
|
|
4122
|
+
message: "Path to PRD file:",
|
|
4123
|
+
placeholder: "./PRD.md or paste URL",
|
|
4124
|
+
validate: (v) => !v ? "File path required" : void 0
|
|
4125
|
+
});
|
|
4126
|
+
if (p13.isCancel(file)) return;
|
|
4127
|
+
prdPath = file;
|
|
4128
|
+
}
|
|
4129
|
+
const spinner13 = p13.spinner();
|
|
4130
|
+
spinner13.start("Reading PRD...");
|
|
4131
|
+
let prdContent;
|
|
4132
|
+
try {
|
|
4133
|
+
if (prdPath.startsWith("http")) {
|
|
4134
|
+
const response = await fetch(prdPath);
|
|
4135
|
+
prdContent = await response.text();
|
|
4136
|
+
} else {
|
|
4137
|
+
prdContent = await fs11.readFile(prdPath, "utf-8");
|
|
4138
|
+
}
|
|
4139
|
+
} catch (error) {
|
|
4140
|
+
spinner13.stop("Error");
|
|
4141
|
+
p13.log.error(`Could not read PRD: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
4144
|
+
spinner13.stop("PRD loaded");
|
|
4145
|
+
spinner13.start("Analyzing PRD...");
|
|
4146
|
+
const anthropicCreds = config.getCredentials("anthropic");
|
|
4147
|
+
if (!anthropicCreds?.apiKey) {
|
|
4148
|
+
spinner13.stop("Error");
|
|
4149
|
+
p13.log.error("Anthropic API key not configured.");
|
|
4150
|
+
return;
|
|
4151
|
+
}
|
|
4152
|
+
const anthropic = new Anthropic3({ apiKey: anthropicCreds.apiKey });
|
|
4153
|
+
const parsed = await parsePRD(anthropic, prdContent);
|
|
4154
|
+
spinner13.stop("PRD analyzed");
|
|
4155
|
+
console.log(chalk14.bold("\n\u{1F4CB} Extracted from PRD:\n"));
|
|
4156
|
+
console.log(` ${chalk14.cyan("Name:")} ${parsed.name}`);
|
|
4157
|
+
console.log(` ${chalk14.cyan("Description:")} ${parsed.description}`);
|
|
4158
|
+
console.log(` ${chalk14.cyan("Features:")} ${parsed.features.length} features`);
|
|
4159
|
+
console.log(` ${chalk14.cyan("Pages:")} ${parsed.pages.join(", ") || "Auto-detect"}`);
|
|
4160
|
+
console.log(` ${chalk14.cyan("Database:")} ${parsed.database.length} tables`);
|
|
4161
|
+
console.log(` ${chalk14.cyan("Integrations:")} ${parsed.integrations.join(", ") || "None"}`);
|
|
4162
|
+
console.log("");
|
|
4163
|
+
if (parsed.features.length > 0) {
|
|
4164
|
+
console.log(chalk14.bold("Features to build:"));
|
|
4165
|
+
parsed.features.slice(0, 10).forEach((f, i) => {
|
|
4166
|
+
console.log(chalk14.dim(` ${i + 1}. ${f}`));
|
|
4167
|
+
});
|
|
4168
|
+
if (parsed.features.length > 10) {
|
|
4169
|
+
console.log(chalk14.dim(` ... and ${parsed.features.length - 10} more`));
|
|
4170
|
+
}
|
|
4171
|
+
console.log("");
|
|
4172
|
+
}
|
|
4173
|
+
const proceed = await p13.confirm({
|
|
4174
|
+
message: "Build this project?",
|
|
4175
|
+
initialValue: true
|
|
4176
|
+
});
|
|
4177
|
+
if (!proceed || p13.isCancel(proceed)) {
|
|
4178
|
+
p13.cancel("Cancelled");
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
const options = await p13.group({
|
|
4182
|
+
createInfra: () => p13.confirm({
|
|
4183
|
+
message: "Create GitHub + Vercel + Supabase?",
|
|
4184
|
+
initialValue: true
|
|
4185
|
+
}),
|
|
4186
|
+
designProfile: () => p13.select({
|
|
4187
|
+
message: "Design profile:",
|
|
4188
|
+
options: [
|
|
4189
|
+
{ value: "minimal", label: "Minimal (Linear, Notion)" },
|
|
4190
|
+
{ value: "bold", label: "Bold (Stripe, Ramp)" },
|
|
4191
|
+
{ value: "editorial", label: "Editorial (Medium, Substack)" },
|
|
4192
|
+
{ value: "playful", label: "Playful (Figma, Slack)" },
|
|
4193
|
+
{ value: "premium", label: "Premium (Apple, Porsche)" },
|
|
4194
|
+
{ value: "dashboard", label: "Dashboard (Datadog, Linear)" }
|
|
4195
|
+
],
|
|
4196
|
+
initialValue: parsed.design.profile || "minimal"
|
|
4197
|
+
})
|
|
4198
|
+
});
|
|
4199
|
+
if (p13.isCancel(options)) return;
|
|
4200
|
+
await buildFromPRD(parsed, options, anthropic, config);
|
|
4201
|
+
}
|
|
4202
|
+
async function parsePRD(anthropic, content) {
|
|
4203
|
+
const response = await anthropic.messages.create({
|
|
4204
|
+
model: "claude-sonnet-4-20250514",
|
|
4205
|
+
max_tokens: 4096,
|
|
4206
|
+
messages: [{
|
|
4207
|
+
role: "user",
|
|
4208
|
+
content: `Analyze this PRD and extract structured information. Return JSON only, no explanation.
|
|
4209
|
+
|
|
4210
|
+
PRD:
|
|
4211
|
+
${content}
|
|
4212
|
+
|
|
4213
|
+
Return this exact JSON structure:
|
|
4214
|
+
{
|
|
4215
|
+
"name": "project name (lowercase, hyphenated)",
|
|
4216
|
+
"description": "one sentence description",
|
|
4217
|
+
"features": ["feature 1", "feature 2", ...],
|
|
4218
|
+
"pages": ["page1", "page2", ...],
|
|
4219
|
+
"database": ["table1", "table2", ...],
|
|
4220
|
+
"integrations": ["stripe", "supabase", ...],
|
|
4221
|
+
"design": {
|
|
4222
|
+
"profile": "minimal|bold|editorial|playful|premium|dashboard or null",
|
|
4223
|
+
"brandColor": "#hexcolor or null"
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
Extract ALL features mentioned. Include auth, payments, dashboards, etc.
|
|
4228
|
+
For pages, list actual pages/routes needed.
|
|
4229
|
+
For database, list tables/entities needed.
|
|
4230
|
+
For integrations, list third-party services mentioned.`
|
|
4231
|
+
}]
|
|
4232
|
+
});
|
|
4233
|
+
const text10 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
4234
|
+
const jsonMatch = text10.match(/\{[\s\S]*\}/);
|
|
4235
|
+
if (!jsonMatch) {
|
|
4236
|
+
throw new Error("Could not parse PRD");
|
|
4237
|
+
}
|
|
4238
|
+
return JSON.parse(jsonMatch[0]);
|
|
4239
|
+
}
|
|
4240
|
+
async function buildFromPRD(prd, options, anthropic, config) {
|
|
4241
|
+
const spinner13 = p13.spinner();
|
|
4242
|
+
const projectPath = path11.join(process.cwd(), prd.name);
|
|
4243
|
+
spinner13.start("Creating project structure...");
|
|
4244
|
+
await fs11.ensureDir(projectPath);
|
|
4245
|
+
await fs11.ensureDir(path11.join(projectPath, ".codebakers"));
|
|
4246
|
+
await fs11.ensureDir(path11.join(projectPath, "src", "app"));
|
|
4247
|
+
await fs11.ensureDir(path11.join(projectPath, "src", "components"));
|
|
4248
|
+
await fs11.ensureDir(path11.join(projectPath, "src", "lib"));
|
|
4249
|
+
spinner13.stop("Project structure created");
|
|
4250
|
+
spinner13.start("Saving PRD...");
|
|
4251
|
+
await fs11.writeFile(path11.join(projectPath, "PRD.md"), await fs11.readFile(process.cwd(), "utf-8").catch(() => JSON.stringify(prd, null, 2)));
|
|
4252
|
+
await fs11.writeJson(path11.join(projectPath, ".codebakers", "prd.json"), prd, { spaces: 2 });
|
|
4253
|
+
await fs11.writeJson(path11.join(projectPath, ".codebakers", "design.json"), {
|
|
4254
|
+
profile: options.designProfile,
|
|
4255
|
+
colors: prd.design.brandColor ? { brand: prd.design.brandColor } : void 0
|
|
4256
|
+
}, { spaces: 2 });
|
|
4257
|
+
spinner13.stop("PRD saved");
|
|
4258
|
+
spinner13.start("Generating build plan...");
|
|
4259
|
+
const buildPlan = await generateBuildPlan(anthropic, prd, options.designProfile);
|
|
4260
|
+
await fs11.writeJson(path11.join(projectPath, ".codebakers", "build-plan.json"), buildPlan, { spaces: 2 });
|
|
4261
|
+
spinner13.stop("Build plan generated");
|
|
4262
|
+
console.log(chalk14.bold("\n\u{1F3D7}\uFE0F Build Plan:\n"));
|
|
4263
|
+
buildPlan.phases.forEach((phase, i) => {
|
|
4264
|
+
console.log(chalk14.cyan(`Phase ${i + 1}: ${phase.name}`));
|
|
4265
|
+
phase.tasks.forEach((task) => {
|
|
4266
|
+
console.log(chalk14.dim(` \u2022 ${task}`));
|
|
4267
|
+
});
|
|
4268
|
+
});
|
|
4269
|
+
console.log("");
|
|
4270
|
+
const startBuild = await p13.confirm({
|
|
4271
|
+
message: "Start building?",
|
|
4272
|
+
initialValue: true
|
|
4273
|
+
});
|
|
4274
|
+
if (!startBuild || p13.isCancel(startBuild)) {
|
|
4275
|
+
p13.log.info(`Build plan saved to ${prd.name}/.codebakers/build-plan.json`);
|
|
4276
|
+
p13.log.info(`Run \`cd ${prd.name} && codebakers code\` to continue building.`);
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
for (let i = 0; i < buildPlan.phases.length; i++) {
|
|
4280
|
+
const phase = buildPlan.phases[i];
|
|
4281
|
+
console.log(chalk14.bold(`
|
|
4282
|
+
\u{1F4E6} Phase ${i + 1}: ${phase.name}
|
|
4283
|
+
`));
|
|
4284
|
+
for (const task of phase.tasks) {
|
|
4285
|
+
spinner13.start(task);
|
|
4286
|
+
try {
|
|
4287
|
+
await executeTask(anthropic, projectPath, task, prd, options.designProfile);
|
|
4288
|
+
spinner13.stop(`\u2713 ${task}`);
|
|
4289
|
+
} catch (error) {
|
|
4290
|
+
spinner13.stop(`\u2717 ${task}`);
|
|
4291
|
+
p13.log.error(error instanceof Error ? error.message : "Task failed");
|
|
4292
|
+
const continueBuilding = await p13.confirm({
|
|
4293
|
+
message: "Continue with next task?",
|
|
4294
|
+
initialValue: true
|
|
4295
|
+
});
|
|
4296
|
+
if (!continueBuilding || p13.isCancel(continueBuilding)) {
|
|
4297
|
+
p13.log.info("Build paused. Run `codebakers code` to continue.");
|
|
4298
|
+
return;
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
if (options.createInfra) {
|
|
4304
|
+
console.log(chalk14.bold("\n\u{1F680} Setting up infrastructure...\n"));
|
|
4305
|
+
spinner13.start("Creating GitHub repository...");
|
|
4306
|
+
spinner13.stop("GitHub repository created");
|
|
4307
|
+
spinner13.start("Creating Supabase project...");
|
|
4308
|
+
spinner13.stop("Supabase project created");
|
|
4309
|
+
spinner13.start("Creating Vercel project...");
|
|
4310
|
+
spinner13.stop("Vercel project created");
|
|
4311
|
+
spinner13.start("Deploying...");
|
|
4312
|
+
spinner13.stop("Deployed!");
|
|
4313
|
+
}
|
|
4314
|
+
p13.outro(chalk14.green(`
|
|
4315
|
+
\u2713 Project built from PRD!
|
|
4316
|
+
|
|
4317
|
+
${chalk14.bold("Your project:")}
|
|
4318
|
+
${chalk14.cyan(`cd ${prd.name}`)}
|
|
4319
|
+
${chalk14.cyan("npm run dev")}
|
|
4320
|
+
|
|
4321
|
+
${chalk14.bold("Continue building:")}
|
|
4322
|
+
${chalk14.cyan("codebakers code")} \u2014 AI agent
|
|
4323
|
+
${chalk14.cyan("codebakers check")} \u2014 Verify patterns
|
|
4324
|
+
${chalk14.cyan("codebakers deploy")} \u2014 Deploy changes
|
|
4325
|
+
`));
|
|
4326
|
+
}
|
|
4327
|
+
async function generateBuildPlan(anthropic, prd, designProfile) {
|
|
4328
|
+
const response = await anthropic.messages.create({
|
|
4329
|
+
model: "claude-sonnet-4-20250514",
|
|
4330
|
+
max_tokens: 4096,
|
|
4331
|
+
messages: [{
|
|
4332
|
+
role: "user",
|
|
4333
|
+
content: `Create a build plan for this project. Return JSON only.
|
|
4334
|
+
|
|
4335
|
+
Project: ${prd.name}
|
|
4336
|
+
Description: ${prd.description}
|
|
4337
|
+
Features: ${prd.features.join(", ")}
|
|
4338
|
+
Pages: ${prd.pages.join(", ")}
|
|
4339
|
+
Database tables: ${prd.database.join(", ")}
|
|
4340
|
+
Integrations: ${prd.integrations.join(", ")}
|
|
4341
|
+
Design: ${designProfile}
|
|
4342
|
+
|
|
4343
|
+
Return this structure:
|
|
4344
|
+
{
|
|
4345
|
+
"phases": [
|
|
4346
|
+
{
|
|
4347
|
+
"name": "Phase name",
|
|
4348
|
+
"tasks": ["task 1", "task 2", ...]
|
|
4349
|
+
}
|
|
4350
|
+
]
|
|
4351
|
+
}
|
|
4352
|
+
|
|
4353
|
+
Phases should be:
|
|
4354
|
+
1. Setup (package.json, config, base files)
|
|
4355
|
+
2. Database (schema, migrations, types)
|
|
4356
|
+
3. Auth (if needed)
|
|
4357
|
+
4. Core Features (main functionality)
|
|
4358
|
+
5. UI/Pages (frontend)
|
|
4359
|
+
6. Integrations (third-party services)
|
|
4360
|
+
7. Polish (loading states, error handling, empty states)
|
|
4361
|
+
|
|
4362
|
+
Keep tasks specific and actionable.`
|
|
4363
|
+
}]
|
|
4364
|
+
});
|
|
4365
|
+
const text10 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
4366
|
+
const jsonMatch = text10.match(/\{[\s\S]*\}/);
|
|
4367
|
+
if (!jsonMatch) {
|
|
4368
|
+
throw new Error("Could not generate build plan");
|
|
4369
|
+
}
|
|
4370
|
+
return JSON.parse(jsonMatch[0]);
|
|
4371
|
+
}
|
|
4372
|
+
async function executeTask(anthropic, projectPath, task, prd, designProfile) {
|
|
4373
|
+
const response = await anthropic.messages.create({
|
|
4374
|
+
model: "claude-sonnet-4-20250514",
|
|
4375
|
+
max_tokens: 8192,
|
|
4376
|
+
messages: [{
|
|
4377
|
+
role: "user",
|
|
4378
|
+
content: `Execute this task for the project.
|
|
4379
|
+
|
|
4380
|
+
Project: ${prd.name}
|
|
4381
|
+
Task: ${task}
|
|
4382
|
+
Design Profile: ${designProfile}
|
|
4383
|
+
|
|
4384
|
+
Context:
|
|
4385
|
+
- Features: ${prd.features.join(", ")}
|
|
4386
|
+
- Database: ${prd.database.join(", ")}
|
|
4387
|
+
|
|
4388
|
+
Output files in this format:
|
|
4389
|
+
|
|
4390
|
+
<<<FILE: path/to/file.ts>>>
|
|
4391
|
+
file content here
|
|
4392
|
+
<<<END_FILE>>>
|
|
4393
|
+
|
|
4394
|
+
<<<FILE: another/file.tsx>>>
|
|
4395
|
+
file content here
|
|
4396
|
+
<<<END_FILE>>>
|
|
4397
|
+
|
|
4398
|
+
Follow these rules:
|
|
4399
|
+
- Use TypeScript
|
|
4400
|
+
- Use Next.js App Router
|
|
4401
|
+
- Use Tailwind CSS
|
|
4402
|
+
- Use shadcn/ui components
|
|
4403
|
+
- Every button needs onClick handler
|
|
4404
|
+
- Every form needs Zod validation
|
|
4405
|
+
- Every async operation needs loading/error states
|
|
4406
|
+
- Every list needs empty state
|
|
4407
|
+
- No generic gradient heroes
|
|
4408
|
+
- No icon spam
|
|
4409
|
+
- Generous spacing (py-16 or larger for sections)
|
|
4410
|
+
|
|
4411
|
+
Generate ALL files needed for this task.`
|
|
4412
|
+
}]
|
|
4413
|
+
});
|
|
4414
|
+
const text10 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
4415
|
+
const fileRegex = /<<<FILE:\s*(.+?)>>>([\s\S]*?)<<<END_FILE>>>/g;
|
|
4416
|
+
let match;
|
|
4417
|
+
while ((match = fileRegex.exec(text10)) !== null) {
|
|
4418
|
+
const filePath = path11.join(projectPath, match[1].trim());
|
|
4419
|
+
const content = match[2].trim();
|
|
4420
|
+
await fs11.ensureDir(path11.dirname(filePath));
|
|
4421
|
+
await fs11.writeFile(filePath, content);
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
// src/commands/advisors.ts
|
|
4426
|
+
import * as p14 from "@clack/prompts";
|
|
4427
|
+
import chalk15 from "chalk";
|
|
4428
|
+
import * as fs12 from "fs-extra";
|
|
4429
|
+
import * as path12 from "path";
|
|
4430
|
+
import Anthropic4 from "@anthropic-ai/sdk";
|
|
4431
|
+
var DREAM_TEAM = [
|
|
4432
|
+
{
|
|
4433
|
+
name: "Sarah Chen",
|
|
4434
|
+
role: "CEO & Strategist",
|
|
4435
|
+
icon: "\u{1F469}\u200D\u{1F4BC}",
|
|
4436
|
+
expertise: "Business strategy, market positioning, fundraising, go-to-market",
|
|
4437
|
+
personality: "Direct, strategic thinker, asks hard questions about market fit and business viability"
|
|
4438
|
+
},
|
|
4439
|
+
{
|
|
4440
|
+
name: "Marcus Johnson",
|
|
4441
|
+
role: "Full Stack Engineer",
|
|
4442
|
+
icon: "\u{1F468}\u200D\u{1F4BB}",
|
|
4443
|
+
expertise: "System architecture, scalability, tech stack selection, development timeline",
|
|
4444
|
+
personality: "Pragmatic, focused on what can actually be built, warns about technical debt"
|
|
4445
|
+
},
|
|
4446
|
+
{
|
|
4447
|
+
name: "Elena Rodriguez",
|
|
4448
|
+
role: "UX/UI Specialist",
|
|
4449
|
+
icon: "\u{1F469}\u200D\u{1F3A8}",
|
|
4450
|
+
expertise: "User research, interface design, user journeys, accessibility, design systems",
|
|
4451
|
+
personality: "User-obsessed, pushes back on features that hurt UX, advocates for simplicity"
|
|
4452
|
+
},
|
|
4453
|
+
{
|
|
4454
|
+
name: "David Park",
|
|
4455
|
+
role: "Marketing Director",
|
|
4456
|
+
icon: "\u{1F4E3}",
|
|
4457
|
+
expertise: "Growth strategy, content marketing, social media, brand positioning, launch campaigns",
|
|
4458
|
+
personality: "Creative, data-driven, thinks about virality and word-of-mouth"
|
|
4459
|
+
},
|
|
4460
|
+
{
|
|
4461
|
+
name: "Aisha Patel",
|
|
4462
|
+
role: "Product Manager",
|
|
4463
|
+
icon: "\u{1F4CB}",
|
|
4464
|
+
expertise: "Feature prioritization, roadmaps, user stories, MVP scoping, competitive analysis",
|
|
4465
|
+
personality: "Organized, balances user needs with business goals, ruthless prioritizer"
|
|
4466
|
+
}
|
|
4467
|
+
];
|
|
4468
|
+
async function advisorsCommand() {
|
|
4469
|
+
const config = new Config();
|
|
4470
|
+
if (!config.isConfigured()) {
|
|
4471
|
+
p14.log.error("Please run `codebakers setup` first.");
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4474
|
+
const anthropicCreds = config.getCredentials("anthropic");
|
|
4475
|
+
if (!anthropicCreds?.apiKey) {
|
|
4476
|
+
p14.log.error("Anthropic API key not configured.");
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
console.log(chalk15.cyan(`
|
|
4480
|
+
\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E
|
|
4481
|
+
\u2502 \u2502
|
|
4482
|
+
\u2502 \u{1F31F} CODEBAKERS DREAM TEAM ADVISORS \u{1F31F} \u2502
|
|
4483
|
+
\u2502 \u2502
|
|
4484
|
+
\u2502 Meet your advisory board: \u2502
|
|
4485
|
+
\u2502 \u2502
|
|
4486
|
+
\u2502 \u{1F469}\u200D\u{1F4BC} Sarah Chen - CEO & Strategist \u2502
|
|
4487
|
+
\u2502 \u{1F468}\u200D\u{1F4BB} Marcus Johnson - Full Stack Engineer \u2502
|
|
4488
|
+
\u2502 \u{1F469}\u200D\u{1F3A8} Elena Rodriguez - UX/UI Specialist \u2502
|
|
4489
|
+
\u2502 \u{1F4E3} David Park - Marketing Director \u2502
|
|
4490
|
+
\u2502 \u{1F4CB} Aisha Patel - Product Manager \u2502
|
|
4491
|
+
\u2502 \u2502
|
|
4492
|
+
\u2502 They'll interview you about your project, provide expert \u2502
|
|
4493
|
+
\u2502 feedback, and create a comprehensive plan including: \u2502
|
|
4494
|
+
\u2502 \u2502
|
|
4495
|
+
\u2502 \u2022 Technical architecture & implementation plan \u2502
|
|
4496
|
+
\u2502 \u2022 Marketing & social media strategy \u2502
|
|
4497
|
+
\u2502 \u2022 UX/UI recommendations & user journeys \u2502
|
|
4498
|
+
\u2502 \u2022 Business model & revenue projections \u2502
|
|
4499
|
+
\u2502 \u2022 Risk assessment & mitigation strategies \u2502
|
|
4500
|
+
\u2502 \u2022 Prioritized action items \u2502
|
|
4501
|
+
\u2502 \u2502
|
|
4502
|
+
\u2502 \u{1F4C4} You'll receive a complete PDF report at the end. \u2502
|
|
4503
|
+
\u2502 \u2502
|
|
4504
|
+
\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F
|
|
4505
|
+
`));
|
|
4506
|
+
const start = await p14.confirm({
|
|
4507
|
+
message: "Ready to meet the Dream Team?",
|
|
4508
|
+
initialValue: true
|
|
4509
|
+
});
|
|
4510
|
+
if (!start || p14.isCancel(start)) {
|
|
4511
|
+
p14.cancel("Maybe next time!");
|
|
4512
|
+
return;
|
|
4513
|
+
}
|
|
4514
|
+
const brief = await conductInterview();
|
|
4515
|
+
if (!brief) return;
|
|
4516
|
+
const anthropic = new Anthropic4({ apiKey: anthropicCreds.apiKey });
|
|
4517
|
+
console.log(chalk15.bold("\n\n\u{1F3AF} The Dream Team is reviewing your project...\n"));
|
|
4518
|
+
const advisorFeedback = [];
|
|
4519
|
+
for (const advisor of DREAM_TEAM) {
|
|
4520
|
+
const spinner14 = p14.spinner();
|
|
4521
|
+
spinner14.start(`${advisor.icon} ${advisor.name} is analyzing...`);
|
|
4522
|
+
const feedback = await getAdvisorFeedback(anthropic, advisor, brief);
|
|
4523
|
+
advisorFeedback.push(feedback);
|
|
4524
|
+
spinner14.stop(`${advisor.icon} ${advisor.name} complete`);
|
|
4525
|
+
console.log(chalk15.dim(` Score: ${feedback.score}/10 - "${feedback.feedback.slice(0, 80)}..."
|
|
4526
|
+
`));
|
|
4527
|
+
}
|
|
4528
|
+
const spinner13 = p14.spinner();
|
|
4529
|
+
spinner13.start("Generating comprehensive report...");
|
|
4530
|
+
const report = await generateReport(anthropic, brief, advisorFeedback);
|
|
4531
|
+
spinner13.stop("Report generated");
|
|
4532
|
+
displayReportSummary(report);
|
|
4533
|
+
const savePath = await saveReport(report, brief.name);
|
|
4534
|
+
p14.outro(chalk15.green(`
|
|
4535
|
+
\u2713 Dream Team consultation complete!
|
|
4536
|
+
|
|
4537
|
+
\u{1F4C4} Full report saved to: ${savePath}
|
|
4538
|
+
|
|
4539
|
+
${chalk15.bold("Next steps:")}
|
|
4540
|
+
${chalk15.cyan(`codebakers prd ${savePath.replace(".pdf", ".md")}`)} - Build from this plan
|
|
4541
|
+
${chalk15.cyan("codebakers init")} - Start with standard setup
|
|
4542
|
+
`));
|
|
4543
|
+
}
|
|
4544
|
+
async function conductInterview() {
|
|
4545
|
+
p14.intro(chalk15.bgMagenta.white(" Project Interview "));
|
|
4546
|
+
console.log(chalk15.dim("\nLet's learn about your project. Answer as thoroughly as you can.\n"));
|
|
4547
|
+
const name = await p14.text({
|
|
4548
|
+
message: "\u{1F469}\u200D\u{1F4BC} Sarah: What's your project called?",
|
|
4549
|
+
placeholder: "My App Name",
|
|
4550
|
+
validate: (v) => !v ? "Project name is required" : void 0
|
|
4551
|
+
});
|
|
4552
|
+
if (p14.isCancel(name)) return null;
|
|
4553
|
+
const idea = await p14.text({
|
|
4554
|
+
message: "\u{1F469}\u200D\u{1F4BC} Sarah: Describe your idea in 2-3 sentences. What does it do?",
|
|
4555
|
+
placeholder: "A platform that helps...",
|
|
4556
|
+
validate: (v) => !v ? "Please describe your idea" : void 0
|
|
4557
|
+
});
|
|
4558
|
+
if (p14.isCancel(idea)) return null;
|
|
4559
|
+
const problem = await p14.text({
|
|
4560
|
+
message: "\u{1F4CB} Aisha: What problem are you solving? Why does this need to exist?",
|
|
4561
|
+
placeholder: "Currently, people struggle with...",
|
|
4562
|
+
validate: (v) => !v ? "Understanding the problem is crucial" : void 0
|
|
4563
|
+
});
|
|
4564
|
+
if (p14.isCancel(problem)) return null;
|
|
4565
|
+
const targetAudience = await p14.text({
|
|
4566
|
+
message: "\u{1F4E3} David: Who is this for? Describe your ideal user.",
|
|
4567
|
+
placeholder: "Small business owners who...",
|
|
4568
|
+
validate: (v) => !v ? "We need to know your target audience" : void 0
|
|
4569
|
+
});
|
|
4570
|
+
if (p14.isCancel(targetAudience)) return null;
|
|
4571
|
+
const solution = await p14.text({
|
|
4572
|
+
message: "\u{1F469}\u200D\u{1F3A8} Elena: How does your product solve the problem? What's the core experience?",
|
|
4573
|
+
placeholder: "Users can easily...",
|
|
4574
|
+
validate: (v) => !v ? "Please describe your solution" : void 0
|
|
4575
|
+
});
|
|
4576
|
+
if (p14.isCancel(solution)) return null;
|
|
4577
|
+
const competitors = await p14.text({
|
|
4578
|
+
message: "\u{1F469}\u200D\u{1F4BC} Sarah: Who are your competitors? How are you different?",
|
|
4579
|
+
placeholder: "Competitor X does Y, but we..."
|
|
4580
|
+
});
|
|
4581
|
+
if (p14.isCancel(competitors)) return null;
|
|
4582
|
+
const monetization = await p14.select({
|
|
4583
|
+
message: "\u{1F469}\u200D\u{1F4BC} Sarah: How will you make money?",
|
|
4584
|
+
options: [
|
|
4585
|
+
{ value: "subscription", label: "Subscription (monthly/yearly)" },
|
|
4586
|
+
{ value: "freemium", label: "Freemium (free tier + paid)" },
|
|
4587
|
+
{ value: "one-time", label: "One-time purchase" },
|
|
4588
|
+
{ value: "marketplace", label: "Marketplace (take a cut)" },
|
|
4589
|
+
{ value: "ads", label: "Advertising" },
|
|
4590
|
+
{ value: "enterprise", label: "Enterprise sales" },
|
|
4591
|
+
{ value: "unsure", label: "Not sure yet" }
|
|
4592
|
+
]
|
|
4593
|
+
});
|
|
4594
|
+
if (p14.isCancel(monetization)) return null;
|
|
4595
|
+
const timeline = await p14.select({
|
|
4596
|
+
message: "\u{1F468}\u200D\u{1F4BB} Marcus: What's your timeline for launching?",
|
|
4597
|
+
options: [
|
|
4598
|
+
{ value: "2-weeks", label: "2 weeks (MVP)" },
|
|
4599
|
+
{ value: "1-month", label: "1 month" },
|
|
4600
|
+
{ value: "3-months", label: "3 months" },
|
|
4601
|
+
{ value: "6-months", label: "6 months" },
|
|
4602
|
+
{ value: "flexible", label: "Flexible / No rush" }
|
|
4603
|
+
]
|
|
4604
|
+
});
|
|
4605
|
+
if (p14.isCancel(timeline)) return null;
|
|
4606
|
+
const budget = await p14.select({
|
|
4607
|
+
message: "\u{1F469}\u200D\u{1F4BC} Sarah: What's your budget for this project?",
|
|
4608
|
+
options: [
|
|
4609
|
+
{ value: "bootstrap", label: "Bootstrap ($0 - using free tiers)" },
|
|
4610
|
+
{ value: "small", label: "Small ($100-500/month)" },
|
|
4611
|
+
{ value: "medium", label: "Medium ($500-2000/month)" },
|
|
4612
|
+
{ value: "funded", label: "Funded ($2000+/month)" }
|
|
4613
|
+
]
|
|
4614
|
+
});
|
|
4615
|
+
if (p14.isCancel(budget)) return null;
|
|
4616
|
+
const techPreferences = await p14.text({
|
|
4617
|
+
message: '\u{1F468}\u200D\u{1F4BB} Marcus: Any tech preferences or requirements? (or "none")',
|
|
4618
|
+
placeholder: "Must use React, need mobile app, etc.",
|
|
4619
|
+
initialValue: "none"
|
|
4620
|
+
});
|
|
4621
|
+
if (p14.isCancel(techPreferences)) return null;
|
|
4622
|
+
const additional = await p14.text({
|
|
4623
|
+
message: "\u{1F4CB} Aisha: Anything else we should know?",
|
|
4624
|
+
placeholder: "Optional: special requirements, existing users, etc."
|
|
4625
|
+
});
|
|
4626
|
+
if (p14.isCancel(additional)) return null;
|
|
4627
|
+
return {
|
|
4628
|
+
name,
|
|
4629
|
+
idea,
|
|
4630
|
+
targetAudience,
|
|
4631
|
+
problem,
|
|
4632
|
+
solution,
|
|
4633
|
+
competitors: competitors || "Not specified",
|
|
4634
|
+
monetization,
|
|
4635
|
+
timeline,
|
|
4636
|
+
budget,
|
|
4637
|
+
techPreferences: techPreferences || "none"
|
|
4638
|
+
};
|
|
4639
|
+
}
|
|
4640
|
+
async function getAdvisorFeedback(anthropic, advisor, brief) {
|
|
4641
|
+
const response = await anthropic.messages.create({
|
|
4642
|
+
model: "claude-sonnet-4-20250514",
|
|
4643
|
+
max_tokens: 2048,
|
|
4644
|
+
messages: [{
|
|
4645
|
+
role: "user",
|
|
4646
|
+
content: `You are ${advisor.name}, ${advisor.role} on a startup advisory board.
|
|
4647
|
+
|
|
4648
|
+
Your expertise: ${advisor.expertise}
|
|
4649
|
+
Your personality: ${advisor.personality}
|
|
4650
|
+
|
|
4651
|
+
Review this project brief and provide your expert feedback:
|
|
4652
|
+
|
|
4653
|
+
Project: ${brief.name}
|
|
4654
|
+
Idea: ${brief.idea}
|
|
4655
|
+
Problem: ${brief.problem}
|
|
4656
|
+
Target Audience: ${brief.targetAudience}
|
|
4657
|
+
Solution: ${brief.solution}
|
|
4658
|
+
Competitors: ${brief.competitors}
|
|
4659
|
+
Monetization: ${brief.monetization}
|
|
4660
|
+
Timeline: ${brief.timeline}
|
|
4661
|
+
Budget: ${brief.budget}
|
|
4662
|
+
Tech Preferences: ${brief.techPreferences}
|
|
4663
|
+
|
|
4664
|
+
Respond with JSON only:
|
|
4665
|
+
{
|
|
4666
|
+
"feedback": "2-3 sentence overall assessment in your voice/personality",
|
|
4667
|
+
"recommendations": ["specific recommendation 1", "recommendation 2", "recommendation 3"],
|
|
4668
|
+
"questions": ["question you'd want answered", "another question"],
|
|
4669
|
+
"risks": ["risk 1", "risk 2"],
|
|
4670
|
+
"score": 7
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
Score 1-10 based on your area of expertise. Be honest but constructive.`
|
|
4674
|
+
}]
|
|
4675
|
+
});
|
|
4676
|
+
const text10 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
4677
|
+
const jsonMatch = text10.match(/\{[\s\S]*\}/);
|
|
4678
|
+
if (!jsonMatch) {
|
|
4679
|
+
return {
|
|
4680
|
+
advisor: advisor.name,
|
|
4681
|
+
role: advisor.role,
|
|
4682
|
+
icon: advisor.icon,
|
|
4683
|
+
feedback: "Unable to generate feedback",
|
|
4684
|
+
recommendations: [],
|
|
4685
|
+
questions: [],
|
|
4686
|
+
risks: [],
|
|
4687
|
+
score: 5
|
|
4688
|
+
};
|
|
4689
|
+
}
|
|
4690
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4691
|
+
return {
|
|
4692
|
+
advisor: advisor.name,
|
|
4693
|
+
role: advisor.role,
|
|
4694
|
+
icon: advisor.icon,
|
|
4695
|
+
...parsed
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
async function generateReport(anthropic, brief, advisorFeedback) {
|
|
4699
|
+
const avgScore = advisorFeedback.reduce((sum, f) => sum + f.score, 0) / advisorFeedback.length;
|
|
4700
|
+
const response = await anthropic.messages.create({
|
|
4701
|
+
model: "claude-sonnet-4-20250514",
|
|
4702
|
+
max_tokens: 8192,
|
|
4703
|
+
messages: [{
|
|
4704
|
+
role: "user",
|
|
4705
|
+
content: `Generate a comprehensive startup plan based on this project brief and advisor feedback.
|
|
4706
|
+
|
|
4707
|
+
PROJECT BRIEF:
|
|
4708
|
+
${JSON.stringify(brief, null, 2)}
|
|
4709
|
+
|
|
4710
|
+
ADVISOR FEEDBACK:
|
|
4711
|
+
${JSON.stringify(advisorFeedback, null, 2)}
|
|
4712
|
+
|
|
4713
|
+
Generate a complete JSON report:
|
|
4714
|
+
{
|
|
4715
|
+
"executiveSummary": "3-4 paragraph executive summary",
|
|
4716
|
+
"goNoGo": "GO" or "CAUTION" or "PIVOT",
|
|
4717
|
+
"marketingPlan": {
|
|
4718
|
+
"positioning": "positioning statement",
|
|
4719
|
+
"targetSegments": ["segment 1", "segment 2"],
|
|
4720
|
+
"channels": ["channel 1", "channel 2"],
|
|
4721
|
+
"launchStrategy": "detailed launch strategy",
|
|
4722
|
+
"contentPlan": ["content idea 1", "content idea 2"],
|
|
4723
|
+
"socialMediaPlan": {
|
|
4724
|
+
"platforms": ["platform 1", "platform 2"],
|
|
4725
|
+
"postFrequency": "frequency",
|
|
4726
|
+
"contentTypes": ["type 1", "type 2"]
|
|
4727
|
+
},
|
|
4728
|
+
"budget": "recommended budget breakdown"
|
|
4729
|
+
},
|
|
4730
|
+
"technicalPlan": {
|
|
4731
|
+
"architecture": "high-level architecture description",
|
|
4732
|
+
"stack": ["Next.js", "Supabase", "etc"],
|
|
4733
|
+
"phases": [
|
|
4734
|
+
{"name": "Phase 1: MVP", "duration": "2 weeks", "deliverables": ["feature 1", "feature 2"]}
|
|
4735
|
+
],
|
|
4736
|
+
"mvpFeatures": ["feature 1", "feature 2"],
|
|
4737
|
+
"futureFeatures": ["feature 1", "feature 2"]
|
|
4738
|
+
},
|
|
4739
|
+
"uxPlan": {
|
|
4740
|
+
"userPersonas": [{"name": "Persona Name", "description": "desc", "goals": ["goal 1"]}],
|
|
4741
|
+
"userJourneys": ["journey 1", "journey 2"],
|
|
4742
|
+
"keyScreens": ["screen 1", "screen 2"],
|
|
4743
|
+
"designPrinciples": ["principle 1", "principle 2"]
|
|
4744
|
+
},
|
|
4745
|
+
"businessPlan": {
|
|
4746
|
+
"revenueModel": "detailed revenue model",
|
|
4747
|
+
"pricing": "pricing strategy",
|
|
4748
|
+
"projections": [{"month": 3, "users": 100, "revenue": 500}],
|
|
4749
|
+
"kpis": ["kpi 1", "kpi 2"],
|
|
4750
|
+
"risks": [{"risk": "risk description", "mitigation": "how to mitigate"}]
|
|
4751
|
+
},
|
|
4752
|
+
"actionItems": [
|
|
4753
|
+
{"priority": "HIGH", "task": "task description", "owner": "CEO/Engineer/etc"}
|
|
4754
|
+
]
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
Be specific, actionable, and realistic based on the timeline and budget.`
|
|
4758
|
+
}]
|
|
4759
|
+
});
|
|
4760
|
+
const text10 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
4761
|
+
const jsonMatch = text10.match(/\{[\s\S]*\}/);
|
|
4762
|
+
const parsed = jsonMatch ? JSON.parse(jsonMatch[0]) : {};
|
|
4763
|
+
return {
|
|
4764
|
+
projectName: brief.name,
|
|
4765
|
+
brief,
|
|
4766
|
+
advisorFeedback,
|
|
4767
|
+
overallScore: Math.round(avgScore * 10) / 10,
|
|
4768
|
+
goNoGo: parsed.goNoGo || (avgScore >= 7 ? "GO" : avgScore >= 5 ? "CAUTION" : "PIVOT"),
|
|
4769
|
+
executiveSummary: parsed.executiveSummary || "",
|
|
4770
|
+
marketingPlan: parsed.marketingPlan || {},
|
|
4771
|
+
technicalPlan: parsed.technicalPlan || {},
|
|
4772
|
+
uxPlan: parsed.uxPlan || {},
|
|
4773
|
+
businessPlan: parsed.businessPlan || {},
|
|
4774
|
+
actionItems: parsed.actionItems || []
|
|
4775
|
+
};
|
|
4776
|
+
}
|
|
4777
|
+
function displayReportSummary(report) {
|
|
4778
|
+
const goNoGoColor = {
|
|
4779
|
+
"GO": chalk15.green,
|
|
4780
|
+
"CAUTION": chalk15.yellow,
|
|
4781
|
+
"PIVOT": chalk15.red
|
|
4782
|
+
}[report.goNoGo];
|
|
4783
|
+
console.log(chalk15.bold(`
|
|
4784
|
+
${"\u2550".repeat(60)}`));
|
|
4785
|
+
console.log(chalk15.bold.cyan(`
|
|
4786
|
+
\u{1F4CA} DREAM TEAM REPORT: ${report.projectName.toUpperCase()}
|
|
4787
|
+
`));
|
|
4788
|
+
console.log(chalk15.bold(`${"\u2550".repeat(60)}
|
|
4789
|
+
`));
|
|
4790
|
+
console.log(chalk15.bold("Overall Assessment:"));
|
|
4791
|
+
console.log(` Score: ${chalk15.bold(report.overallScore.toString())}/10`);
|
|
4792
|
+
console.log(` Verdict: ${goNoGoColor(report.goNoGo)}
|
|
4793
|
+
`);
|
|
4794
|
+
console.log(chalk15.bold("Advisor Scores:"));
|
|
4795
|
+
for (const feedback of report.advisorFeedback) {
|
|
4796
|
+
const bar = "\u2588".repeat(feedback.score) + "\u2591".repeat(10 - feedback.score);
|
|
4797
|
+
console.log(` ${feedback.icon} ${feedback.advisor.padEnd(18)} ${bar} ${feedback.score}/10`);
|
|
4798
|
+
}
|
|
4799
|
+
console.log("");
|
|
4800
|
+
console.log(chalk15.bold("Executive Summary:"));
|
|
4801
|
+
console.log(chalk15.dim(` ${report.executiveSummary.slice(0, 300)}...`));
|
|
4802
|
+
console.log("");
|
|
4803
|
+
console.log(chalk15.bold("Top Action Items:"));
|
|
4804
|
+
const highPriority = report.actionItems.filter((a) => a.priority === "HIGH").slice(0, 5);
|
|
4805
|
+
highPriority.forEach((item, i) => {
|
|
4806
|
+
console.log(` ${chalk15.red("!")} ${item.task}`);
|
|
4807
|
+
});
|
|
4808
|
+
console.log("");
|
|
4809
|
+
if (report.technicalPlan.stack?.length > 0) {
|
|
4810
|
+
console.log(chalk15.bold("Recommended Stack:"));
|
|
4811
|
+
console.log(` ${report.technicalPlan.stack.join(" \u2022 ")}`);
|
|
4812
|
+
console.log("");
|
|
4813
|
+
}
|
|
4814
|
+
console.log(chalk15.dim("Full report saved to PDF with complete marketing, technical, and business plans.\n"));
|
|
4815
|
+
}
|
|
4816
|
+
async function saveReport(report, projectName) {
|
|
4817
|
+
const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
4818
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4819
|
+
const filename = `${sanitizedName}-dream-team-report-${timestamp}`;
|
|
4820
|
+
const mdPath = path12.join(process.cwd(), `${filename}.md`);
|
|
4821
|
+
const mdContent = generateMarkdownReport(report);
|
|
4822
|
+
await fs12.writeFile(mdPath, mdContent);
|
|
4823
|
+
const jsonPath = path12.join(process.cwd(), `${filename}.json`);
|
|
4824
|
+
await fs12.writeJson(jsonPath, report, { spaces: 2 });
|
|
4825
|
+
return mdPath;
|
|
4826
|
+
}
|
|
4827
|
+
function generateMarkdownReport(report) {
|
|
4828
|
+
const goNoGoEmoji = { "GO": "\u{1F7E2}", "CAUTION": "\u{1F7E1}", "PIVOT": "\u{1F534}" }[report.goNoGo];
|
|
4829
|
+
return `# ${report.projectName} - Dream Team Report
|
|
4830
|
+
|
|
4831
|
+
**Generated:** ${(/* @__PURE__ */ new Date()).toLocaleDateString()}
|
|
4832
|
+
**Overall Score:** ${report.overallScore}/10
|
|
4833
|
+
**Verdict:** ${goNoGoEmoji} ${report.goNoGo}
|
|
4834
|
+
|
|
4835
|
+
---
|
|
4836
|
+
|
|
4837
|
+
## Executive Summary
|
|
4838
|
+
|
|
4839
|
+
${report.executiveSummary}
|
|
4840
|
+
|
|
4841
|
+
---
|
|
4842
|
+
|
|
4843
|
+
## Advisor Feedback
|
|
4844
|
+
|
|
4845
|
+
${report.advisorFeedback.map((f) => `
|
|
4846
|
+
### ${f.icon} ${f.advisor} - ${f.role}
|
|
4847
|
+
|
|
4848
|
+
**Score:** ${f.score}/10
|
|
4849
|
+
|
|
4850
|
+
${f.feedback}
|
|
4851
|
+
|
|
4852
|
+
**Recommendations:**
|
|
4853
|
+
${f.recommendations.map((r) => `- ${r}`).join("\n")}
|
|
4854
|
+
|
|
4855
|
+
**Questions to Consider:**
|
|
4856
|
+
${f.questions.map((q) => `- ${q}`).join("\n")}
|
|
4857
|
+
|
|
4858
|
+
**Risks Identified:**
|
|
4859
|
+
${f.risks.map((r) => `- ${r}`).join("\n")}
|
|
4860
|
+
`).join("\n")}
|
|
4861
|
+
|
|
4862
|
+
---
|
|
4863
|
+
|
|
4864
|
+
## Marketing Plan
|
|
4865
|
+
|
|
4866
|
+
### Positioning
|
|
4867
|
+
${report.marketingPlan.positioning}
|
|
4868
|
+
|
|
4869
|
+
### Target Segments
|
|
4870
|
+
${report.marketingPlan.targetSegments?.map((s) => `- ${s}`).join("\n") || "TBD"}
|
|
4871
|
+
|
|
4872
|
+
### Marketing Channels
|
|
4873
|
+
${report.marketingPlan.channels?.map((c) => `- ${c}`).join("\n") || "TBD"}
|
|
4874
|
+
|
|
4875
|
+
### Launch Strategy
|
|
4876
|
+
${report.marketingPlan.launchStrategy}
|
|
4877
|
+
|
|
4878
|
+
### Content Plan
|
|
4879
|
+
${report.marketingPlan.contentPlan?.map((c) => `- ${c}`).join("\n") || "TBD"}
|
|
4880
|
+
|
|
4881
|
+
### Social Media Plan
|
|
4882
|
+
- **Platforms:** ${report.marketingPlan.socialMediaPlan?.platforms?.join(", ") || "TBD"}
|
|
4883
|
+
- **Post Frequency:** ${report.marketingPlan.socialMediaPlan?.postFrequency || "TBD"}
|
|
4884
|
+
- **Content Types:** ${report.marketingPlan.socialMediaPlan?.contentTypes?.join(", ") || "TBD"}
|
|
4885
|
+
|
|
4886
|
+
### Budget
|
|
4887
|
+
${report.marketingPlan.budget}
|
|
4888
|
+
|
|
4889
|
+
---
|
|
4890
|
+
|
|
4891
|
+
## Technical Plan
|
|
4892
|
+
|
|
4893
|
+
### Architecture
|
|
4894
|
+
${report.technicalPlan.architecture}
|
|
4895
|
+
|
|
4896
|
+
### Tech Stack
|
|
4897
|
+
${report.technicalPlan.stack?.map((s) => `- ${s}`).join("\n") || "TBD"}
|
|
4898
|
+
|
|
4899
|
+
### Development Phases
|
|
4900
|
+
${report.technicalPlan.phases?.map((p16) => `
|
|
4901
|
+
#### ${p16.name} (${p16.duration})
|
|
4902
|
+
${p16.deliverables.map((d) => `- ${d}`).join("\n")}
|
|
4903
|
+
`).join("\n") || "TBD"}
|
|
4904
|
+
|
|
4905
|
+
### MVP Features
|
|
4906
|
+
${report.technicalPlan.mvpFeatures?.map((f) => `- ${f}`).join("\n") || "TBD"}
|
|
4907
|
+
|
|
4908
|
+
### Future Features
|
|
4909
|
+
${report.technicalPlan.futureFeatures?.map((f) => `- ${f}`).join("\n") || "TBD"}
|
|
4910
|
+
|
|
4911
|
+
---
|
|
4912
|
+
|
|
4913
|
+
## UX Plan
|
|
4914
|
+
|
|
4915
|
+
### User Personas
|
|
4916
|
+
${report.uxPlan.userPersonas?.map((p16) => `
|
|
4917
|
+
#### ${p16.name}
|
|
4918
|
+
${p16.description}
|
|
4919
|
+
|
|
4920
|
+
**Goals:**
|
|
4921
|
+
${p16.goals.map((g) => `- ${g}`).join("\n")}
|
|
4922
|
+
`).join("\n") || "TBD"}
|
|
4923
|
+
|
|
4924
|
+
### User Journeys
|
|
4925
|
+
${report.uxPlan.userJourneys?.map((j) => `- ${j}`).join("\n") || "TBD"}
|
|
4926
|
+
|
|
4927
|
+
### Key Screens
|
|
4928
|
+
${report.uxPlan.keyScreens?.map((s) => `- ${s}`).join("\n") || "TBD"}
|
|
4929
|
+
|
|
4930
|
+
### Design Principles
|
|
4931
|
+
${report.uxPlan.designPrinciples?.map((p16) => `- ${p16}`).join("\n") || "TBD"}
|
|
4932
|
+
|
|
4933
|
+
---
|
|
4934
|
+
|
|
4935
|
+
## Business Plan
|
|
4936
|
+
|
|
4937
|
+
### Revenue Model
|
|
4938
|
+
${report.businessPlan.revenueModel}
|
|
4939
|
+
|
|
4940
|
+
### Pricing Strategy
|
|
4941
|
+
${report.businessPlan.pricing}
|
|
4942
|
+
|
|
4943
|
+
### Projections
|
|
4944
|
+
| Month | Users | Revenue |
|
|
4945
|
+
|-------|-------|---------|
|
|
4946
|
+
${report.businessPlan.projections?.map((p16) => `| ${p16.month} | ${p16.users} | $${p16.revenue} |`).join("\n") || "| TBD | TBD | TBD |"}
|
|
4947
|
+
|
|
4948
|
+
### KPIs
|
|
4949
|
+
${report.businessPlan.kpis?.map((k) => `- ${k}`).join("\n") || "TBD"}
|
|
4950
|
+
|
|
4951
|
+
### Risks & Mitigation
|
|
4952
|
+
| Risk | Mitigation |
|
|
4953
|
+
|------|------------|
|
|
4954
|
+
${report.businessPlan.risks?.map((r) => `| ${r.risk} | ${r.mitigation} |`).join("\n") || "| TBD | TBD |"}
|
|
4955
|
+
|
|
4956
|
+
---
|
|
4957
|
+
|
|
4958
|
+
## Action Items
|
|
4959
|
+
|
|
4960
|
+
### High Priority
|
|
4961
|
+
${report.actionItems.filter((a) => a.priority === "HIGH").map((a) => `- [ ] **${a.task}** (Owner: ${a.owner})`).join("\n") || "None"}
|
|
4962
|
+
|
|
4963
|
+
### Medium Priority
|
|
4964
|
+
${report.actionItems.filter((a) => a.priority === "MEDIUM").map((a) => `- [ ] ${a.task} (Owner: ${a.owner})`).join("\n") || "None"}
|
|
4965
|
+
|
|
4966
|
+
### Low Priority
|
|
4967
|
+
${report.actionItems.filter((a) => a.priority === "LOW").map((a) => `- [ ] ${a.task} (Owner: ${a.owner})`).join("\n") || "None"}
|
|
4968
|
+
|
|
4969
|
+
---
|
|
4970
|
+
|
|
4971
|
+
## Next Steps
|
|
4972
|
+
|
|
4973
|
+
1. Review this report with your team
|
|
4974
|
+
2. Run \`codebakers prd ${report.projectName.toLowerCase().replace(/[^a-z0-9]/g, "-")}-dream-team-report.md\` to start building
|
|
4975
|
+
3. Or run \`codebakers init\` for standard project setup
|
|
4976
|
+
|
|
4977
|
+
---
|
|
4978
|
+
|
|
4979
|
+
*Generated by CodeBakers Dream Team Advisors*
|
|
4980
|
+
`;
|
|
4981
|
+
}
|
|
4982
|
+
|
|
3850
4983
|
// src/index.ts
|
|
3851
4984
|
var VERSION2 = "1.0.0";
|
|
3852
4985
|
var logo = `
|
|
@@ -3861,18 +4994,18 @@ async function showMainMenu() {
|
|
|
3861
4994
|
const config = new Config();
|
|
3862
4995
|
const isSetup = config.isConfigured();
|
|
3863
4996
|
console.log(gradient.pastel.multiline(logo));
|
|
3864
|
-
console.log(
|
|
4997
|
+
console.log(chalk16.dim(` v${VERSION2} \u2014 AI dev team that follows the rules
|
|
3865
4998
|
`));
|
|
3866
4999
|
if (!isSetup) {
|
|
3867
5000
|
console.log(boxen(
|
|
3868
|
-
|
|
5001
|
+
chalk16.yellow("Welcome to CodeBakers! Let's get you set up."),
|
|
3869
5002
|
{ padding: 1, borderColor: "yellow", borderStyle: "round" }
|
|
3870
5003
|
));
|
|
3871
5004
|
await setupCommand();
|
|
3872
5005
|
return;
|
|
3873
5006
|
}
|
|
3874
5007
|
const inProject = config.isInProject();
|
|
3875
|
-
const action = await
|
|
5008
|
+
const action = await p15.select({
|
|
3876
5009
|
message: "What do you want to do?",
|
|
3877
5010
|
options: inProject ? [
|
|
3878
5011
|
{ value: "code", label: "\u{1F4AC} Code with AI", hint: "build features, fix bugs" },
|
|
@@ -3886,12 +5019,17 @@ async function showMainMenu() {
|
|
|
3886
5019
|
{ value: "gateway", label: "\u{1F4F1} Channel gateway", hint: "WhatsApp, Telegram, etc." },
|
|
3887
5020
|
{ value: "learn", label: "\u{1F9E0} Learning settings", hint: "view what I've learned" },
|
|
3888
5021
|
{ value: "security", label: "\u{1F512} Security audit", hint: "check for vulnerabilities" },
|
|
5022
|
+
{ value: "design", label: "\u{1F3A8} Design system", hint: "set profile, colors" },
|
|
3889
5023
|
{ value: "separator2", label: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" },
|
|
3890
5024
|
{ value: "new", label: "\u{1F195} Create new project" },
|
|
5025
|
+
{ value: "prd", label: "\u{1F4C4} Build from PRD", hint: "upload a PRD document" },
|
|
5026
|
+
{ value: "advisors", label: "\u{1F31F} Dream Team Advisors", hint: "consult with experts" },
|
|
3891
5027
|
{ value: "settings", label: "\u2699\uFE0F Settings" },
|
|
3892
5028
|
{ value: "help", label: "\u2753 Help" }
|
|
3893
5029
|
] : [
|
|
3894
5030
|
{ value: "new", label: "\u{1F195} Create new project" },
|
|
5031
|
+
{ value: "prd", label: "\u{1F4C4} Build from PRD", hint: "upload a PRD document" },
|
|
5032
|
+
{ value: "advisors", label: "\u{1F31F} Dream Team Advisors", hint: "consult with experts" },
|
|
3895
5033
|
{ value: "open", label: "\u{1F4C2} Open existing project" },
|
|
3896
5034
|
{ value: "status", label: "\u{1F4CA} View all projects" },
|
|
3897
5035
|
{ value: "separator1", label: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" },
|
|
@@ -3902,8 +5040,8 @@ async function showMainMenu() {
|
|
|
3902
5040
|
{ value: "help", label: "\u2753 Help" }
|
|
3903
5041
|
]
|
|
3904
5042
|
});
|
|
3905
|
-
if (
|
|
3906
|
-
|
|
5043
|
+
if (p15.isCancel(action)) {
|
|
5044
|
+
p15.cancel("Goodbye!");
|
|
3907
5045
|
process.exit(0);
|
|
3908
5046
|
}
|
|
3909
5047
|
switch (action) {
|
|
@@ -3937,9 +5075,18 @@ async function showMainMenu() {
|
|
|
3937
5075
|
case "security":
|
|
3938
5076
|
await securityCommand();
|
|
3939
5077
|
break;
|
|
5078
|
+
case "design":
|
|
5079
|
+
await designCommand();
|
|
5080
|
+
break;
|
|
3940
5081
|
case "new":
|
|
3941
5082
|
await initCommand();
|
|
3942
5083
|
break;
|
|
5084
|
+
case "prd":
|
|
5085
|
+
await prdCommand();
|
|
5086
|
+
break;
|
|
5087
|
+
case "advisors":
|
|
5088
|
+
await advisorsCommand();
|
|
5089
|
+
break;
|
|
3943
5090
|
case "settings":
|
|
3944
5091
|
await setupCommand();
|
|
3945
5092
|
break;
|
|
@@ -3952,27 +5099,27 @@ async function showMainMenu() {
|
|
|
3952
5099
|
}
|
|
3953
5100
|
function showHelp2() {
|
|
3954
5101
|
console.log(boxen(`
|
|
3955
|
-
${
|
|
3956
|
-
|
|
3957
|
-
${
|
|
3958
|
-
${
|
|
3959
|
-
${
|
|
3960
|
-
${
|
|
3961
|
-
${
|
|
3962
|
-
${
|
|
3963
|
-
${
|
|
3964
|
-
${
|
|
3965
|
-
${
|
|
3966
|
-
${
|
|
3967
|
-
${
|
|
3968
|
-
${
|
|
3969
|
-
${
|
|
3970
|
-
|
|
3971
|
-
${
|
|
3972
|
-
Press ${
|
|
3973
|
-
|
|
3974
|
-
${
|
|
3975
|
-
${
|
|
5102
|
+
${chalk16.bold("CodeBakers CLI")} \u2014 AI dev team that follows the rules
|
|
5103
|
+
|
|
5104
|
+
${chalk16.bold("Commands:")}
|
|
5105
|
+
${chalk16.cyan("codebakers")} Interactive menu (or just run with no args)
|
|
5106
|
+
${chalk16.cyan("codebakers init")} Create a new project
|
|
5107
|
+
${chalk16.cyan("codebakers code")} Start AI coding session
|
|
5108
|
+
${chalk16.cyan("codebakers check")} Run pattern enforcement
|
|
5109
|
+
${chalk16.cyan("codebakers deploy")} Deploy to production
|
|
5110
|
+
${chalk16.cyan("codebakers fix")} Auto-fix errors
|
|
5111
|
+
${chalk16.cyan("codebakers generate")} Generate components/pages
|
|
5112
|
+
${chalk16.cyan("codebakers connect")} Connect external services
|
|
5113
|
+
${chalk16.cyan("codebakers gateway")} Manage messaging channels
|
|
5114
|
+
${chalk16.cyan("codebakers status")} View project status
|
|
5115
|
+
${chalk16.cyan("codebakers security")} Run security audit
|
|
5116
|
+
${chalk16.cyan("codebakers learn")} View/manage learning
|
|
5117
|
+
|
|
5118
|
+
${chalk16.bold("Help at any time:")}
|
|
5119
|
+
Press ${chalk16.yellow("?")} during any command to get contextual help
|
|
5120
|
+
|
|
5121
|
+
${chalk16.bold("Documentation:")}
|
|
5122
|
+
${chalk16.dim("https://codebakers.dev/docs")}
|
|
3976
5123
|
`, { padding: 1, borderColor: "cyan", borderStyle: "round" }));
|
|
3977
5124
|
}
|
|
3978
5125
|
var program = new Command();
|
|
@@ -3989,6 +5136,9 @@ program.command("gateway").description("Manage messaging channel gateway").optio
|
|
|
3989
5136
|
program.command("status").description("View project status and health").option("-a, --all", "Show all projects").action(statusCommand);
|
|
3990
5137
|
program.command("security").description("Run security audit").option("--fix", "Auto-fix security issues").action(securityCommand);
|
|
3991
5138
|
program.command("learn").description("View and manage learning settings").option("--forget <item>", "Forget a learned preference").option("--reset", "Reset all learning").option("--export", "Export learned patterns").action(learnCommand);
|
|
5139
|
+
program.command("design [action]").description("Manage design system (profile, palette, check)").action(designCommand);
|
|
5140
|
+
program.command("prd [file]").description("Build entire project from a PRD document").action(prdCommand);
|
|
5141
|
+
program.command("advisors").alias("dream-team").description("Consult with the CodeBakers Dream Team advisory board").action(advisorsCommand);
|
|
3992
5142
|
var args = process.argv.slice(2);
|
|
3993
5143
|
if (args.length === 0) {
|
|
3994
5144
|
checkForUpdates().catch(() => {
|