codebakers 2.1.0 → 2.1.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 +763 -250
- package/package.json +1 -1
- package/src/commands/website.ts +643 -0
- package/src/index.ts +14 -2
package/dist/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
import { Command } from "commander";
|
|
14
|
-
import * as
|
|
15
|
-
import
|
|
14
|
+
import * as p21 from "@clack/prompts";
|
|
15
|
+
import chalk21 from "chalk";
|
|
16
16
|
import boxen from "boxen";
|
|
17
17
|
import gradient from "gradient-string";
|
|
18
18
|
|
|
@@ -65,10 +65,10 @@ async function setupCommand() {
|
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
if (action === "reset") {
|
|
68
|
-
const
|
|
68
|
+
const confirm12 = await p.confirm({
|
|
69
69
|
message: "Are you sure? This will remove all credentials and settings."
|
|
70
70
|
});
|
|
71
|
-
if (
|
|
71
|
+
if (confirm12) {
|
|
72
72
|
p.outro(chalk2.yellow("Configuration reset. Run `codebakers setup` again."));
|
|
73
73
|
}
|
|
74
74
|
return;
|
|
@@ -132,7 +132,7 @@ ${chalk2.dim("and are never sent to our servers.")}`,
|
|
|
132
132
|
p.outro(chalk2.green("\u2713 Setup complete! Run `codebakers init` to create your first project."));
|
|
133
133
|
}
|
|
134
134
|
async function connectService(config, serviceKey, serviceName, required) {
|
|
135
|
-
const
|
|
135
|
+
const spinner17 = p.spinner();
|
|
136
136
|
switch (serviceKey) {
|
|
137
137
|
case "github": {
|
|
138
138
|
p.log.info(`${chalk2.bold("GitHub")} - Opens browser for OAuth authorization`);
|
|
@@ -606,10 +606,10 @@ var SupabaseService = class {
|
|
|
606
606
|
throw new Error("Failed to list projects");
|
|
607
607
|
}
|
|
608
608
|
const projects = await response.json();
|
|
609
|
-
return projects.map((
|
|
610
|
-
id:
|
|
611
|
-
name:
|
|
612
|
-
region:
|
|
609
|
+
return projects.map((p22) => ({
|
|
610
|
+
id: p22.id,
|
|
611
|
+
name: p22.name,
|
|
612
|
+
region: p22.region
|
|
613
613
|
}));
|
|
614
614
|
}
|
|
615
615
|
};
|
|
@@ -1029,20 +1029,20 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1029
1029
|
p2.cancel("Project creation cancelled.");
|
|
1030
1030
|
return;
|
|
1031
1031
|
}
|
|
1032
|
-
const
|
|
1032
|
+
const spinner17 = p2.spinner();
|
|
1033
1033
|
const projectPath = path.join(process.cwd(), projectName);
|
|
1034
1034
|
try {
|
|
1035
|
-
|
|
1035
|
+
spinner17.start("Creating local project...");
|
|
1036
1036
|
await createLocalProject(projectPath, projectConfig);
|
|
1037
|
-
|
|
1038
|
-
|
|
1037
|
+
spinner17.stop("Local project created");
|
|
1038
|
+
spinner17.start("Installing dependencies...");
|
|
1039
1039
|
await execa3("pnpm", ["install"], { cwd: projectPath });
|
|
1040
|
-
|
|
1040
|
+
spinner17.stop("Dependencies installed");
|
|
1041
1041
|
if (services.includes("github")) {
|
|
1042
|
-
|
|
1042
|
+
spinner17.start("Creating GitHub repository...");
|
|
1043
1043
|
const github = new GitHubService(config);
|
|
1044
1044
|
const repo = await github.createRepo(projectName, { private: true });
|
|
1045
|
-
|
|
1045
|
+
spinner17.stop(`GitHub repo created: ${repo.html_url}`);
|
|
1046
1046
|
await execa3("git", ["init"], { cwd: projectPath });
|
|
1047
1047
|
await execa3("git", ["add", "."], { cwd: projectPath });
|
|
1048
1048
|
await execa3("git", ["commit", "-m", "Initial commit by CodeBakers"], { cwd: projectPath });
|
|
@@ -1050,10 +1050,10 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1050
1050
|
await execa3("git", ["push", "-u", "origin", "main"], { cwd: projectPath });
|
|
1051
1051
|
}
|
|
1052
1052
|
if (services.includes("supabase")) {
|
|
1053
|
-
|
|
1053
|
+
spinner17.start("Creating Supabase project...");
|
|
1054
1054
|
const supabase = new SupabaseService(config);
|
|
1055
1055
|
const project = await supabase.createProject(projectName);
|
|
1056
|
-
|
|
1056
|
+
spinner17.stop(`Supabase project created: ${project.name}`);
|
|
1057
1057
|
await fs.writeJson(
|
|
1058
1058
|
path.join(projectPath, ".codebakers", "supabase.json"),
|
|
1059
1059
|
{ projectId: project.id, projectUrl: project.api_url },
|
|
@@ -1061,26 +1061,26 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1061
1061
|
);
|
|
1062
1062
|
}
|
|
1063
1063
|
if (services.includes("vercel")) {
|
|
1064
|
-
|
|
1064
|
+
spinner17.start("Creating Vercel project...");
|
|
1065
1065
|
const vercel = new VercelService(config);
|
|
1066
1066
|
const project = await vercel.createProject(projectName);
|
|
1067
|
-
|
|
1067
|
+
spinner17.stop(`Vercel project created`);
|
|
1068
1068
|
if (domain) {
|
|
1069
|
-
|
|
1069
|
+
spinner17.start(`Configuring domain: ${domain}...`);
|
|
1070
1070
|
await vercel.addDomain(projectName, domain);
|
|
1071
|
-
|
|
1071
|
+
spinner17.stop("Domain configured");
|
|
1072
1072
|
}
|
|
1073
|
-
|
|
1073
|
+
spinner17.start("Deploying to Vercel...");
|
|
1074
1074
|
const deployment = await vercel.deploy(projectPath);
|
|
1075
|
-
|
|
1075
|
+
spinner17.stop(`Deployed: ${deployment.url}`);
|
|
1076
1076
|
}
|
|
1077
|
-
|
|
1077
|
+
spinner17.start("Generating CLAUDE.md...");
|
|
1078
1078
|
const claudeMd = generateClaudeMd(projectConfig);
|
|
1079
1079
|
await fs.writeFile(path.join(projectPath, "CLAUDE.md"), claudeMd);
|
|
1080
|
-
|
|
1081
|
-
|
|
1080
|
+
spinner17.stop("CLAUDE.md generated");
|
|
1081
|
+
spinner17.start("Setting up CodeBakers enforcement...");
|
|
1082
1082
|
await setupGitHooks(projectPath);
|
|
1083
|
-
|
|
1083
|
+
spinner17.stop("CodeBakers enforcement configured");
|
|
1084
1084
|
config.addProject({
|
|
1085
1085
|
name: projectName,
|
|
1086
1086
|
path: projectPath,
|
|
@@ -1100,7 +1100,7 @@ ${chalk3.dim("Shortcuts:")}
|
|
|
1100
1100
|
${chalk3.cyan("codebakers deploy")} \u2014 Deploy to production
|
|
1101
1101
|
`));
|
|
1102
1102
|
} catch (error) {
|
|
1103
|
-
|
|
1103
|
+
spinner17.stop("Error occurred");
|
|
1104
1104
|
p2.log.error(`Failed to create project: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1105
1105
|
const cleanup = await p2.confirm({
|
|
1106
1106
|
message: "Clean up partially created project?"
|
|
@@ -1826,17 +1826,17 @@ async function checkCommand(options = {}) {
|
|
|
1826
1826
|
return;
|
|
1827
1827
|
}
|
|
1828
1828
|
p3.intro(chalk4.bgCyan.black(" CodeBakers Pattern Check "));
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1829
|
+
const spinner17 = p3.spinner();
|
|
1830
|
+
spinner17.start("Analyzing code...");
|
|
1831
1831
|
const result = await runPatternCheck(options.fix || false);
|
|
1832
|
-
|
|
1832
|
+
spinner17.stop("Analysis complete");
|
|
1833
1833
|
displayResults(result);
|
|
1834
1834
|
if (result.violations.length > 0 && options.fix) {
|
|
1835
1835
|
const fixable = result.violations.filter((v) => v.autoFixable);
|
|
1836
1836
|
if (fixable.length > 0) {
|
|
1837
|
-
|
|
1837
|
+
spinner17.start(`Auto-fixing ${fixable.length} violations...`);
|
|
1838
1838
|
await autoFix(fixable);
|
|
1839
|
-
|
|
1839
|
+
spinner17.stop("Auto-fix complete");
|
|
1840
1840
|
}
|
|
1841
1841
|
}
|
|
1842
1842
|
const errors = result.violations.filter((v) => v.severity === "error");
|
|
@@ -2040,8 +2040,8 @@ async function getVoiceInput(prompt) {
|
|
|
2040
2040
|
return null;
|
|
2041
2041
|
}
|
|
2042
2042
|
await playBeep();
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2043
|
+
const spinner17 = p4.spinner();
|
|
2044
|
+
spinner17.start("\u{1F534} Recording...");
|
|
2045
2045
|
try {
|
|
2046
2046
|
let transcription = "";
|
|
2047
2047
|
if (process.platform === "win32") {
|
|
@@ -2051,16 +2051,16 @@ async function getVoiceInput(prompt) {
|
|
|
2051
2051
|
} else {
|
|
2052
2052
|
transcription = await recordWithLinux();
|
|
2053
2053
|
}
|
|
2054
|
-
|
|
2054
|
+
spinner17.stop("Recording complete");
|
|
2055
2055
|
if (transcription) {
|
|
2056
2056
|
console.log(chalk5.green(`
|
|
2057
2057
|
\u2713 Heard: "${transcription}"
|
|
2058
2058
|
`));
|
|
2059
|
-
const
|
|
2059
|
+
const confirm12 = await p4.confirm({
|
|
2060
2060
|
message: "Is this correct?",
|
|
2061
2061
|
initialValue: true
|
|
2062
2062
|
});
|
|
2063
|
-
if (
|
|
2063
|
+
if (confirm12 && !p4.isCancel(confirm12)) {
|
|
2064
2064
|
return transcription;
|
|
2065
2065
|
} else {
|
|
2066
2066
|
const action = await p4.select({
|
|
@@ -2075,20 +2075,20 @@ async function getVoiceInput(prompt) {
|
|
|
2075
2075
|
if (action === "retry") {
|
|
2076
2076
|
return await getVoiceInput(prompt);
|
|
2077
2077
|
} else {
|
|
2078
|
-
const
|
|
2079
|
-
return p4.isCancel(
|
|
2078
|
+
const text16 = await p4.text({ message: "Type your response:" });
|
|
2079
|
+
return p4.isCancel(text16) ? null : text16;
|
|
2080
2080
|
}
|
|
2081
2081
|
}
|
|
2082
2082
|
} else {
|
|
2083
2083
|
console.log(chalk5.yellow("\n No speech detected. Try again or type your response.\n"));
|
|
2084
|
-
const
|
|
2085
|
-
return p4.isCancel(
|
|
2084
|
+
const text16 = await p4.text({ message: "Type instead:" });
|
|
2085
|
+
return p4.isCancel(text16) ? null : text16;
|
|
2086
2086
|
}
|
|
2087
2087
|
} catch (error) {
|
|
2088
|
-
|
|
2088
|
+
spinner17.stop("Recording failed");
|
|
2089
2089
|
console.log(chalk5.yellow("Voice input failed. Please type instead."));
|
|
2090
|
-
const
|
|
2091
|
-
return p4.isCancel(
|
|
2090
|
+
const text16 = await p4.text({ message: prompt });
|
|
2091
|
+
return p4.isCancel(text16) ? null : text16;
|
|
2092
2092
|
}
|
|
2093
2093
|
}
|
|
2094
2094
|
async function playBeep() {
|
|
@@ -2223,10 +2223,10 @@ async function transcribeWithWhisper(audioFile) {
|
|
|
2223
2223
|
], { timeout: 6e4 });
|
|
2224
2224
|
const txtFile = outputBase + ".txt";
|
|
2225
2225
|
if (await fs4.pathExists(txtFile)) {
|
|
2226
|
-
const
|
|
2226
|
+
const text16 = await fs4.readFile(txtFile, "utf-8");
|
|
2227
2227
|
await fs4.remove(txtFile).catch(() => {
|
|
2228
2228
|
});
|
|
2229
|
-
return
|
|
2229
|
+
return text16.trim();
|
|
2230
2230
|
}
|
|
2231
2231
|
} catch {
|
|
2232
2232
|
}
|
|
@@ -2333,9 +2333,9 @@ async function readFile5(filePath) {
|
|
|
2333
2333
|
name
|
|
2334
2334
|
};
|
|
2335
2335
|
} else if (pdfExtensions.includes(ext)) {
|
|
2336
|
-
const
|
|
2337
|
-
if (
|
|
2338
|
-
return { content:
|
|
2336
|
+
const text16 = await extractPdfText(cleanPath);
|
|
2337
|
+
if (text16) {
|
|
2338
|
+
return { content: text16, type: "pdf", name };
|
|
2339
2339
|
}
|
|
2340
2340
|
return {
|
|
2341
2341
|
content: `[PDF file: ${name} - text extraction not available]`,
|
|
@@ -2407,19 +2407,19 @@ function formatFilesForContext(files) {
|
|
|
2407
2407
|
context += "--- END FILES ---\n\n";
|
|
2408
2408
|
return context;
|
|
2409
2409
|
}
|
|
2410
|
-
function looksLikePaste(
|
|
2411
|
-
if (
|
|
2412
|
-
if (
|
|
2413
|
-
if (
|
|
2414
|
-
if (
|
|
2410
|
+
function looksLikePaste(text16) {
|
|
2411
|
+
if (text16.includes("\n") && text16.split("\n").length > 3) return true;
|
|
2412
|
+
if (text16.includes("function ") || text16.includes("const ") || text16.includes("import ") || text16.includes("export ") || text16.includes("class ") || text16.includes("def ") || text16.includes("public ") || text16.includes("private ")) return true;
|
|
2413
|
+
if (text16.startsWith("{") && text16.endsWith("}") || text16.startsWith("[") && text16.endsWith("]")) return true;
|
|
2414
|
+
if (text16.length > 200 && !text16.includes("\n")) return true;
|
|
2415
2415
|
return false;
|
|
2416
2416
|
}
|
|
2417
|
-
async function handlePastedContent(
|
|
2418
|
-
if (!looksLikePaste(
|
|
2417
|
+
async function handlePastedContent(text16) {
|
|
2418
|
+
if (!looksLikePaste(text16)) {
|
|
2419
2419
|
return null;
|
|
2420
2420
|
}
|
|
2421
2421
|
console.log(chalk6.cyan("\n\u{1F4CB} Detected pasted content!\n"));
|
|
2422
|
-
const preview =
|
|
2422
|
+
const preview = text16.length > 200 ? text16.slice(0, 200) + "..." : text16;
|
|
2423
2423
|
console.log(chalk6.dim(preview));
|
|
2424
2424
|
console.log("");
|
|
2425
2425
|
const action = await p5.select({
|
|
@@ -2435,7 +2435,7 @@ async function handlePastedContent(text15) {
|
|
|
2435
2435
|
});
|
|
2436
2436
|
if (p5.isCancel(action)) return null;
|
|
2437
2437
|
if (action === "literal") {
|
|
2438
|
-
return { prompt:
|
|
2438
|
+
return { prompt: text16, context: "" };
|
|
2439
2439
|
}
|
|
2440
2440
|
if (action === "custom") {
|
|
2441
2441
|
const instruction = await p5.text({
|
|
@@ -2449,7 +2449,7 @@ async function handlePastedContent(text15) {
|
|
|
2449
2449
|
|
|
2450
2450
|
--- PASTED CODE ---
|
|
2451
2451
|
\`\`\`
|
|
2452
|
-
${
|
|
2452
|
+
${text16}
|
|
2453
2453
|
\`\`\`
|
|
2454
2454
|
--- END ---
|
|
2455
2455
|
|
|
@@ -2468,7 +2468,7 @@ ${text15}
|
|
|
2468
2468
|
|
|
2469
2469
|
--- PASTED CODE ---
|
|
2470
2470
|
\`\`\`
|
|
2471
|
-
${
|
|
2471
|
+
${text16}
|
|
2472
2472
|
\`\`\`
|
|
2473
2473
|
--- END ---
|
|
2474
2474
|
|
|
@@ -2629,14 +2629,14 @@ ${clipContent}
|
|
|
2629
2629
|
}
|
|
2630
2630
|
}
|
|
2631
2631
|
async function processUserInput(userInput, messages, anthropic, systemPrompt, projectContext, config) {
|
|
2632
|
-
const
|
|
2632
|
+
const spinner17 = p6.spinner();
|
|
2633
2633
|
messages.push({ role: "user", content: userInput });
|
|
2634
2634
|
const wizardResult = await checkForWizard(userInput);
|
|
2635
2635
|
if (wizardResult) {
|
|
2636
2636
|
messages[messages.length - 1].content = wizardResult;
|
|
2637
2637
|
}
|
|
2638
2638
|
try {
|
|
2639
|
-
|
|
2639
|
+
spinner17.start("Thinking...");
|
|
2640
2640
|
const response = await anthropic.messages.create({
|
|
2641
2641
|
model: "claude-sonnet-4-20250514",
|
|
2642
2642
|
max_tokens: 8192,
|
|
@@ -2646,7 +2646,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2646
2646
|
content: m.content
|
|
2647
2647
|
}))
|
|
2648
2648
|
});
|
|
2649
|
-
|
|
2649
|
+
spinner17.stop("");
|
|
2650
2650
|
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
|
|
2651
2651
|
messages.push({ role: "assistant", content: assistantMessage });
|
|
2652
2652
|
const actions = parseActions(assistantMessage);
|
|
@@ -2661,11 +2661,11 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2661
2661
|
initialValue: true
|
|
2662
2662
|
});
|
|
2663
2663
|
if (proceed && !p6.isCancel(proceed)) {
|
|
2664
|
-
|
|
2664
|
+
spinner17.start("Building...");
|
|
2665
2665
|
for (const action of actions) {
|
|
2666
|
-
await executeAction(action,
|
|
2666
|
+
await executeAction(action, spinner17);
|
|
2667
2667
|
}
|
|
2668
|
-
|
|
2668
|
+
spinner17.stop("Build complete");
|
|
2669
2669
|
console.log(chalk7.dim("\n\u{1F50D} Running CodeBakers check..."));
|
|
2670
2670
|
const checkResult = await runPatternCheck(false);
|
|
2671
2671
|
if (checkResult.violations.length > 0) {
|
|
@@ -2676,9 +2676,9 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2676
2676
|
initialValue: true
|
|
2677
2677
|
});
|
|
2678
2678
|
if (autoFix2 && !p6.isCancel(autoFix2)) {
|
|
2679
|
-
|
|
2679
|
+
spinner17.start("Auto-fixing...");
|
|
2680
2680
|
await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
|
|
2681
|
-
|
|
2681
|
+
spinner17.stop("Violations fixed");
|
|
2682
2682
|
}
|
|
2683
2683
|
} else {
|
|
2684
2684
|
console.log(chalk7.green("\u2713 All patterns satisfied"));
|
|
@@ -2689,7 +2689,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2689
2689
|
console.log("\n" + assistantMessage + "\n");
|
|
2690
2690
|
}
|
|
2691
2691
|
} catch (error) {
|
|
2692
|
-
|
|
2692
|
+
spinner17.stop("Error");
|
|
2693
2693
|
console.log(chalk7.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2694
2694
|
}
|
|
2695
2695
|
}
|
|
@@ -2801,14 +2801,14 @@ function parseActions(response) {
|
|
|
2801
2801
|
}
|
|
2802
2802
|
return actions;
|
|
2803
2803
|
}
|
|
2804
|
-
async function executeAction(action,
|
|
2804
|
+
async function executeAction(action, spinner17) {
|
|
2805
2805
|
const cwd = process.cwd();
|
|
2806
2806
|
switch (action.type) {
|
|
2807
2807
|
case "CREATE_FILE": {
|
|
2808
2808
|
const filePath = path5.join(cwd, action.path);
|
|
2809
2809
|
await fs6.ensureDir(path5.dirname(filePath));
|
|
2810
2810
|
await fs6.writeFile(filePath, action.content);
|
|
2811
|
-
|
|
2811
|
+
spinner17.message(`Created ${action.path}`);
|
|
2812
2812
|
break;
|
|
2813
2813
|
}
|
|
2814
2814
|
case "EDIT_FILE": {
|
|
@@ -2818,13 +2818,13 @@ async function executeAction(action, spinner16) {
|
|
|
2818
2818
|
if (action.find && content.includes(action.find)) {
|
|
2819
2819
|
content = content.replace(action.find, action.replace || "");
|
|
2820
2820
|
await fs6.writeFile(filePath, content);
|
|
2821
|
-
|
|
2821
|
+
spinner17.message(`Edited ${action.path}`);
|
|
2822
2822
|
}
|
|
2823
2823
|
}
|
|
2824
2824
|
break;
|
|
2825
2825
|
}
|
|
2826
2826
|
case "RUN_COMMAND": {
|
|
2827
|
-
|
|
2827
|
+
spinner17.message(`Running: ${action.command}`);
|
|
2828
2828
|
const [cmd, ...args2] = action.command.split(" ");
|
|
2829
2829
|
await execa6(cmd, args2, { cwd, stdio: "pipe" });
|
|
2830
2830
|
break;
|
|
@@ -2833,7 +2833,7 @@ async function executeAction(action, spinner16) {
|
|
|
2833
2833
|
const filePath = path5.join(cwd, action.path);
|
|
2834
2834
|
if (await fs6.pathExists(filePath)) {
|
|
2835
2835
|
await fs6.remove(filePath);
|
|
2836
|
-
|
|
2836
|
+
spinner17.message(`Deleted ${action.path}`);
|
|
2837
2837
|
}
|
|
2838
2838
|
break;
|
|
2839
2839
|
}
|
|
@@ -3081,12 +3081,12 @@ async function deployCommand(options = {}) {
|
|
|
3081
3081
|
return;
|
|
3082
3082
|
}
|
|
3083
3083
|
p7.intro(chalk8.bgCyan.black(" Deploy to Production "));
|
|
3084
|
-
const
|
|
3084
|
+
const spinner17 = p7.spinner();
|
|
3085
3085
|
if (options.check !== false) {
|
|
3086
|
-
|
|
3086
|
+
spinner17.start("Running CodeBakers check...");
|
|
3087
3087
|
const checkResult = await runPatternCheck(false);
|
|
3088
3088
|
if (!checkResult.passed) {
|
|
3089
|
-
|
|
3089
|
+
spinner17.stop("");
|
|
3090
3090
|
const errors = checkResult.violations.filter((v) => v.severity === "error");
|
|
3091
3091
|
if (errors.length > 0) {
|
|
3092
3092
|
p7.log.error(`${errors.length} pattern violations found. Fix before deploying.`);
|
|
@@ -3107,9 +3107,9 @@ async function deployCommand(options = {}) {
|
|
|
3107
3107
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3108
3108
|
return;
|
|
3109
3109
|
}
|
|
3110
|
-
|
|
3110
|
+
spinner17.start("Auto-fixing with AI...");
|
|
3111
3111
|
await autoFixWithAI(config, errors);
|
|
3112
|
-
|
|
3112
|
+
spinner17.stop("Auto-fix complete");
|
|
3113
3113
|
const recheck = await runPatternCheck(false);
|
|
3114
3114
|
if (!recheck.passed) {
|
|
3115
3115
|
p7.log.error("Some violations remain. Manual fix required.");
|
|
@@ -3118,23 +3118,23 @@ async function deployCommand(options = {}) {
|
|
|
3118
3118
|
}
|
|
3119
3119
|
}
|
|
3120
3120
|
}
|
|
3121
|
-
|
|
3121
|
+
spinner17.stop("Pattern check passed");
|
|
3122
3122
|
}
|
|
3123
|
-
|
|
3123
|
+
spinner17.start("Running TypeScript check...");
|
|
3124
3124
|
try {
|
|
3125
3125
|
await execa7("npx", ["tsc", "--noEmit"], { cwd: process.cwd() });
|
|
3126
|
-
|
|
3126
|
+
spinner17.stop("TypeScript check passed");
|
|
3127
3127
|
} catch (error) {
|
|
3128
|
-
|
|
3128
|
+
spinner17.stop("");
|
|
3129
3129
|
p7.log.error("TypeScript errors found.");
|
|
3130
3130
|
const fix = await p7.confirm({
|
|
3131
3131
|
message: "Attempt to fix TypeScript errors?",
|
|
3132
3132
|
initialValue: true
|
|
3133
3133
|
});
|
|
3134
3134
|
if (fix && !p7.isCancel(fix)) {
|
|
3135
|
-
|
|
3135
|
+
spinner17.start("Fixing TypeScript errors...");
|
|
3136
3136
|
const fixed = await fixTypeScriptErrors(config);
|
|
3137
|
-
|
|
3137
|
+
spinner17.stop(fixed ? "TypeScript errors fixed" : "Could not auto-fix all errors");
|
|
3138
3138
|
if (!fixed) {
|
|
3139
3139
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3140
3140
|
return;
|
|
@@ -3144,12 +3144,12 @@ async function deployCommand(options = {}) {
|
|
|
3144
3144
|
return;
|
|
3145
3145
|
}
|
|
3146
3146
|
}
|
|
3147
|
-
|
|
3147
|
+
spinner17.start("Building project...");
|
|
3148
3148
|
try {
|
|
3149
3149
|
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3150
|
-
|
|
3150
|
+
spinner17.stop("Build successful");
|
|
3151
3151
|
} catch (error) {
|
|
3152
|
-
|
|
3152
|
+
spinner17.stop("");
|
|
3153
3153
|
p7.log.error("Build failed.");
|
|
3154
3154
|
const errorOutput = error instanceof Error ? error.message : "Unknown error";
|
|
3155
3155
|
console.log(chalk8.dim(errorOutput));
|
|
@@ -3158,16 +3158,16 @@ async function deployCommand(options = {}) {
|
|
|
3158
3158
|
initialValue: true
|
|
3159
3159
|
});
|
|
3160
3160
|
if (fix && !p7.isCancel(fix)) {
|
|
3161
|
-
|
|
3161
|
+
spinner17.start("Fixing build errors...");
|
|
3162
3162
|
const fixed = await fixBuildErrors(config, errorOutput);
|
|
3163
|
-
|
|
3163
|
+
spinner17.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
|
|
3164
3164
|
if (fixed) {
|
|
3165
|
-
|
|
3165
|
+
spinner17.start("Retrying build...");
|
|
3166
3166
|
try {
|
|
3167
3167
|
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3168
|
-
|
|
3168
|
+
spinner17.stop("Build successful");
|
|
3169
3169
|
} catch {
|
|
3170
|
-
|
|
3170
|
+
spinner17.stop("Build still failing");
|
|
3171
3171
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3172
3172
|
return;
|
|
3173
3173
|
}
|
|
@@ -3180,10 +3180,10 @@ async function deployCommand(options = {}) {
|
|
|
3180
3180
|
return;
|
|
3181
3181
|
}
|
|
3182
3182
|
}
|
|
3183
|
-
|
|
3183
|
+
spinner17.start("Checking for uncommitted changes...");
|
|
3184
3184
|
const { stdout: gitStatus } = await execa7("git", ["status", "--porcelain"], { cwd: process.cwd() });
|
|
3185
3185
|
if (gitStatus.trim()) {
|
|
3186
|
-
|
|
3186
|
+
spinner17.stop("");
|
|
3187
3187
|
const commit = await p7.confirm({
|
|
3188
3188
|
message: "You have uncommitted changes. Commit before deploying?",
|
|
3189
3189
|
initialValue: true
|
|
@@ -3197,20 +3197,20 @@ async function deployCommand(options = {}) {
|
|
|
3197
3197
|
if (!p7.isCancel(message)) {
|
|
3198
3198
|
await execa7("git", ["add", "."], { cwd: process.cwd() });
|
|
3199
3199
|
await execa7("git", ["commit", "-m", message], { cwd: process.cwd() });
|
|
3200
|
-
|
|
3200
|
+
spinner17.start("Pushing to GitHub...");
|
|
3201
3201
|
await execa7("git", ["push"], { cwd: process.cwd() });
|
|
3202
|
-
|
|
3202
|
+
spinner17.stop("Pushed to GitHub");
|
|
3203
3203
|
}
|
|
3204
3204
|
}
|
|
3205
3205
|
} else {
|
|
3206
|
-
|
|
3206
|
+
spinner17.stop("No uncommitted changes");
|
|
3207
3207
|
}
|
|
3208
3208
|
const deployType = options.preview ? "preview" : "production";
|
|
3209
|
-
|
|
3209
|
+
spinner17.start(`Deploying to ${deployType}...`);
|
|
3210
3210
|
try {
|
|
3211
3211
|
const vercel = new VercelService(config);
|
|
3212
3212
|
const deployment = await vercel.deploy(process.cwd(), !options.preview);
|
|
3213
|
-
|
|
3213
|
+
spinner17.stop("Deployment complete!");
|
|
3214
3214
|
console.log(boxedOutput(`
|
|
3215
3215
|
${chalk8.green("\u2713")} Deployed successfully!
|
|
3216
3216
|
|
|
@@ -3222,7 +3222,7 @@ ${chalk8.dim("View in Vercel Dashboard:")}
|
|
|
3222
3222
|
${chalk8.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
3223
3223
|
`));
|
|
3224
3224
|
} catch (error) {
|
|
3225
|
-
|
|
3225
|
+
spinner17.stop("");
|
|
3226
3226
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
3227
3227
|
p7.log.error(`Deployment failed: ${errorMsg}`);
|
|
3228
3228
|
if (errorMsg.includes("Build failed")) {
|
|
@@ -3231,8 +3231,8 @@ ${chalk8.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
|
3231
3231
|
initialValue: true
|
|
3232
3232
|
});
|
|
3233
3233
|
if (retry && !p7.isCancel(retry)) {
|
|
3234
|
-
|
|
3235
|
-
|
|
3234
|
+
spinner17.start("Analyzing Vercel build error...");
|
|
3235
|
+
spinner17.stop("Fix attempted");
|
|
3236
3236
|
}
|
|
3237
3237
|
}
|
|
3238
3238
|
p7.outro(chalk8.red("Deploy failed."));
|
|
@@ -3598,10 +3598,10 @@ you'll need a Meta Business account.
|
|
|
3598
3598
|
initialValue: true
|
|
3599
3599
|
});
|
|
3600
3600
|
if (!proceed || p9.isCancel(proceed)) return;
|
|
3601
|
-
const
|
|
3602
|
-
|
|
3601
|
+
const spinner17 = p9.spinner();
|
|
3602
|
+
spinner17.start("Generating QR code...");
|
|
3603
3603
|
try {
|
|
3604
|
-
|
|
3604
|
+
spinner17.stop("");
|
|
3605
3605
|
console.log(chalk10.cyan(`
|
|
3606
3606
|
\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
|
|
3607
3607
|
\u2551 \u2551
|
|
@@ -3633,7 +3633,7 @@ Scan this QR code with WhatsApp:
|
|
|
3633
3633
|
p9.log.success("WhatsApp connected!");
|
|
3634
3634
|
}
|
|
3635
3635
|
} catch (error) {
|
|
3636
|
-
|
|
3636
|
+
spinner17.stop("Error connecting WhatsApp");
|
|
3637
3637
|
p9.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
3638
3638
|
}
|
|
3639
3639
|
}
|
|
@@ -3664,15 +3664,15 @@ To create a Telegram bot:
|
|
|
3664
3664
|
}
|
|
3665
3665
|
});
|
|
3666
3666
|
if (p9.isCancel(token)) return;
|
|
3667
|
-
const
|
|
3668
|
-
|
|
3667
|
+
const spinner17 = p9.spinner();
|
|
3668
|
+
spinner17.start("Verifying bot token...");
|
|
3669
3669
|
try {
|
|
3670
3670
|
const response = await fetch(`https://api.telegram.org/bot${token}/getMe`);
|
|
3671
3671
|
const data = await response.json();
|
|
3672
3672
|
if (!data.ok) {
|
|
3673
3673
|
throw new Error(data.description || "Invalid token");
|
|
3674
3674
|
}
|
|
3675
|
-
|
|
3675
|
+
spinner17.stop("Bot verified!");
|
|
3676
3676
|
config.setChannelConfig("telegram", {
|
|
3677
3677
|
enabled: true,
|
|
3678
3678
|
botToken: token,
|
|
@@ -3680,7 +3680,7 @@ To create a Telegram bot:
|
|
|
3680
3680
|
});
|
|
3681
3681
|
p9.log.success(`Connected to @${data.result.username}`);
|
|
3682
3682
|
} catch (error) {
|
|
3683
|
-
|
|
3683
|
+
spinner17.stop("Verification failed");
|
|
3684
3684
|
p9.log.error(error instanceof Error ? error.message : "Invalid token");
|
|
3685
3685
|
}
|
|
3686
3686
|
}
|
|
@@ -3818,10 +3818,10 @@ This requires:
|
|
|
3818
3818
|
p9.log.info("iMessage support coming soon.");
|
|
3819
3819
|
}
|
|
3820
3820
|
async function startAllChannels(config) {
|
|
3821
|
-
const
|
|
3822
|
-
|
|
3821
|
+
const spinner17 = p9.spinner();
|
|
3822
|
+
spinner17.start("Starting channel gateway...");
|
|
3823
3823
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3824
|
-
|
|
3824
|
+
spinner17.stop("Gateway started");
|
|
3825
3825
|
console.log(chalk10.green(`
|
|
3826
3826
|
\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
|
|
3827
3827
|
\u2551 Gateway is running! \u2551
|
|
@@ -3835,10 +3835,10 @@ async function startAllChannels(config) {
|
|
|
3835
3835
|
`));
|
|
3836
3836
|
}
|
|
3837
3837
|
async function stopAllChannels(config) {
|
|
3838
|
-
const
|
|
3839
|
-
|
|
3838
|
+
const spinner17 = p9.spinner();
|
|
3839
|
+
spinner17.start("Stopping gateway...");
|
|
3840
3840
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3841
|
-
|
|
3841
|
+
spinner17.stop("Gateway stopped");
|
|
3842
3842
|
}
|
|
3843
3843
|
async function deployGatewayWizard(config) {
|
|
3844
3844
|
p9.log.info(chalk10.bold("Deploy Gateway to Cloud"));
|
|
@@ -3953,10 +3953,10 @@ import glob2 from "fast-glob";
|
|
|
3953
3953
|
import * as path8 from "path";
|
|
3954
3954
|
async function securityCommand() {
|
|
3955
3955
|
p11.intro(chalk12.bgCyan.black(" Security Audit "));
|
|
3956
|
-
const
|
|
3957
|
-
|
|
3956
|
+
const spinner17 = p11.spinner();
|
|
3957
|
+
spinner17.start("Scanning for security issues...");
|
|
3958
3958
|
const issues = await runSecurityScan();
|
|
3959
|
-
|
|
3959
|
+
spinner17.stop("Scan complete");
|
|
3960
3960
|
if (issues.length === 0) {
|
|
3961
3961
|
console.log(chalk12.green("\n\u2713 No security issues found!\n"));
|
|
3962
3962
|
displaySecurityScore(100);
|
|
@@ -4050,10 +4050,10 @@ async function generateCommand(type) {
|
|
|
4050
4050
|
validate: (v) => !v ? "Name is required" : void 0
|
|
4051
4051
|
});
|
|
4052
4052
|
if (p12.isCancel(name)) return;
|
|
4053
|
-
const
|
|
4054
|
-
|
|
4053
|
+
const spinner17 = p12.spinner();
|
|
4054
|
+
spinner17.start("Generating...");
|
|
4055
4055
|
await generateFile(generateType, name);
|
|
4056
|
-
|
|
4056
|
+
spinner17.stop(`Generated ${name}`);
|
|
4057
4057
|
p12.outro("");
|
|
4058
4058
|
}
|
|
4059
4059
|
async function generateFile(type, name) {
|
|
@@ -4198,13 +4198,13 @@ import * as p13 from "@clack/prompts";
|
|
|
4198
4198
|
import chalk14 from "chalk";
|
|
4199
4199
|
async function fixCommand() {
|
|
4200
4200
|
p13.intro(chalk14.bgCyan.black(" Auto-Fix "));
|
|
4201
|
-
const
|
|
4202
|
-
|
|
4201
|
+
const spinner17 = p13.spinner();
|
|
4202
|
+
spinner17.start("Analyzing code...");
|
|
4203
4203
|
const result = await runPatternCheck(true);
|
|
4204
4204
|
if (result.passed) {
|
|
4205
|
-
|
|
4205
|
+
spinner17.stop("No issues found!");
|
|
4206
4206
|
} else {
|
|
4207
|
-
|
|
4207
|
+
spinner17.stop(`Fixed ${result.violations.length} issues`);
|
|
4208
4208
|
}
|
|
4209
4209
|
p13.outro(chalk14.green("Done!"));
|
|
4210
4210
|
}
|
|
@@ -4405,8 +4405,8 @@ function generateColorPalette(hex) {
|
|
|
4405
4405
|
};
|
|
4406
4406
|
}
|
|
4407
4407
|
async function checkDesign() {
|
|
4408
|
-
const
|
|
4409
|
-
|
|
4408
|
+
const spinner17 = p14.spinner();
|
|
4409
|
+
spinner17.start("Checking design quality...");
|
|
4410
4410
|
const cwd = process.cwd();
|
|
4411
4411
|
const issues = [];
|
|
4412
4412
|
const glob3 = (await import("fast-glob")).default;
|
|
@@ -4432,7 +4432,7 @@ async function checkDesign() {
|
|
|
4432
4432
|
}
|
|
4433
4433
|
}
|
|
4434
4434
|
}
|
|
4435
|
-
|
|
4435
|
+
spinner17.stop("Check complete");
|
|
4436
4436
|
if (issues.length === 0) {
|
|
4437
4437
|
console.log(chalk15.green("\n\u2713 No design issues found!\n"));
|
|
4438
4438
|
} else {
|
|
@@ -4530,8 +4530,8 @@ async function detectMigrationTool() {
|
|
|
4530
4530
|
return null;
|
|
4531
4531
|
}
|
|
4532
4532
|
async function checkMigrationStatus(tool) {
|
|
4533
|
-
const
|
|
4534
|
-
|
|
4533
|
+
const spinner17 = p15.spinner();
|
|
4534
|
+
spinner17.start("Checking migration status...");
|
|
4535
4535
|
try {
|
|
4536
4536
|
let result;
|
|
4537
4537
|
switch (tool) {
|
|
@@ -4545,7 +4545,7 @@ async function checkMigrationStatus(tool) {
|
|
|
4545
4545
|
result = await execa8("npx", ["supabase", "migration", "list"], { cwd: process.cwd(), reject: false });
|
|
4546
4546
|
break;
|
|
4547
4547
|
}
|
|
4548
|
-
|
|
4548
|
+
spinner17.stop("Status check complete");
|
|
4549
4549
|
if (result?.stdout) {
|
|
4550
4550
|
console.log(chalk16.dim(result.stdout));
|
|
4551
4551
|
}
|
|
@@ -4553,7 +4553,7 @@ async function checkMigrationStatus(tool) {
|
|
|
4553
4553
|
console.log(chalk16.yellow(result.stderr));
|
|
4554
4554
|
}
|
|
4555
4555
|
} catch (error) {
|
|
4556
|
-
|
|
4556
|
+
spinner17.stop("Error checking status");
|
|
4557
4557
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4558
4558
|
}
|
|
4559
4559
|
}
|
|
@@ -4564,8 +4564,8 @@ async function generateMigration(tool) {
|
|
|
4564
4564
|
validate: (v) => !v ? "Name required" : void 0
|
|
4565
4565
|
});
|
|
4566
4566
|
if (p15.isCancel(name)) return;
|
|
4567
|
-
const
|
|
4568
|
-
|
|
4567
|
+
const spinner17 = p15.spinner();
|
|
4568
|
+
spinner17.start("Generating migration...");
|
|
4569
4569
|
try {
|
|
4570
4570
|
let result;
|
|
4571
4571
|
switch (tool) {
|
|
@@ -4579,18 +4579,18 @@ async function generateMigration(tool) {
|
|
|
4579
4579
|
result = await execa8("npx", ["supabase", "migration", "new", name], { cwd: process.cwd(), reject: false });
|
|
4580
4580
|
break;
|
|
4581
4581
|
}
|
|
4582
|
-
|
|
4582
|
+
spinner17.stop("Migration generated");
|
|
4583
4583
|
if (result?.stdout) {
|
|
4584
4584
|
console.log(chalk16.dim(result.stdout));
|
|
4585
4585
|
}
|
|
4586
4586
|
} catch (error) {
|
|
4587
|
-
|
|
4587
|
+
spinner17.stop("Error generating migration");
|
|
4588
4588
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4589
4589
|
}
|
|
4590
4590
|
}
|
|
4591
4591
|
async function pushMigration(tool) {
|
|
4592
|
-
const
|
|
4593
|
-
|
|
4592
|
+
const spinner17 = p15.spinner();
|
|
4593
|
+
spinner17.start("Pushing migration to database...");
|
|
4594
4594
|
try {
|
|
4595
4595
|
let result;
|
|
4596
4596
|
let migrationSql = "";
|
|
@@ -4616,7 +4616,7 @@ async function pushMigration(tool) {
|
|
|
4616
4616
|
break;
|
|
4617
4617
|
}
|
|
4618
4618
|
if (result?.exitCode !== 0) {
|
|
4619
|
-
|
|
4619
|
+
spinner17.stop("Migration push failed");
|
|
4620
4620
|
const errorOutput = result?.stderr || result?.stdout || "";
|
|
4621
4621
|
console.log(chalk16.red("\nError:\n"));
|
|
4622
4622
|
console.log(chalk16.dim(errorOutput));
|
|
@@ -4669,13 +4669,13 @@ Also saved to: ${sqlPath}
|
|
|
4669
4669
|
}
|
|
4670
4670
|
return;
|
|
4671
4671
|
}
|
|
4672
|
-
|
|
4672
|
+
spinner17.stop("Migration pushed successfully!");
|
|
4673
4673
|
if (result?.stdout) {
|
|
4674
4674
|
console.log(chalk16.dim(result.stdout));
|
|
4675
4675
|
}
|
|
4676
4676
|
p15.log.success("Database updated!");
|
|
4677
4677
|
} catch (error) {
|
|
4678
|
-
|
|
4678
|
+
spinner17.stop("Error");
|
|
4679
4679
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
4680
4680
|
p15.log.error(errorMsg);
|
|
4681
4681
|
const migrationSql = await extractMigrationSQL(tool, errorMsg);
|
|
@@ -4687,8 +4687,8 @@ Also saved to: ${sqlPath}
|
|
|
4687
4687
|
}
|
|
4688
4688
|
}
|
|
4689
4689
|
async function pullSchema(tool) {
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4690
|
+
const spinner17 = p15.spinner();
|
|
4691
|
+
spinner17.start("Pulling schema from database...");
|
|
4692
4692
|
try {
|
|
4693
4693
|
let result;
|
|
4694
4694
|
switch (tool) {
|
|
@@ -4702,12 +4702,12 @@ async function pullSchema(tool) {
|
|
|
4702
4702
|
result = await execa8("npx", ["supabase", "db", "pull"], { cwd: process.cwd(), reject: false });
|
|
4703
4703
|
break;
|
|
4704
4704
|
}
|
|
4705
|
-
|
|
4705
|
+
spinner17.stop("Schema pulled");
|
|
4706
4706
|
if (result?.stdout) {
|
|
4707
4707
|
console.log(chalk16.dim(result.stdout));
|
|
4708
4708
|
}
|
|
4709
4709
|
} catch (error) {
|
|
4710
|
-
|
|
4710
|
+
spinner17.stop("Error pulling schema");
|
|
4711
4711
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4712
4712
|
}
|
|
4713
4713
|
}
|
|
@@ -4770,21 +4770,21 @@ async function extractMigrationSQL(tool, errorOutput) {
|
|
|
4770
4770
|
}
|
|
4771
4771
|
return null;
|
|
4772
4772
|
}
|
|
4773
|
-
async function copyToClipboard(
|
|
4773
|
+
async function copyToClipboard(text16) {
|
|
4774
4774
|
try {
|
|
4775
4775
|
const platform = process.platform;
|
|
4776
4776
|
if (platform === "win32") {
|
|
4777
|
-
const proc = await execa8("clip", { input:
|
|
4777
|
+
const proc = await execa8("clip", { input: text16, reject: false });
|
|
4778
4778
|
return proc.exitCode === 0;
|
|
4779
4779
|
} else if (platform === "darwin") {
|
|
4780
|
-
const proc = await execa8("pbcopy", { input:
|
|
4780
|
+
const proc = await execa8("pbcopy", { input: text16, reject: false });
|
|
4781
4781
|
return proc.exitCode === 0;
|
|
4782
4782
|
} else {
|
|
4783
4783
|
try {
|
|
4784
|
-
const proc = await execa8("xclip", ["-selection", "clipboard"], { input:
|
|
4784
|
+
const proc = await execa8("xclip", ["-selection", "clipboard"], { input: text16, reject: false });
|
|
4785
4785
|
return proc.exitCode === 0;
|
|
4786
4786
|
} catch {
|
|
4787
|
-
const proc = await execa8("xsel", ["--clipboard", "--input"], { input:
|
|
4787
|
+
const proc = await execa8("xsel", ["--clipboard", "--input"], { input: text16, reject: false });
|
|
4788
4788
|
return proc.exitCode === 0;
|
|
4789
4789
|
}
|
|
4790
4790
|
}
|
|
@@ -4845,10 +4845,10 @@ async function prdMakerCommand() {
|
|
|
4845
4845
|
const input = await conductPRDInterview(inputMethod);
|
|
4846
4846
|
if (!input) return;
|
|
4847
4847
|
const anthropic = new Anthropic3({ apiKey: anthropicCreds.apiKey });
|
|
4848
|
-
const
|
|
4849
|
-
|
|
4848
|
+
const spinner17 = p16.spinner();
|
|
4849
|
+
spinner17.start("Generating professional PRD...");
|
|
4850
4850
|
const prd = await generatePRD(anthropic, input);
|
|
4851
|
-
|
|
4851
|
+
spinner17.stop("PRD generated!");
|
|
4852
4852
|
const filename = `${input.projectName.toLowerCase().replace(/[^a-z0-9]/g, "-")}-prd.md`;
|
|
4853
4853
|
const filepath = path12.join(process.cwd(), filename);
|
|
4854
4854
|
await fs13.writeFile(filepath, prd);
|
|
@@ -5053,11 +5053,11 @@ async function getVoiceInput2(prompt) {
|
|
|
5053
5053
|
initialValue: true
|
|
5054
5054
|
});
|
|
5055
5055
|
if (!ready || p16.isCancel(ready)) {
|
|
5056
|
-
const
|
|
5057
|
-
return p16.isCancel(
|
|
5056
|
+
const text17 = await p16.text({ message: "Type instead:" });
|
|
5057
|
+
return p16.isCancel(text17) ? null : text17;
|
|
5058
5058
|
}
|
|
5059
|
-
const
|
|
5060
|
-
|
|
5059
|
+
const spinner17 = p16.spinner();
|
|
5060
|
+
spinner17.start("\u{1F534} Recording... (press Ctrl+C to stop)");
|
|
5061
5061
|
try {
|
|
5062
5062
|
let transcription = "";
|
|
5063
5063
|
if (process.platform === "win32") {
|
|
@@ -5067,16 +5067,16 @@ async function getVoiceInput2(prompt) {
|
|
|
5067
5067
|
} else {
|
|
5068
5068
|
transcription = await recordWithLinux2();
|
|
5069
5069
|
}
|
|
5070
|
-
|
|
5070
|
+
spinner17.stop("Recording complete");
|
|
5071
5071
|
if (transcription) {
|
|
5072
5072
|
console.log(chalk17.green(`
|
|
5073
5073
|
Heard: "${transcription}"
|
|
5074
5074
|
`));
|
|
5075
|
-
const
|
|
5075
|
+
const confirm12 = await p16.confirm({
|
|
5076
5076
|
message: "Is this correct?",
|
|
5077
5077
|
initialValue: true
|
|
5078
5078
|
});
|
|
5079
|
-
if (
|
|
5079
|
+
if (confirm12 && !p16.isCancel(confirm12)) {
|
|
5080
5080
|
return transcription;
|
|
5081
5081
|
} else {
|
|
5082
5082
|
const retry = await p16.select({
|
|
@@ -5090,17 +5090,17 @@ async function getVoiceInput2(prompt) {
|
|
|
5090
5090
|
if (retry === "retry") {
|
|
5091
5091
|
return await getVoiceInput2(prompt);
|
|
5092
5092
|
} else {
|
|
5093
|
-
const
|
|
5094
|
-
return p16.isCancel(
|
|
5093
|
+
const text17 = await p16.text({ message: "Type your response:" });
|
|
5094
|
+
return p16.isCancel(text17) ? null : text17;
|
|
5095
5095
|
}
|
|
5096
5096
|
}
|
|
5097
5097
|
}
|
|
5098
5098
|
} catch (error) {
|
|
5099
|
-
|
|
5099
|
+
spinner17.stop("Recording failed");
|
|
5100
5100
|
console.log(chalk17.yellow("Voice input failed. Falling back to text."));
|
|
5101
5101
|
}
|
|
5102
|
-
const
|
|
5103
|
-
return p16.isCancel(
|
|
5102
|
+
const text16 = await p16.text({ message: prompt });
|
|
5103
|
+
return p16.isCancel(text16) ? null : text16;
|
|
5104
5104
|
}
|
|
5105
5105
|
async function recordWithWindowsSpeech2() {
|
|
5106
5106
|
const psScript = `
|
|
@@ -5155,9 +5155,9 @@ async function recordWithMacOS2() {
|
|
|
5155
5155
|
});
|
|
5156
5156
|
const txtFile = tempFile.replace(".wav", ".txt");
|
|
5157
5157
|
if (await fs13.pathExists(txtFile)) {
|
|
5158
|
-
const
|
|
5158
|
+
const text16 = await fs13.readFile(txtFile, "utf-8");
|
|
5159
5159
|
await fs13.remove(txtFile);
|
|
5160
|
-
return
|
|
5160
|
+
return text16.trim();
|
|
5161
5161
|
}
|
|
5162
5162
|
} catch {
|
|
5163
5163
|
}
|
|
@@ -5205,9 +5205,9 @@ async function recordWithLinux2() {
|
|
|
5205
5205
|
});
|
|
5206
5206
|
const txtFile = tempFile.replace(".wav", ".txt");
|
|
5207
5207
|
if (await fs13.pathExists(txtFile)) {
|
|
5208
|
-
const
|
|
5208
|
+
const text16 = await fs13.readFile(txtFile, "utf-8");
|
|
5209
5209
|
await fs13.remove(txtFile);
|
|
5210
|
-
return
|
|
5210
|
+
return text16.trim();
|
|
5211
5211
|
}
|
|
5212
5212
|
} catch {
|
|
5213
5213
|
}
|
|
@@ -5337,10 +5337,10 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5337
5337
|
\u255A\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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
5338
5338
|
`));
|
|
5339
5339
|
const anthropic = new Anthropic4({ apiKey: anthropicCreds.apiKey });
|
|
5340
|
-
const
|
|
5341
|
-
|
|
5340
|
+
const spinner17 = p17.spinner();
|
|
5341
|
+
spinner17.start("Analyzing PRD...");
|
|
5342
5342
|
const buildPlan = await analyzePRD(anthropic, prdContent);
|
|
5343
|
-
|
|
5343
|
+
spinner17.stop("PRD analyzed");
|
|
5344
5344
|
displayBuildPlan(buildPlan);
|
|
5345
5345
|
const totalAgents = buildPlan.waves.reduce((sum, w) => sum + w.agents.length, 0);
|
|
5346
5346
|
const useParallel = !options.sequential && totalAgents > 2;
|
|
@@ -5366,9 +5366,9 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5366
5366
|
}
|
|
5367
5367
|
await fs14.ensureDir(projectPath);
|
|
5368
5368
|
process.chdir(projectPath);
|
|
5369
|
-
|
|
5369
|
+
spinner17.start("Initializing project...");
|
|
5370
5370
|
await execa9("git", ["init"], { cwd: projectPath });
|
|
5371
|
-
|
|
5371
|
+
spinner17.stop("Project initialized");
|
|
5372
5372
|
await runSetupPhase(anthropic, buildPlan, projectPath);
|
|
5373
5373
|
const startTime = Date.now();
|
|
5374
5374
|
if (useParallel) {
|
|
@@ -5377,9 +5377,9 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5377
5377
|
await executeSequentialBuild(anthropic, buildPlan, projectPath, config);
|
|
5378
5378
|
}
|
|
5379
5379
|
await runIntegrationPhase(anthropic, buildPlan, projectPath);
|
|
5380
|
-
|
|
5380
|
+
spinner17.start("Installing dependencies...");
|
|
5381
5381
|
await execa9("npm", ["install"], { cwd: projectPath, reject: false });
|
|
5382
|
-
|
|
5382
|
+
spinner17.stop("Dependencies installed");
|
|
5383
5383
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
5384
5384
|
const minutes = Math.floor(elapsed / 60);
|
|
5385
5385
|
const seconds = elapsed % 60;
|
|
@@ -5450,8 +5450,8 @@ Think about:
|
|
|
5450
5450
|
- What needs data from other features (dashboards, reports)?`
|
|
5451
5451
|
}]
|
|
5452
5452
|
});
|
|
5453
|
-
const
|
|
5454
|
-
const jsonMatch =
|
|
5453
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
5454
|
+
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
5455
5455
|
if (!jsonMatch) {
|
|
5456
5456
|
throw new Error("Failed to parse PRD analysis");
|
|
5457
5457
|
}
|
|
@@ -5490,8 +5490,8 @@ function displayBuildPlan(plan) {
|
|
|
5490
5490
|
`));
|
|
5491
5491
|
}
|
|
5492
5492
|
async function runSetupPhase(anthropic, plan, projectPath) {
|
|
5493
|
-
const
|
|
5494
|
-
|
|
5493
|
+
const spinner17 = p17.spinner();
|
|
5494
|
+
spinner17.start("Setting up project structure...");
|
|
5495
5495
|
await fs14.ensureDir(path13.join(projectPath, "src/app"));
|
|
5496
5496
|
await fs14.ensureDir(path13.join(projectPath, "src/components/ui"));
|
|
5497
5497
|
await fs14.ensureDir(path13.join(projectPath, "src/lib"));
|
|
@@ -5535,7 +5535,7 @@ Use TypeScript. Include all necessary imports.`
|
|
|
5535
5535
|
await writeFilesFromResponse(setupText, projectPath);
|
|
5536
5536
|
await execa9("git", ["add", "."], { cwd: projectPath });
|
|
5537
5537
|
await execa9("git", ["commit", "-m", "Initial setup"], { cwd: projectPath });
|
|
5538
|
-
|
|
5538
|
+
spinner17.stop("Project structure ready");
|
|
5539
5539
|
}
|
|
5540
5540
|
async function executeParallelBuild(anthropic, plan, projectPath, config) {
|
|
5541
5541
|
for (const wave of plan.waves) {
|
|
@@ -5585,15 +5585,15 @@ async function executeAgentWithProgress(anthropic, agent, projectPath, plan, dis
|
|
|
5585
5585
|
async function executeSequentialBuild(anthropic, plan, projectPath, config) {
|
|
5586
5586
|
for (const wave of plan.waves) {
|
|
5587
5587
|
for (const agent of wave.agents) {
|
|
5588
|
-
const
|
|
5589
|
-
|
|
5588
|
+
const spinner17 = p17.spinner();
|
|
5589
|
+
spinner17.start(`Building ${agent.name}...`);
|
|
5590
5590
|
try {
|
|
5591
5591
|
await executeAgent(anthropic, agent, projectPath, plan, (progress, action) => {
|
|
5592
|
-
|
|
5592
|
+
spinner17.message = `${agent.name}: ${action} (${progress}%)`;
|
|
5593
5593
|
});
|
|
5594
|
-
|
|
5594
|
+
spinner17.stop(`\u2713 ${agent.name} complete`);
|
|
5595
5595
|
} catch (error) {
|
|
5596
|
-
|
|
5596
|
+
spinner17.stop(`\u2717 ${agent.name} failed`);
|
|
5597
5597
|
throw error;
|
|
5598
5598
|
}
|
|
5599
5599
|
await mergeWaveBranches([agent], projectPath);
|
|
@@ -5695,9 +5695,9 @@ Start with the index.ts that exports everything.`
|
|
|
5695
5695
|
max_tokens: 8192,
|
|
5696
5696
|
messages
|
|
5697
5697
|
});
|
|
5698
|
-
const
|
|
5699
|
-
messages.push({ role: "assistant", content:
|
|
5700
|
-
const questionMatch =
|
|
5698
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
5699
|
+
messages.push({ role: "assistant", content: text16 });
|
|
5700
|
+
const questionMatch = text16.match(/<<<ASK_USER>>>([\s\S]*?)<<<END_ASK>>>/);
|
|
5701
5701
|
if (questionMatch) {
|
|
5702
5702
|
const questionBlock = questionMatch[1];
|
|
5703
5703
|
const questionLine = questionBlock.match(/question:\s*(.+)/);
|
|
@@ -5726,7 +5726,7 @@ Now continue building the feature with this choice. Generate the code files.`
|
|
|
5726
5726
|
}
|
|
5727
5727
|
}
|
|
5728
5728
|
if (onProgress) onProgress(60, "Writing files...");
|
|
5729
|
-
const files = await writeFilesFromResponse(
|
|
5729
|
+
const files = await writeFilesFromResponse(text16, projectPath);
|
|
5730
5730
|
agent.files = files;
|
|
5731
5731
|
if (onProgress) onProgress(80, "Validating...");
|
|
5732
5732
|
if (files.length === 0) {
|
|
@@ -5809,8 +5809,8 @@ Common fixes:
|
|
|
5809
5809
|
- Timeout \u2192 retry with simpler approach`
|
|
5810
5810
|
}]
|
|
5811
5811
|
});
|
|
5812
|
-
const
|
|
5813
|
-
const jsonMatch =
|
|
5812
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
5813
|
+
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
5814
5814
|
if (!jsonMatch) {
|
|
5815
5815
|
return {
|
|
5816
5816
|
canFix: false,
|
|
@@ -5822,8 +5822,8 @@ Common fixes:
|
|
|
5822
5822
|
return JSON.parse(jsonMatch[0]);
|
|
5823
5823
|
}
|
|
5824
5824
|
async function runIntegrationPhase(anthropic, plan, projectPath) {
|
|
5825
|
-
const
|
|
5826
|
-
|
|
5825
|
+
const spinner17 = p17.spinner();
|
|
5826
|
+
spinner17.start("Running integration...");
|
|
5827
5827
|
const features = plan.waves.flatMap((w) => w.agents);
|
|
5828
5828
|
const response = await anthropic.messages.create({
|
|
5829
5829
|
model: "claude-sonnet-4-20250514",
|
|
@@ -5850,11 +5850,11 @@ content
|
|
|
5850
5850
|
Make sure all features are accessible and properly connected.`
|
|
5851
5851
|
}]
|
|
5852
5852
|
});
|
|
5853
|
-
const
|
|
5854
|
-
await writeFilesFromResponse(
|
|
5853
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
5854
|
+
await writeFilesFromResponse(text16, projectPath);
|
|
5855
5855
|
await execa9("git", ["add", "."], { cwd: projectPath });
|
|
5856
5856
|
await execa9("git", ["commit", "-m", "Integration: wire up all features"], { cwd: projectPath, reject: false });
|
|
5857
|
-
|
|
5857
|
+
spinner17.stop("Integration complete");
|
|
5858
5858
|
}
|
|
5859
5859
|
async function mergeWaveBranches(agents, projectPath) {
|
|
5860
5860
|
for (const agent of agents) {
|
|
@@ -5943,11 +5943,11 @@ var ProgressDisplay = class {
|
|
|
5943
5943
|
return chalk18.green("\u2588".repeat(filled)) + chalk18.gray("\u2591".repeat(empty));
|
|
5944
5944
|
}
|
|
5945
5945
|
};
|
|
5946
|
-
async function writeFilesFromResponse(
|
|
5946
|
+
async function writeFilesFromResponse(text16, projectPath) {
|
|
5947
5947
|
const fileRegex = /<<<FILE:\s*(.+?)>>>([\s\S]*?)<<<END_FILE>>>/g;
|
|
5948
5948
|
const files = [];
|
|
5949
5949
|
let match;
|
|
5950
|
-
while ((match = fileRegex.exec(
|
|
5950
|
+
while ((match = fileRegex.exec(text16)) !== null) {
|
|
5951
5951
|
const filePath = path13.join(projectPath, match[1].trim());
|
|
5952
5952
|
const content = match[2].trim();
|
|
5953
5953
|
await fs14.ensureDir(path13.dirname(filePath));
|
|
@@ -6638,17 +6638,17 @@ async function installIntegration(integration, config) {
|
|
|
6638
6638
|
steps.push({ name: "Configure credentials", done: false });
|
|
6639
6639
|
}
|
|
6640
6640
|
steps.push({ name: "Setup integration", done: false });
|
|
6641
|
-
const
|
|
6641
|
+
const spinner17 = p18.spinner();
|
|
6642
6642
|
if (integration.packages && integration.packages.length > 0) {
|
|
6643
|
-
|
|
6643
|
+
spinner17.start(`Installing ${integration.packages.join(", ")}...`);
|
|
6644
6644
|
try {
|
|
6645
6645
|
await execa10("npm", ["install", ...integration.packages], {
|
|
6646
6646
|
cwd: process.cwd(),
|
|
6647
6647
|
reject: false
|
|
6648
6648
|
});
|
|
6649
|
-
|
|
6649
|
+
spinner17.stop(`\u2713 Packages installed`);
|
|
6650
6650
|
} catch {
|
|
6651
|
-
|
|
6651
|
+
spinner17.stop(`\u2713 Packages installed (or already present)`);
|
|
6652
6652
|
}
|
|
6653
6653
|
}
|
|
6654
6654
|
if (integration.envVars.length > 0) {
|
|
@@ -6676,7 +6676,7 @@ async function installIntegration(integration, config) {
|
|
|
6676
6676
|
console.log(chalk19.green(` \u2713 Credentials saved to .env.local
|
|
6677
6677
|
`));
|
|
6678
6678
|
}
|
|
6679
|
-
|
|
6679
|
+
spinner17.start("Generating setup code...");
|
|
6680
6680
|
const setupCode = await generateSetupCode(integration);
|
|
6681
6681
|
if (setupCode) {
|
|
6682
6682
|
for (const file of setupCode) {
|
|
@@ -6684,7 +6684,7 @@ async function installIntegration(integration, config) {
|
|
|
6684
6684
|
await fs15.writeFile(file.path, file.content);
|
|
6685
6685
|
}
|
|
6686
6686
|
}
|
|
6687
|
-
|
|
6687
|
+
spinner17.stop("\u2713 Setup complete");
|
|
6688
6688
|
console.log(chalk19.green(`
|
|
6689
6689
|
\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\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
|
|
6690
6690
|
\u2551 \u2713 ${integration.name} installed successfully!
|
|
@@ -6829,9 +6829,514 @@ function sleep2(ms) {
|
|
|
6829
6829
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6830
6830
|
}
|
|
6831
6831
|
|
|
6832
|
-
// src/
|
|
6832
|
+
// src/commands/website.ts
|
|
6833
6833
|
import * as p19 from "@clack/prompts";
|
|
6834
|
+
import chalk20 from "chalk";
|
|
6835
|
+
import * as fs16 from "fs-extra";
|
|
6836
|
+
import * as path15 from "path";
|
|
6834
6837
|
import Anthropic5 from "@anthropic-ai/sdk";
|
|
6838
|
+
import { execa as execa11 } from "execa";
|
|
6839
|
+
var TEMPLATES = [
|
|
6840
|
+
{
|
|
6841
|
+
id: "landing",
|
|
6842
|
+
name: "Landing Page",
|
|
6843
|
+
description: "Convert visitors into customers",
|
|
6844
|
+
icon: "\u{1F680}",
|
|
6845
|
+
sections: ["hero", "features", "testimonials", "pricing", "cta", "footer"],
|
|
6846
|
+
style: "bold"
|
|
6847
|
+
},
|
|
6848
|
+
{
|
|
6849
|
+
id: "saas",
|
|
6850
|
+
name: "SaaS Website",
|
|
6851
|
+
description: "Software product marketing site",
|
|
6852
|
+
icon: "\u{1F4BB}",
|
|
6853
|
+
sections: ["hero", "features", "how-it-works", "pricing", "faq", "testimonials", "cta", "footer"],
|
|
6854
|
+
style: "minimal"
|
|
6855
|
+
},
|
|
6856
|
+
{
|
|
6857
|
+
id: "portfolio",
|
|
6858
|
+
name: "Portfolio",
|
|
6859
|
+
description: "Showcase your work",
|
|
6860
|
+
icon: "\u{1F3A8}",
|
|
6861
|
+
sections: ["hero", "about", "projects", "skills", "contact", "footer"],
|
|
6862
|
+
style: "elegant"
|
|
6863
|
+
},
|
|
6864
|
+
{
|
|
6865
|
+
id: "agency",
|
|
6866
|
+
name: "Agency Website",
|
|
6867
|
+
description: "Professional services company",
|
|
6868
|
+
icon: "\u{1F3E2}",
|
|
6869
|
+
sections: ["hero", "services", "portfolio", "team", "testimonials", "contact", "footer"],
|
|
6870
|
+
style: "corporate"
|
|
6871
|
+
},
|
|
6872
|
+
{
|
|
6873
|
+
id: "startup",
|
|
6874
|
+
name: "Startup Page",
|
|
6875
|
+
description: "Early stage company",
|
|
6876
|
+
icon: "\u26A1",
|
|
6877
|
+
sections: ["hero", "problem", "solution", "features", "team", "waitlist", "footer"],
|
|
6878
|
+
style: "bold"
|
|
6879
|
+
},
|
|
6880
|
+
{
|
|
6881
|
+
id: "restaurant",
|
|
6882
|
+
name: "Restaurant",
|
|
6883
|
+
description: "Food & dining establishment",
|
|
6884
|
+
icon: "\u{1F37D}\uFE0F",
|
|
6885
|
+
sections: ["hero", "menu", "about", "gallery", "reservations", "location", "footer"],
|
|
6886
|
+
style: "elegant"
|
|
6887
|
+
},
|
|
6888
|
+
{
|
|
6889
|
+
id: "ecommerce",
|
|
6890
|
+
name: "E-commerce",
|
|
6891
|
+
description: "Online store",
|
|
6892
|
+
icon: "\u{1F6D2}",
|
|
6893
|
+
sections: ["hero", "featured-products", "categories", "bestsellers", "newsletter", "footer"],
|
|
6894
|
+
style: "minimal"
|
|
6895
|
+
},
|
|
6896
|
+
{
|
|
6897
|
+
id: "blog",
|
|
6898
|
+
name: "Blog",
|
|
6899
|
+
description: "Content & articles",
|
|
6900
|
+
icon: "\u{1F4DD}",
|
|
6901
|
+
sections: ["hero", "featured-posts", "categories", "newsletter", "about", "footer"],
|
|
6902
|
+
style: "minimal"
|
|
6903
|
+
},
|
|
6904
|
+
{
|
|
6905
|
+
id: "event",
|
|
6906
|
+
name: "Event Page",
|
|
6907
|
+
description: "Conference or meetup",
|
|
6908
|
+
icon: "\u{1F389}",
|
|
6909
|
+
sections: ["hero", "speakers", "schedule", "venue", "sponsors", "tickets", "footer"],
|
|
6910
|
+
style: "bold"
|
|
6911
|
+
},
|
|
6912
|
+
{
|
|
6913
|
+
id: "nonprofit",
|
|
6914
|
+
name: "Nonprofit",
|
|
6915
|
+
description: "Charity or cause",
|
|
6916
|
+
icon: "\u{1F49A}",
|
|
6917
|
+
sections: ["hero", "mission", "impact", "programs", "donate", "volunteer", "footer"],
|
|
6918
|
+
style: "elegant"
|
|
6919
|
+
},
|
|
6920
|
+
{
|
|
6921
|
+
id: "personal",
|
|
6922
|
+
name: "Personal Website",
|
|
6923
|
+
description: "Your online presence",
|
|
6924
|
+
icon: "\u{1F464}",
|
|
6925
|
+
sections: ["hero", "about", "experience", "projects", "blog", "contact", "footer"],
|
|
6926
|
+
style: "minimal"
|
|
6927
|
+
},
|
|
6928
|
+
{
|
|
6929
|
+
id: "coming-soon",
|
|
6930
|
+
name: "Coming Soon",
|
|
6931
|
+
description: "Pre-launch teaser",
|
|
6932
|
+
icon: "\u23F3",
|
|
6933
|
+
sections: ["hero", "countdown", "features-preview", "waitlist", "footer"],
|
|
6934
|
+
style: "bold"
|
|
6935
|
+
},
|
|
6936
|
+
{
|
|
6937
|
+
id: "custom",
|
|
6938
|
+
name: "Custom",
|
|
6939
|
+
description: "Build from scratch with AI",
|
|
6940
|
+
icon: "\u2728",
|
|
6941
|
+
sections: [],
|
|
6942
|
+
style: "minimal"
|
|
6943
|
+
}
|
|
6944
|
+
];
|
|
6945
|
+
async function websiteCommand() {
|
|
6946
|
+
const config = new Config();
|
|
6947
|
+
if (!config.isConfigured()) {
|
|
6948
|
+
p19.log.error("Please run `codebakers setup` first.");
|
|
6949
|
+
return;
|
|
6950
|
+
}
|
|
6951
|
+
const anthropicCreds = config.getCredentials("anthropic");
|
|
6952
|
+
if (!anthropicCreds?.apiKey) {
|
|
6953
|
+
p19.log.error("Anthropic API key not configured.");
|
|
6954
|
+
return;
|
|
6955
|
+
}
|
|
6956
|
+
console.log(chalk20.cyan(`
|
|
6957
|
+
\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\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
|
|
6958
|
+
\u2551 \u{1F310} WEBSITE BUILDER \u2551
|
|
6959
|
+
\u2551 \u2551
|
|
6960
|
+
\u2551 Describe your website in plain English. \u2551
|
|
6961
|
+
\u2551 AI builds it in minutes. \u2551
|
|
6962
|
+
\u255A\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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
6963
|
+
`));
|
|
6964
|
+
const approach = await p19.select({
|
|
6965
|
+
message: "How would you like to build your website?",
|
|
6966
|
+
options: [
|
|
6967
|
+
{ value: "describe", label: "\u{1F4AC} Describe it", hint: "Tell me what you want in plain English" },
|
|
6968
|
+
{ value: "template", label: "\u{1F4CB} Start from template", hint: "Choose a pre-made structure" },
|
|
6969
|
+
{ value: "url", label: "\u{1F517} Clone a design", hint: "Describe a site you like" }
|
|
6970
|
+
]
|
|
6971
|
+
});
|
|
6972
|
+
if (p19.isCancel(approach)) return;
|
|
6973
|
+
const anthropic = new Anthropic5({ apiKey: anthropicCreds.apiKey });
|
|
6974
|
+
let websiteSpec;
|
|
6975
|
+
if (approach === "describe") {
|
|
6976
|
+
websiteSpec = await describeWebsite(anthropic);
|
|
6977
|
+
} else if (approach === "template") {
|
|
6978
|
+
websiteSpec = await templateWebsite(anthropic);
|
|
6979
|
+
} else {
|
|
6980
|
+
websiteSpec = await cloneDesign(anthropic);
|
|
6981
|
+
}
|
|
6982
|
+
if (!websiteSpec) return;
|
|
6983
|
+
console.log(chalk20.cyan(`
|
|
6984
|
+
\u{1F4CB} Website Plan:
|
|
6985
|
+
`));
|
|
6986
|
+
console.log(chalk20.bold(` ${websiteSpec.name}`));
|
|
6987
|
+
console.log(chalk20.dim(` ${websiteSpec.description}
|
|
6988
|
+
`));
|
|
6989
|
+
console.log(chalk20.dim(` Style: ${websiteSpec.style}`));
|
|
6990
|
+
console.log(chalk20.dim(` Sections: ${websiteSpec.sections.join(", ")}
|
|
6991
|
+
`));
|
|
6992
|
+
const confirm12 = await p19.confirm({
|
|
6993
|
+
message: "Build this website?",
|
|
6994
|
+
initialValue: true
|
|
6995
|
+
});
|
|
6996
|
+
if (!confirm12 || p19.isCancel(confirm12)) return;
|
|
6997
|
+
await buildWebsite(anthropic, websiteSpec, config);
|
|
6998
|
+
}
|
|
6999
|
+
async function describeWebsite(anthropic) {
|
|
7000
|
+
console.log(chalk20.dim("\n Describe your website. Be as detailed as you want.\n"));
|
|
7001
|
+
console.log(chalk20.dim(" Examples:"));
|
|
7002
|
+
console.log(chalk20.dim(' \u2022 "A landing page for my AI writing tool called WriteBot"'));
|
|
7003
|
+
console.log(chalk20.dim(' \u2022 "Portfolio site for a photographer, dark theme, minimal"'));
|
|
7004
|
+
console.log(chalk20.dim(' \u2022 "Coffee shop website with menu, location, and online ordering"\n'));
|
|
7005
|
+
const description = await textWithVoice({
|
|
7006
|
+
message: "Describe your website:",
|
|
7007
|
+
placeholder: "A landing page for..."
|
|
7008
|
+
});
|
|
7009
|
+
if (p19.isCancel(description)) return null;
|
|
7010
|
+
const spinner17 = p19.spinner();
|
|
7011
|
+
spinner17.start("Understanding your vision...");
|
|
7012
|
+
const response = await anthropic.messages.create({
|
|
7013
|
+
model: "claude-sonnet-4-20250514",
|
|
7014
|
+
max_tokens: 2048,
|
|
7015
|
+
messages: [{
|
|
7016
|
+
role: "user",
|
|
7017
|
+
content: `Based on this website description, create a detailed specification.
|
|
7018
|
+
|
|
7019
|
+
Description: "${description}"
|
|
7020
|
+
|
|
7021
|
+
Return JSON only:
|
|
7022
|
+
{
|
|
7023
|
+
"name": "Project name (kebab-case)",
|
|
7024
|
+
"description": "One line description",
|
|
7025
|
+
"style": "minimal | bold | elegant | playful | corporate",
|
|
7026
|
+
"colorScheme": {
|
|
7027
|
+
"primary": "#hex",
|
|
7028
|
+
"secondary": "#hex",
|
|
7029
|
+
"accent": "#hex",
|
|
7030
|
+
"background": "#hex",
|
|
7031
|
+
"text": "#hex"
|
|
7032
|
+
},
|
|
7033
|
+
"sections": ["hero", "features", ...],
|
|
7034
|
+
"content": {
|
|
7035
|
+
"hero": {
|
|
7036
|
+
"headline": "...",
|
|
7037
|
+
"subheadline": "...",
|
|
7038
|
+
"cta": "..."
|
|
7039
|
+
},
|
|
7040
|
+
"features": [
|
|
7041
|
+
{ "title": "...", "description": "...", "icon": "..." }
|
|
7042
|
+
]
|
|
7043
|
+
// etc for each section
|
|
7044
|
+
},
|
|
7045
|
+
"features": ["responsive", "dark-mode", "animations", etc]
|
|
7046
|
+
}
|
|
7047
|
+
|
|
7048
|
+
Make the content compelling and professional. Use appropriate sections for the type of website.`
|
|
7049
|
+
}]
|
|
7050
|
+
});
|
|
7051
|
+
spinner17.stop("Got it!");
|
|
7052
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7053
|
+
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7054
|
+
if (!jsonMatch) {
|
|
7055
|
+
p19.log.error("Failed to understand website description");
|
|
7056
|
+
return null;
|
|
7057
|
+
}
|
|
7058
|
+
return JSON.parse(jsonMatch[0]);
|
|
7059
|
+
}
|
|
7060
|
+
async function templateWebsite(anthropic) {
|
|
7061
|
+
const template = await p19.select({
|
|
7062
|
+
message: "Choose a template:",
|
|
7063
|
+
options: TEMPLATES.map((t) => ({
|
|
7064
|
+
value: t.id,
|
|
7065
|
+
label: `${t.icon} ${t.name}`,
|
|
7066
|
+
hint: t.description
|
|
7067
|
+
}))
|
|
7068
|
+
});
|
|
7069
|
+
if (p19.isCancel(template)) return null;
|
|
7070
|
+
const selectedTemplate = TEMPLATES.find((t) => t.id === template);
|
|
7071
|
+
const name = await p19.text({
|
|
7072
|
+
message: "What is your business/project name?",
|
|
7073
|
+
placeholder: "My Awesome Project"
|
|
7074
|
+
});
|
|
7075
|
+
if (p19.isCancel(name)) return null;
|
|
7076
|
+
const tagline = await p19.text({
|
|
7077
|
+
message: "Tagline or one-line description:",
|
|
7078
|
+
placeholder: "The best solution for..."
|
|
7079
|
+
});
|
|
7080
|
+
if (p19.isCancel(tagline)) return null;
|
|
7081
|
+
const details = await textWithVoice({
|
|
7082
|
+
message: "Any other details? (or press enter to skip)",
|
|
7083
|
+
placeholder: "We help startups with..., Our colors are blue and white..."
|
|
7084
|
+
});
|
|
7085
|
+
if (p19.isCancel(details)) return null;
|
|
7086
|
+
const spinner17 = p19.spinner();
|
|
7087
|
+
spinner17.start("Customizing template...");
|
|
7088
|
+
const response = await anthropic.messages.create({
|
|
7089
|
+
model: "claude-sonnet-4-20250514",
|
|
7090
|
+
max_tokens: 2048,
|
|
7091
|
+
messages: [{
|
|
7092
|
+
role: "user",
|
|
7093
|
+
content: `Create a website spec based on this template and customization.
|
|
7094
|
+
|
|
7095
|
+
Template: ${selectedTemplate.name}
|
|
7096
|
+
Sections: ${selectedTemplate.sections.join(", ")}
|
|
7097
|
+
Default Style: ${selectedTemplate.style}
|
|
7098
|
+
|
|
7099
|
+
Business Name: ${name}
|
|
7100
|
+
Tagline: ${tagline}
|
|
7101
|
+
Additional Details: ${details || "None provided"}
|
|
7102
|
+
|
|
7103
|
+
Return JSON only:
|
|
7104
|
+
{
|
|
7105
|
+
"name": "project-name-kebab",
|
|
7106
|
+
"description": "${tagline}",
|
|
7107
|
+
"style": "${selectedTemplate.style}",
|
|
7108
|
+
"colorScheme": {
|
|
7109
|
+
"primary": "#hex",
|
|
7110
|
+
"secondary": "#hex",
|
|
7111
|
+
"accent": "#hex",
|
|
7112
|
+
"background": "#hex",
|
|
7113
|
+
"text": "#hex"
|
|
7114
|
+
},
|
|
7115
|
+
"sections": ${JSON.stringify(selectedTemplate.sections)},
|
|
7116
|
+
"content": {
|
|
7117
|
+
// Content for each section, tailored to the business
|
|
7118
|
+
},
|
|
7119
|
+
"features": ["responsive", "dark-mode", etc]
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
Make the content specific and compelling for this business.`
|
|
7123
|
+
}]
|
|
7124
|
+
});
|
|
7125
|
+
spinner17.stop("Template customized!");
|
|
7126
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7127
|
+
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7128
|
+
if (!jsonMatch) {
|
|
7129
|
+
p19.log.error("Failed to customize template");
|
|
7130
|
+
return null;
|
|
7131
|
+
}
|
|
7132
|
+
return JSON.parse(jsonMatch[0]);
|
|
7133
|
+
}
|
|
7134
|
+
async function cloneDesign(anthropic) {
|
|
7135
|
+
console.log(chalk20.dim("\n Describe a website design you like.\n"));
|
|
7136
|
+
console.log(chalk20.dim(" Examples:"));
|
|
7137
|
+
console.log(chalk20.dim(' \u2022 "Like Linear.app - minimal, clean, dark mode"'));
|
|
7138
|
+
console.log(chalk20.dim(' \u2022 "Like Stripe - professional, lots of gradients"'));
|
|
7139
|
+
console.log(chalk20.dim(' \u2022 "Like Notion - simple, friendly, illustrated"\n'));
|
|
7140
|
+
const inspiration = await textWithVoice({
|
|
7141
|
+
message: "What site do you want to be inspired by?",
|
|
7142
|
+
placeholder: "Like Linear.app but for..."
|
|
7143
|
+
});
|
|
7144
|
+
if (p19.isCancel(inspiration)) return null;
|
|
7145
|
+
const purpose = await p19.text({
|
|
7146
|
+
message: "What is YOUR website for?",
|
|
7147
|
+
placeholder: "A project management tool for designers"
|
|
7148
|
+
});
|
|
7149
|
+
if (p19.isCancel(purpose)) return null;
|
|
7150
|
+
const spinner17 = p19.spinner();
|
|
7151
|
+
spinner17.start("Analyzing design inspiration...");
|
|
7152
|
+
const response = await anthropic.messages.create({
|
|
7153
|
+
model: "claude-sonnet-4-20250514",
|
|
7154
|
+
max_tokens: 2048,
|
|
7155
|
+
messages: [{
|
|
7156
|
+
role: "user",
|
|
7157
|
+
content: `Create a website spec inspired by another site's design.
|
|
7158
|
+
|
|
7159
|
+
Inspiration: "${inspiration}"
|
|
7160
|
+
Purpose: "${purpose}"
|
|
7161
|
+
|
|
7162
|
+
Analyze the design style of the inspiration (based on your knowledge of popular websites) and create a spec that captures that aesthetic for this new purpose.
|
|
7163
|
+
|
|
7164
|
+
Return JSON only:
|
|
7165
|
+
{
|
|
7166
|
+
"name": "project-name-kebab",
|
|
7167
|
+
"description": "One line description",
|
|
7168
|
+
"style": "minimal | bold | elegant | playful | corporate",
|
|
7169
|
+
"colorScheme": {
|
|
7170
|
+
"primary": "#hex - inspired by the reference",
|
|
7171
|
+
"secondary": "#hex",
|
|
7172
|
+
"accent": "#hex",
|
|
7173
|
+
"background": "#hex",
|
|
7174
|
+
"text": "#hex"
|
|
7175
|
+
},
|
|
7176
|
+
"sections": ["appropriate sections for the purpose"],
|
|
7177
|
+
"content": {
|
|
7178
|
+
// Compelling content for each section
|
|
7179
|
+
},
|
|
7180
|
+
"features": ["responsive", "dark-mode", "animations", "gradients", etc - based on inspiration]
|
|
7181
|
+
}
|
|
7182
|
+
|
|
7183
|
+
Capture the FEEL of the inspiration but make it original.`
|
|
7184
|
+
}]
|
|
7185
|
+
});
|
|
7186
|
+
spinner17.stop("Design analyzed!");
|
|
7187
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7188
|
+
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7189
|
+
if (!jsonMatch) {
|
|
7190
|
+
p19.log.error("Failed to analyze design");
|
|
7191
|
+
return null;
|
|
7192
|
+
}
|
|
7193
|
+
return JSON.parse(jsonMatch[0]);
|
|
7194
|
+
}
|
|
7195
|
+
async function buildWebsite(anthropic, spec, config) {
|
|
7196
|
+
const projectPath = path15.join(process.cwd(), spec.name);
|
|
7197
|
+
if (await fs16.pathExists(projectPath)) {
|
|
7198
|
+
const overwrite = await p19.confirm({
|
|
7199
|
+
message: `${spec.name} already exists. Overwrite?`,
|
|
7200
|
+
initialValue: false
|
|
7201
|
+
});
|
|
7202
|
+
if (!overwrite || p19.isCancel(overwrite)) return;
|
|
7203
|
+
await fs16.remove(projectPath);
|
|
7204
|
+
}
|
|
7205
|
+
console.log(chalk20.cyan(`
|
|
7206
|
+
\u{1F3D7}\uFE0F Building ${spec.name}...
|
|
7207
|
+
`));
|
|
7208
|
+
const spinner17 = p19.spinner();
|
|
7209
|
+
spinner17.start("Creating Next.js project...");
|
|
7210
|
+
await execa11("npx", [
|
|
7211
|
+
"create-next-app@latest",
|
|
7212
|
+
spec.name,
|
|
7213
|
+
"--typescript",
|
|
7214
|
+
"--tailwind",
|
|
7215
|
+
"--eslint",
|
|
7216
|
+
"--app",
|
|
7217
|
+
"--src-dir",
|
|
7218
|
+
"--import-alias",
|
|
7219
|
+
"@/*",
|
|
7220
|
+
"--no-git"
|
|
7221
|
+
], { cwd: process.cwd(), reject: false });
|
|
7222
|
+
spinner17.stop("Project created");
|
|
7223
|
+
spinner17.start("Setting up shadcn/ui...");
|
|
7224
|
+
await execa11("npx", ["shadcn@latest", "init", "-y", "-d"], {
|
|
7225
|
+
cwd: projectPath,
|
|
7226
|
+
reject: false
|
|
7227
|
+
});
|
|
7228
|
+
await execa11("npx", ["shadcn@latest", "add", "button", "card", "input", "badge", "-y"], {
|
|
7229
|
+
cwd: projectPath,
|
|
7230
|
+
reject: false
|
|
7231
|
+
});
|
|
7232
|
+
spinner17.stop("UI components ready");
|
|
7233
|
+
spinner17.start("Generating website code...");
|
|
7234
|
+
const response = await anthropic.messages.create({
|
|
7235
|
+
model: "claude-sonnet-4-20250514",
|
|
7236
|
+
max_tokens: 16e3,
|
|
7237
|
+
messages: [{
|
|
7238
|
+
role: "user",
|
|
7239
|
+
content: `Generate a complete Next.js website based on this specification.
|
|
7240
|
+
|
|
7241
|
+
Website Spec:
|
|
7242
|
+
${JSON.stringify(spec, null, 2)}
|
|
7243
|
+
|
|
7244
|
+
Generate these files:
|
|
7245
|
+
|
|
7246
|
+
1. src/app/page.tsx - Main landing page with ALL sections
|
|
7247
|
+
2. src/app/layout.tsx - Root layout with fonts, metadata
|
|
7248
|
+
3. src/app/globals.css - Tailwind config with custom colors
|
|
7249
|
+
4. src/components/sections/Hero.tsx
|
|
7250
|
+
5. src/components/sections/[OtherSections].tsx - One for each section
|
|
7251
|
+
6. src/components/ui/Navbar.tsx
|
|
7252
|
+
7. src/components/ui/Footer.tsx
|
|
7253
|
+
8. tailwind.config.ts - With custom colors from colorScheme
|
|
7254
|
+
|
|
7255
|
+
Requirements:
|
|
7256
|
+
- Use TypeScript
|
|
7257
|
+
- Use Tailwind CSS for styling
|
|
7258
|
+
- Use shadcn/ui components (Button, Card, Input, Badge)
|
|
7259
|
+
- Make it responsive (mobile-first)
|
|
7260
|
+
- Add smooth scroll behavior
|
|
7261
|
+
- Use modern design patterns
|
|
7262
|
+
- Include hover effects and transitions
|
|
7263
|
+
- Use Lucide icons where appropriate
|
|
7264
|
+
|
|
7265
|
+
Color scheme to use:
|
|
7266
|
+
- Primary: ${spec.colorScheme.primary}
|
|
7267
|
+
- Secondary: ${spec.colorScheme.secondary}
|
|
7268
|
+
- Accent: ${spec.colorScheme.accent}
|
|
7269
|
+
- Background: ${spec.colorScheme.background}
|
|
7270
|
+
- Text: ${spec.colorScheme.text}
|
|
7271
|
+
|
|
7272
|
+
Style: ${spec.style}
|
|
7273
|
+
${spec.style === "minimal" ? "Clean, lots of whitespace, simple" : ""}
|
|
7274
|
+
${spec.style === "bold" ? "Strong colors, big typography, impactful" : ""}
|
|
7275
|
+
${spec.style === "elegant" ? "Refined, sophisticated, subtle animations" : ""}
|
|
7276
|
+
${spec.style === "playful" ? "Fun, colorful, friendly illustrations" : ""}
|
|
7277
|
+
${spec.style === "corporate" ? "Professional, trustworthy, structured" : ""}
|
|
7278
|
+
|
|
7279
|
+
Output format:
|
|
7280
|
+
<<<FILE: path/to/file.tsx>>>
|
|
7281
|
+
content
|
|
7282
|
+
<<<END_FILE>>>
|
|
7283
|
+
|
|
7284
|
+
Make it production-quality and visually impressive.`
|
|
7285
|
+
}]
|
|
7286
|
+
});
|
|
7287
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7288
|
+
const fileRegex = /<<<FILE:\s*(.+?)>>>([\s\S]*?)<<<END_FILE>>>/g;
|
|
7289
|
+
let match;
|
|
7290
|
+
let fileCount = 0;
|
|
7291
|
+
while ((match = fileRegex.exec(text16)) !== null) {
|
|
7292
|
+
const filePath = path15.join(projectPath, match[1].trim());
|
|
7293
|
+
const content = match[2].trim();
|
|
7294
|
+
await fs16.ensureDir(path15.dirname(filePath));
|
|
7295
|
+
await fs16.writeFile(filePath, content);
|
|
7296
|
+
fileCount++;
|
|
7297
|
+
}
|
|
7298
|
+
spinner17.stop(`Generated ${fileCount} files`);
|
|
7299
|
+
spinner17.start("Initializing git...");
|
|
7300
|
+
await execa11("git", ["init"], { cwd: projectPath, reject: false });
|
|
7301
|
+
await execa11("git", ["add", "."], { cwd: projectPath, reject: false });
|
|
7302
|
+
await execa11("git", ["commit", "-m", "Initial website build by CodeBakers"], { cwd: projectPath, reject: false });
|
|
7303
|
+
spinner17.stop("Git initialized");
|
|
7304
|
+
console.log(chalk20.green(`
|
|
7305
|
+
\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\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
|
|
7306
|
+
\u2551 \u2705 Website built successfully! \u2551
|
|
7307
|
+
\u2560\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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
7308
|
+
\u2551 \u2551
|
|
7309
|
+
\u2551 ${spec.name.padEnd(55)}\u2551
|
|
7310
|
+
\u2551 ${spec.description.substring(0, 55).padEnd(55)}\u2551
|
|
7311
|
+
\u2551 \u2551
|
|
7312
|
+
\u2551 Next steps: \u2551
|
|
7313
|
+
\u2551 cd ${spec.name.padEnd(52)}\u2551
|
|
7314
|
+
\u2551 npm run dev \u2551
|
|
7315
|
+
\u2551 \u2551
|
|
7316
|
+
\u2551 Then open http://localhost:3000 \u2551
|
|
7317
|
+
\u2551 \u2551
|
|
7318
|
+
\u2551 Ready to deploy? \u2551
|
|
7319
|
+
\u2551 codebakers deploy \u2551
|
|
7320
|
+
\u2551 \u2551
|
|
7321
|
+
\u255A\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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
7322
|
+
`));
|
|
7323
|
+
const openDev = await p19.confirm({
|
|
7324
|
+
message: "Start development server now?",
|
|
7325
|
+
initialValue: true
|
|
7326
|
+
});
|
|
7327
|
+
if (openDev && !p19.isCancel(openDev)) {
|
|
7328
|
+
console.log(chalk20.dim("\n Starting dev server...\n"));
|
|
7329
|
+
process.chdir(projectPath);
|
|
7330
|
+
await execa11("npm", ["run", "dev"], {
|
|
7331
|
+
stdio: "inherit",
|
|
7332
|
+
reject: false
|
|
7333
|
+
});
|
|
7334
|
+
}
|
|
7335
|
+
}
|
|
7336
|
+
|
|
7337
|
+
// src/utils/nlp.ts
|
|
7338
|
+
import * as p20 from "@clack/prompts";
|
|
7339
|
+
import Anthropic6 from "@anthropic-ai/sdk";
|
|
6835
7340
|
var COMMAND_PATTERNS = {
|
|
6836
7341
|
init: {
|
|
6837
7342
|
patterns: ["new project", "create app", "start project", "initialize", "new app", "start building", "create new"],
|
|
@@ -6900,7 +7405,7 @@ async function parseNaturalLanguage(input, config) {
|
|
|
6900
7405
|
if (!anthropicCreds?.apiKey) {
|
|
6901
7406
|
return quickMatch;
|
|
6902
7407
|
}
|
|
6903
|
-
const anthropic = new
|
|
7408
|
+
const anthropic = new Anthropic6({ apiKey: anthropicCreds.apiKey });
|
|
6904
7409
|
try {
|
|
6905
7410
|
const result = await parseWithAI(anthropic, input);
|
|
6906
7411
|
return result;
|
|
@@ -6952,7 +7457,7 @@ function extractArgs(input, pattern) {
|
|
|
6952
7457
|
const patternWords = pattern.toLowerCase().split(/\s+/);
|
|
6953
7458
|
const inputWords = input.split(/\s+/);
|
|
6954
7459
|
const args2 = inputWords.filter(
|
|
6955
|
-
(word) => !patternWords.some((
|
|
7460
|
+
(word) => !patternWords.some((p22) => word.toLowerCase().includes(p22) || p22.includes(word.toLowerCase()))
|
|
6956
7461
|
);
|
|
6957
7462
|
return args2.filter((a) => a.length > 2);
|
|
6958
7463
|
}
|
|
@@ -6983,8 +7488,8 @@ If the input is asking to build/create/add a specific feature, use "code" and pu
|
|
|
6983
7488
|
If unclear between multiple commands, use the most likely one with lower confidence.`
|
|
6984
7489
|
}]
|
|
6985
7490
|
});
|
|
6986
|
-
const
|
|
6987
|
-
const jsonMatch =
|
|
7491
|
+
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7492
|
+
const jsonMatch = text16.match(/\{[\s\S]*?\}/);
|
|
6988
7493
|
if (!jsonMatch) {
|
|
6989
7494
|
return {
|
|
6990
7495
|
command: "code",
|
|
@@ -7000,11 +7505,11 @@ async function clarifyCommand(parsed) {
|
|
|
7000
7505
|
return parsed;
|
|
7001
7506
|
}
|
|
7002
7507
|
if (parsed.confidence >= 0.5) {
|
|
7003
|
-
const
|
|
7508
|
+
const confirm12 = await p20.confirm({
|
|
7004
7509
|
message: `Did you mean: ${parsed.interpretation}?`,
|
|
7005
7510
|
initialValue: true
|
|
7006
7511
|
});
|
|
7007
|
-
if (
|
|
7512
|
+
if (confirm12 && !p20.isCancel(confirm12)) {
|
|
7008
7513
|
return { ...parsed, confidence: 1 };
|
|
7009
7514
|
}
|
|
7010
7515
|
}
|
|
@@ -7019,19 +7524,19 @@ async function clarifyCommand(parsed) {
|
|
|
7019
7524
|
{ value: "prd-maker", label: "\u{1F4DD} Create PRD", description: "Document your idea" },
|
|
7020
7525
|
{ value: "other", label: "\u2753 Something else", description: "Describe what you need" }
|
|
7021
7526
|
];
|
|
7022
|
-
const selection = await
|
|
7527
|
+
const selection = await p20.select({
|
|
7023
7528
|
message: "What would you like to do?",
|
|
7024
7529
|
options: options.map((o) => ({ value: o.value, label: o.label, hint: o.description }))
|
|
7025
7530
|
});
|
|
7026
|
-
if (
|
|
7531
|
+
if (p20.isCancel(selection)) {
|
|
7027
7532
|
return { ...parsed, command: "cancel", confidence: 1 };
|
|
7028
7533
|
}
|
|
7029
7534
|
if (selection === "other") {
|
|
7030
|
-
const description = await
|
|
7535
|
+
const description = await p20.text({
|
|
7031
7536
|
message: "Describe what you want to do:",
|
|
7032
7537
|
placeholder: "I want to..."
|
|
7033
7538
|
});
|
|
7034
|
-
if (
|
|
7539
|
+
if (p20.isCancel(description)) {
|
|
7035
7540
|
return { ...parsed, command: "cancel", confidence: 1 };
|
|
7036
7541
|
}
|
|
7037
7542
|
return {
|
|
@@ -7049,19 +7554,19 @@ async function clarifyCommand(parsed) {
|
|
|
7049
7554
|
};
|
|
7050
7555
|
}
|
|
7051
7556
|
async function clarifyDeployTarget() {
|
|
7052
|
-
const target = await
|
|
7557
|
+
const target = await p20.select({
|
|
7053
7558
|
message: "Where do you want to deploy?",
|
|
7054
7559
|
options: [
|
|
7055
7560
|
{ value: "preview", label: "\u{1F50D} Preview", hint: "Test URL to review changes" },
|
|
7056
7561
|
{ value: "production", label: "\u{1F680} Production", hint: "Live site for users" }
|
|
7057
7562
|
]
|
|
7058
7563
|
});
|
|
7059
|
-
if (
|
|
7564
|
+
if (p20.isCancel(target)) return null;
|
|
7060
7565
|
return target;
|
|
7061
7566
|
}
|
|
7062
7567
|
|
|
7063
7568
|
// src/index.ts
|
|
7064
|
-
var VERSION2 = "2.1.
|
|
7569
|
+
var VERSION2 = "2.1.1";
|
|
7065
7570
|
var logo = `
|
|
7066
7571
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
7067
7572
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|
|
@@ -7074,18 +7579,18 @@ async function showMainMenu() {
|
|
|
7074
7579
|
const config = new Config();
|
|
7075
7580
|
const isSetup = config.isConfigured();
|
|
7076
7581
|
console.log(gradient.pastel.multiline(logo));
|
|
7077
|
-
console.log(
|
|
7582
|
+
console.log(chalk21.dim(` v${VERSION2} \u2014 AI dev team that follows the rules
|
|
7078
7583
|
`));
|
|
7079
7584
|
if (!isSetup) {
|
|
7080
7585
|
console.log(boxen(
|
|
7081
|
-
|
|
7586
|
+
chalk21.yellow("Welcome to CodeBakers! Let's get you set up."),
|
|
7082
7587
|
{ padding: 1, borderColor: "yellow", borderStyle: "round" }
|
|
7083
7588
|
));
|
|
7084
7589
|
await setupCommand();
|
|
7085
7590
|
return;
|
|
7086
7591
|
}
|
|
7087
7592
|
const inProject = config.isInProject();
|
|
7088
|
-
const action = await
|
|
7593
|
+
const action = await p21.select({
|
|
7089
7594
|
message: "What do you want to do? (or type naturally)",
|
|
7090
7595
|
options: inProject ? [
|
|
7091
7596
|
{ value: "code", label: "\u{1F4AC} Code with AI", hint: "build features, fix bugs" },
|
|
@@ -7104,6 +7609,7 @@ async function showMainMenu() {
|
|
|
7104
7609
|
{ value: "design", label: "\u{1F3A8} Design system", hint: "set profile, colors" },
|
|
7105
7610
|
{ 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" },
|
|
7106
7611
|
{ value: "new", label: "\u{1F195} Create new project" },
|
|
7612
|
+
{ value: "website", label: "\u{1F310} Website Builder", hint: "Describe \u2192 AI builds it" },
|
|
7107
7613
|
{ value: "build", label: "\u{1F3D7}\uFE0F Parallel Build", hint: "3 agents from PRD (swarm)" },
|
|
7108
7614
|
{ value: "prd", label: "\u{1F4C4} Build from PRD", hint: "sequential build" },
|
|
7109
7615
|
{ value: "prd-maker", label: "\u270F\uFE0F Create PRD", hint: "interview \u2192 generate PRD" },
|
|
@@ -7112,6 +7618,7 @@ async function showMainMenu() {
|
|
|
7112
7618
|
{ value: "help", label: "\u2753 Help" }
|
|
7113
7619
|
] : [
|
|
7114
7620
|
{ value: "new", label: "\u{1F195} Create new project" },
|
|
7621
|
+
{ value: "website", label: "\u{1F310} Website Builder", hint: "Describe \u2192 AI builds it" },
|
|
7115
7622
|
{ value: "build", label: "\u{1F3D7}\uFE0F Parallel Build", hint: "3 agents from PRD (swarm)" },
|
|
7116
7623
|
{ value: "prd", label: "\u{1F4C4} Build from PRD", hint: "sequential build" },
|
|
7117
7624
|
{ value: "prd-maker", label: "\u270F\uFE0F Create PRD", hint: "interview \u2192 generate PRD" },
|
|
@@ -7126,8 +7633,8 @@ async function showMainMenu() {
|
|
|
7126
7633
|
{ value: "help", label: "\u2753 Help" }
|
|
7127
7634
|
]
|
|
7128
7635
|
});
|
|
7129
|
-
if (
|
|
7130
|
-
|
|
7636
|
+
if (p21.isCancel(action)) {
|
|
7637
|
+
p21.cancel("Goodbye!");
|
|
7131
7638
|
process.exit(0);
|
|
7132
7639
|
}
|
|
7133
7640
|
switch (action) {
|
|
@@ -7173,6 +7680,9 @@ async function showMainMenu() {
|
|
|
7173
7680
|
case "new":
|
|
7174
7681
|
await initCommand();
|
|
7175
7682
|
break;
|
|
7683
|
+
case "website":
|
|
7684
|
+
await websiteCommand();
|
|
7685
|
+
break;
|
|
7176
7686
|
case "build":
|
|
7177
7687
|
await buildCommand();
|
|
7178
7688
|
break;
|
|
@@ -7197,27 +7707,27 @@ async function showMainMenu() {
|
|
|
7197
7707
|
}
|
|
7198
7708
|
function showHelp2() {
|
|
7199
7709
|
console.log(boxen(`
|
|
7200
|
-
${
|
|
7201
|
-
|
|
7202
|
-
${
|
|
7203
|
-
${
|
|
7204
|
-
${
|
|
7205
|
-
${
|
|
7206
|
-
${
|
|
7207
|
-
${
|
|
7208
|
-
${
|
|
7209
|
-
${
|
|
7210
|
-
${
|
|
7211
|
-
${
|
|
7212
|
-
${
|
|
7213
|
-
${
|
|
7214
|
-
${
|
|
7215
|
-
|
|
7216
|
-
${
|
|
7217
|
-
Press ${
|
|
7218
|
-
|
|
7219
|
-
${
|
|
7220
|
-
${
|
|
7710
|
+
${chalk21.bold("CodeBakers CLI")} \u2014 AI dev team that follows the rules
|
|
7711
|
+
|
|
7712
|
+
${chalk21.bold("Commands:")}
|
|
7713
|
+
${chalk21.cyan("codebakers")} Interactive menu (or just run with no args)
|
|
7714
|
+
${chalk21.cyan("codebakers init")} Create a new project
|
|
7715
|
+
${chalk21.cyan("codebakers code")} Start AI coding session
|
|
7716
|
+
${chalk21.cyan("codebakers check")} Run pattern enforcement
|
|
7717
|
+
${chalk21.cyan("codebakers deploy")} Deploy to production
|
|
7718
|
+
${chalk21.cyan("codebakers fix")} Auto-fix errors
|
|
7719
|
+
${chalk21.cyan("codebakers generate")} Generate components/pages
|
|
7720
|
+
${chalk21.cyan("codebakers connect")} Connect external services
|
|
7721
|
+
${chalk21.cyan("codebakers gateway")} Manage messaging channels
|
|
7722
|
+
${chalk21.cyan("codebakers status")} View project status
|
|
7723
|
+
${chalk21.cyan("codebakers security")} Run security audit
|
|
7724
|
+
${chalk21.cyan("codebakers learn")} View/manage learning
|
|
7725
|
+
|
|
7726
|
+
${chalk21.bold("Help at any time:")}
|
|
7727
|
+
Press ${chalk21.yellow("?")} during any command to get contextual help
|
|
7728
|
+
|
|
7729
|
+
${chalk21.bold("Documentation:")}
|
|
7730
|
+
${chalk21.dim("https://codebakers.dev/docs")}
|
|
7221
7731
|
`, { padding: 1, borderColor: "cyan", borderStyle: "round" }));
|
|
7222
7732
|
}
|
|
7223
7733
|
var program = new Command();
|
|
@@ -7241,9 +7751,10 @@ program.command("migrate").alias("db").description("Database migrations (push, g
|
|
|
7241
7751
|
program.command("prd-maker").alias("create-prd").description("Create a PRD through guided interview (supports voice input)").action(prdMakerCommand);
|
|
7242
7752
|
program.command("build [prd-file]").alias("swarm").description("Parallel build with 3 AI agents (self-healing)").option("--sequential", "Disable parallel execution").action(buildCommand);
|
|
7243
7753
|
program.command("integrate [integration]").alias("add").description("One-click integrations (50+ services with browser auth)").action(integrateCommand);
|
|
7754
|
+
program.command("website").alias("site").description("Build a website by describing it in plain English").action(websiteCommand);
|
|
7244
7755
|
async function handleNaturalLanguage(input) {
|
|
7245
7756
|
const config = new Config();
|
|
7246
|
-
console.log(
|
|
7757
|
+
console.log(chalk21.dim("\n Parsing your request...\n"));
|
|
7247
7758
|
const parsed = await parseNaturalLanguage(input, config);
|
|
7248
7759
|
if (!parsed) {
|
|
7249
7760
|
await codeCommand(input);
|
|
@@ -7331,6 +7842,8 @@ if (args.length === 0) {
|
|
|
7331
7842
|
"swarm",
|
|
7332
7843
|
"integrate",
|
|
7333
7844
|
"add",
|
|
7845
|
+
"website",
|
|
7846
|
+
"site",
|
|
7334
7847
|
"help"
|
|
7335
7848
|
];
|
|
7336
7849
|
const firstArg = args[0];
|