codebakers 2.0.10 → 2.1.0

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 CHANGED
@@ -11,8 +11,8 @@ import {
11
11
 
12
12
  // src/index.ts
13
13
  import { Command } from "commander";
14
- import * as p19 from "@clack/prompts";
15
- import chalk19 from "chalk";
14
+ import * as p20 from "@clack/prompts";
15
+ import chalk20 from "chalk";
16
16
  import boxen from "boxen";
17
17
  import gradient from "gradient-string";
18
18
 
@@ -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 spinner15 = p.spinner();
135
+ const spinner16 = 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((p20) => ({
610
- id: p20.id,
611
- name: p20.name,
612
- region: p20.region
609
+ return projects.map((p21) => ({
610
+ id: p21.id,
611
+ name: p21.name,
612
+ region: p21.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 spinner15 = p2.spinner();
1032
+ const spinner16 = p2.spinner();
1033
1033
  const projectPath = path.join(process.cwd(), projectName);
1034
1034
  try {
1035
- spinner15.start("Creating local project...");
1035
+ spinner16.start("Creating local project...");
1036
1036
  await createLocalProject(projectPath, projectConfig);
1037
- spinner15.stop("Local project created");
1038
- spinner15.start("Installing dependencies...");
1037
+ spinner16.stop("Local project created");
1038
+ spinner16.start("Installing dependencies...");
1039
1039
  await execa3("pnpm", ["install"], { cwd: projectPath });
1040
- spinner15.stop("Dependencies installed");
1040
+ spinner16.stop("Dependencies installed");
1041
1041
  if (services.includes("github")) {
1042
- spinner15.start("Creating GitHub repository...");
1042
+ spinner16.start("Creating GitHub repository...");
1043
1043
  const github = new GitHubService(config);
1044
1044
  const repo = await github.createRepo(projectName, { private: true });
1045
- spinner15.stop(`GitHub repo created: ${repo.html_url}`);
1045
+ spinner16.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
- spinner15.start("Creating Supabase project...");
1053
+ spinner16.start("Creating Supabase project...");
1054
1054
  const supabase = new SupabaseService(config);
1055
1055
  const project = await supabase.createProject(projectName);
1056
- spinner15.stop(`Supabase project created: ${project.name}`);
1056
+ spinner16.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
- spinner15.start("Creating Vercel project...");
1064
+ spinner16.start("Creating Vercel project...");
1065
1065
  const vercel = new VercelService(config);
1066
1066
  const project = await vercel.createProject(projectName);
1067
- spinner15.stop(`Vercel project created`);
1067
+ spinner16.stop(`Vercel project created`);
1068
1068
  if (domain) {
1069
- spinner15.start(`Configuring domain: ${domain}...`);
1069
+ spinner16.start(`Configuring domain: ${domain}...`);
1070
1070
  await vercel.addDomain(projectName, domain);
1071
- spinner15.stop("Domain configured");
1071
+ spinner16.stop("Domain configured");
1072
1072
  }
1073
- spinner15.start("Deploying to Vercel...");
1073
+ spinner16.start("Deploying to Vercel...");
1074
1074
  const deployment = await vercel.deploy(projectPath);
1075
- spinner15.stop(`Deployed: ${deployment.url}`);
1075
+ spinner16.stop(`Deployed: ${deployment.url}`);
1076
1076
  }
1077
- spinner15.start("Generating CLAUDE.md...");
1077
+ spinner16.start("Generating CLAUDE.md...");
1078
1078
  const claudeMd = generateClaudeMd(projectConfig);
1079
1079
  await fs.writeFile(path.join(projectPath, "CLAUDE.md"), claudeMd);
1080
- spinner15.stop("CLAUDE.md generated");
1081
- spinner15.start("Setting up CodeBakers enforcement...");
1080
+ spinner16.stop("CLAUDE.md generated");
1081
+ spinner16.start("Setting up CodeBakers enforcement...");
1082
1082
  await setupGitHooks(projectPath);
1083
- spinner15.stop("CodeBakers enforcement configured");
1083
+ spinner16.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
- spinner15.stop("Error occurred");
1103
+ spinner16.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 spinner15 = p3.spinner();
1830
- spinner15.start("Analyzing code...");
1829
+ const spinner16 = p3.spinner();
1830
+ spinner16.start("Analyzing code...");
1831
1831
  const result = await runPatternCheck(options.fix || false);
1832
- spinner15.stop("Analysis complete");
1832
+ spinner16.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
- spinner15.start(`Auto-fixing ${fixable.length} violations...`);
1837
+ spinner16.start(`Auto-fixing ${fixable.length} violations...`);
1838
1838
  await autoFix(fixable);
1839
- spinner15.stop("Auto-fix complete");
1839
+ spinner16.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 spinner15 = p4.spinner();
2044
- spinner15.start("\u{1F534} Recording...");
2043
+ const spinner16 = p4.spinner();
2044
+ spinner16.start("\u{1F534} Recording...");
2045
2045
  try {
2046
2046
  let transcription = "";
2047
2047
  if (process.platform === "win32") {
@@ -2051,7 +2051,7 @@ async function getVoiceInput(prompt) {
2051
2051
  } else {
2052
2052
  transcription = await recordWithLinux();
2053
2053
  }
2054
- spinner15.stop("Recording complete");
2054
+ spinner16.stop("Recording complete");
2055
2055
  if (transcription) {
2056
2056
  console.log(chalk5.green(`
2057
2057
  \u2713 Heard: "${transcription}"
@@ -2075,20 +2075,20 @@ async function getVoiceInput(prompt) {
2075
2075
  if (action === "retry") {
2076
2076
  return await getVoiceInput(prompt);
2077
2077
  } else {
2078
- const text14 = await p4.text({ message: "Type your response:" });
2079
- return p4.isCancel(text14) ? null : text14;
2078
+ const text15 = await p4.text({ message: "Type your response:" });
2079
+ return p4.isCancel(text15) ? null : text15;
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 text14 = await p4.text({ message: "Type instead:" });
2085
- return p4.isCancel(text14) ? null : text14;
2084
+ const text15 = await p4.text({ message: "Type instead:" });
2085
+ return p4.isCancel(text15) ? null : text15;
2086
2086
  }
2087
2087
  } catch (error) {
2088
- spinner15.stop("Recording failed");
2088
+ spinner16.stop("Recording failed");
2089
2089
  console.log(chalk5.yellow("Voice input failed. Please type instead."));
2090
- const text14 = await p4.text({ message: prompt });
2091
- return p4.isCancel(text14) ? null : text14;
2090
+ const text15 = await p4.text({ message: prompt });
2091
+ return p4.isCancel(text15) ? null : text15;
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 text14 = await fs4.readFile(txtFile, "utf-8");
2226
+ const text15 = await fs4.readFile(txtFile, "utf-8");
2227
2227
  await fs4.remove(txtFile).catch(() => {
2228
2228
  });
2229
- return text14.trim();
2229
+ return text15.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 text14 = await extractPdfText(cleanPath);
2337
- if (text14) {
2338
- return { content: text14, type: "pdf", name };
2336
+ const text15 = await extractPdfText(cleanPath);
2337
+ if (text15) {
2338
+ return { content: text15, 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(text14) {
2411
- if (text14.includes("\n") && text14.split("\n").length > 3) return true;
2412
- if (text14.includes("function ") || text14.includes("const ") || text14.includes("import ") || text14.includes("export ") || text14.includes("class ") || text14.includes("def ") || text14.includes("public ") || text14.includes("private ")) return true;
2413
- if (text14.startsWith("{") && text14.endsWith("}") || text14.startsWith("[") && text14.endsWith("]")) return true;
2414
- if (text14.length > 200 && !text14.includes("\n")) return true;
2410
+ function looksLikePaste(text15) {
2411
+ if (text15.includes("\n") && text15.split("\n").length > 3) return true;
2412
+ if (text15.includes("function ") || text15.includes("const ") || text15.includes("import ") || text15.includes("export ") || text15.includes("class ") || text15.includes("def ") || text15.includes("public ") || text15.includes("private ")) return true;
2413
+ if (text15.startsWith("{") && text15.endsWith("}") || text15.startsWith("[") && text15.endsWith("]")) return true;
2414
+ if (text15.length > 200 && !text15.includes("\n")) return true;
2415
2415
  return false;
2416
2416
  }
2417
- async function handlePastedContent(text14) {
2418
- if (!looksLikePaste(text14)) {
2417
+ async function handlePastedContent(text15) {
2418
+ if (!looksLikePaste(text15)) {
2419
2419
  return null;
2420
2420
  }
2421
2421
  console.log(chalk6.cyan("\n\u{1F4CB} Detected pasted content!\n"));
2422
- const preview = text14.length > 200 ? text14.slice(0, 200) + "..." : text14;
2422
+ const preview = text15.length > 200 ? text15.slice(0, 200) + "..." : text15;
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(text14) {
2435
2435
  });
2436
2436
  if (p5.isCancel(action)) return null;
2437
2437
  if (action === "literal") {
2438
- return { prompt: text14, context: "" };
2438
+ return { prompt: text15, context: "" };
2439
2439
  }
2440
2440
  if (action === "custom") {
2441
2441
  const instruction = await p5.text({
@@ -2449,7 +2449,7 @@ async function handlePastedContent(text14) {
2449
2449
 
2450
2450
  --- PASTED CODE ---
2451
2451
  \`\`\`
2452
- ${text14}
2452
+ ${text15}
2453
2453
  \`\`\`
2454
2454
  --- END ---
2455
2455
 
@@ -2468,7 +2468,7 @@ ${text14}
2468
2468
 
2469
2469
  --- PASTED CODE ---
2470
2470
  \`\`\`
2471
- ${text14}
2471
+ ${text15}
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 spinner15 = p6.spinner();
2632
+ const spinner16 = 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
- spinner15.start("Thinking...");
2639
+ spinner16.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
- spinner15.stop("");
2649
+ spinner16.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
- spinner15.start("Building...");
2664
+ spinner16.start("Building...");
2665
2665
  for (const action of actions) {
2666
- await executeAction(action, spinner15);
2666
+ await executeAction(action, spinner16);
2667
2667
  }
2668
- spinner15.stop("Build complete");
2668
+ spinner16.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
- spinner15.start("Auto-fixing...");
2679
+ spinner16.start("Auto-fixing...");
2680
2680
  await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
2681
- spinner15.stop("Violations fixed");
2681
+ spinner16.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
- spinner15.stop("Error");
2692
+ spinner16.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, spinner15) {
2804
+ async function executeAction(action, spinner16) {
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
- spinner15.message(`Created ${action.path}`);
2811
+ spinner16.message(`Created ${action.path}`);
2812
2812
  break;
2813
2813
  }
2814
2814
  case "EDIT_FILE": {
@@ -2818,13 +2818,13 @@ async function executeAction(action, spinner15) {
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
- spinner15.message(`Edited ${action.path}`);
2821
+ spinner16.message(`Edited ${action.path}`);
2822
2822
  }
2823
2823
  }
2824
2824
  break;
2825
2825
  }
2826
2826
  case "RUN_COMMAND": {
2827
- spinner15.message(`Running: ${action.command}`);
2827
+ spinner16.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, spinner15) {
2833
2833
  const filePath = path5.join(cwd, action.path);
2834
2834
  if (await fs6.pathExists(filePath)) {
2835
2835
  await fs6.remove(filePath);
2836
- spinner15.message(`Deleted ${action.path}`);
2836
+ spinner16.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 spinner15 = p7.spinner();
3084
+ const spinner16 = p7.spinner();
3085
3085
  if (options.check !== false) {
3086
- spinner15.start("Running CodeBakers check...");
3086
+ spinner16.start("Running CodeBakers check...");
3087
3087
  const checkResult = await runPatternCheck(false);
3088
3088
  if (!checkResult.passed) {
3089
- spinner15.stop("");
3089
+ spinner16.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
- spinner15.start("Auto-fixing with AI...");
3110
+ spinner16.start("Auto-fixing with AI...");
3111
3111
  await autoFixWithAI(config, errors);
3112
- spinner15.stop("Auto-fix complete");
3112
+ spinner16.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
- spinner15.stop("Pattern check passed");
3121
+ spinner16.stop("Pattern check passed");
3122
3122
  }
3123
- spinner15.start("Running TypeScript check...");
3123
+ spinner16.start("Running TypeScript check...");
3124
3124
  try {
3125
3125
  await execa7("npx", ["tsc", "--noEmit"], { cwd: process.cwd() });
3126
- spinner15.stop("TypeScript check passed");
3126
+ spinner16.stop("TypeScript check passed");
3127
3127
  } catch (error) {
3128
- spinner15.stop("");
3128
+ spinner16.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
- spinner15.start("Fixing TypeScript errors...");
3135
+ spinner16.start("Fixing TypeScript errors...");
3136
3136
  const fixed = await fixTypeScriptErrors(config);
3137
- spinner15.stop(fixed ? "TypeScript errors fixed" : "Could not auto-fix all errors");
3137
+ spinner16.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
- spinner15.start("Building project...");
3147
+ spinner16.start("Building project...");
3148
3148
  try {
3149
3149
  await execa7("pnpm", ["build"], { cwd: process.cwd() });
3150
- spinner15.stop("Build successful");
3150
+ spinner16.stop("Build successful");
3151
3151
  } catch (error) {
3152
- spinner15.stop("");
3152
+ spinner16.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
- spinner15.start("Fixing build errors...");
3161
+ spinner16.start("Fixing build errors...");
3162
3162
  const fixed = await fixBuildErrors(config, errorOutput);
3163
- spinner15.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
3163
+ spinner16.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
3164
3164
  if (fixed) {
3165
- spinner15.start("Retrying build...");
3165
+ spinner16.start("Retrying build...");
3166
3166
  try {
3167
3167
  await execa7("pnpm", ["build"], { cwd: process.cwd() });
3168
- spinner15.stop("Build successful");
3168
+ spinner16.stop("Build successful");
3169
3169
  } catch {
3170
- spinner15.stop("Build still failing");
3170
+ spinner16.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
- spinner15.start("Checking for uncommitted changes...");
3183
+ spinner16.start("Checking for uncommitted changes...");
3184
3184
  const { stdout: gitStatus } = await execa7("git", ["status", "--porcelain"], { cwd: process.cwd() });
3185
3185
  if (gitStatus.trim()) {
3186
- spinner15.stop("");
3186
+ spinner16.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
- spinner15.start("Pushing to GitHub...");
3200
+ spinner16.start("Pushing to GitHub...");
3201
3201
  await execa7("git", ["push"], { cwd: process.cwd() });
3202
- spinner15.stop("Pushed to GitHub");
3202
+ spinner16.stop("Pushed to GitHub");
3203
3203
  }
3204
3204
  }
3205
3205
  } else {
3206
- spinner15.stop("No uncommitted changes");
3206
+ spinner16.stop("No uncommitted changes");
3207
3207
  }
3208
3208
  const deployType = options.preview ? "preview" : "production";
3209
- spinner15.start(`Deploying to ${deployType}...`);
3209
+ spinner16.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
- spinner15.stop("Deployment complete!");
3213
+ spinner16.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
- spinner15.stop("");
3225
+ spinner16.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
- spinner15.start("Analyzing Vercel build error...");
3235
- spinner15.stop("Fix attempted");
3234
+ spinner16.start("Analyzing Vercel build error...");
3235
+ spinner16.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 spinner15 = p9.spinner();
3602
- spinner15.start("Generating QR code...");
3601
+ const spinner16 = p9.spinner();
3602
+ spinner16.start("Generating QR code...");
3603
3603
  try {
3604
- spinner15.stop("");
3604
+ spinner16.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
- spinner15.stop("Error connecting WhatsApp");
3636
+ spinner16.stop("Error connecting WhatsApp");
3637
3637
  p9.log.error(error instanceof Error ? error.message : "Unknown error");
3638
3638
  }
3639
3639
  }
@@ -3651,8 +3651,8 @@ To create a Telegram bot:
3651
3651
  initialValue: true
3652
3652
  });
3653
3653
  if (openBotFather && !p9.isCancel(openBotFather)) {
3654
- const open2 = (await import("open")).default;
3655
- await open2("https://t.me/botfather");
3654
+ const open3 = (await import("open")).default;
3655
+ await open3("https://t.me/botfather");
3656
3656
  }
3657
3657
  const token = await p9.text({
3658
3658
  message: "Paste your bot token:",
@@ -3664,15 +3664,15 @@ To create a Telegram bot:
3664
3664
  }
3665
3665
  });
3666
3666
  if (p9.isCancel(token)) return;
3667
- const spinner15 = p9.spinner();
3668
- spinner15.start("Verifying bot token...");
3667
+ const spinner16 = p9.spinner();
3668
+ spinner16.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
- spinner15.stop("Bot verified!");
3675
+ spinner16.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
- spinner15.stop("Verification failed");
3683
+ spinner16.stop("Verification failed");
3684
3684
  p9.log.error(error instanceof Error ? error.message : "Invalid token");
3685
3685
  }
3686
3686
  }
@@ -3699,8 +3699,8 @@ To create a Discord bot:
3699
3699
  initialValue: true
3700
3700
  });
3701
3701
  if (openPortal && !p9.isCancel(openPortal)) {
3702
- const open2 = (await import("open")).default;
3703
- await open2("https://discord.com/developers/applications");
3702
+ const open3 = (await import("open")).default;
3703
+ await open3("https://discord.com/developers/applications");
3704
3704
  }
3705
3705
  const token = await p9.text({
3706
3706
  message: "Paste your bot token:",
@@ -3736,8 +3736,8 @@ To create a Slack app:
3736
3736
  initialValue: true
3737
3737
  });
3738
3738
  if (openSlack && !p9.isCancel(openSlack)) {
3739
- const open2 = (await import("open")).default;
3740
- await open2("https://api.slack.com/apps");
3739
+ const open3 = (await import("open")).default;
3740
+ await open3("https://api.slack.com/apps");
3741
3741
  }
3742
3742
  const token = await p9.text({
3743
3743
  message: "Paste your Bot User OAuth Token:",
@@ -3775,8 +3775,8 @@ async function connectSMS(config) {
3775
3775
  initialValue: true
3776
3776
  });
3777
3777
  if (openTwilio && !p9.isCancel(openTwilio)) {
3778
- const open2 = (await import("open")).default;
3779
- await open2("https://console.twilio.com/");
3778
+ const open3 = (await import("open")).default;
3779
+ await open3("https://console.twilio.com/");
3780
3780
  }
3781
3781
  const accountSid = await p9.text({
3782
3782
  message: "Account SID:",
@@ -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 spinner15 = p9.spinner();
3822
- spinner15.start("Starting channel gateway...");
3821
+ const spinner16 = p9.spinner();
3822
+ spinner16.start("Starting channel gateway...");
3823
3823
  await new Promise((resolve) => setTimeout(resolve, 1e3));
3824
- spinner15.stop("Gateway started");
3824
+ spinner16.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 spinner15 = p9.spinner();
3839
- spinner15.start("Stopping gateway...");
3838
+ const spinner16 = p9.spinner();
3839
+ spinner16.start("Stopping gateway...");
3840
3840
  await new Promise((resolve) => setTimeout(resolve, 500));
3841
- spinner15.stop("Gateway stopped");
3841
+ spinner16.stop("Gateway stopped");
3842
3842
  }
3843
3843
  async function deployGatewayWizard(config) {
3844
3844
  p9.log.info(chalk10.bold("Deploy Gateway to Cloud"));
@@ -3896,8 +3896,8 @@ async function deployToVPS(config) {
3896
3896
  initialValue: true
3897
3897
  });
3898
3898
  if (proceed && !p9.isCancel(proceed)) {
3899
- const open2 = (await import("open")).default;
3900
- await open2("https://cloud.digitalocean.com/");
3899
+ const open3 = (await import("open")).default;
3900
+ await open3("https://cloud.digitalocean.com/");
3901
3901
  }
3902
3902
  } else {
3903
3903
  p9.log.info(chalk10.dim(`
@@ -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 spinner15 = p11.spinner();
3957
- spinner15.start("Scanning for security issues...");
3956
+ const spinner16 = p11.spinner();
3957
+ spinner16.start("Scanning for security issues...");
3958
3958
  const issues = await runSecurityScan();
3959
- spinner15.stop("Scan complete");
3959
+ spinner16.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 spinner15 = p12.spinner();
4054
- spinner15.start("Generating...");
4053
+ const spinner16 = p12.spinner();
4054
+ spinner16.start("Generating...");
4055
4055
  await generateFile(generateType, name);
4056
- spinner15.stop(`Generated ${name}`);
4056
+ spinner16.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 spinner15 = p13.spinner();
4202
- spinner15.start("Analyzing code...");
4201
+ const spinner16 = p13.spinner();
4202
+ spinner16.start("Analyzing code...");
4203
4203
  const result = await runPatternCheck(true);
4204
4204
  if (result.passed) {
4205
- spinner15.stop("No issues found!");
4205
+ spinner16.stop("No issues found!");
4206
4206
  } else {
4207
- spinner15.stop(`Fixed ${result.violations.length} issues`);
4207
+ spinner16.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 spinner15 = p14.spinner();
4409
- spinner15.start("Checking design quality...");
4408
+ const spinner16 = p14.spinner();
4409
+ spinner16.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
- spinner15.stop("Check complete");
4435
+ spinner16.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 spinner15 = p15.spinner();
4534
- spinner15.start("Checking migration status...");
4533
+ const spinner16 = p15.spinner();
4534
+ spinner16.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
- spinner15.stop("Status check complete");
4548
+ spinner16.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
- spinner15.stop("Error checking status");
4556
+ spinner16.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 spinner15 = p15.spinner();
4568
- spinner15.start("Generating migration...");
4567
+ const spinner16 = p15.spinner();
4568
+ spinner16.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
- spinner15.stop("Migration generated");
4582
+ spinner16.stop("Migration generated");
4583
4583
  if (result?.stdout) {
4584
4584
  console.log(chalk16.dim(result.stdout));
4585
4585
  }
4586
4586
  } catch (error) {
4587
- spinner15.stop("Error generating migration");
4587
+ spinner16.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 spinner15 = p15.spinner();
4593
- spinner15.start("Pushing migration to database...");
4592
+ const spinner16 = p15.spinner();
4593
+ spinner16.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
- spinner15.stop("Migration push failed");
4619
+ spinner16.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
- spinner15.stop("Migration pushed successfully!");
4672
+ spinner16.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
- spinner15.stop("Error");
4678
+ spinner16.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 spinner15 = p15.spinner();
4691
- spinner15.start("Pulling schema from database...");
4690
+ const spinner16 = p15.spinner();
4691
+ spinner16.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
- spinner15.stop("Schema pulled");
4705
+ spinner16.stop("Schema pulled");
4706
4706
  if (result?.stdout) {
4707
4707
  console.log(chalk16.dim(result.stdout));
4708
4708
  }
4709
4709
  } catch (error) {
4710
- spinner15.stop("Error pulling schema");
4710
+ spinner16.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(text14) {
4773
+ async function copyToClipboard(text15) {
4774
4774
  try {
4775
4775
  const platform = process.platform;
4776
4776
  if (platform === "win32") {
4777
- const proc = await execa8("clip", { input: text14, reject: false });
4777
+ const proc = await execa8("clip", { input: text15, reject: false });
4778
4778
  return proc.exitCode === 0;
4779
4779
  } else if (platform === "darwin") {
4780
- const proc = await execa8("pbcopy", { input: text14, reject: false });
4780
+ const proc = await execa8("pbcopy", { input: text15, reject: false });
4781
4781
  return proc.exitCode === 0;
4782
4782
  } else {
4783
4783
  try {
4784
- const proc = await execa8("xclip", ["-selection", "clipboard"], { input: text14, reject: false });
4784
+ const proc = await execa8("xclip", ["-selection", "clipboard"], { input: text15, reject: false });
4785
4785
  return proc.exitCode === 0;
4786
4786
  } catch {
4787
- const proc = await execa8("xsel", ["--clipboard", "--input"], { input: text14, reject: false });
4787
+ const proc = await execa8("xsel", ["--clipboard", "--input"], { input: text15, 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 spinner15 = p16.spinner();
4849
- spinner15.start("Generating professional PRD...");
4848
+ const spinner16 = p16.spinner();
4849
+ spinner16.start("Generating professional PRD...");
4850
4850
  const prd = await generatePRD(anthropic, input);
4851
- spinner15.stop("PRD generated!");
4851
+ spinner16.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 text15 = await p16.text({ message: "Type instead:" });
5057
- return p16.isCancel(text15) ? null : text15;
5056
+ const text16 = await p16.text({ message: "Type instead:" });
5057
+ return p16.isCancel(text16) ? null : text16;
5058
5058
  }
5059
- const spinner15 = p16.spinner();
5060
- spinner15.start("\u{1F534} Recording... (press Ctrl+C to stop)");
5059
+ const spinner16 = p16.spinner();
5060
+ spinner16.start("\u{1F534} Recording... (press Ctrl+C to stop)");
5061
5061
  try {
5062
5062
  let transcription = "";
5063
5063
  if (process.platform === "win32") {
@@ -5067,7 +5067,7 @@ async function getVoiceInput2(prompt) {
5067
5067
  } else {
5068
5068
  transcription = await recordWithLinux2();
5069
5069
  }
5070
- spinner15.stop("Recording complete");
5070
+ spinner16.stop("Recording complete");
5071
5071
  if (transcription) {
5072
5072
  console.log(chalk17.green(`
5073
5073
  Heard: "${transcription}"
@@ -5090,17 +5090,17 @@ async function getVoiceInput2(prompt) {
5090
5090
  if (retry === "retry") {
5091
5091
  return await getVoiceInput2(prompt);
5092
5092
  } else {
5093
- const text15 = await p16.text({ message: "Type your response:" });
5094
- return p16.isCancel(text15) ? null : text15;
5093
+ const text16 = await p16.text({ message: "Type your response:" });
5094
+ return p16.isCancel(text16) ? null : text16;
5095
5095
  }
5096
5096
  }
5097
5097
  }
5098
5098
  } catch (error) {
5099
- spinner15.stop("Recording failed");
5099
+ spinner16.stop("Recording failed");
5100
5100
  console.log(chalk17.yellow("Voice input failed. Falling back to text."));
5101
5101
  }
5102
- const text14 = await p16.text({ message: prompt });
5103
- return p16.isCancel(text14) ? null : text14;
5102
+ const text15 = await p16.text({ message: prompt });
5103
+ return p16.isCancel(text15) ? null : text15;
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 text14 = await fs13.readFile(txtFile, "utf-8");
5158
+ const text15 = await fs13.readFile(txtFile, "utf-8");
5159
5159
  await fs13.remove(txtFile);
5160
- return text14.trim();
5160
+ return text15.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 text14 = await fs13.readFile(txtFile, "utf-8");
5208
+ const text15 = await fs13.readFile(txtFile, "utf-8");
5209
5209
  await fs13.remove(txtFile);
5210
- return text14.trim();
5210
+ return text15.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 spinner15 = p17.spinner();
5341
- spinner15.start("Analyzing PRD...");
5340
+ const spinner16 = p17.spinner();
5341
+ spinner16.start("Analyzing PRD...");
5342
5342
  const buildPlan = await analyzePRD(anthropic, prdContent);
5343
- spinner15.stop("PRD analyzed");
5343
+ spinner16.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
- spinner15.start("Initializing project...");
5369
+ spinner16.start("Initializing project...");
5370
5370
  await execa9("git", ["init"], { cwd: projectPath });
5371
- spinner15.stop("Project initialized");
5371
+ spinner16.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
- spinner15.start("Installing dependencies...");
5380
+ spinner16.start("Installing dependencies...");
5381
5381
  await execa9("npm", ["install"], { cwd: projectPath, reject: false });
5382
- spinner15.stop("Dependencies installed");
5382
+ spinner16.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 text14 = response.content[0].type === "text" ? response.content[0].text : "";
5454
- const jsonMatch = text14.match(/\{[\s\S]*\}/);
5453
+ const text15 = response.content[0].type === "text" ? response.content[0].text : "";
5454
+ const jsonMatch = text15.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 spinner15 = p17.spinner();
5494
- spinner15.start("Setting up project structure...");
5493
+ const spinner16 = p17.spinner();
5494
+ spinner16.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
- spinner15.stop("Project structure ready");
5538
+ spinner16.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 spinner15 = p17.spinner();
5589
- spinner15.start(`Building ${agent.name}...`);
5588
+ const spinner16 = p17.spinner();
5589
+ spinner16.start(`Building ${agent.name}...`);
5590
5590
  try {
5591
5591
  await executeAgent(anthropic, agent, projectPath, plan, (progress, action) => {
5592
- spinner15.message = `${agent.name}: ${action} (${progress}%)`;
5592
+ spinner16.message = `${agent.name}: ${action} (${progress}%)`;
5593
5593
  });
5594
- spinner15.stop(`\u2713 ${agent.name} complete`);
5594
+ spinner16.stop(`\u2713 ${agent.name} complete`);
5595
5595
  } catch (error) {
5596
- spinner15.stop(`\u2717 ${agent.name} failed`);
5596
+ spinner16.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 text14 = response.content[0].type === "text" ? response.content[0].text : "";
5699
- messages.push({ role: "assistant", content: text14 });
5700
- const questionMatch = text14.match(/<<<ASK_USER>>>([\s\S]*?)<<<END_ASK>>>/);
5698
+ const text15 = response.content[0].type === "text" ? response.content[0].text : "";
5699
+ messages.push({ role: "assistant", content: text15 });
5700
+ const questionMatch = text15.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(text14, projectPath);
5729
+ const files = await writeFilesFromResponse(text15, 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 text14 = response.content[0].type === "text" ? response.content[0].text : "";
5813
- const jsonMatch = text14.match(/\{[\s\S]*\}/);
5812
+ const text15 = response.content[0].type === "text" ? response.content[0].text : "";
5813
+ const jsonMatch = text15.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 spinner15 = p17.spinner();
5826
- spinner15.start("Running integration...");
5825
+ const spinner16 = p17.spinner();
5826
+ spinner16.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 text14 = response.content[0].type === "text" ? response.content[0].text : "";
5854
- await writeFilesFromResponse(text14, projectPath);
5853
+ const text15 = response.content[0].type === "text" ? response.content[0].text : "";
5854
+ await writeFilesFromResponse(text15, 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
- spinner15.stop("Integration complete");
5857
+ spinner16.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(text14, projectPath) {
5946
+ async function writeFilesFromResponse(text15, 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(text14)) !== null) {
5950
+ while ((match = fileRegex.exec(text15)) !== 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));
@@ -5960,8 +5960,877 @@ function sleep(ms) {
5960
5960
  return new Promise((resolve) => setTimeout(resolve, ms));
5961
5961
  }
5962
5962
 
5963
- // src/utils/nlp.ts
5963
+ // src/commands/integrate.ts
5964
5964
  import * as p18 from "@clack/prompts";
5965
+ import chalk19 from "chalk";
5966
+ import * as fs15 from "fs-extra";
5967
+ import * as path14 from "path";
5968
+ import open2 from "open";
5969
+ import { execa as execa10 } from "execa";
5970
+ var INTEGRATIONS = [
5971
+ // ═══════════════════════════════════════════════════════════════════════════
5972
+ // AUTH PROVIDERS
5973
+ // ═══════════════════════════════════════════════════════════════════════════
5974
+ {
5975
+ id: "clerk",
5976
+ name: "Clerk",
5977
+ description: "Complete user management & authentication",
5978
+ category: "auth",
5979
+ icon: "\u{1F510}",
5980
+ authType: "oauth",
5981
+ oauthUrl: "https://dashboard.clerk.com/apps/new",
5982
+ envVars: ["NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", "CLERK_SECRET_KEY"],
5983
+ packages: ["@clerk/nextjs"],
5984
+ docs: "https://clerk.com/docs"
5985
+ },
5986
+ {
5987
+ id: "auth0",
5988
+ name: "Auth0",
5989
+ description: "Enterprise authentication platform",
5990
+ category: "auth",
5991
+ icon: "\u{1F512}",
5992
+ authType: "oauth",
5993
+ oauthUrl: "https://manage.auth0.com/dashboard",
5994
+ envVars: ["AUTH0_SECRET", "AUTH0_BASE_URL", "AUTH0_ISSUER_BASE_URL", "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET"],
5995
+ packages: ["@auth0/nextjs-auth0"],
5996
+ docs: "https://auth0.com/docs"
5997
+ },
5998
+ {
5999
+ id: "nextauth",
6000
+ name: "NextAuth.js",
6001
+ description: "Open source authentication for Next.js",
6002
+ category: "auth",
6003
+ icon: "\u{1F511}",
6004
+ authType: "npm",
6005
+ envVars: ["NEXTAUTH_SECRET", "NEXTAUTH_URL"],
6006
+ packages: ["next-auth"],
6007
+ docs: "https://next-auth.js.org"
6008
+ },
6009
+ {
6010
+ id: "supabase-auth",
6011
+ name: "Supabase Auth",
6012
+ description: "Authentication with Supabase",
6013
+ category: "auth",
6014
+ icon: "\u26A1",
6015
+ authType: "oauth",
6016
+ oauthUrl: "https://supabase.com/dashboard/projects",
6017
+ envVars: ["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"],
6018
+ packages: ["@supabase/supabase-js", "@supabase/auth-helpers-nextjs"],
6019
+ docs: "https://supabase.com/docs/guides/auth"
6020
+ },
6021
+ {
6022
+ id: "firebase-auth",
6023
+ name: "Firebase Auth",
6024
+ description: "Google Firebase authentication",
6025
+ category: "auth",
6026
+ icon: "\u{1F525}",
6027
+ authType: "oauth",
6028
+ oauthUrl: "https://console.firebase.google.com",
6029
+ envVars: ["NEXT_PUBLIC_FIREBASE_API_KEY", "NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN", "NEXT_PUBLIC_FIREBASE_PROJECT_ID"],
6030
+ packages: ["firebase"],
6031
+ docs: "https://firebase.google.com/docs/auth"
6032
+ },
6033
+ // ═══════════════════════════════════════════════════════════════════════════
6034
+ // DATABASES
6035
+ // ═══════════════════════════════════════════════════════════════════════════
6036
+ {
6037
+ id: "supabase",
6038
+ name: "Supabase",
6039
+ description: "Postgres database with realtime & auth",
6040
+ category: "database",
6041
+ icon: "\u26A1",
6042
+ authType: "oauth",
6043
+ oauthUrl: "https://supabase.com/dashboard/projects",
6044
+ envVars: ["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY"],
6045
+ packages: ["@supabase/supabase-js"],
6046
+ docs: "https://supabase.com/docs"
6047
+ },
6048
+ {
6049
+ id: "planetscale",
6050
+ name: "PlanetScale",
6051
+ description: "Serverless MySQL database",
6052
+ category: "database",
6053
+ icon: "\u{1FA90}",
6054
+ authType: "oauth",
6055
+ oauthUrl: "https://app.planetscale.com",
6056
+ envVars: ["DATABASE_URL"],
6057
+ packages: ["@planetscale/database"],
6058
+ docs: "https://planetscale.com/docs"
6059
+ },
6060
+ {
6061
+ id: "neon",
6062
+ name: "Neon",
6063
+ description: "Serverless Postgres",
6064
+ category: "database",
6065
+ icon: "\u{1F418}",
6066
+ authType: "oauth",
6067
+ oauthUrl: "https://console.neon.tech",
6068
+ envVars: ["DATABASE_URL"],
6069
+ packages: ["@neondatabase/serverless"],
6070
+ docs: "https://neon.tech/docs"
6071
+ },
6072
+ {
6073
+ id: "turso",
6074
+ name: "Turso",
6075
+ description: "Edge SQLite database",
6076
+ category: "database",
6077
+ icon: "\u{1F422}",
6078
+ authType: "oauth",
6079
+ oauthUrl: "https://turso.tech/app",
6080
+ envVars: ["TURSO_DATABASE_URL", "TURSO_AUTH_TOKEN"],
6081
+ packages: ["@libsql/client"],
6082
+ docs: "https://docs.turso.tech"
6083
+ },
6084
+ {
6085
+ id: "mongodb",
6086
+ name: "MongoDB Atlas",
6087
+ description: "NoSQL document database",
6088
+ category: "database",
6089
+ icon: "\u{1F343}",
6090
+ authType: "oauth",
6091
+ oauthUrl: "https://cloud.mongodb.com",
6092
+ envVars: ["MONGODB_URI"],
6093
+ packages: ["mongodb"],
6094
+ docs: "https://www.mongodb.com/docs"
6095
+ },
6096
+ {
6097
+ id: "prisma",
6098
+ name: "Prisma",
6099
+ description: "Type-safe ORM for any database",
6100
+ category: "database",
6101
+ icon: "\u{1F537}",
6102
+ authType: "npm",
6103
+ envVars: ["DATABASE_URL"],
6104
+ packages: ["prisma", "@prisma/client"],
6105
+ docs: "https://www.prisma.io/docs"
6106
+ },
6107
+ {
6108
+ id: "drizzle",
6109
+ name: "Drizzle ORM",
6110
+ description: "Lightweight TypeScript ORM",
6111
+ category: "database",
6112
+ icon: "\u{1F4A7}",
6113
+ authType: "npm",
6114
+ envVars: ["DATABASE_URL"],
6115
+ packages: ["drizzle-orm", "drizzle-kit"],
6116
+ docs: "https://orm.drizzle.team"
6117
+ },
6118
+ // ═══════════════════════════════════════════════════════════════════════════
6119
+ // PAYMENTS
6120
+ // ═══════════════════════════════════════════════════════════════════════════
6121
+ {
6122
+ id: "stripe",
6123
+ name: "Stripe",
6124
+ description: "Payment processing & subscriptions",
6125
+ category: "payments",
6126
+ icon: "\u{1F4B3}",
6127
+ authType: "oauth",
6128
+ oauthUrl: "https://dashboard.stripe.com/apikeys",
6129
+ envVars: ["STRIPE_SECRET_KEY", "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"],
6130
+ packages: ["stripe", "@stripe/stripe-js"],
6131
+ docs: "https://stripe.com/docs"
6132
+ },
6133
+ {
6134
+ id: "lemonsqueezy",
6135
+ name: "Lemon Squeezy",
6136
+ description: "Merchant of record for SaaS",
6137
+ category: "payments",
6138
+ icon: "\u{1F34B}",
6139
+ authType: "oauth",
6140
+ oauthUrl: "https://app.lemonsqueezy.com/settings/api",
6141
+ envVars: ["LEMONSQUEEZY_API_KEY", "LEMONSQUEEZY_STORE_ID", "LEMONSQUEEZY_WEBHOOK_SECRET"],
6142
+ packages: ["@lemonsqueezy/lemonsqueezy.js"],
6143
+ docs: "https://docs.lemonsqueezy.com"
6144
+ },
6145
+ {
6146
+ id: "paddle",
6147
+ name: "Paddle",
6148
+ description: "Payment infrastructure for SaaS",
6149
+ category: "payments",
6150
+ icon: "\u{1F3D3}",
6151
+ authType: "oauth",
6152
+ oauthUrl: "https://vendors.paddle.com/authentication",
6153
+ envVars: ["PADDLE_VENDOR_ID", "PADDLE_API_KEY", "PADDLE_PUBLIC_KEY"],
6154
+ packages: ["@paddle/paddle-js"],
6155
+ docs: "https://developer.paddle.com"
6156
+ },
6157
+ // ═══════════════════════════════════════════════════════════════════════════
6158
+ // EMAIL
6159
+ // ═══════════════════════════════════════════════════════════════════════════
6160
+ {
6161
+ id: "resend",
6162
+ name: "Resend",
6163
+ description: "Modern email API for developers",
6164
+ category: "email",
6165
+ icon: "\u{1F4E7}",
6166
+ authType: "oauth",
6167
+ oauthUrl: "https://resend.com/api-keys",
6168
+ envVars: ["RESEND_API_KEY"],
6169
+ packages: ["resend"],
6170
+ docs: "https://resend.com/docs"
6171
+ },
6172
+ {
6173
+ id: "sendgrid",
6174
+ name: "SendGrid",
6175
+ description: "Email delivery service",
6176
+ category: "email",
6177
+ icon: "\u{1F4E8}",
6178
+ authType: "oauth",
6179
+ oauthUrl: "https://app.sendgrid.com/settings/api_keys",
6180
+ envVars: ["SENDGRID_API_KEY"],
6181
+ packages: ["@sendgrid/mail"],
6182
+ docs: "https://docs.sendgrid.com"
6183
+ },
6184
+ {
6185
+ id: "postmark",
6186
+ name: "Postmark",
6187
+ description: "Transactional email service",
6188
+ category: "email",
6189
+ icon: "\u{1F4EC}",
6190
+ authType: "oauth",
6191
+ oauthUrl: "https://account.postmarkapp.com/servers",
6192
+ envVars: ["POSTMARK_API_KEY"],
6193
+ packages: ["postmark"],
6194
+ docs: "https://postmarkapp.com/developer"
6195
+ },
6196
+ {
6197
+ id: "mailgun",
6198
+ name: "Mailgun",
6199
+ description: "Email API service",
6200
+ category: "email",
6201
+ icon: "\u{1F4EE}",
6202
+ authType: "oauth",
6203
+ oauthUrl: "https://app.mailgun.com/app/account/security/api_keys",
6204
+ envVars: ["MAILGUN_API_KEY", "MAILGUN_DOMAIN"],
6205
+ packages: ["mailgun.js"],
6206
+ docs: "https://documentation.mailgun.com"
6207
+ },
6208
+ {
6209
+ id: "react-email",
6210
+ name: "React Email",
6211
+ description: "Build emails with React components",
6212
+ category: "email",
6213
+ icon: "\u269B\uFE0F",
6214
+ authType: "npm",
6215
+ envVars: [],
6216
+ packages: ["react-email", "@react-email/components"],
6217
+ docs: "https://react.email/docs"
6218
+ },
6219
+ // ═══════════════════════════════════════════════════════════════════════════
6220
+ // STORAGE
6221
+ // ═══════════════════════════════════════════════════════════════════════════
6222
+ {
6223
+ id: "uploadthing",
6224
+ name: "UploadThing",
6225
+ description: "File uploads for Next.js",
6226
+ category: "storage",
6227
+ icon: "\u{1F4E4}",
6228
+ authType: "oauth",
6229
+ oauthUrl: "https://uploadthing.com/dashboard",
6230
+ envVars: ["UPLOADTHING_SECRET", "UPLOADTHING_APP_ID"],
6231
+ packages: ["uploadthing", "@uploadthing/react"],
6232
+ docs: "https://docs.uploadthing.com"
6233
+ },
6234
+ {
6235
+ id: "cloudinary",
6236
+ name: "Cloudinary",
6237
+ description: "Image & video management",
6238
+ category: "storage",
6239
+ icon: "\u2601\uFE0F",
6240
+ authType: "oauth",
6241
+ oauthUrl: "https://console.cloudinary.com/settings/api-keys",
6242
+ envVars: ["CLOUDINARY_CLOUD_NAME", "CLOUDINARY_API_KEY", "CLOUDINARY_API_SECRET"],
6243
+ packages: ["cloudinary"],
6244
+ docs: "https://cloudinary.com/documentation"
6245
+ },
6246
+ {
6247
+ id: "aws-s3",
6248
+ name: "AWS S3",
6249
+ description: "Amazon cloud storage",
6250
+ category: "storage",
6251
+ icon: "\u{1FAA3}",
6252
+ authType: "oauth",
6253
+ oauthUrl: "https://console.aws.amazon.com/iam/home#/security_credentials",
6254
+ envVars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_S3_BUCKET"],
6255
+ packages: ["@aws-sdk/client-s3"],
6256
+ docs: "https://docs.aws.amazon.com/s3"
6257
+ },
6258
+ {
6259
+ id: "vercel-blob",
6260
+ name: "Vercel Blob",
6261
+ description: "File storage by Vercel",
6262
+ category: "storage",
6263
+ icon: "\u25B2",
6264
+ authType: "oauth",
6265
+ oauthUrl: "https://vercel.com/dashboard/stores",
6266
+ envVars: ["BLOB_READ_WRITE_TOKEN"],
6267
+ packages: ["@vercel/blob"],
6268
+ docs: "https://vercel.com/docs/storage/vercel-blob"
6269
+ },
6270
+ // ═══════════════════════════════════════════════════════════════════════════
6271
+ // ANALYTICS
6272
+ // ═══════════════════════════════════════════════════════════════════════════
6273
+ {
6274
+ id: "vercel-analytics",
6275
+ name: "Vercel Analytics",
6276
+ description: "Web analytics by Vercel",
6277
+ category: "analytics",
6278
+ icon: "\u{1F4CA}",
6279
+ authType: "npm",
6280
+ envVars: [],
6281
+ packages: ["@vercel/analytics"],
6282
+ docs: "https://vercel.com/docs/analytics"
6283
+ },
6284
+ {
6285
+ id: "posthog",
6286
+ name: "PostHog",
6287
+ description: "Product analytics & feature flags",
6288
+ category: "analytics",
6289
+ icon: "\u{1F994}",
6290
+ authType: "oauth",
6291
+ oauthUrl: "https://app.posthog.com/project/settings",
6292
+ envVars: ["NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST"],
6293
+ packages: ["posthog-js"],
6294
+ docs: "https://posthog.com/docs"
6295
+ },
6296
+ {
6297
+ id: "mixpanel",
6298
+ name: "Mixpanel",
6299
+ description: "Event-based analytics",
6300
+ category: "analytics",
6301
+ icon: "\u{1F4C8}",
6302
+ authType: "oauth",
6303
+ oauthUrl: "https://mixpanel.com/settings/project",
6304
+ envVars: ["NEXT_PUBLIC_MIXPANEL_TOKEN"],
6305
+ packages: ["mixpanel-browser"],
6306
+ docs: "https://docs.mixpanel.com"
6307
+ },
6308
+ {
6309
+ id: "plausible",
6310
+ name: "Plausible",
6311
+ description: "Privacy-friendly analytics",
6312
+ category: "analytics",
6313
+ icon: "\u{1F4C9}",
6314
+ authType: "oauth",
6315
+ oauthUrl: "https://plausible.io/sites",
6316
+ envVars: ["NEXT_PUBLIC_PLAUSIBLE_DOMAIN"],
6317
+ packages: ["next-plausible"],
6318
+ docs: "https://plausible.io/docs"
6319
+ },
6320
+ // ═══════════════════════════════════════════════════════════════════════════
6321
+ // AI
6322
+ // ═══════════════════════════════════════════════════════════════════════════
6323
+ {
6324
+ id: "openai",
6325
+ name: "OpenAI",
6326
+ description: "GPT models & DALL-E",
6327
+ category: "ai",
6328
+ icon: "\u{1F916}",
6329
+ authType: "oauth",
6330
+ oauthUrl: "https://platform.openai.com/api-keys",
6331
+ envVars: ["OPENAI_API_KEY"],
6332
+ packages: ["openai"],
6333
+ docs: "https://platform.openai.com/docs"
6334
+ },
6335
+ {
6336
+ id: "anthropic",
6337
+ name: "Anthropic",
6338
+ description: "Claude AI models",
6339
+ category: "ai",
6340
+ icon: "\u{1F9E0}",
6341
+ authType: "oauth",
6342
+ oauthUrl: "https://console.anthropic.com/settings/keys",
6343
+ envVars: ["ANTHROPIC_API_KEY"],
6344
+ packages: ["@anthropic-ai/sdk"],
6345
+ docs: "https://docs.anthropic.com"
6346
+ },
6347
+ {
6348
+ id: "replicate",
6349
+ name: "Replicate",
6350
+ description: "Run ML models in the cloud",
6351
+ category: "ai",
6352
+ icon: "\u{1F504}",
6353
+ authType: "oauth",
6354
+ oauthUrl: "https://replicate.com/account/api-tokens",
6355
+ envVars: ["REPLICATE_API_TOKEN"],
6356
+ packages: ["replicate"],
6357
+ docs: "https://replicate.com/docs"
6358
+ },
6359
+ {
6360
+ id: "vercel-ai",
6361
+ name: "Vercel AI SDK",
6362
+ description: "Build AI-powered apps",
6363
+ category: "ai",
6364
+ icon: "\u2728",
6365
+ authType: "npm",
6366
+ envVars: [],
6367
+ packages: ["ai"],
6368
+ docs: "https://sdk.vercel.ai/docs"
6369
+ },
6370
+ {
6371
+ id: "elevenlabs",
6372
+ name: "ElevenLabs",
6373
+ description: "AI voice generation",
6374
+ category: "ai",
6375
+ icon: "\u{1F399}\uFE0F",
6376
+ authType: "oauth",
6377
+ oauthUrl: "https://elevenlabs.io/app/settings/api-keys",
6378
+ envVars: ["ELEVENLABS_API_KEY"],
6379
+ packages: ["elevenlabs"],
6380
+ docs: "https://elevenlabs.io/docs"
6381
+ },
6382
+ // ═══════════════════════════════════════════════════════════════════════════
6383
+ // CMS
6384
+ // ═══════════════════════════════════════════════════════════════════════════
6385
+ {
6386
+ id: "sanity",
6387
+ name: "Sanity",
6388
+ description: "Headless CMS with real-time collaboration",
6389
+ category: "cms",
6390
+ icon: "\u{1F4DD}",
6391
+ authType: "oauth",
6392
+ oauthUrl: "https://www.sanity.io/manage",
6393
+ envVars: ["NEXT_PUBLIC_SANITY_PROJECT_ID", "NEXT_PUBLIC_SANITY_DATASET", "SANITY_API_TOKEN"],
6394
+ packages: ["@sanity/client", "next-sanity"],
6395
+ docs: "https://www.sanity.io/docs"
6396
+ },
6397
+ {
6398
+ id: "contentful",
6399
+ name: "Contentful",
6400
+ description: "Enterprise headless CMS",
6401
+ category: "cms",
6402
+ icon: "\u{1F4C4}",
6403
+ authType: "oauth",
6404
+ oauthUrl: "https://app.contentful.com/account/profile/cma_tokens",
6405
+ envVars: ["CONTENTFUL_SPACE_ID", "CONTENTFUL_ACCESS_TOKEN"],
6406
+ packages: ["contentful"],
6407
+ docs: "https://www.contentful.com/developers/docs"
6408
+ },
6409
+ {
6410
+ id: "strapi",
6411
+ name: "Strapi",
6412
+ description: "Open-source headless CMS",
6413
+ category: "cms",
6414
+ icon: "\u{1F680}",
6415
+ authType: "npm",
6416
+ envVars: ["STRAPI_URL", "STRAPI_API_TOKEN"],
6417
+ packages: [],
6418
+ docs: "https://docs.strapi.io"
6419
+ },
6420
+ // ═══════════════════════════════════════════════════════════════════════════
6421
+ // MESSAGING
6422
+ // ═══════════════════════════════════════════════════════════════════════════
6423
+ {
6424
+ id: "twilio",
6425
+ name: "Twilio",
6426
+ description: "SMS, voice & WhatsApp",
6427
+ category: "messaging",
6428
+ icon: "\u{1F4F1}",
6429
+ authType: "oauth",
6430
+ oauthUrl: "https://console.twilio.com/us1/account/keys-credentials/api-keys",
6431
+ envVars: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN", "TWILIO_PHONE_NUMBER"],
6432
+ packages: ["twilio"],
6433
+ docs: "https://www.twilio.com/docs"
6434
+ },
6435
+ {
6436
+ id: "pusher",
6437
+ name: "Pusher",
6438
+ description: "Realtime websockets",
6439
+ category: "messaging",
6440
+ icon: "\u{1F514}",
6441
+ authType: "oauth",
6442
+ oauthUrl: "https://dashboard.pusher.com",
6443
+ envVars: ["PUSHER_APP_ID", "PUSHER_KEY", "PUSHER_SECRET", "NEXT_PUBLIC_PUSHER_KEY"],
6444
+ packages: ["pusher", "pusher-js"],
6445
+ docs: "https://pusher.com/docs"
6446
+ },
6447
+ {
6448
+ id: "knock",
6449
+ name: "Knock",
6450
+ description: "Notification infrastructure",
6451
+ category: "messaging",
6452
+ icon: "\u{1F514}",
6453
+ authType: "oauth",
6454
+ oauthUrl: "https://dashboard.knock.app",
6455
+ envVars: ["KNOCK_API_KEY", "NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY"],
6456
+ packages: ["@knocklabs/node", "@knocklabs/react"],
6457
+ docs: "https://docs.knock.app"
6458
+ },
6459
+ {
6460
+ id: "stream",
6461
+ name: "Stream",
6462
+ description: "Chat & activity feeds",
6463
+ category: "messaging",
6464
+ icon: "\u{1F4AC}",
6465
+ authType: "oauth",
6466
+ oauthUrl: "https://dashboard.getstream.io",
6467
+ envVars: ["STREAM_API_KEY", "STREAM_API_SECRET"],
6468
+ packages: ["stream-chat", "stream-chat-react"],
6469
+ docs: "https://getstream.io/docs"
6470
+ },
6471
+ // ═══════════════════════════════════════════════════════════════════════════
6472
+ // MONITORING
6473
+ // ═══════════════════════════════════════════════════════════════════════════
6474
+ {
6475
+ id: "sentry",
6476
+ name: "Sentry",
6477
+ description: "Error tracking & performance",
6478
+ category: "monitoring",
6479
+ icon: "\u{1F41B}",
6480
+ authType: "oauth",
6481
+ oauthUrl: "https://sentry.io/settings/account/api/auth-tokens/",
6482
+ envVars: ["SENTRY_DSN", "SENTRY_AUTH_TOKEN"],
6483
+ packages: ["@sentry/nextjs"],
6484
+ docs: "https://docs.sentry.io"
6485
+ },
6486
+ {
6487
+ id: "logrocket",
6488
+ name: "LogRocket",
6489
+ description: "Session replay & monitoring",
6490
+ category: "monitoring",
6491
+ icon: "\u{1F680}",
6492
+ authType: "oauth",
6493
+ oauthUrl: "https://app.logrocket.com/settings/setup",
6494
+ envVars: ["NEXT_PUBLIC_LOGROCKET_APP_ID"],
6495
+ packages: ["logrocket"],
6496
+ docs: "https://docs.logrocket.com"
6497
+ },
6498
+ // ═══════════════════════════════════════════════════════════════════════════
6499
+ // DEPLOYMENT
6500
+ // ═══════════════════════════════════════════════════════════════════════════
6501
+ {
6502
+ id: "vercel",
6503
+ name: "Vercel",
6504
+ description: "Deploy Next.js apps",
6505
+ category: "deployment",
6506
+ icon: "\u25B2",
6507
+ authType: "oauth",
6508
+ oauthUrl: "https://vercel.com/account/tokens",
6509
+ envVars: ["VERCEL_TOKEN"],
6510
+ packages: ["vercel"],
6511
+ docs: "https://vercel.com/docs"
6512
+ },
6513
+ {
6514
+ id: "github",
6515
+ name: "GitHub",
6516
+ description: "Code hosting & CI/CD",
6517
+ category: "deployment",
6518
+ icon: "\u{1F419}",
6519
+ authType: "oauth",
6520
+ oauthUrl: "https://github.com/settings/tokens",
6521
+ envVars: ["GITHUB_TOKEN"],
6522
+ packages: ["octokit"],
6523
+ docs: "https://docs.github.com"
6524
+ },
6525
+ // ═══════════════════════════════════════════════════════════════════════════
6526
+ // OTHER
6527
+ // ═══════════════════════════════════════════════════════════════════════════
6528
+ {
6529
+ id: "crisp",
6530
+ name: "Crisp",
6531
+ description: "Customer support chat",
6532
+ category: "other",
6533
+ icon: "\u{1F4AC}",
6534
+ authType: "oauth",
6535
+ oauthUrl: "https://app.crisp.chat/settings/website",
6536
+ envVars: ["NEXT_PUBLIC_CRISP_WEBSITE_ID"],
6537
+ packages: ["crisp-sdk-web"],
6538
+ docs: "https://docs.crisp.chat"
6539
+ },
6540
+ {
6541
+ id: "intercom",
6542
+ name: "Intercom",
6543
+ description: "Customer messaging platform",
6544
+ category: "other",
6545
+ icon: "\u{1F4AC}",
6546
+ authType: "oauth",
6547
+ oauthUrl: "https://app.intercom.com/a/apps/_/developer-hub",
6548
+ envVars: ["NEXT_PUBLIC_INTERCOM_APP_ID"],
6549
+ packages: ["@intercom/messenger-js-sdk"],
6550
+ docs: "https://developers.intercom.com"
6551
+ },
6552
+ {
6553
+ id: "cal",
6554
+ name: "Cal.com",
6555
+ description: "Scheduling infrastructure",
6556
+ category: "other",
6557
+ icon: "\u{1F4C5}",
6558
+ authType: "oauth",
6559
+ oauthUrl: "https://app.cal.com/settings/developer/api-keys",
6560
+ envVars: ["CAL_API_KEY"],
6561
+ packages: ["@calcom/embed-react"],
6562
+ docs: "https://cal.com/docs"
6563
+ }
6564
+ ];
6565
+ async function integrateCommand(integrationId) {
6566
+ const config = new Config();
6567
+ console.log(chalk19.cyan(`
6568
+ \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
6569
+ \u2551 \u{1F50C} ONE-CLICK INTEGRATIONS \u2551
6570
+ \u2551 \u2551
6571
+ \u2551 ${INTEGRATIONS.length} integrations available \u2551
6572
+ \u2551 Browser-based auth \u2022 Auto-install \u2022 Ready in seconds \u2551
6573
+ \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
6574
+ `));
6575
+ if (integrationId) {
6576
+ const integration2 = INTEGRATIONS.find((i) => i.id === integrationId);
6577
+ if (!integration2) {
6578
+ p18.log.error(`Unknown integration: ${integrationId}`);
6579
+ console.log(chalk19.dim(`Run 'codebakers integrate' to see all available integrations`));
6580
+ return;
6581
+ }
6582
+ await installIntegration(integration2, config);
6583
+ return;
6584
+ }
6585
+ const categories = [...new Set(INTEGRATIONS.map((i) => i.category))];
6586
+ const category = await p18.select({
6587
+ message: "Choose a category:",
6588
+ options: [
6589
+ { value: "all", label: "\u{1F4CB} Show all integrations" },
6590
+ { value: "search", label: "\u{1F50D} Search by name" },
6591
+ ...categories.map((c) => ({
6592
+ value: c,
6593
+ label: getCategoryLabel(c),
6594
+ hint: `${INTEGRATIONS.filter((i) => i.category === c).length} integrations`
6595
+ }))
6596
+ ]
6597
+ });
6598
+ if (p18.isCancel(category)) return;
6599
+ let filteredIntegrations = INTEGRATIONS;
6600
+ if (category === "search") {
6601
+ const query = await p18.text({
6602
+ message: "Search integrations:",
6603
+ placeholder: "stripe, auth, email..."
6604
+ });
6605
+ if (p18.isCancel(query)) return;
6606
+ const q = query.toLowerCase();
6607
+ filteredIntegrations = INTEGRATIONS.filter(
6608
+ (i) => i.name.toLowerCase().includes(q) || i.description.toLowerCase().includes(q) || i.id.toLowerCase().includes(q)
6609
+ );
6610
+ } else if (category !== "all") {
6611
+ filteredIntegrations = INTEGRATIONS.filter((i) => i.category === category);
6612
+ }
6613
+ if (filteredIntegrations.length === 0) {
6614
+ p18.log.warn("No integrations found");
6615
+ return;
6616
+ }
6617
+ const selected = await p18.select({
6618
+ message: "Select an integration to install:",
6619
+ options: filteredIntegrations.map((i) => ({
6620
+ value: i.id,
6621
+ label: `${i.icon} ${i.name}`,
6622
+ hint: i.description
6623
+ }))
6624
+ });
6625
+ if (p18.isCancel(selected)) return;
6626
+ const integration = INTEGRATIONS.find((i) => i.id === selected);
6627
+ await installIntegration(integration, config);
6628
+ }
6629
+ async function installIntegration(integration, config) {
6630
+ console.log(chalk19.cyan(`
6631
+ Installing ${integration.icon} ${integration.name}...
6632
+ `));
6633
+ const steps = [];
6634
+ if (integration.packages && integration.packages.length > 0) {
6635
+ steps.push({ name: "Install packages", done: false });
6636
+ }
6637
+ if (integration.envVars.length > 0) {
6638
+ steps.push({ name: "Configure credentials", done: false });
6639
+ }
6640
+ steps.push({ name: "Setup integration", done: false });
6641
+ const spinner16 = p18.spinner();
6642
+ if (integration.packages && integration.packages.length > 0) {
6643
+ spinner16.start(`Installing ${integration.packages.join(", ")}...`);
6644
+ try {
6645
+ await execa10("npm", ["install", ...integration.packages], {
6646
+ cwd: process.cwd(),
6647
+ reject: false
6648
+ });
6649
+ spinner16.stop(`\u2713 Packages installed`);
6650
+ } catch {
6651
+ spinner16.stop(`\u2713 Packages installed (or already present)`);
6652
+ }
6653
+ }
6654
+ if (integration.envVars.length > 0) {
6655
+ if (integration.authType === "oauth" && integration.oauthUrl) {
6656
+ console.log(chalk19.cyan(`
6657
+ Opening ${integration.name} in your browser...`));
6658
+ console.log(chalk19.dim(` Get your API keys and paste them below.
6659
+ `));
6660
+ await open2(integration.oauthUrl);
6661
+ await sleep2(1500);
6662
+ }
6663
+ const credentials = {};
6664
+ for (const envVar of integration.envVars) {
6665
+ const isPublic = envVar.startsWith("NEXT_PUBLIC_");
6666
+ const hint = isPublic ? "(public, will be in client bundle)" : "(secret, server-only)";
6667
+ const value = await p18.text({
6668
+ message: `${envVar} ${chalk19.dim(hint)}:`,
6669
+ placeholder: "Paste your key here...",
6670
+ validate: (v) => !v ? "Required" : void 0
6671
+ });
6672
+ if (p18.isCancel(value)) return;
6673
+ credentials[envVar] = value;
6674
+ }
6675
+ await saveEnvVars(credentials);
6676
+ console.log(chalk19.green(` \u2713 Credentials saved to .env.local
6677
+ `));
6678
+ }
6679
+ spinner16.start("Generating setup code...");
6680
+ const setupCode = await generateSetupCode(integration);
6681
+ if (setupCode) {
6682
+ for (const file of setupCode) {
6683
+ await fs15.ensureDir(path14.dirname(file.path));
6684
+ await fs15.writeFile(file.path, file.content);
6685
+ }
6686
+ }
6687
+ spinner16.stop("\u2713 Setup complete");
6688
+ console.log(chalk19.green(`
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
+ \u2551 \u2713 ${integration.name} installed successfully!
6691
+ \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
6692
+ `));
6693
+ if (integration.packages && integration.packages.length > 0) {
6694
+ console.log(chalk19.dim(` Packages: ${integration.packages.join(", ")}`));
6695
+ }
6696
+ if (integration.envVars.length > 0) {
6697
+ console.log(chalk19.dim(` Env vars: ${integration.envVars.join(", ")}`));
6698
+ }
6699
+ console.log(`
6700
+ \u2551 \u{1F4DA} Docs: ${integration.docs}
6701
+ \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
6702
+ `);
6703
+ }
6704
+ function getCategoryLabel(category) {
6705
+ const labels = {
6706
+ auth: "\u{1F510} Authentication",
6707
+ database: "\u{1F5C4}\uFE0F Databases",
6708
+ payments: "\u{1F4B3} Payments",
6709
+ email: "\u{1F4E7} Email",
6710
+ storage: "\u{1F4E6} Storage",
6711
+ analytics: "\u{1F4CA} Analytics",
6712
+ ai: "\u{1F916} AI & ML",
6713
+ cms: "\u{1F4DD} CMS",
6714
+ messaging: "\u{1F4AC} Messaging",
6715
+ monitoring: "\u{1F50D} Monitoring",
6716
+ deployment: "\u{1F680} Deployment",
6717
+ other: "\u{1F527} Other"
6718
+ };
6719
+ return labels[category] || category;
6720
+ }
6721
+ async function saveEnvVars(vars) {
6722
+ const envPath = path14.join(process.cwd(), ".env.local");
6723
+ let content = "";
6724
+ if (await fs15.pathExists(envPath)) {
6725
+ content = await fs15.readFile(envPath, "utf-8");
6726
+ if (!content.endsWith("\n")) {
6727
+ content += "\n";
6728
+ }
6729
+ content += "\n# Added by CodeBakers\n";
6730
+ }
6731
+ for (const [key, value] of Object.entries(vars)) {
6732
+ const regex = new RegExp(`^${key}=`, "m");
6733
+ if (regex.test(content)) {
6734
+ content = content.replace(regex, `${key}=${value}`);
6735
+ } else {
6736
+ content += `${key}=${value}
6737
+ `;
6738
+ }
6739
+ }
6740
+ await fs15.writeFile(envPath, content);
6741
+ }
6742
+ async function generateSetupCode(integration) {
6743
+ const files = [];
6744
+ switch (integration.id) {
6745
+ case "clerk":
6746
+ files.push({
6747
+ path: "src/middleware.ts",
6748
+ content: `import { clerkMiddleware } from '@clerk/nextjs/server';
6749
+
6750
+ export default clerkMiddleware();
6751
+
6752
+ export const config = {
6753
+ matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
6754
+ };
6755
+ `
6756
+ });
6757
+ break;
6758
+ case "stripe":
6759
+ files.push({
6760
+ path: "src/lib/stripe.ts",
6761
+ content: `import Stripe from 'stripe';
6762
+
6763
+ export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
6764
+ apiVersion: '2023-10-16',
6765
+ typescript: true,
6766
+ });
6767
+ `
6768
+ });
6769
+ break;
6770
+ case "supabase":
6771
+ files.push({
6772
+ path: "src/lib/supabase.ts",
6773
+ content: `import { createClient } from '@supabase/supabase-js';
6774
+
6775
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
6776
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
6777
+
6778
+ export const supabase = createClient(supabaseUrl, supabaseAnonKey);
6779
+ `
6780
+ });
6781
+ break;
6782
+ case "resend":
6783
+ files.push({
6784
+ path: "src/lib/resend.ts",
6785
+ content: `import { Resend } from 'resend';
6786
+
6787
+ export const resend = new Resend(process.env.RESEND_API_KEY);
6788
+ `
6789
+ });
6790
+ break;
6791
+ case "openai":
6792
+ files.push({
6793
+ path: "src/lib/openai.ts",
6794
+ content: `import OpenAI from 'openai';
6795
+
6796
+ export const openai = new OpenAI({
6797
+ apiKey: process.env.OPENAI_API_KEY,
6798
+ });
6799
+ `
6800
+ });
6801
+ break;
6802
+ case "anthropic":
6803
+ files.push({
6804
+ path: "src/lib/anthropic.ts",
6805
+ content: `import Anthropic from '@anthropic-ai/sdk';
6806
+
6807
+ export const anthropic = new Anthropic({
6808
+ apiKey: process.env.ANTHROPIC_API_KEY,
6809
+ });
6810
+ `
6811
+ });
6812
+ break;
6813
+ case "sentry":
6814
+ files.push({
6815
+ path: "sentry.client.config.ts",
6816
+ content: `import * as Sentry from '@sentry/nextjs';
6817
+
6818
+ Sentry.init({
6819
+ dsn: process.env.SENTRY_DSN,
6820
+ tracesSampleRate: 1.0,
6821
+ });
6822
+ `
6823
+ });
6824
+ break;
6825
+ }
6826
+ return files.length > 0 ? files : null;
6827
+ }
6828
+ function sleep2(ms) {
6829
+ return new Promise((resolve) => setTimeout(resolve, ms));
6830
+ }
6831
+
6832
+ // src/utils/nlp.ts
6833
+ import * as p19 from "@clack/prompts";
5965
6834
  import Anthropic5 from "@anthropic-ai/sdk";
5966
6835
  var COMMAND_PATTERNS = {
5967
6836
  init: {
@@ -6083,7 +6952,7 @@ function extractArgs(input, pattern) {
6083
6952
  const patternWords = pattern.toLowerCase().split(/\s+/);
6084
6953
  const inputWords = input.split(/\s+/);
6085
6954
  const args2 = inputWords.filter(
6086
- (word) => !patternWords.some((p20) => word.toLowerCase().includes(p20) || p20.includes(word.toLowerCase()))
6955
+ (word) => !patternWords.some((p21) => word.toLowerCase().includes(p21) || p21.includes(word.toLowerCase()))
6087
6956
  );
6088
6957
  return args2.filter((a) => a.length > 2);
6089
6958
  }
@@ -6114,8 +6983,8 @@ If the input is asking to build/create/add a specific feature, use "code" and pu
6114
6983
  If unclear between multiple commands, use the most likely one with lower confidence.`
6115
6984
  }]
6116
6985
  });
6117
- const text14 = response.content[0].type === "text" ? response.content[0].text : "";
6118
- const jsonMatch = text14.match(/\{[\s\S]*?\}/);
6986
+ const text15 = response.content[0].type === "text" ? response.content[0].text : "";
6987
+ const jsonMatch = text15.match(/\{[\s\S]*?\}/);
6119
6988
  if (!jsonMatch) {
6120
6989
  return {
6121
6990
  command: "code",
@@ -6131,11 +7000,11 @@ async function clarifyCommand(parsed) {
6131
7000
  return parsed;
6132
7001
  }
6133
7002
  if (parsed.confidence >= 0.5) {
6134
- const confirm11 = await p18.confirm({
7003
+ const confirm11 = await p19.confirm({
6135
7004
  message: `Did you mean: ${parsed.interpretation}?`,
6136
7005
  initialValue: true
6137
7006
  });
6138
- if (confirm11 && !p18.isCancel(confirm11)) {
7007
+ if (confirm11 && !p19.isCancel(confirm11)) {
6139
7008
  return { ...parsed, confidence: 1 };
6140
7009
  }
6141
7010
  }
@@ -6150,19 +7019,19 @@ async function clarifyCommand(parsed) {
6150
7019
  { value: "prd-maker", label: "\u{1F4DD} Create PRD", description: "Document your idea" },
6151
7020
  { value: "other", label: "\u2753 Something else", description: "Describe what you need" }
6152
7021
  ];
6153
- const selection = await p18.select({
7022
+ const selection = await p19.select({
6154
7023
  message: "What would you like to do?",
6155
7024
  options: options.map((o) => ({ value: o.value, label: o.label, hint: o.description }))
6156
7025
  });
6157
- if (p18.isCancel(selection)) {
7026
+ if (p19.isCancel(selection)) {
6158
7027
  return { ...parsed, command: "cancel", confidence: 1 };
6159
7028
  }
6160
7029
  if (selection === "other") {
6161
- const description = await p18.text({
7030
+ const description = await p19.text({
6162
7031
  message: "Describe what you want to do:",
6163
7032
  placeholder: "I want to..."
6164
7033
  });
6165
- if (p18.isCancel(description)) {
7034
+ if (p19.isCancel(description)) {
6166
7035
  return { ...parsed, command: "cancel", confidence: 1 };
6167
7036
  }
6168
7037
  return {
@@ -6180,19 +7049,19 @@ async function clarifyCommand(parsed) {
6180
7049
  };
6181
7050
  }
6182
7051
  async function clarifyDeployTarget() {
6183
- const target = await p18.select({
7052
+ const target = await p19.select({
6184
7053
  message: "Where do you want to deploy?",
6185
7054
  options: [
6186
7055
  { value: "preview", label: "\u{1F50D} Preview", hint: "Test URL to review changes" },
6187
7056
  { value: "production", label: "\u{1F680} Production", hint: "Live site for users" }
6188
7057
  ]
6189
7058
  });
6190
- if (p18.isCancel(target)) return null;
7059
+ if (p19.isCancel(target)) return null;
6191
7060
  return target;
6192
7061
  }
6193
7062
 
6194
7063
  // src/index.ts
6195
- var VERSION2 = "2.0.9";
7064
+ var VERSION2 = "2.1.0";
6196
7065
  var logo = `
6197
7066
  \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
6198
7067
  \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
@@ -6205,21 +7074,22 @@ async function showMainMenu() {
6205
7074
  const config = new Config();
6206
7075
  const isSetup = config.isConfigured();
6207
7076
  console.log(gradient.pastel.multiline(logo));
6208
- console.log(chalk19.dim(` v${VERSION2} \u2014 AI dev team that follows the rules
7077
+ console.log(chalk20.dim(` v${VERSION2} \u2014 AI dev team that follows the rules
6209
7078
  `));
6210
7079
  if (!isSetup) {
6211
7080
  console.log(boxen(
6212
- chalk19.yellow("Welcome to CodeBakers! Let's get you set up."),
7081
+ chalk20.yellow("Welcome to CodeBakers! Let's get you set up."),
6213
7082
  { padding: 1, borderColor: "yellow", borderStyle: "round" }
6214
7083
  ));
6215
7084
  await setupCommand();
6216
7085
  return;
6217
7086
  }
6218
7087
  const inProject = config.isInProject();
6219
- const action = await p19.select({
7088
+ const action = await p20.select({
6220
7089
  message: "What do you want to do? (or type naturally)",
6221
7090
  options: inProject ? [
6222
7091
  { value: "code", label: "\u{1F4AC} Code with AI", hint: "build features, fix bugs" },
7092
+ { value: "integrate", label: "\u{1F50C} One-Click Integrations", hint: "50+ services, browser auth" },
6223
7093
  { value: "check", label: "\u{1F50D} Check code quality", hint: "run pattern enforcement" },
6224
7094
  { value: "deploy", label: "\u{1F680} Deploy", hint: "deploy to Vercel" },
6225
7095
  { value: "migrate", label: "\u{1F5C4}\uFE0F Database migrations", hint: "push, generate, status" },
@@ -6256,14 +7126,17 @@ async function showMainMenu() {
6256
7126
  { value: "help", label: "\u2753 Help" }
6257
7127
  ]
6258
7128
  });
6259
- if (p19.isCancel(action)) {
6260
- p19.cancel("Goodbye!");
7129
+ if (p20.isCancel(action)) {
7130
+ p20.cancel("Goodbye!");
6261
7131
  process.exit(0);
6262
7132
  }
6263
7133
  switch (action) {
6264
7134
  case "code":
6265
7135
  await codeCommand();
6266
7136
  break;
7137
+ case "integrate":
7138
+ await integrateCommand();
7139
+ break;
6267
7140
  case "check":
6268
7141
  await checkCommand();
6269
7142
  break;
@@ -6324,27 +7197,27 @@ async function showMainMenu() {
6324
7197
  }
6325
7198
  function showHelp2() {
6326
7199
  console.log(boxen(`
6327
- ${chalk19.bold("CodeBakers CLI")} \u2014 AI dev team that follows the rules
6328
-
6329
- ${chalk19.bold("Commands:")}
6330
- ${chalk19.cyan("codebakers")} Interactive menu (or just run with no args)
6331
- ${chalk19.cyan("codebakers init")} Create a new project
6332
- ${chalk19.cyan("codebakers code")} Start AI coding session
6333
- ${chalk19.cyan("codebakers check")} Run pattern enforcement
6334
- ${chalk19.cyan("codebakers deploy")} Deploy to production
6335
- ${chalk19.cyan("codebakers fix")} Auto-fix errors
6336
- ${chalk19.cyan("codebakers generate")} Generate components/pages
6337
- ${chalk19.cyan("codebakers connect")} Connect external services
6338
- ${chalk19.cyan("codebakers gateway")} Manage messaging channels
6339
- ${chalk19.cyan("codebakers status")} View project status
6340
- ${chalk19.cyan("codebakers security")} Run security audit
6341
- ${chalk19.cyan("codebakers learn")} View/manage learning
6342
-
6343
- ${chalk19.bold("Help at any time:")}
6344
- Press ${chalk19.yellow("?")} during any command to get contextual help
6345
-
6346
- ${chalk19.bold("Documentation:")}
6347
- ${chalk19.dim("https://codebakers.dev/docs")}
7200
+ ${chalk20.bold("CodeBakers CLI")} \u2014 AI dev team that follows the rules
7201
+
7202
+ ${chalk20.bold("Commands:")}
7203
+ ${chalk20.cyan("codebakers")} Interactive menu (or just run with no args)
7204
+ ${chalk20.cyan("codebakers init")} Create a new project
7205
+ ${chalk20.cyan("codebakers code")} Start AI coding session
7206
+ ${chalk20.cyan("codebakers check")} Run pattern enforcement
7207
+ ${chalk20.cyan("codebakers deploy")} Deploy to production
7208
+ ${chalk20.cyan("codebakers fix")} Auto-fix errors
7209
+ ${chalk20.cyan("codebakers generate")} Generate components/pages
7210
+ ${chalk20.cyan("codebakers connect")} Connect external services
7211
+ ${chalk20.cyan("codebakers gateway")} Manage messaging channels
7212
+ ${chalk20.cyan("codebakers status")} View project status
7213
+ ${chalk20.cyan("codebakers security")} Run security audit
7214
+ ${chalk20.cyan("codebakers learn")} View/manage learning
7215
+
7216
+ ${chalk20.bold("Help at any time:")}
7217
+ Press ${chalk20.yellow("?")} during any command to get contextual help
7218
+
7219
+ ${chalk20.bold("Documentation:")}
7220
+ ${chalk20.dim("https://codebakers.dev/docs")}
6348
7221
  `, { padding: 1, borderColor: "cyan", borderStyle: "round" }));
6349
7222
  }
6350
7223
  var program = new Command();
@@ -6367,9 +7240,10 @@ program.command("advisors").alias("dream-team").description("Consult with the Co
6367
7240
  program.command("migrate").alias("db").description("Database migrations (push, generate, status)").option("--push", "Push migrations to database").option("--generate", "Generate new migration").option("--status", "Check migration status").action(migrateCommand);
6368
7241
  program.command("prd-maker").alias("create-prd").description("Create a PRD through guided interview (supports voice input)").action(prdMakerCommand);
6369
7242
  program.command("build [prd-file]").alias("swarm").description("Parallel build with 3 AI agents (self-healing)").option("--sequential", "Disable parallel execution").action(buildCommand);
7243
+ program.command("integrate [integration]").alias("add").description("One-click integrations (50+ services with browser auth)").action(integrateCommand);
6370
7244
  async function handleNaturalLanguage(input) {
6371
7245
  const config = new Config();
6372
- console.log(chalk19.dim("\n Parsing your request...\n"));
7246
+ console.log(chalk20.dim("\n Parsing your request...\n"));
6373
7247
  const parsed = await parseNaturalLanguage(input, config);
6374
7248
  if (!parsed) {
6375
7249
  await codeCommand(input);
@@ -6455,6 +7329,8 @@ if (args.length === 0) {
6455
7329
  "prd-maker",
6456
7330
  "build",
6457
7331
  "swarm",
7332
+ "integrate",
7333
+ "add",
6458
7334
  "help"
6459
7335
  ];
6460
7336
  const firstArg = args[0];