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 +1123 -247
- package/package.json +1 -1
- package/src/commands/integrate.ts +985 -0
- package/src/index.ts +14 -2
package/dist/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
import { Command } from "commander";
|
|
14
|
-
import * as
|
|
15
|
-
import
|
|
14
|
+
import * as 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
|
|
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((
|
|
610
|
-
id:
|
|
611
|
-
name:
|
|
612
|
-
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
|
|
1032
|
+
const spinner16 = p2.spinner();
|
|
1033
1033
|
const projectPath = path.join(process.cwd(), projectName);
|
|
1034
1034
|
try {
|
|
1035
|
-
|
|
1035
|
+
spinner16.start("Creating local project...");
|
|
1036
1036
|
await createLocalProject(projectPath, projectConfig);
|
|
1037
|
-
|
|
1038
|
-
|
|
1037
|
+
spinner16.stop("Local project created");
|
|
1038
|
+
spinner16.start("Installing dependencies...");
|
|
1039
1039
|
await execa3("pnpm", ["install"], { cwd: projectPath });
|
|
1040
|
-
|
|
1040
|
+
spinner16.stop("Dependencies installed");
|
|
1041
1041
|
if (services.includes("github")) {
|
|
1042
|
-
|
|
1042
|
+
spinner16.start("Creating GitHub repository...");
|
|
1043
1043
|
const github = new GitHubService(config);
|
|
1044
1044
|
const repo = await github.createRepo(projectName, { private: true });
|
|
1045
|
-
|
|
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
|
-
|
|
1053
|
+
spinner16.start("Creating Supabase project...");
|
|
1054
1054
|
const supabase = new SupabaseService(config);
|
|
1055
1055
|
const project = await supabase.createProject(projectName);
|
|
1056
|
-
|
|
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
|
-
|
|
1064
|
+
spinner16.start("Creating Vercel project...");
|
|
1065
1065
|
const vercel = new VercelService(config);
|
|
1066
1066
|
const project = await vercel.createProject(projectName);
|
|
1067
|
-
|
|
1067
|
+
spinner16.stop(`Vercel project created`);
|
|
1068
1068
|
if (domain) {
|
|
1069
|
-
|
|
1069
|
+
spinner16.start(`Configuring domain: ${domain}...`);
|
|
1070
1070
|
await vercel.addDomain(projectName, domain);
|
|
1071
|
-
|
|
1071
|
+
spinner16.stop("Domain configured");
|
|
1072
1072
|
}
|
|
1073
|
-
|
|
1073
|
+
spinner16.start("Deploying to Vercel...");
|
|
1074
1074
|
const deployment = await vercel.deploy(projectPath);
|
|
1075
|
-
|
|
1075
|
+
spinner16.stop(`Deployed: ${deployment.url}`);
|
|
1076
1076
|
}
|
|
1077
|
-
|
|
1077
|
+
spinner16.start("Generating CLAUDE.md...");
|
|
1078
1078
|
const claudeMd = generateClaudeMd(projectConfig);
|
|
1079
1079
|
await fs.writeFile(path.join(projectPath, "CLAUDE.md"), claudeMd);
|
|
1080
|
-
|
|
1081
|
-
|
|
1080
|
+
spinner16.stop("CLAUDE.md generated");
|
|
1081
|
+
spinner16.start("Setting up CodeBakers enforcement...");
|
|
1082
1082
|
await setupGitHooks(projectPath);
|
|
1083
|
-
|
|
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
|
-
|
|
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
|
|
1830
|
-
|
|
1829
|
+
const spinner16 = p3.spinner();
|
|
1830
|
+
spinner16.start("Analyzing code...");
|
|
1831
1831
|
const result = await runPatternCheck(options.fix || false);
|
|
1832
|
-
|
|
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
|
-
|
|
1837
|
+
spinner16.start(`Auto-fixing ${fixable.length} violations...`);
|
|
1838
1838
|
await autoFix(fixable);
|
|
1839
|
-
|
|
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
|
|
2044
|
-
|
|
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
|
-
|
|
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
|
|
2079
|
-
return p4.isCancel(
|
|
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
|
|
2085
|
-
return p4.isCancel(
|
|
2084
|
+
const text15 = await p4.text({ message: "Type instead:" });
|
|
2085
|
+
return p4.isCancel(text15) ? null : text15;
|
|
2086
2086
|
}
|
|
2087
2087
|
} catch (error) {
|
|
2088
|
-
|
|
2088
|
+
spinner16.stop("Recording failed");
|
|
2089
2089
|
console.log(chalk5.yellow("Voice input failed. Please type instead."));
|
|
2090
|
-
const
|
|
2091
|
-
return p4.isCancel(
|
|
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
|
|
2226
|
+
const text15 = await fs4.readFile(txtFile, "utf-8");
|
|
2227
2227
|
await fs4.remove(txtFile).catch(() => {
|
|
2228
2228
|
});
|
|
2229
|
-
return
|
|
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
|
|
2337
|
-
if (
|
|
2338
|
-
return { content:
|
|
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(
|
|
2411
|
-
if (
|
|
2412
|
-
if (
|
|
2413
|
-
if (
|
|
2414
|
-
if (
|
|
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(
|
|
2418
|
-
if (!looksLikePaste(
|
|
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 =
|
|
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:
|
|
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
|
-
${
|
|
2452
|
+
${text15}
|
|
2453
2453
|
\`\`\`
|
|
2454
2454
|
--- END ---
|
|
2455
2455
|
|
|
@@ -2468,7 +2468,7 @@ ${text14}
|
|
|
2468
2468
|
|
|
2469
2469
|
--- PASTED CODE ---
|
|
2470
2470
|
\`\`\`
|
|
2471
|
-
${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2664
|
+
spinner16.start("Building...");
|
|
2665
2665
|
for (const action of actions) {
|
|
2666
|
-
await executeAction(action,
|
|
2666
|
+
await executeAction(action, spinner16);
|
|
2667
2667
|
}
|
|
2668
|
-
|
|
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
|
-
|
|
2679
|
+
spinner16.start("Auto-fixing...");
|
|
2680
2680
|
await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
|
|
2681
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
2821
|
+
spinner16.message(`Edited ${action.path}`);
|
|
2822
2822
|
}
|
|
2823
2823
|
}
|
|
2824
2824
|
break;
|
|
2825
2825
|
}
|
|
2826
2826
|
case "RUN_COMMAND": {
|
|
2827
|
-
|
|
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
|
-
|
|
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
|
|
3084
|
+
const spinner16 = p7.spinner();
|
|
3085
3085
|
if (options.check !== false) {
|
|
3086
|
-
|
|
3086
|
+
spinner16.start("Running CodeBakers check...");
|
|
3087
3087
|
const checkResult = await runPatternCheck(false);
|
|
3088
3088
|
if (!checkResult.passed) {
|
|
3089
|
-
|
|
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
|
-
|
|
3110
|
+
spinner16.start("Auto-fixing with AI...");
|
|
3111
3111
|
await autoFixWithAI(config, errors);
|
|
3112
|
-
|
|
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
|
-
|
|
3121
|
+
spinner16.stop("Pattern check passed");
|
|
3122
3122
|
}
|
|
3123
|
-
|
|
3123
|
+
spinner16.start("Running TypeScript check...");
|
|
3124
3124
|
try {
|
|
3125
3125
|
await execa7("npx", ["tsc", "--noEmit"], { cwd: process.cwd() });
|
|
3126
|
-
|
|
3126
|
+
spinner16.stop("TypeScript check passed");
|
|
3127
3127
|
} catch (error) {
|
|
3128
|
-
|
|
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
|
-
|
|
3135
|
+
spinner16.start("Fixing TypeScript errors...");
|
|
3136
3136
|
const fixed = await fixTypeScriptErrors(config);
|
|
3137
|
-
|
|
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
|
-
|
|
3147
|
+
spinner16.start("Building project...");
|
|
3148
3148
|
try {
|
|
3149
3149
|
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3150
|
-
|
|
3150
|
+
spinner16.stop("Build successful");
|
|
3151
3151
|
} catch (error) {
|
|
3152
|
-
|
|
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
|
-
|
|
3161
|
+
spinner16.start("Fixing build errors...");
|
|
3162
3162
|
const fixed = await fixBuildErrors(config, errorOutput);
|
|
3163
|
-
|
|
3163
|
+
spinner16.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
|
|
3164
3164
|
if (fixed) {
|
|
3165
|
-
|
|
3165
|
+
spinner16.start("Retrying build...");
|
|
3166
3166
|
try {
|
|
3167
3167
|
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3168
|
-
|
|
3168
|
+
spinner16.stop("Build successful");
|
|
3169
3169
|
} catch {
|
|
3170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3200
|
+
spinner16.start("Pushing to GitHub...");
|
|
3201
3201
|
await execa7("git", ["push"], { cwd: process.cwd() });
|
|
3202
|
-
|
|
3202
|
+
spinner16.stop("Pushed to GitHub");
|
|
3203
3203
|
}
|
|
3204
3204
|
}
|
|
3205
3205
|
} else {
|
|
3206
|
-
|
|
3206
|
+
spinner16.stop("No uncommitted changes");
|
|
3207
3207
|
}
|
|
3208
3208
|
const deployType = options.preview ? "preview" : "production";
|
|
3209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3235
|
-
|
|
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
|
|
3602
|
-
|
|
3601
|
+
const spinner16 = p9.spinner();
|
|
3602
|
+
spinner16.start("Generating QR code...");
|
|
3603
3603
|
try {
|
|
3604
|
-
|
|
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
|
-
|
|
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
|
|
3655
|
-
await
|
|
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
|
|
3668
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3703
|
-
await
|
|
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
|
|
3740
|
-
await
|
|
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
|
|
3779
|
-
await
|
|
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
|
|
3822
|
-
|
|
3821
|
+
const spinner16 = p9.spinner();
|
|
3822
|
+
spinner16.start("Starting channel gateway...");
|
|
3823
3823
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3824
|
-
|
|
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
|
|
3839
|
-
|
|
3838
|
+
const spinner16 = p9.spinner();
|
|
3839
|
+
spinner16.start("Stopping gateway...");
|
|
3840
3840
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3841
|
-
|
|
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
|
|
3900
|
-
await
|
|
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
|
|
3957
|
-
|
|
3956
|
+
const spinner16 = p11.spinner();
|
|
3957
|
+
spinner16.start("Scanning for security issues...");
|
|
3958
3958
|
const issues = await runSecurityScan();
|
|
3959
|
-
|
|
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
|
|
4054
|
-
|
|
4053
|
+
const spinner16 = p12.spinner();
|
|
4054
|
+
spinner16.start("Generating...");
|
|
4055
4055
|
await generateFile(generateType, name);
|
|
4056
|
-
|
|
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
|
|
4202
|
-
|
|
4201
|
+
const spinner16 = p13.spinner();
|
|
4202
|
+
spinner16.start("Analyzing code...");
|
|
4203
4203
|
const result = await runPatternCheck(true);
|
|
4204
4204
|
if (result.passed) {
|
|
4205
|
-
|
|
4205
|
+
spinner16.stop("No issues found!");
|
|
4206
4206
|
} else {
|
|
4207
|
-
|
|
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
|
|
4409
|
-
|
|
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
|
-
|
|
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
|
|
4534
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4568
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4593
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4691
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
4849
|
-
|
|
4848
|
+
const spinner16 = p16.spinner();
|
|
4849
|
+
spinner16.start("Generating professional PRD...");
|
|
4850
4850
|
const prd = await generatePRD(anthropic, input);
|
|
4851
|
-
|
|
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
|
|
5057
|
-
return p16.isCancel(
|
|
5056
|
+
const text16 = await p16.text({ message: "Type instead:" });
|
|
5057
|
+
return p16.isCancel(text16) ? null : text16;
|
|
5058
5058
|
}
|
|
5059
|
-
const
|
|
5060
|
-
|
|
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
|
-
|
|
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
|
|
5094
|
-
return p16.isCancel(
|
|
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
|
-
|
|
5099
|
+
spinner16.stop("Recording failed");
|
|
5100
5100
|
console.log(chalk17.yellow("Voice input failed. Falling back to text."));
|
|
5101
5101
|
}
|
|
5102
|
-
const
|
|
5103
|
-
return p16.isCancel(
|
|
5102
|
+
const 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
|
|
5158
|
+
const text15 = await fs13.readFile(txtFile, "utf-8");
|
|
5159
5159
|
await fs13.remove(txtFile);
|
|
5160
|
-
return
|
|
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
|
|
5208
|
+
const text15 = await fs13.readFile(txtFile, "utf-8");
|
|
5209
5209
|
await fs13.remove(txtFile);
|
|
5210
|
-
return
|
|
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
|
|
5341
|
-
|
|
5340
|
+
const spinner16 = p17.spinner();
|
|
5341
|
+
spinner16.start("Analyzing PRD...");
|
|
5342
5342
|
const buildPlan = await analyzePRD(anthropic, prdContent);
|
|
5343
|
-
|
|
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
|
-
|
|
5369
|
+
spinner16.start("Initializing project...");
|
|
5370
5370
|
await execa9("git", ["init"], { cwd: projectPath });
|
|
5371
|
-
|
|
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
|
-
|
|
5380
|
+
spinner16.start("Installing dependencies...");
|
|
5381
5381
|
await execa9("npm", ["install"], { cwd: projectPath, reject: false });
|
|
5382
|
-
|
|
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
|
|
5454
|
-
const jsonMatch =
|
|
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
|
|
5494
|
-
|
|
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
|
-
|
|
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
|
|
5589
|
-
|
|
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
|
-
|
|
5592
|
+
spinner16.message = `${agent.name}: ${action} (${progress}%)`;
|
|
5593
5593
|
});
|
|
5594
|
-
|
|
5594
|
+
spinner16.stop(`\u2713 ${agent.name} complete`);
|
|
5595
5595
|
} catch (error) {
|
|
5596
|
-
|
|
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
|
|
5699
|
-
messages.push({ role: "assistant", content:
|
|
5700
|
-
const questionMatch =
|
|
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(
|
|
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
|
|
5813
|
-
const jsonMatch =
|
|
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
|
|
5826
|
-
|
|
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
|
|
5854
|
-
await writeFilesFromResponse(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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/
|
|
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((
|
|
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
|
|
6118
|
-
const jsonMatch =
|
|
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
|
|
7003
|
+
const confirm11 = await p19.confirm({
|
|
6135
7004
|
message: `Did you mean: ${parsed.interpretation}?`,
|
|
6136
7005
|
initialValue: true
|
|
6137
7006
|
});
|
|
6138
|
-
if (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
|
|
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 (
|
|
7026
|
+
if (p19.isCancel(selection)) {
|
|
6158
7027
|
return { ...parsed, command: "cancel", confidence: 1 };
|
|
6159
7028
|
}
|
|
6160
7029
|
if (selection === "other") {
|
|
6161
|
-
const description = await
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 (
|
|
6260
|
-
|
|
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
|
-
${
|
|
6328
|
-
|
|
6329
|
-
${
|
|
6330
|
-
${
|
|
6331
|
-
${
|
|
6332
|
-
${
|
|
6333
|
-
${
|
|
6334
|
-
${
|
|
6335
|
-
${
|
|
6336
|
-
${
|
|
6337
|
-
${
|
|
6338
|
-
${
|
|
6339
|
-
${
|
|
6340
|
-
${
|
|
6341
|
-
${
|
|
6342
|
-
|
|
6343
|
-
${
|
|
6344
|
-
Press ${
|
|
6345
|
-
|
|
6346
|
-
${
|
|
6347
|
-
${
|
|
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(
|
|
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];
|