codebakers 2.1.2 → 2.2.1
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/advisors-GGUCFS4E.js +7 -0
- package/dist/{chunk-RCC7FYEU.js → chunk-ASIJIQYC.js} +21 -20
- package/dist/{chunk-YGVDLNXY.js → chunk-ND6T4UDY.js} +1 -1
- package/dist/{chunk-FWQNLNTI.js → chunk-YUSDTJD6.js} +1 -1
- package/dist/index.js +1100 -1055
- package/dist/prd-AIEY63YY.js +7 -0
- package/package.json +1 -1
- package/src/commands/build.ts +34 -3
- package/src/commands/code.ts +28 -3
- package/src/commands/deploy.ts +36 -2
- package/src/commands/init.ts +11 -1
- package/src/commands/integrate.ts +464 -565
- package/src/commands/setup.ts +375 -357
- package/src/commands/website.ts +17 -2
- package/src/index.ts +29 -22
- package/src/utils/config.ts +24 -23
- package/dist/advisors-J3S64IZK.js +0 -7
- package/dist/prd-HBUCYLVG.js +0 -7
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
prdCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ND6T4UDY.js";
|
|
5
5
|
import {
|
|
6
6
|
advisorsCommand
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YUSDTJD6.js";
|
|
8
8
|
import {
|
|
9
9
|
Config
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ASIJIQYC.js";
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
import { Command } from "commander";
|
|
@@ -43,6 +43,159 @@ async function checkForUpdates() {
|
|
|
43
43
|
import * as p from "@clack/prompts";
|
|
44
44
|
import chalk2 from "chalk";
|
|
45
45
|
import open from "open";
|
|
46
|
+
var SERVICES = {
|
|
47
|
+
anthropic: {
|
|
48
|
+
key: "anthropic",
|
|
49
|
+
name: "Anthropic (Claude)",
|
|
50
|
+
description: "Powers the AI coding agent",
|
|
51
|
+
whyNeeded: "Without this, CodeBakers cannot generate any code. This is the brain of the system.",
|
|
52
|
+
url: "https://console.anthropic.com/settings/keys",
|
|
53
|
+
urlDescription: "Create an API key",
|
|
54
|
+
fields: [{
|
|
55
|
+
key: "apiKey",
|
|
56
|
+
label: "Anthropic API Key",
|
|
57
|
+
placeholder: "sk-ant-...",
|
|
58
|
+
validate: (v) => {
|
|
59
|
+
if (!v) return "API key is required";
|
|
60
|
+
if (!v.startsWith("sk-ant-")) return "Should start with sk-ant-";
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
}]
|
|
64
|
+
},
|
|
65
|
+
github: {
|
|
66
|
+
key: "github",
|
|
67
|
+
name: "GitHub",
|
|
68
|
+
description: "Create repos and push code",
|
|
69
|
+
whyNeeded: "Without this, CodeBakers cannot create repositories or save your code to GitHub.",
|
|
70
|
+
url: "https://github.com/settings/tokens/new?scopes=repo,user&description=CodeBakers",
|
|
71
|
+
urlDescription: 'Create a token with "repo" and "user" scopes checked',
|
|
72
|
+
fields: [{
|
|
73
|
+
key: "token",
|
|
74
|
+
label: "GitHub Token",
|
|
75
|
+
placeholder: "ghp_... or github_pat_...",
|
|
76
|
+
validate: (v) => {
|
|
77
|
+
if (!v) return "Token is required";
|
|
78
|
+
if (!v.startsWith("ghp_") && !v.startsWith("github_pat_")) {
|
|
79
|
+
return "Should start with ghp_ or github_pat_";
|
|
80
|
+
}
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
}]
|
|
84
|
+
},
|
|
85
|
+
vercel: {
|
|
86
|
+
key: "vercel",
|
|
87
|
+
name: "Vercel",
|
|
88
|
+
description: "Deploy your apps to production",
|
|
89
|
+
whyNeeded: "Without this, CodeBakers cannot deploy your apps. You'll have to deploy manually.",
|
|
90
|
+
url: "https://vercel.com/account/tokens",
|
|
91
|
+
urlDescription: 'Click "Create" to generate a new token',
|
|
92
|
+
fields: [{
|
|
93
|
+
key: "token",
|
|
94
|
+
label: "Vercel Token",
|
|
95
|
+
placeholder: "vercel_..."
|
|
96
|
+
}]
|
|
97
|
+
},
|
|
98
|
+
supabase: {
|
|
99
|
+
key: "supabase",
|
|
100
|
+
name: "Supabase",
|
|
101
|
+
description: "Database and authentication",
|
|
102
|
+
whyNeeded: "Without this, CodeBakers cannot create databases for your apps. You'll need to set up databases manually.",
|
|
103
|
+
url: "https://supabase.com/dashboard/account/tokens",
|
|
104
|
+
urlDescription: "Generate a new access token",
|
|
105
|
+
fields: [{
|
|
106
|
+
key: "accessToken",
|
|
107
|
+
label: "Supabase Access Token",
|
|
108
|
+
placeholder: "sbp_..."
|
|
109
|
+
}]
|
|
110
|
+
},
|
|
111
|
+
openai: {
|
|
112
|
+
key: "openai",
|
|
113
|
+
name: "OpenAI",
|
|
114
|
+
description: "GPT models, embeddings, DALL-E",
|
|
115
|
+
whyNeeded: "Optional. Only needed if you want to use OpenAI models instead of or alongside Claude.",
|
|
116
|
+
url: "https://platform.openai.com/api-keys",
|
|
117
|
+
urlDescription: "Create a new API key",
|
|
118
|
+
fields: [{
|
|
119
|
+
key: "apiKey",
|
|
120
|
+
label: "OpenAI API Key",
|
|
121
|
+
placeholder: "sk-..."
|
|
122
|
+
}]
|
|
123
|
+
},
|
|
124
|
+
stripe: {
|
|
125
|
+
key: "stripe",
|
|
126
|
+
name: "Stripe",
|
|
127
|
+
description: "Payment processing",
|
|
128
|
+
whyNeeded: "Optional. Only needed if your app accepts payments.",
|
|
129
|
+
url: "https://dashboard.stripe.com/apikeys",
|
|
130
|
+
urlDescription: "Copy your Secret key (sk_live or sk_test)",
|
|
131
|
+
fields: [{
|
|
132
|
+
key: "secretKey",
|
|
133
|
+
label: "Stripe Secret Key",
|
|
134
|
+
placeholder: "sk_live_... or sk_test_..."
|
|
135
|
+
}]
|
|
136
|
+
},
|
|
137
|
+
twilio: {
|
|
138
|
+
key: "twilio",
|
|
139
|
+
name: "Twilio",
|
|
140
|
+
description: "SMS, voice calls, WhatsApp",
|
|
141
|
+
whyNeeded: "Optional. Only needed if your app sends SMS or makes calls.",
|
|
142
|
+
url: "https://console.twilio.com/",
|
|
143
|
+
urlDescription: "Find Account SID and Auth Token on the dashboard",
|
|
144
|
+
fields: [
|
|
145
|
+
{
|
|
146
|
+
key: "accountSid",
|
|
147
|
+
label: "Account SID",
|
|
148
|
+
placeholder: "AC..."
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "authToken",
|
|
152
|
+
label: "Auth Token",
|
|
153
|
+
placeholder: "..."
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
resend: {
|
|
158
|
+
key: "resend",
|
|
159
|
+
name: "Resend",
|
|
160
|
+
description: "Email sending",
|
|
161
|
+
whyNeeded: "Optional. Only needed if your app sends emails.",
|
|
162
|
+
url: "https://resend.com/api-keys",
|
|
163
|
+
urlDescription: "Create an API key",
|
|
164
|
+
fields: [{
|
|
165
|
+
key: "apiKey",
|
|
166
|
+
label: "Resend API Key",
|
|
167
|
+
placeholder: "re_..."
|
|
168
|
+
}]
|
|
169
|
+
},
|
|
170
|
+
vapi: {
|
|
171
|
+
key: "vapi",
|
|
172
|
+
name: "VAPI",
|
|
173
|
+
description: "Voice AI agents",
|
|
174
|
+
whyNeeded: "Optional. Only needed if you're building voice agents.",
|
|
175
|
+
url: "https://dashboard.vapi.ai/",
|
|
176
|
+
urlDescription: "Copy your API key from the dashboard",
|
|
177
|
+
fields: [{
|
|
178
|
+
key: "apiKey",
|
|
179
|
+
label: "VAPI API Key",
|
|
180
|
+
placeholder: "..."
|
|
181
|
+
}]
|
|
182
|
+
},
|
|
183
|
+
elevenlabs: {
|
|
184
|
+
key: "elevenlabs",
|
|
185
|
+
name: "ElevenLabs",
|
|
186
|
+
description: "AI voice generation",
|
|
187
|
+
whyNeeded: "Optional. Only needed if you want AI-generated voices.",
|
|
188
|
+
url: "https://elevenlabs.io/app/settings/api-keys",
|
|
189
|
+
urlDescription: "Create an API key",
|
|
190
|
+
fields: [{
|
|
191
|
+
key: "apiKey",
|
|
192
|
+
label: "ElevenLabs API Key",
|
|
193
|
+
placeholder: "..."
|
|
194
|
+
}]
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
var CORE_SERVICES = ["anthropic", "github", "vercel", "supabase"];
|
|
198
|
+
var OPTIONAL_SERVICES = ["openai", "stripe", "twilio", "resend", "vapi", "elevenlabs"];
|
|
46
199
|
async function setupCommand() {
|
|
47
200
|
const config = new Config();
|
|
48
201
|
p.intro(chalk2.bgCyan.black(" CodeBakers Setup "));
|
|
@@ -52,7 +205,7 @@ async function setupCommand() {
|
|
|
52
205
|
options: [
|
|
53
206
|
{ value: "view", label: "\u{1F440} View connected services" },
|
|
54
207
|
{ value: "add", label: "\u2795 Add another service" },
|
|
55
|
-
{ value: "update", label: "\u{1F504} Update
|
|
208
|
+
{ value: "update", label: "\u{1F504} Update a service" },
|
|
56
209
|
{ value: "reset", label: "\u{1F5D1}\uFE0F Reset all configuration" },
|
|
57
210
|
{ value: "back", label: "\u2190 Back" }
|
|
58
211
|
]
|
|
@@ -60,341 +213,198 @@ async function setupCommand() {
|
|
|
60
213
|
if (p.isCancel(action) || action === "back") {
|
|
61
214
|
return;
|
|
62
215
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
216
|
+
switch (action) {
|
|
217
|
+
case "view":
|
|
218
|
+
showConnectedServices(config);
|
|
219
|
+
return;
|
|
220
|
+
case "add":
|
|
221
|
+
await addService(config);
|
|
222
|
+
return;
|
|
223
|
+
case "update":
|
|
224
|
+
await updateService(config);
|
|
225
|
+
return;
|
|
226
|
+
case "reset":
|
|
227
|
+
await resetConfig(config);
|
|
228
|
+
return;
|
|
75
229
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
230
|
+
}
|
|
231
|
+
console.log(chalk2.cyan(`
|
|
232
|
+
Welcome to CodeBakers! Let's connect your services.
|
|
233
|
+
|
|
234
|
+
${chalk2.dim("Your credentials are stored locally in ~/.codebakers/")}
|
|
235
|
+
${chalk2.dim("and are never sent to our servers.")}
|
|
236
|
+
|
|
237
|
+
${chalk2.bold("Core Services (Recommended):")}
|
|
238
|
+
${chalk2.dim("These power the main CodeBakers features.")}
|
|
239
|
+
|
|
240
|
+
`));
|
|
241
|
+
let skippedCore = [];
|
|
242
|
+
for (const serviceKey of CORE_SERVICES) {
|
|
243
|
+
const service = SERVICES[serviceKey];
|
|
244
|
+
const connected = await connectService(config, service);
|
|
245
|
+
if (!connected) {
|
|
246
|
+
skippedCore.push(service.name);
|
|
79
247
|
}
|
|
80
248
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const requiredServices = [
|
|
89
|
-
{ name: "GitHub", key: "github", required: true },
|
|
90
|
-
{ name: "Vercel", key: "vercel", required: true },
|
|
91
|
-
{ name: "Supabase", key: "supabase", required: true },
|
|
92
|
-
{ name: "Anthropic (Claude)", key: "anthropic", required: true }
|
|
93
|
-
];
|
|
94
|
-
const optionalServices = [
|
|
95
|
-
{ name: "OpenAI", key: "openai" },
|
|
96
|
-
{ name: "Stripe", key: "stripe" },
|
|
97
|
-
{ name: "Twilio", key: "twilio" },
|
|
98
|
-
{ name: "VAPI", key: "vapi" },
|
|
99
|
-
{ name: "Resend", key: "resend" },
|
|
100
|
-
{ name: "ElevenLabs", key: "elevenLabs" },
|
|
101
|
-
{ name: "Microsoft Graph", key: "microsoft" },
|
|
102
|
-
{ name: "Google APIs", key: "google" }
|
|
103
|
-
];
|
|
104
|
-
p.log.step("Connecting required services...");
|
|
105
|
-
for (const service of requiredServices) {
|
|
106
|
-
await connectService(config, service.key, service.name, true);
|
|
249
|
+
if (skippedCore.length > 0) {
|
|
250
|
+
console.log(chalk2.yellow(`
|
|
251
|
+
\u26A0\uFE0F You skipped: ${skippedCore.join(", ")}
|
|
252
|
+
|
|
253
|
+
Some features won't work without these services.
|
|
254
|
+
Run ${chalk2.bold("codebakers setup")} anytime to add them.
|
|
255
|
+
`));
|
|
107
256
|
}
|
|
108
257
|
const addOptional = await p.confirm({
|
|
109
|
-
message: "
|
|
258
|
+
message: "Add optional services? (Stripe, Twilio, Resend, etc.)",
|
|
110
259
|
initialValue: false
|
|
111
260
|
});
|
|
112
261
|
if (addOptional && !p.isCancel(addOptional)) {
|
|
113
262
|
const selected = await p.multiselect({
|
|
114
|
-
message: "Select services to
|
|
115
|
-
options:
|
|
116
|
-
value:
|
|
117
|
-
label:
|
|
263
|
+
message: "Select services to add:",
|
|
264
|
+
options: OPTIONAL_SERVICES.map((key) => ({
|
|
265
|
+
value: key,
|
|
266
|
+
label: `${SERVICES[key].name} - ${SERVICES[key].description}`
|
|
118
267
|
})),
|
|
119
268
|
required: false
|
|
120
269
|
});
|
|
121
|
-
if (!p.isCancel(selected)) {
|
|
270
|
+
if (!p.isCancel(selected) && Array.isArray(selected)) {
|
|
122
271
|
for (const serviceKey of selected) {
|
|
123
|
-
|
|
124
|
-
if (service) {
|
|
125
|
-
await connectService(config, service.key, service.name, false);
|
|
126
|
-
}
|
|
272
|
+
await connectService(config, SERVICES[serviceKey]);
|
|
127
273
|
}
|
|
128
274
|
}
|
|
129
275
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
276
|
+
const connectedCount = [...CORE_SERVICES, ...OPTIONAL_SERVICES].filter(
|
|
277
|
+
(key) => config.getCredentials(key)
|
|
278
|
+
).length;
|
|
279
|
+
p.outro(chalk2.green(`
|
|
280
|
+
\u2713 Setup complete! ${connectedCount} services connected.
|
|
281
|
+
|
|
282
|
+
Run ${chalk2.bold("codebakers")} to get started.
|
|
283
|
+
`));
|
|
133
284
|
}
|
|
134
|
-
async function connectService(config,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
case "vercel": {
|
|
163
|
-
p.log.info(`${chalk2.bold("Vercel")} - Opens browser for OAuth authorization`);
|
|
164
|
-
const proceed = await p.confirm({
|
|
165
|
-
message: "Open browser to authorize Vercel?",
|
|
166
|
-
initialValue: true
|
|
167
|
-
});
|
|
168
|
-
if (p.isCancel(proceed) || !proceed) {
|
|
169
|
-
if (required) {
|
|
170
|
-
p.log.warn("Vercel is required. Skipping for now.");
|
|
171
|
-
}
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
p.log.info(chalk2.dim("Opening browser..."));
|
|
175
|
-
await open("https://vercel.com/account/tokens");
|
|
176
|
-
const token = await p.text({
|
|
177
|
-
message: "Paste your Vercel token:",
|
|
178
|
-
placeholder: "vercel_..."
|
|
179
|
-
});
|
|
180
|
-
if (!p.isCancel(token) && token) {
|
|
181
|
-
config.setCredentials("vercel", { token });
|
|
182
|
-
p.log.success("Vercel connected!");
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
case "supabase": {
|
|
188
|
-
p.log.info(`${chalk2.bold("Supabase")} - Opens browser for OAuth authorization`);
|
|
189
|
-
const proceed = await p.confirm({
|
|
190
|
-
message: "Open browser to authorize Supabase?",
|
|
191
|
-
initialValue: true
|
|
192
|
-
});
|
|
193
|
-
if (p.isCancel(proceed) || !proceed) {
|
|
194
|
-
if (required) {
|
|
195
|
-
p.log.warn("Supabase is required. Skipping for now.");
|
|
196
|
-
}
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
p.log.info(chalk2.dim("Opening browser..."));
|
|
200
|
-
await open("https://supabase.com/dashboard/account/tokens");
|
|
201
|
-
const token = await p.text({
|
|
202
|
-
message: "Paste your Supabase access token:",
|
|
203
|
-
placeholder: "sbp_..."
|
|
204
|
-
});
|
|
205
|
-
if (!p.isCancel(token) && token) {
|
|
206
|
-
config.setCredentials("supabase", { accessToken: token });
|
|
207
|
-
p.log.success("Supabase connected!");
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
case "anthropic": {
|
|
213
|
-
p.log.info(`${chalk2.bold("Anthropic (Claude)")} - Powers the AI coding agent`);
|
|
214
|
-
const openBrowser = await p.confirm({
|
|
215
|
-
message: "Open browser to get API key?",
|
|
216
|
-
initialValue: true
|
|
217
|
-
});
|
|
218
|
-
if (openBrowser && !p.isCancel(openBrowser)) {
|
|
219
|
-
await open("https://console.anthropic.com/settings/keys");
|
|
220
|
-
}
|
|
221
|
-
const apiKey = await p.text({
|
|
222
|
-
message: "Paste your Anthropic API key:",
|
|
223
|
-
placeholder: "sk-ant-...",
|
|
224
|
-
validate: (value) => {
|
|
225
|
-
if (!value && required) return "API key is required";
|
|
226
|
-
if (value && !value.startsWith("sk-ant-")) return "Invalid API key format";
|
|
227
|
-
return void 0;
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
if (!p.isCancel(apiKey) && apiKey) {
|
|
231
|
-
config.setCredentials("anthropic", { apiKey });
|
|
232
|
-
p.log.success("Anthropic connected!");
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
case "openai": {
|
|
238
|
-
const openBrowser = await p.confirm({
|
|
239
|
-
message: "Open browser to get OpenAI API key?",
|
|
240
|
-
initialValue: true
|
|
241
|
-
});
|
|
242
|
-
if (openBrowser && !p.isCancel(openBrowser)) {
|
|
243
|
-
await open("https://platform.openai.com/api-keys");
|
|
244
|
-
}
|
|
245
|
-
const apiKey = await p.text({
|
|
246
|
-
message: "Paste your OpenAI API key:",
|
|
247
|
-
placeholder: "sk-..."
|
|
248
|
-
});
|
|
249
|
-
if (!p.isCancel(apiKey) && apiKey) {
|
|
250
|
-
config.setCredentials("openai", { apiKey });
|
|
251
|
-
p.log.success("OpenAI connected!");
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
case "stripe": {
|
|
257
|
-
const openBrowser = await p.confirm({
|
|
258
|
-
message: "Open browser to get Stripe API keys?",
|
|
259
|
-
initialValue: true
|
|
260
|
-
});
|
|
261
|
-
if (openBrowser && !p.isCancel(openBrowser)) {
|
|
262
|
-
await open("https://dashboard.stripe.com/apikeys");
|
|
263
|
-
}
|
|
264
|
-
const secretKey = await p.text({
|
|
265
|
-
message: "Paste your Stripe secret key:",
|
|
266
|
-
placeholder: "sk_live_... or sk_test_..."
|
|
267
|
-
});
|
|
268
|
-
if (!p.isCancel(secretKey) && secretKey) {
|
|
269
|
-
config.setCredentials("stripe", { secretKey });
|
|
270
|
-
p.log.success("Stripe connected!");
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
case "twilio": {
|
|
276
|
-
const openBrowser = await p.confirm({
|
|
277
|
-
message: "Open browser to get Twilio credentials?",
|
|
278
|
-
initialValue: true
|
|
279
|
-
});
|
|
280
|
-
if (openBrowser && !p.isCancel(openBrowser)) {
|
|
281
|
-
await open("https://console.twilio.com/");
|
|
282
|
-
}
|
|
283
|
-
const accountSid = await p.text({
|
|
284
|
-
message: "Paste your Twilio Account SID:",
|
|
285
|
-
placeholder: "AC..."
|
|
286
|
-
});
|
|
287
|
-
const authToken = await p.text({
|
|
288
|
-
message: "Paste your Twilio Auth Token:",
|
|
289
|
-
placeholder: "..."
|
|
290
|
-
});
|
|
291
|
-
if (!p.isCancel(accountSid) && !p.isCancel(authToken) && accountSid && authToken) {
|
|
292
|
-
config.setCredentials("twilio", {
|
|
293
|
-
accountSid,
|
|
294
|
-
authToken
|
|
295
|
-
});
|
|
296
|
-
p.log.success("Twilio connected!");
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
break;
|
|
285
|
+
async function connectService(config, service) {
|
|
286
|
+
console.log("");
|
|
287
|
+
console.log(chalk2.bold(` ${service.name}`));
|
|
288
|
+
console.log(chalk2.dim(` ${service.description}`));
|
|
289
|
+
console.log("");
|
|
290
|
+
const action = await p.select({
|
|
291
|
+
message: `Connect ${service.name}?`,
|
|
292
|
+
options: [
|
|
293
|
+
{ value: "connect", label: "\u2713 Yes, connect now" },
|
|
294
|
+
{ value: "skip", label: "\u2192 Skip for now" },
|
|
295
|
+
{ value: "why", label: "? Why do I need this?" }
|
|
296
|
+
]
|
|
297
|
+
});
|
|
298
|
+
if (p.isCancel(action)) return false;
|
|
299
|
+
if (action === "why") {
|
|
300
|
+
console.log(chalk2.yellow(`
|
|
301
|
+
${service.whyNeeded}
|
|
302
|
+
`));
|
|
303
|
+
const proceed = await p.confirm({
|
|
304
|
+
message: `Connect ${service.name}?`,
|
|
305
|
+
initialValue: true
|
|
306
|
+
});
|
|
307
|
+
if (!proceed || p.isCancel(proceed)) {
|
|
308
|
+
console.log(chalk2.dim(` Skipped ${service.name}`));
|
|
309
|
+
return false;
|
|
300
310
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
311
|
+
} else if (action === "skip") {
|
|
312
|
+
console.log(chalk2.dim(` Skipped ${service.name}`));
|
|
313
|
+
console.log(chalk2.dim(` ${service.whyNeeded}`));
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
const openBrowser = await p.confirm({
|
|
317
|
+
message: "Open browser to get credentials?",
|
|
318
|
+
initialValue: true
|
|
319
|
+
});
|
|
320
|
+
if (openBrowser && !p.isCancel(openBrowser)) {
|
|
321
|
+
console.log(chalk2.dim(`
|
|
322
|
+
Opening: ${service.url}`));
|
|
323
|
+
console.log(chalk2.dim(` ${service.urlDescription}
|
|
324
|
+
`));
|
|
325
|
+
await open(service.url);
|
|
326
|
+
}
|
|
327
|
+
const credentials = {};
|
|
328
|
+
for (const field of service.fields) {
|
|
329
|
+
const value = await p.text({
|
|
330
|
+
message: `${field.label}:`,
|
|
331
|
+
placeholder: field.placeholder,
|
|
332
|
+
validate: field.validate
|
|
333
|
+
});
|
|
334
|
+
if (p.isCancel(value)) return false;
|
|
335
|
+
if (!value) {
|
|
336
|
+
console.log(chalk2.dim(` Skipped ${service.name} (no ${field.label} provided)`));
|
|
337
|
+
return false;
|
|
319
338
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
339
|
+
credentials[field.key] = value;
|
|
340
|
+
}
|
|
341
|
+
config.setCredentials(service.key, credentials);
|
|
342
|
+
console.log(chalk2.green(` \u2713 ${service.name} connected!`));
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
function showConnectedServices(config) {
|
|
346
|
+
console.log(chalk2.bold("\n Connected Services:\n"));
|
|
347
|
+
const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
|
|
348
|
+
let connectedCount = 0;
|
|
349
|
+
for (const key of allServices) {
|
|
350
|
+
const service = SERVICES[key];
|
|
351
|
+
const creds = config.getCredentials(key);
|
|
352
|
+
if (creds) {
|
|
353
|
+
console.log(chalk2.green(` \u2713 ${service.name}`));
|
|
354
|
+
connectedCount++;
|
|
355
|
+
} else {
|
|
356
|
+
console.log(chalk2.dim(` \u25CB ${service.name} (not connected)`));
|
|
338
357
|
}
|
|
339
|
-
default:
|
|
340
|
-
p.log.warn(`Service ${serviceName} not yet implemented`);
|
|
341
|
-
return false;
|
|
342
358
|
}
|
|
343
|
-
|
|
359
|
+
console.log(chalk2.dim(`
|
|
360
|
+
${connectedCount}/${allServices.length} services connected
|
|
361
|
+
`));
|
|
344
362
|
}
|
|
345
363
|
async function addService(config) {
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
{ value: "google", label: "Google APIs" }
|
|
359
|
-
];
|
|
360
|
-
const service = await p.select({
|
|
361
|
-
message: "Which service do you want to connect?",
|
|
362
|
-
options: services
|
|
364
|
+
const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
|
|
365
|
+
const unconnected = allServices.filter((key) => !config.getCredentials(key));
|
|
366
|
+
if (unconnected.length === 0) {
|
|
367
|
+
p.log.info("All services are already connected!");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const selected = await p.select({
|
|
371
|
+
message: "Select service to add:",
|
|
372
|
+
options: unconnected.map((key) => ({
|
|
373
|
+
value: key,
|
|
374
|
+
label: `${SERVICES[key].name} - ${SERVICES[key].description}`
|
|
375
|
+
}))
|
|
363
376
|
});
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
377
|
+
if (p.isCancel(selected)) return;
|
|
378
|
+
await connectService(config, SERVICES[selected]);
|
|
379
|
+
}
|
|
380
|
+
async function updateService(config) {
|
|
381
|
+
const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
|
|
382
|
+
const connected = allServices.filter((key) => config.getCredentials(key));
|
|
383
|
+
if (connected.length === 0) {
|
|
384
|
+
p.log.info("No services connected yet. Run setup first.");
|
|
385
|
+
return;
|
|
369
386
|
}
|
|
387
|
+
const selected = await p.select({
|
|
388
|
+
message: "Select service to update:",
|
|
389
|
+
options: connected.map((key) => ({
|
|
390
|
+
value: key,
|
|
391
|
+
label: SERVICES[key].name
|
|
392
|
+
}))
|
|
393
|
+
});
|
|
394
|
+
if (p.isCancel(selected)) return;
|
|
395
|
+
await connectService(config, SERVICES[selected]);
|
|
370
396
|
}
|
|
371
|
-
function
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
{ key: "vapi", name: "VAPI" },
|
|
381
|
-
{ key: "resend", name: "Resend" },
|
|
382
|
-
{ key: "elevenLabs", name: "ElevenLabs" },
|
|
383
|
-
{ key: "microsoft", name: "Microsoft" },
|
|
384
|
-
{ key: "google", name: "Google" }
|
|
385
|
-
];
|
|
386
|
-
console.log("\n" + chalk2.bold("Connected Services:") + "\n");
|
|
387
|
-
for (const service of services) {
|
|
388
|
-
const creds = config.getCredentials(service.key);
|
|
389
|
-
const isConnected = creds && Object.values(creds).some((v) => v);
|
|
390
|
-
const status = isConnected ? chalk2.green("\u2713 Connected") : chalk2.dim("\u25CB Not connected");
|
|
391
|
-
console.log(` ${service.name.padEnd(15)} ${status}`);
|
|
397
|
+
async function resetConfig(config) {
|
|
398
|
+
const confirm13 = await p.confirm({
|
|
399
|
+
message: "Are you sure? This will remove ALL credentials.",
|
|
400
|
+
initialValue: false
|
|
401
|
+
});
|
|
402
|
+
if (!confirm13 || p.isCancel(confirm13)) return;
|
|
403
|
+
const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
|
|
404
|
+
for (const key of allServices) {
|
|
405
|
+
config.setCredentials(key, null);
|
|
392
406
|
}
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
async function installPatterns(config) {
|
|
396
|
-
const patternsDir = config.getPatternsDir();
|
|
397
|
-
p.log.info(chalk2.dim(`Patterns installed to ${patternsDir}`));
|
|
407
|
+
p.log.success("Configuration reset. Run `codebakers setup` to reconfigure.");
|
|
398
408
|
}
|
|
399
409
|
|
|
400
410
|
// src/commands/init.ts
|
|
@@ -1029,20 +1039,28 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1029
1039
|
p2.cancel("Project creation cancelled.");
|
|
1030
1040
|
return;
|
|
1031
1041
|
}
|
|
1032
|
-
const
|
|
1042
|
+
const spinner16 = p2.spinner();
|
|
1033
1043
|
const projectPath = path.join(process.cwd(), projectName);
|
|
1034
1044
|
try {
|
|
1035
|
-
|
|
1045
|
+
spinner16.start("Creating local project...");
|
|
1036
1046
|
await createLocalProject(projectPath, projectConfig);
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1047
|
+
spinner16.stop("Local project created");
|
|
1048
|
+
spinner16.start("Installing dependencies...");
|
|
1049
|
+
try {
|
|
1050
|
+
await execa3("npm", ["install"], { cwd: projectPath });
|
|
1051
|
+
} catch {
|
|
1052
|
+
try {
|
|
1053
|
+
await execa3("pnpm", ["install"], { cwd: projectPath });
|
|
1054
|
+
} catch {
|
|
1055
|
+
await execa3("yarn", ["install"], { cwd: projectPath });
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
spinner16.stop("Dependencies installed");
|
|
1041
1059
|
if (services.includes("github")) {
|
|
1042
|
-
|
|
1060
|
+
spinner16.start("Creating GitHub repository...");
|
|
1043
1061
|
const github = new GitHubService(config);
|
|
1044
1062
|
const repo = await github.createRepo(projectName, { private: true });
|
|
1045
|
-
|
|
1063
|
+
spinner16.stop(`GitHub repo created: ${repo.html_url}`);
|
|
1046
1064
|
await execa3("git", ["init"], { cwd: projectPath });
|
|
1047
1065
|
await execa3("git", ["add", "."], { cwd: projectPath });
|
|
1048
1066
|
await execa3("git", ["commit", "-m", "Initial commit by CodeBakers"], { cwd: projectPath });
|
|
@@ -1050,10 +1068,10 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1050
1068
|
await execa3("git", ["push", "-u", "origin", "main"], { cwd: projectPath });
|
|
1051
1069
|
}
|
|
1052
1070
|
if (services.includes("supabase")) {
|
|
1053
|
-
|
|
1071
|
+
spinner16.start("Creating Supabase project...");
|
|
1054
1072
|
const supabase = new SupabaseService(config);
|
|
1055
1073
|
const project = await supabase.createProject(projectName);
|
|
1056
|
-
|
|
1074
|
+
spinner16.stop(`Supabase project created: ${project.name}`);
|
|
1057
1075
|
await fs.writeJson(
|
|
1058
1076
|
path.join(projectPath, ".codebakers", "supabase.json"),
|
|
1059
1077
|
{ projectId: project.id, projectUrl: project.api_url },
|
|
@@ -1061,26 +1079,26 @@ Domain: ${domain || "Vercel default"}`,
|
|
|
1061
1079
|
);
|
|
1062
1080
|
}
|
|
1063
1081
|
if (services.includes("vercel")) {
|
|
1064
|
-
|
|
1082
|
+
spinner16.start("Creating Vercel project...");
|
|
1065
1083
|
const vercel = new VercelService(config);
|
|
1066
1084
|
const project = await vercel.createProject(projectName);
|
|
1067
|
-
|
|
1085
|
+
spinner16.stop(`Vercel project created`);
|
|
1068
1086
|
if (domain) {
|
|
1069
|
-
|
|
1087
|
+
spinner16.start(`Configuring domain: ${domain}...`);
|
|
1070
1088
|
await vercel.addDomain(projectName, domain);
|
|
1071
|
-
|
|
1089
|
+
spinner16.stop("Domain configured");
|
|
1072
1090
|
}
|
|
1073
|
-
|
|
1091
|
+
spinner16.start("Deploying to Vercel...");
|
|
1074
1092
|
const deployment = await vercel.deploy(projectPath);
|
|
1075
|
-
|
|
1093
|
+
spinner16.stop(`Deployed: ${deployment.url}`);
|
|
1076
1094
|
}
|
|
1077
|
-
|
|
1095
|
+
spinner16.start("Generating CLAUDE.md...");
|
|
1078
1096
|
const claudeMd = generateClaudeMd(projectConfig);
|
|
1079
1097
|
await fs.writeFile(path.join(projectPath, "CLAUDE.md"), claudeMd);
|
|
1080
|
-
|
|
1081
|
-
|
|
1098
|
+
spinner16.stop("CLAUDE.md generated");
|
|
1099
|
+
spinner16.start("Setting up CodeBakers enforcement...");
|
|
1082
1100
|
await setupGitHooks(projectPath);
|
|
1083
|
-
|
|
1101
|
+
spinner16.stop("CodeBakers enforcement configured");
|
|
1084
1102
|
config.addProject({
|
|
1085
1103
|
name: projectName,
|
|
1086
1104
|
path: projectPath,
|
|
@@ -1100,7 +1118,7 @@ ${chalk3.dim("Shortcuts:")}
|
|
|
1100
1118
|
${chalk3.cyan("codebakers deploy")} \u2014 Deploy to production
|
|
1101
1119
|
`));
|
|
1102
1120
|
} catch (error) {
|
|
1103
|
-
|
|
1121
|
+
spinner16.stop("Error occurred");
|
|
1104
1122
|
p2.log.error(`Failed to create project: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1105
1123
|
const cleanup = await p2.confirm({
|
|
1106
1124
|
message: "Clean up partially created project?"
|
|
@@ -1826,17 +1844,17 @@ async function checkCommand(options = {}) {
|
|
|
1826
1844
|
return;
|
|
1827
1845
|
}
|
|
1828
1846
|
p3.intro(chalk4.bgCyan.black(" CodeBakers Pattern Check "));
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1847
|
+
const spinner16 = p3.spinner();
|
|
1848
|
+
spinner16.start("Analyzing code...");
|
|
1831
1849
|
const result = await runPatternCheck(options.fix || false);
|
|
1832
|
-
|
|
1850
|
+
spinner16.stop("Analysis complete");
|
|
1833
1851
|
displayResults(result);
|
|
1834
1852
|
if (result.violations.length > 0 && options.fix) {
|
|
1835
1853
|
const fixable = result.violations.filter((v) => v.autoFixable);
|
|
1836
1854
|
if (fixable.length > 0) {
|
|
1837
|
-
|
|
1855
|
+
spinner16.start(`Auto-fixing ${fixable.length} violations...`);
|
|
1838
1856
|
await autoFix(fixable);
|
|
1839
|
-
|
|
1857
|
+
spinner16.stop("Auto-fix complete");
|
|
1840
1858
|
}
|
|
1841
1859
|
}
|
|
1842
1860
|
const errors = result.violations.filter((v) => v.severity === "error");
|
|
@@ -2040,8 +2058,8 @@ async function getVoiceInput(prompt) {
|
|
|
2040
2058
|
return null;
|
|
2041
2059
|
}
|
|
2042
2060
|
await playBeep();
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2061
|
+
const spinner16 = p4.spinner();
|
|
2062
|
+
spinner16.start("\u{1F534} Recording...");
|
|
2045
2063
|
try {
|
|
2046
2064
|
let transcription = "";
|
|
2047
2065
|
if (process.platform === "win32") {
|
|
@@ -2051,16 +2069,16 @@ async function getVoiceInput(prompt) {
|
|
|
2051
2069
|
} else {
|
|
2052
2070
|
transcription = await recordWithLinux();
|
|
2053
2071
|
}
|
|
2054
|
-
|
|
2072
|
+
spinner16.stop("Recording complete");
|
|
2055
2073
|
if (transcription) {
|
|
2056
2074
|
console.log(chalk5.green(`
|
|
2057
2075
|
\u2713 Heard: "${transcription}"
|
|
2058
2076
|
`));
|
|
2059
|
-
const
|
|
2077
|
+
const confirm13 = await p4.confirm({
|
|
2060
2078
|
message: "Is this correct?",
|
|
2061
2079
|
initialValue: true
|
|
2062
2080
|
});
|
|
2063
|
-
if (
|
|
2081
|
+
if (confirm13 && !p4.isCancel(confirm13)) {
|
|
2064
2082
|
return transcription;
|
|
2065
2083
|
} else {
|
|
2066
2084
|
const action = await p4.select({
|
|
@@ -2085,7 +2103,7 @@ async function getVoiceInput(prompt) {
|
|
|
2085
2103
|
return p4.isCancel(text16) ? null : text16;
|
|
2086
2104
|
}
|
|
2087
2105
|
} catch (error) {
|
|
2088
|
-
|
|
2106
|
+
spinner16.stop("Recording failed");
|
|
2089
2107
|
console.log(chalk5.yellow("Voice input failed. Please type instead."));
|
|
2090
2108
|
const text16 = await p4.text({ message: prompt });
|
|
2091
2109
|
return p4.isCancel(text16) ? null : text16;
|
|
@@ -2480,16 +2498,41 @@ ${text16}
|
|
|
2480
2498
|
async function codeCommand(prompt, options = {}) {
|
|
2481
2499
|
const config = new Config();
|
|
2482
2500
|
if (!config.isConfigured()) {
|
|
2483
|
-
|
|
2501
|
+
console.log(chalk7.yellow(`
|
|
2502
|
+
\u26A0\uFE0F CodeBakers isn't set up yet.
|
|
2503
|
+
|
|
2504
|
+
Run this first:
|
|
2505
|
+
${chalk7.cyan("codebakers setup")}
|
|
2506
|
+
|
|
2507
|
+
This connects your API keys (Anthropic, GitHub, etc.)
|
|
2508
|
+
so CodeBakers can generate code and deploy for you.
|
|
2509
|
+
`));
|
|
2484
2510
|
return;
|
|
2485
2511
|
}
|
|
2486
2512
|
if (!config.isInProject()) {
|
|
2487
|
-
|
|
2513
|
+
console.log(chalk7.yellow(`
|
|
2514
|
+
\u26A0\uFE0F You're not in a CodeBakers project.
|
|
2515
|
+
|
|
2516
|
+
Options:
|
|
2517
|
+
${chalk7.cyan("codebakers init")} Create a new project here
|
|
2518
|
+
${chalk7.cyan("codebakers website")} Build a website by describing it
|
|
2519
|
+
${chalk7.cyan("cd my-project")} Navigate to an existing project
|
|
2520
|
+
`));
|
|
2488
2521
|
return;
|
|
2489
2522
|
}
|
|
2490
2523
|
const anthropicCreds = config.getCredentials("anthropic");
|
|
2491
2524
|
if (!anthropicCreds?.apiKey) {
|
|
2492
|
-
|
|
2525
|
+
console.log(chalk7.yellow(`
|
|
2526
|
+
\u26A0\uFE0F Anthropic API key not configured.
|
|
2527
|
+
|
|
2528
|
+
The AI coding agent needs Claude to work.
|
|
2529
|
+
|
|
2530
|
+
Run this to add your API key:
|
|
2531
|
+
${chalk7.cyan("codebakers setup")}
|
|
2532
|
+
|
|
2533
|
+
Get an API key at:
|
|
2534
|
+
${chalk7.dim("https://console.anthropic.com/settings/keys")}
|
|
2535
|
+
`));
|
|
2493
2536
|
return;
|
|
2494
2537
|
}
|
|
2495
2538
|
const anthropic = new Anthropic({
|
|
@@ -2629,14 +2672,14 @@ ${clipContent}
|
|
|
2629
2672
|
}
|
|
2630
2673
|
}
|
|
2631
2674
|
async function processUserInput(userInput, messages, anthropic, systemPrompt, projectContext, config) {
|
|
2632
|
-
const
|
|
2675
|
+
const spinner16 = p6.spinner();
|
|
2633
2676
|
messages.push({ role: "user", content: userInput });
|
|
2634
2677
|
const wizardResult = await checkForWizard(userInput);
|
|
2635
2678
|
if (wizardResult) {
|
|
2636
2679
|
messages[messages.length - 1].content = wizardResult;
|
|
2637
2680
|
}
|
|
2638
2681
|
try {
|
|
2639
|
-
|
|
2682
|
+
spinner16.start("Thinking...");
|
|
2640
2683
|
const response = await anthropic.messages.create({
|
|
2641
2684
|
model: "claude-sonnet-4-20250514",
|
|
2642
2685
|
max_tokens: 8192,
|
|
@@ -2646,7 +2689,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2646
2689
|
content: m.content
|
|
2647
2690
|
}))
|
|
2648
2691
|
});
|
|
2649
|
-
|
|
2692
|
+
spinner16.stop("");
|
|
2650
2693
|
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
|
|
2651
2694
|
messages.push({ role: "assistant", content: assistantMessage });
|
|
2652
2695
|
const actions = parseActions(assistantMessage);
|
|
@@ -2661,11 +2704,11 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2661
2704
|
initialValue: true
|
|
2662
2705
|
});
|
|
2663
2706
|
if (proceed && !p6.isCancel(proceed)) {
|
|
2664
|
-
|
|
2707
|
+
spinner16.start("Building...");
|
|
2665
2708
|
for (const action of actions) {
|
|
2666
|
-
await executeAction(action,
|
|
2709
|
+
await executeAction(action, spinner16);
|
|
2667
2710
|
}
|
|
2668
|
-
|
|
2711
|
+
spinner16.stop("Build complete");
|
|
2669
2712
|
console.log(chalk7.dim("\n\u{1F50D} Running CodeBakers check..."));
|
|
2670
2713
|
const checkResult = await runPatternCheck(false);
|
|
2671
2714
|
if (checkResult.violations.length > 0) {
|
|
@@ -2676,9 +2719,9 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2676
2719
|
initialValue: true
|
|
2677
2720
|
});
|
|
2678
2721
|
if (autoFix2 && !p6.isCancel(autoFix2)) {
|
|
2679
|
-
|
|
2722
|
+
spinner16.start("Auto-fixing...");
|
|
2680
2723
|
await autoFixViolations(checkResult.violations, anthropic, systemPrompt);
|
|
2681
|
-
|
|
2724
|
+
spinner16.stop("Violations fixed");
|
|
2682
2725
|
}
|
|
2683
2726
|
} else {
|
|
2684
2727
|
console.log(chalk7.green("\u2713 All patterns satisfied"));
|
|
@@ -2689,7 +2732,7 @@ async function processUserInput(userInput, messages, anthropic, systemPrompt, pr
|
|
|
2689
2732
|
console.log("\n" + assistantMessage + "\n");
|
|
2690
2733
|
}
|
|
2691
2734
|
} catch (error) {
|
|
2692
|
-
|
|
2735
|
+
spinner16.stop("Error");
|
|
2693
2736
|
console.log(chalk7.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2694
2737
|
}
|
|
2695
2738
|
}
|
|
@@ -2801,14 +2844,14 @@ function parseActions(response) {
|
|
|
2801
2844
|
}
|
|
2802
2845
|
return actions;
|
|
2803
2846
|
}
|
|
2804
|
-
async function executeAction(action,
|
|
2847
|
+
async function executeAction(action, spinner16) {
|
|
2805
2848
|
const cwd = process.cwd();
|
|
2806
2849
|
switch (action.type) {
|
|
2807
2850
|
case "CREATE_FILE": {
|
|
2808
2851
|
const filePath = path5.join(cwd, action.path);
|
|
2809
2852
|
await fs6.ensureDir(path5.dirname(filePath));
|
|
2810
2853
|
await fs6.writeFile(filePath, action.content);
|
|
2811
|
-
|
|
2854
|
+
spinner16.message(`Created ${action.path}`);
|
|
2812
2855
|
break;
|
|
2813
2856
|
}
|
|
2814
2857
|
case "EDIT_FILE": {
|
|
@@ -2818,13 +2861,13 @@ async function executeAction(action, spinner17) {
|
|
|
2818
2861
|
if (action.find && content.includes(action.find)) {
|
|
2819
2862
|
content = content.replace(action.find, action.replace || "");
|
|
2820
2863
|
await fs6.writeFile(filePath, content);
|
|
2821
|
-
|
|
2864
|
+
spinner16.message(`Edited ${action.path}`);
|
|
2822
2865
|
}
|
|
2823
2866
|
}
|
|
2824
2867
|
break;
|
|
2825
2868
|
}
|
|
2826
2869
|
case "RUN_COMMAND": {
|
|
2827
|
-
|
|
2870
|
+
spinner16.message(`Running: ${action.command}`);
|
|
2828
2871
|
const [cmd, ...args2] = action.command.split(" ");
|
|
2829
2872
|
await execa6(cmd, args2, { cwd, stdio: "pipe" });
|
|
2830
2873
|
break;
|
|
@@ -2833,7 +2876,7 @@ async function executeAction(action, spinner17) {
|
|
|
2833
2876
|
const filePath = path5.join(cwd, action.path);
|
|
2834
2877
|
if (await fs6.pathExists(filePath)) {
|
|
2835
2878
|
await fs6.remove(filePath);
|
|
2836
|
-
|
|
2879
|
+
spinner16.message(`Deleted ${action.path}`);
|
|
2837
2880
|
}
|
|
2838
2881
|
break;
|
|
2839
2882
|
}
|
|
@@ -3077,16 +3120,39 @@ import Anthropic2 from "@anthropic-ai/sdk";
|
|
|
3077
3120
|
async function deployCommand(options = {}) {
|
|
3078
3121
|
const config = new Config();
|
|
3079
3122
|
if (!config.isInProject()) {
|
|
3080
|
-
|
|
3123
|
+
console.log(chalk8.yellow(`
|
|
3124
|
+
\u26A0\uFE0F You're not in a CodeBakers project.
|
|
3125
|
+
|
|
3126
|
+
Navigate to your project first:
|
|
3127
|
+
${chalk8.cyan("cd my-project")}
|
|
3128
|
+
|
|
3129
|
+
Or create a new one:
|
|
3130
|
+
${chalk8.cyan("codebakers init")}
|
|
3131
|
+
`));
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
const vercelCreds = config.getCredentials("vercel");
|
|
3135
|
+
if (!vercelCreds?.token) {
|
|
3136
|
+
console.log(chalk8.yellow(`
|
|
3137
|
+
\u26A0\uFE0F Vercel isn't connected.
|
|
3138
|
+
|
|
3139
|
+
CodeBakers deploys to Vercel by default.
|
|
3140
|
+
|
|
3141
|
+
Run this to connect:
|
|
3142
|
+
${chalk8.cyan("codebakers setup")}
|
|
3143
|
+
|
|
3144
|
+
Or deploy manually:
|
|
3145
|
+
${chalk8.cyan("npx vercel")}
|
|
3146
|
+
`));
|
|
3081
3147
|
return;
|
|
3082
3148
|
}
|
|
3083
3149
|
p7.intro(chalk8.bgCyan.black(" Deploy to Production "));
|
|
3084
|
-
const
|
|
3150
|
+
const spinner16 = p7.spinner();
|
|
3085
3151
|
if (options.check !== false) {
|
|
3086
|
-
|
|
3152
|
+
spinner16.start("Running CodeBakers check...");
|
|
3087
3153
|
const checkResult = await runPatternCheck(false);
|
|
3088
3154
|
if (!checkResult.passed) {
|
|
3089
|
-
|
|
3155
|
+
spinner16.stop("");
|
|
3090
3156
|
const errors = checkResult.violations.filter((v) => v.severity === "error");
|
|
3091
3157
|
if (errors.length > 0) {
|
|
3092
3158
|
p7.log.error(`${errors.length} pattern violations found. Fix before deploying.`);
|
|
@@ -3107,9 +3173,9 @@ async function deployCommand(options = {}) {
|
|
|
3107
3173
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3108
3174
|
return;
|
|
3109
3175
|
}
|
|
3110
|
-
|
|
3176
|
+
spinner16.start("Auto-fixing with AI...");
|
|
3111
3177
|
await autoFixWithAI(config, errors);
|
|
3112
|
-
|
|
3178
|
+
spinner16.stop("Auto-fix complete");
|
|
3113
3179
|
const recheck = await runPatternCheck(false);
|
|
3114
3180
|
if (!recheck.passed) {
|
|
3115
3181
|
p7.log.error("Some violations remain. Manual fix required.");
|
|
@@ -3118,23 +3184,23 @@ async function deployCommand(options = {}) {
|
|
|
3118
3184
|
}
|
|
3119
3185
|
}
|
|
3120
3186
|
}
|
|
3121
|
-
|
|
3187
|
+
spinner16.stop("Pattern check passed");
|
|
3122
3188
|
}
|
|
3123
|
-
|
|
3189
|
+
spinner16.start("Running TypeScript check...");
|
|
3124
3190
|
try {
|
|
3125
3191
|
await execa7("npx", ["tsc", "--noEmit"], { cwd: process.cwd() });
|
|
3126
|
-
|
|
3192
|
+
spinner16.stop("TypeScript check passed");
|
|
3127
3193
|
} catch (error) {
|
|
3128
|
-
|
|
3194
|
+
spinner16.stop("");
|
|
3129
3195
|
p7.log.error("TypeScript errors found.");
|
|
3130
3196
|
const fix = await p7.confirm({
|
|
3131
3197
|
message: "Attempt to fix TypeScript errors?",
|
|
3132
3198
|
initialValue: true
|
|
3133
3199
|
});
|
|
3134
3200
|
if (fix && !p7.isCancel(fix)) {
|
|
3135
|
-
|
|
3201
|
+
spinner16.start("Fixing TypeScript errors...");
|
|
3136
3202
|
const fixed = await fixTypeScriptErrors(config);
|
|
3137
|
-
|
|
3203
|
+
spinner16.stop(fixed ? "TypeScript errors fixed" : "Could not auto-fix all errors");
|
|
3138
3204
|
if (!fixed) {
|
|
3139
3205
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3140
3206
|
return;
|
|
@@ -3144,12 +3210,20 @@ async function deployCommand(options = {}) {
|
|
|
3144
3210
|
return;
|
|
3145
3211
|
}
|
|
3146
3212
|
}
|
|
3147
|
-
|
|
3213
|
+
spinner16.start("Building project...");
|
|
3148
3214
|
try {
|
|
3149
|
-
|
|
3150
|
-
|
|
3215
|
+
try {
|
|
3216
|
+
await execa7("npm", ["run", "build"], { cwd: process.cwd() });
|
|
3217
|
+
} catch {
|
|
3218
|
+
try {
|
|
3219
|
+
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3220
|
+
} catch {
|
|
3221
|
+
await execa7("yarn", ["build"], { cwd: process.cwd() });
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
spinner16.stop("Build successful");
|
|
3151
3225
|
} catch (error) {
|
|
3152
|
-
|
|
3226
|
+
spinner16.stop("");
|
|
3153
3227
|
p7.log.error("Build failed.");
|
|
3154
3228
|
const errorOutput = error instanceof Error ? error.message : "Unknown error";
|
|
3155
3229
|
console.log(chalk8.dim(errorOutput));
|
|
@@ -3158,16 +3232,16 @@ async function deployCommand(options = {}) {
|
|
|
3158
3232
|
initialValue: true
|
|
3159
3233
|
});
|
|
3160
3234
|
if (fix && !p7.isCancel(fix)) {
|
|
3161
|
-
|
|
3235
|
+
spinner16.start("Fixing build errors...");
|
|
3162
3236
|
const fixed = await fixBuildErrors(config, errorOutput);
|
|
3163
|
-
|
|
3237
|
+
spinner16.stop(fixed ? "Build errors fixed" : "Could not auto-fix");
|
|
3164
3238
|
if (fixed) {
|
|
3165
|
-
|
|
3239
|
+
spinner16.start("Retrying build...");
|
|
3166
3240
|
try {
|
|
3167
3241
|
await execa7("pnpm", ["build"], { cwd: process.cwd() });
|
|
3168
|
-
|
|
3242
|
+
spinner16.stop("Build successful");
|
|
3169
3243
|
} catch {
|
|
3170
|
-
|
|
3244
|
+
spinner16.stop("Build still failing");
|
|
3171
3245
|
p7.outro(chalk8.red("Deploy cancelled."));
|
|
3172
3246
|
return;
|
|
3173
3247
|
}
|
|
@@ -3180,10 +3254,10 @@ async function deployCommand(options = {}) {
|
|
|
3180
3254
|
return;
|
|
3181
3255
|
}
|
|
3182
3256
|
}
|
|
3183
|
-
|
|
3257
|
+
spinner16.start("Checking for uncommitted changes...");
|
|
3184
3258
|
const { stdout: gitStatus } = await execa7("git", ["status", "--porcelain"], { cwd: process.cwd() });
|
|
3185
3259
|
if (gitStatus.trim()) {
|
|
3186
|
-
|
|
3260
|
+
spinner16.stop("");
|
|
3187
3261
|
const commit = await p7.confirm({
|
|
3188
3262
|
message: "You have uncommitted changes. Commit before deploying?",
|
|
3189
3263
|
initialValue: true
|
|
@@ -3197,20 +3271,20 @@ async function deployCommand(options = {}) {
|
|
|
3197
3271
|
if (!p7.isCancel(message)) {
|
|
3198
3272
|
await execa7("git", ["add", "."], { cwd: process.cwd() });
|
|
3199
3273
|
await execa7("git", ["commit", "-m", message], { cwd: process.cwd() });
|
|
3200
|
-
|
|
3274
|
+
spinner16.start("Pushing to GitHub...");
|
|
3201
3275
|
await execa7("git", ["push"], { cwd: process.cwd() });
|
|
3202
|
-
|
|
3276
|
+
spinner16.stop("Pushed to GitHub");
|
|
3203
3277
|
}
|
|
3204
3278
|
}
|
|
3205
3279
|
} else {
|
|
3206
|
-
|
|
3280
|
+
spinner16.stop("No uncommitted changes");
|
|
3207
3281
|
}
|
|
3208
3282
|
const deployType = options.preview ? "preview" : "production";
|
|
3209
|
-
|
|
3283
|
+
spinner16.start(`Deploying to ${deployType}...`);
|
|
3210
3284
|
try {
|
|
3211
3285
|
const vercel = new VercelService(config);
|
|
3212
3286
|
const deployment = await vercel.deploy(process.cwd(), !options.preview);
|
|
3213
|
-
|
|
3287
|
+
spinner16.stop("Deployment complete!");
|
|
3214
3288
|
console.log(boxedOutput(`
|
|
3215
3289
|
${chalk8.green("\u2713")} Deployed successfully!
|
|
3216
3290
|
|
|
@@ -3222,7 +3296,7 @@ ${chalk8.dim("View in Vercel Dashboard:")}
|
|
|
3222
3296
|
${chalk8.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
3223
3297
|
`));
|
|
3224
3298
|
} catch (error) {
|
|
3225
|
-
|
|
3299
|
+
spinner16.stop("");
|
|
3226
3300
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
3227
3301
|
p7.log.error(`Deployment failed: ${errorMsg}`);
|
|
3228
3302
|
if (errorMsg.includes("Build failed")) {
|
|
@@ -3231,8 +3305,8 @@ ${chalk8.dim(deployment.dashboardUrl || "https://vercel.com/dashboard")}
|
|
|
3231
3305
|
initialValue: true
|
|
3232
3306
|
});
|
|
3233
3307
|
if (retry && !p7.isCancel(retry)) {
|
|
3234
|
-
|
|
3235
|
-
|
|
3308
|
+
spinner16.start("Analyzing Vercel build error...");
|
|
3309
|
+
spinner16.stop("Fix attempted");
|
|
3236
3310
|
}
|
|
3237
3311
|
}
|
|
3238
3312
|
p7.outro(chalk8.red("Deploy failed."));
|
|
@@ -3598,10 +3672,10 @@ you'll need a Meta Business account.
|
|
|
3598
3672
|
initialValue: true
|
|
3599
3673
|
});
|
|
3600
3674
|
if (!proceed || p9.isCancel(proceed)) return;
|
|
3601
|
-
const
|
|
3602
|
-
|
|
3675
|
+
const spinner16 = p9.spinner();
|
|
3676
|
+
spinner16.start("Generating QR code...");
|
|
3603
3677
|
try {
|
|
3604
|
-
|
|
3678
|
+
spinner16.stop("");
|
|
3605
3679
|
console.log(chalk10.cyan(`
|
|
3606
3680
|
\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
3681
|
\u2551 \u2551
|
|
@@ -3633,7 +3707,7 @@ Scan this QR code with WhatsApp:
|
|
|
3633
3707
|
p9.log.success("WhatsApp connected!");
|
|
3634
3708
|
}
|
|
3635
3709
|
} catch (error) {
|
|
3636
|
-
|
|
3710
|
+
spinner16.stop("Error connecting WhatsApp");
|
|
3637
3711
|
p9.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
3638
3712
|
}
|
|
3639
3713
|
}
|
|
@@ -3664,15 +3738,15 @@ To create a Telegram bot:
|
|
|
3664
3738
|
}
|
|
3665
3739
|
});
|
|
3666
3740
|
if (p9.isCancel(token)) return;
|
|
3667
|
-
const
|
|
3668
|
-
|
|
3741
|
+
const spinner16 = p9.spinner();
|
|
3742
|
+
spinner16.start("Verifying bot token...");
|
|
3669
3743
|
try {
|
|
3670
3744
|
const response = await fetch(`https://api.telegram.org/bot${token}/getMe`);
|
|
3671
3745
|
const data = await response.json();
|
|
3672
3746
|
if (!data.ok) {
|
|
3673
3747
|
throw new Error(data.description || "Invalid token");
|
|
3674
3748
|
}
|
|
3675
|
-
|
|
3749
|
+
spinner16.stop("Bot verified!");
|
|
3676
3750
|
config.setChannelConfig("telegram", {
|
|
3677
3751
|
enabled: true,
|
|
3678
3752
|
botToken: token,
|
|
@@ -3680,7 +3754,7 @@ To create a Telegram bot:
|
|
|
3680
3754
|
});
|
|
3681
3755
|
p9.log.success(`Connected to @${data.result.username}`);
|
|
3682
3756
|
} catch (error) {
|
|
3683
|
-
|
|
3757
|
+
spinner16.stop("Verification failed");
|
|
3684
3758
|
p9.log.error(error instanceof Error ? error.message : "Invalid token");
|
|
3685
3759
|
}
|
|
3686
3760
|
}
|
|
@@ -3818,10 +3892,10 @@ This requires:
|
|
|
3818
3892
|
p9.log.info("iMessage support coming soon.");
|
|
3819
3893
|
}
|
|
3820
3894
|
async function startAllChannels(config) {
|
|
3821
|
-
const
|
|
3822
|
-
|
|
3895
|
+
const spinner16 = p9.spinner();
|
|
3896
|
+
spinner16.start("Starting channel gateway...");
|
|
3823
3897
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3824
|
-
|
|
3898
|
+
spinner16.stop("Gateway started");
|
|
3825
3899
|
console.log(chalk10.green(`
|
|
3826
3900
|
\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
3901
|
\u2551 Gateway is running! \u2551
|
|
@@ -3835,10 +3909,10 @@ async function startAllChannels(config) {
|
|
|
3835
3909
|
`));
|
|
3836
3910
|
}
|
|
3837
3911
|
async function stopAllChannels(config) {
|
|
3838
|
-
const
|
|
3839
|
-
|
|
3912
|
+
const spinner16 = p9.spinner();
|
|
3913
|
+
spinner16.start("Stopping gateway...");
|
|
3840
3914
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3841
|
-
|
|
3915
|
+
spinner16.stop("Gateway stopped");
|
|
3842
3916
|
}
|
|
3843
3917
|
async function deployGatewayWizard(config) {
|
|
3844
3918
|
p9.log.info(chalk10.bold("Deploy Gateway to Cloud"));
|
|
@@ -3953,10 +4027,10 @@ import glob2 from "fast-glob";
|
|
|
3953
4027
|
import * as path8 from "path";
|
|
3954
4028
|
async function securityCommand() {
|
|
3955
4029
|
p11.intro(chalk12.bgCyan.black(" Security Audit "));
|
|
3956
|
-
const
|
|
3957
|
-
|
|
4030
|
+
const spinner16 = p11.spinner();
|
|
4031
|
+
spinner16.start("Scanning for security issues...");
|
|
3958
4032
|
const issues = await runSecurityScan();
|
|
3959
|
-
|
|
4033
|
+
spinner16.stop("Scan complete");
|
|
3960
4034
|
if (issues.length === 0) {
|
|
3961
4035
|
console.log(chalk12.green("\n\u2713 No security issues found!\n"));
|
|
3962
4036
|
displaySecurityScore(100);
|
|
@@ -4050,10 +4124,10 @@ async function generateCommand(type) {
|
|
|
4050
4124
|
validate: (v) => !v ? "Name is required" : void 0
|
|
4051
4125
|
});
|
|
4052
4126
|
if (p12.isCancel(name)) return;
|
|
4053
|
-
const
|
|
4054
|
-
|
|
4127
|
+
const spinner16 = p12.spinner();
|
|
4128
|
+
spinner16.start("Generating...");
|
|
4055
4129
|
await generateFile(generateType, name);
|
|
4056
|
-
|
|
4130
|
+
spinner16.stop(`Generated ${name}`);
|
|
4057
4131
|
p12.outro("");
|
|
4058
4132
|
}
|
|
4059
4133
|
async function generateFile(type, name) {
|
|
@@ -4198,13 +4272,13 @@ import * as p13 from "@clack/prompts";
|
|
|
4198
4272
|
import chalk14 from "chalk";
|
|
4199
4273
|
async function fixCommand() {
|
|
4200
4274
|
p13.intro(chalk14.bgCyan.black(" Auto-Fix "));
|
|
4201
|
-
const
|
|
4202
|
-
|
|
4275
|
+
const spinner16 = p13.spinner();
|
|
4276
|
+
spinner16.start("Analyzing code...");
|
|
4203
4277
|
const result = await runPatternCheck(true);
|
|
4204
4278
|
if (result.passed) {
|
|
4205
|
-
|
|
4279
|
+
spinner16.stop("No issues found!");
|
|
4206
4280
|
} else {
|
|
4207
|
-
|
|
4281
|
+
spinner16.stop(`Fixed ${result.violations.length} issues`);
|
|
4208
4282
|
}
|
|
4209
4283
|
p13.outro(chalk14.green("Done!"));
|
|
4210
4284
|
}
|
|
@@ -4405,8 +4479,8 @@ function generateColorPalette(hex) {
|
|
|
4405
4479
|
};
|
|
4406
4480
|
}
|
|
4407
4481
|
async function checkDesign() {
|
|
4408
|
-
const
|
|
4409
|
-
|
|
4482
|
+
const spinner16 = p14.spinner();
|
|
4483
|
+
spinner16.start("Checking design quality...");
|
|
4410
4484
|
const cwd = process.cwd();
|
|
4411
4485
|
const issues = [];
|
|
4412
4486
|
const glob3 = (await import("fast-glob")).default;
|
|
@@ -4432,7 +4506,7 @@ async function checkDesign() {
|
|
|
4432
4506
|
}
|
|
4433
4507
|
}
|
|
4434
4508
|
}
|
|
4435
|
-
|
|
4509
|
+
spinner16.stop("Check complete");
|
|
4436
4510
|
if (issues.length === 0) {
|
|
4437
4511
|
console.log(chalk15.green("\n\u2713 No design issues found!\n"));
|
|
4438
4512
|
} else {
|
|
@@ -4530,8 +4604,8 @@ async function detectMigrationTool() {
|
|
|
4530
4604
|
return null;
|
|
4531
4605
|
}
|
|
4532
4606
|
async function checkMigrationStatus(tool) {
|
|
4533
|
-
const
|
|
4534
|
-
|
|
4607
|
+
const spinner16 = p15.spinner();
|
|
4608
|
+
spinner16.start("Checking migration status...");
|
|
4535
4609
|
try {
|
|
4536
4610
|
let result;
|
|
4537
4611
|
switch (tool) {
|
|
@@ -4545,7 +4619,7 @@ async function checkMigrationStatus(tool) {
|
|
|
4545
4619
|
result = await execa8("npx", ["supabase", "migration", "list"], { cwd: process.cwd(), reject: false });
|
|
4546
4620
|
break;
|
|
4547
4621
|
}
|
|
4548
|
-
|
|
4622
|
+
spinner16.stop("Status check complete");
|
|
4549
4623
|
if (result?.stdout) {
|
|
4550
4624
|
console.log(chalk16.dim(result.stdout));
|
|
4551
4625
|
}
|
|
@@ -4553,7 +4627,7 @@ async function checkMigrationStatus(tool) {
|
|
|
4553
4627
|
console.log(chalk16.yellow(result.stderr));
|
|
4554
4628
|
}
|
|
4555
4629
|
} catch (error) {
|
|
4556
|
-
|
|
4630
|
+
spinner16.stop("Error checking status");
|
|
4557
4631
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4558
4632
|
}
|
|
4559
4633
|
}
|
|
@@ -4564,8 +4638,8 @@ async function generateMigration(tool) {
|
|
|
4564
4638
|
validate: (v) => !v ? "Name required" : void 0
|
|
4565
4639
|
});
|
|
4566
4640
|
if (p15.isCancel(name)) return;
|
|
4567
|
-
const
|
|
4568
|
-
|
|
4641
|
+
const spinner16 = p15.spinner();
|
|
4642
|
+
spinner16.start("Generating migration...");
|
|
4569
4643
|
try {
|
|
4570
4644
|
let result;
|
|
4571
4645
|
switch (tool) {
|
|
@@ -4579,18 +4653,18 @@ async function generateMigration(tool) {
|
|
|
4579
4653
|
result = await execa8("npx", ["supabase", "migration", "new", name], { cwd: process.cwd(), reject: false });
|
|
4580
4654
|
break;
|
|
4581
4655
|
}
|
|
4582
|
-
|
|
4656
|
+
spinner16.stop("Migration generated");
|
|
4583
4657
|
if (result?.stdout) {
|
|
4584
4658
|
console.log(chalk16.dim(result.stdout));
|
|
4585
4659
|
}
|
|
4586
4660
|
} catch (error) {
|
|
4587
|
-
|
|
4661
|
+
spinner16.stop("Error generating migration");
|
|
4588
4662
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4589
4663
|
}
|
|
4590
4664
|
}
|
|
4591
4665
|
async function pushMigration(tool) {
|
|
4592
|
-
const
|
|
4593
|
-
|
|
4666
|
+
const spinner16 = p15.spinner();
|
|
4667
|
+
spinner16.start("Pushing migration to database...");
|
|
4594
4668
|
try {
|
|
4595
4669
|
let result;
|
|
4596
4670
|
let migrationSql = "";
|
|
@@ -4616,7 +4690,7 @@ async function pushMigration(tool) {
|
|
|
4616
4690
|
break;
|
|
4617
4691
|
}
|
|
4618
4692
|
if (result?.exitCode !== 0) {
|
|
4619
|
-
|
|
4693
|
+
spinner16.stop("Migration push failed");
|
|
4620
4694
|
const errorOutput = result?.stderr || result?.stdout || "";
|
|
4621
4695
|
console.log(chalk16.red("\nError:\n"));
|
|
4622
4696
|
console.log(chalk16.dim(errorOutput));
|
|
@@ -4669,13 +4743,13 @@ Also saved to: ${sqlPath}
|
|
|
4669
4743
|
}
|
|
4670
4744
|
return;
|
|
4671
4745
|
}
|
|
4672
|
-
|
|
4746
|
+
spinner16.stop("Migration pushed successfully!");
|
|
4673
4747
|
if (result?.stdout) {
|
|
4674
4748
|
console.log(chalk16.dim(result.stdout));
|
|
4675
4749
|
}
|
|
4676
4750
|
p15.log.success("Database updated!");
|
|
4677
4751
|
} catch (error) {
|
|
4678
|
-
|
|
4752
|
+
spinner16.stop("Error");
|
|
4679
4753
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
4680
4754
|
p15.log.error(errorMsg);
|
|
4681
4755
|
const migrationSql = await extractMigrationSQL(tool, errorMsg);
|
|
@@ -4687,8 +4761,8 @@ Also saved to: ${sqlPath}
|
|
|
4687
4761
|
}
|
|
4688
4762
|
}
|
|
4689
4763
|
async function pullSchema(tool) {
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4764
|
+
const spinner16 = p15.spinner();
|
|
4765
|
+
spinner16.start("Pulling schema from database...");
|
|
4692
4766
|
try {
|
|
4693
4767
|
let result;
|
|
4694
4768
|
switch (tool) {
|
|
@@ -4702,12 +4776,12 @@ async function pullSchema(tool) {
|
|
|
4702
4776
|
result = await execa8("npx", ["supabase", "db", "pull"], { cwd: process.cwd(), reject: false });
|
|
4703
4777
|
break;
|
|
4704
4778
|
}
|
|
4705
|
-
|
|
4779
|
+
spinner16.stop("Schema pulled");
|
|
4706
4780
|
if (result?.stdout) {
|
|
4707
4781
|
console.log(chalk16.dim(result.stdout));
|
|
4708
4782
|
}
|
|
4709
4783
|
} catch (error) {
|
|
4710
|
-
|
|
4784
|
+
spinner16.stop("Error pulling schema");
|
|
4711
4785
|
p15.log.error(error instanceof Error ? error.message : "Unknown error");
|
|
4712
4786
|
}
|
|
4713
4787
|
}
|
|
@@ -4845,10 +4919,10 @@ async function prdMakerCommand() {
|
|
|
4845
4919
|
const input = await conductPRDInterview(inputMethod);
|
|
4846
4920
|
if (!input) return;
|
|
4847
4921
|
const anthropic = new Anthropic3({ apiKey: anthropicCreds.apiKey });
|
|
4848
|
-
const
|
|
4849
|
-
|
|
4922
|
+
const spinner16 = p16.spinner();
|
|
4923
|
+
spinner16.start("Generating professional PRD...");
|
|
4850
4924
|
const prd = await generatePRD(anthropic, input);
|
|
4851
|
-
|
|
4925
|
+
spinner16.stop("PRD generated!");
|
|
4852
4926
|
const filename = `${input.projectName.toLowerCase().replace(/[^a-z0-9]/g, "-")}-prd.md`;
|
|
4853
4927
|
const filepath = path12.join(process.cwd(), filename);
|
|
4854
4928
|
await fs13.writeFile(filepath, prd);
|
|
@@ -4893,10 +4967,10 @@ async function prdMakerCommand() {
|
|
|
4893
4967
|
return;
|
|
4894
4968
|
}
|
|
4895
4969
|
if (nextStep === "build") {
|
|
4896
|
-
const { prdCommand: prdCommand2 } = await import("./prd-
|
|
4970
|
+
const { prdCommand: prdCommand2 } = await import("./prd-AIEY63YY.js");
|
|
4897
4971
|
await prdCommand2(filepath);
|
|
4898
4972
|
} else if (nextStep === "advisors") {
|
|
4899
|
-
const { advisorsCommand: advisorsCommand2 } = await import("./advisors-
|
|
4973
|
+
const { advisorsCommand: advisorsCommand2 } = await import("./advisors-GGUCFS4E.js");
|
|
4900
4974
|
await advisorsCommand2();
|
|
4901
4975
|
}
|
|
4902
4976
|
}
|
|
@@ -5056,8 +5130,8 @@ async function getVoiceInput2(prompt) {
|
|
|
5056
5130
|
const text17 = await p16.text({ message: "Type instead:" });
|
|
5057
5131
|
return p16.isCancel(text17) ? null : text17;
|
|
5058
5132
|
}
|
|
5059
|
-
const
|
|
5060
|
-
|
|
5133
|
+
const spinner16 = p16.spinner();
|
|
5134
|
+
spinner16.start("\u{1F534} Recording... (press Ctrl+C to stop)");
|
|
5061
5135
|
try {
|
|
5062
5136
|
let transcription = "";
|
|
5063
5137
|
if (process.platform === "win32") {
|
|
@@ -5067,16 +5141,16 @@ async function getVoiceInput2(prompt) {
|
|
|
5067
5141
|
} else {
|
|
5068
5142
|
transcription = await recordWithLinux2();
|
|
5069
5143
|
}
|
|
5070
|
-
|
|
5144
|
+
spinner16.stop("Recording complete");
|
|
5071
5145
|
if (transcription) {
|
|
5072
5146
|
console.log(chalk17.green(`
|
|
5073
5147
|
Heard: "${transcription}"
|
|
5074
5148
|
`));
|
|
5075
|
-
const
|
|
5149
|
+
const confirm13 = await p16.confirm({
|
|
5076
5150
|
message: "Is this correct?",
|
|
5077
5151
|
initialValue: true
|
|
5078
5152
|
});
|
|
5079
|
-
if (
|
|
5153
|
+
if (confirm13 && !p16.isCancel(confirm13)) {
|
|
5080
5154
|
return transcription;
|
|
5081
5155
|
} else {
|
|
5082
5156
|
const retry = await p16.select({
|
|
@@ -5096,7 +5170,7 @@ async function getVoiceInput2(prompt) {
|
|
|
5096
5170
|
}
|
|
5097
5171
|
}
|
|
5098
5172
|
} catch (error) {
|
|
5099
|
-
|
|
5173
|
+
spinner16.stop("Recording failed");
|
|
5100
5174
|
console.log(chalk17.yellow("Voice input failed. Falling back to text."));
|
|
5101
5175
|
}
|
|
5102
5176
|
const text16 = await p16.text({ message: prompt });
|
|
@@ -5308,16 +5382,34 @@ var displayPaused = false;
|
|
|
5308
5382
|
async function buildCommand(prdPath, options = {}) {
|
|
5309
5383
|
const config = new Config();
|
|
5310
5384
|
if (!config.isConfigured()) {
|
|
5311
|
-
|
|
5385
|
+
console.log(chalk18.yellow(`
|
|
5386
|
+
\u26A0\uFE0F CodeBakers isn't set up yet.
|
|
5387
|
+
|
|
5388
|
+
Run this first:
|
|
5389
|
+
${chalk18.cyan("codebakers setup")}
|
|
5390
|
+
`));
|
|
5312
5391
|
return;
|
|
5313
5392
|
}
|
|
5314
5393
|
const anthropicCreds = config.getCredentials("anthropic");
|
|
5315
5394
|
if (!anthropicCreds?.apiKey) {
|
|
5316
|
-
|
|
5395
|
+
console.log(chalk18.yellow(`
|
|
5396
|
+
\u26A0\uFE0F Anthropic API key not configured.
|
|
5397
|
+
|
|
5398
|
+
The parallel build needs Claude AI to work.
|
|
5399
|
+
|
|
5400
|
+
Run this to add your API key:
|
|
5401
|
+
${chalk18.cyan("codebakers setup")}
|
|
5402
|
+
`));
|
|
5317
5403
|
return;
|
|
5318
5404
|
}
|
|
5319
5405
|
let prdFile = prdPath;
|
|
5320
5406
|
if (!prdFile) {
|
|
5407
|
+
console.log(chalk18.dim(`
|
|
5408
|
+
A PRD (Product Requirements Document) describes what you want to build.
|
|
5409
|
+
|
|
5410
|
+
Don't have one? Create one with:
|
|
5411
|
+
${chalk18.cyan("codebakers prd-maker")}
|
|
5412
|
+
`));
|
|
5321
5413
|
const file = await p17.text({
|
|
5322
5414
|
message: "Path to PRD file:",
|
|
5323
5415
|
placeholder: "./my-app-prd.md",
|
|
@@ -5327,20 +5419,32 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5327
5419
|
prdFile = file;
|
|
5328
5420
|
}
|
|
5329
5421
|
if (!await fs14.pathExists(prdFile)) {
|
|
5330
|
-
|
|
5422
|
+
console.log(chalk18.red(`
|
|
5423
|
+
\u274C PRD file not found: ${prdFile}
|
|
5424
|
+
|
|
5425
|
+
Make sure the file exists, or create one:
|
|
5426
|
+
${chalk18.cyan("codebakers prd-maker")}
|
|
5427
|
+
`));
|
|
5331
5428
|
return;
|
|
5332
5429
|
}
|
|
5333
5430
|
const prdContent = await fs14.readFile(prdFile, "utf-8");
|
|
5334
5431
|
console.log(chalk18.cyan(`
|
|
5335
5432
|
\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
|
|
5336
5433
|
\u2551 \u{1F680} CODEBAKERS PARALLEL BUILD \u2551
|
|
5434
|
+
\u2551 \u2551
|
|
5435
|
+
\u2551 This will: \u2551
|
|
5436
|
+
\u2551 1. Analyze your PRD \u2551
|
|
5437
|
+
\u2551 2. Break it into features \u2551
|
|
5438
|
+
\u2551 3. Run up to 3 AI agents in parallel \u2551
|
|
5439
|
+
\u2551 4. Auto-fix any errors \u2551
|
|
5440
|
+
\u2551 5. Merge everything together \u2551
|
|
5337
5441
|
\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
5442
|
`));
|
|
5339
5443
|
const anthropic = new Anthropic4({ apiKey: anthropicCreds.apiKey });
|
|
5340
|
-
const
|
|
5341
|
-
|
|
5444
|
+
const spinner16 = p17.spinner();
|
|
5445
|
+
spinner16.start("Analyzing PRD...");
|
|
5342
5446
|
const buildPlan = await analyzePRD(anthropic, prdContent);
|
|
5343
|
-
|
|
5447
|
+
spinner16.stop("PRD analyzed");
|
|
5344
5448
|
displayBuildPlan(buildPlan);
|
|
5345
5449
|
const totalAgents = buildPlan.waves.reduce((sum, w) => sum + w.agents.length, 0);
|
|
5346
5450
|
const useParallel = !options.sequential && totalAgents > 2;
|
|
@@ -5366,9 +5470,9 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5366
5470
|
}
|
|
5367
5471
|
await fs14.ensureDir(projectPath);
|
|
5368
5472
|
process.chdir(projectPath);
|
|
5369
|
-
|
|
5473
|
+
spinner16.start("Initializing project...");
|
|
5370
5474
|
await execa9("git", ["init"], { cwd: projectPath });
|
|
5371
|
-
|
|
5475
|
+
spinner16.stop("Project initialized");
|
|
5372
5476
|
await runSetupPhase(anthropic, buildPlan, projectPath);
|
|
5373
5477
|
const startTime = Date.now();
|
|
5374
5478
|
if (useParallel) {
|
|
@@ -5377,9 +5481,9 @@ async function buildCommand(prdPath, options = {}) {
|
|
|
5377
5481
|
await executeSequentialBuild(anthropic, buildPlan, projectPath, config);
|
|
5378
5482
|
}
|
|
5379
5483
|
await runIntegrationPhase(anthropic, buildPlan, projectPath);
|
|
5380
|
-
|
|
5484
|
+
spinner16.start("Installing dependencies...");
|
|
5381
5485
|
await execa9("npm", ["install"], { cwd: projectPath, reject: false });
|
|
5382
|
-
|
|
5486
|
+
spinner16.stop("Dependencies installed");
|
|
5383
5487
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
5384
5488
|
const minutes = Math.floor(elapsed / 60);
|
|
5385
5489
|
const seconds = elapsed % 60;
|
|
@@ -5490,8 +5594,8 @@ function displayBuildPlan(plan) {
|
|
|
5490
5594
|
`));
|
|
5491
5595
|
}
|
|
5492
5596
|
async function runSetupPhase(anthropic, plan, projectPath) {
|
|
5493
|
-
const
|
|
5494
|
-
|
|
5597
|
+
const spinner16 = p17.spinner();
|
|
5598
|
+
spinner16.start("Setting up project structure...");
|
|
5495
5599
|
await fs14.ensureDir(path13.join(projectPath, "src/app"));
|
|
5496
5600
|
await fs14.ensureDir(path13.join(projectPath, "src/components/ui"));
|
|
5497
5601
|
await fs14.ensureDir(path13.join(projectPath, "src/lib"));
|
|
@@ -5535,7 +5639,7 @@ Use TypeScript. Include all necessary imports.`
|
|
|
5535
5639
|
await writeFilesFromResponse(setupText, projectPath);
|
|
5536
5640
|
await execa9("git", ["add", "."], { cwd: projectPath });
|
|
5537
5641
|
await execa9("git", ["commit", "-m", "Initial setup"], { cwd: projectPath });
|
|
5538
|
-
|
|
5642
|
+
spinner16.stop("Project structure ready");
|
|
5539
5643
|
}
|
|
5540
5644
|
async function executeParallelBuild(anthropic, plan, projectPath, config) {
|
|
5541
5645
|
for (const wave of plan.waves) {
|
|
@@ -5585,15 +5689,15 @@ async function executeAgentWithProgress(anthropic, agent, projectPath, plan, dis
|
|
|
5585
5689
|
async function executeSequentialBuild(anthropic, plan, projectPath, config) {
|
|
5586
5690
|
for (const wave of plan.waves) {
|
|
5587
5691
|
for (const agent of wave.agents) {
|
|
5588
|
-
const
|
|
5589
|
-
|
|
5692
|
+
const spinner16 = p17.spinner();
|
|
5693
|
+
spinner16.start(`Building ${agent.name}...`);
|
|
5590
5694
|
try {
|
|
5591
5695
|
await executeAgent(anthropic, agent, projectPath, plan, (progress, action) => {
|
|
5592
|
-
|
|
5696
|
+
spinner16.message = `${agent.name}: ${action} (${progress}%)`;
|
|
5593
5697
|
});
|
|
5594
|
-
|
|
5698
|
+
spinner16.stop(`\u2713 ${agent.name} complete`);
|
|
5595
5699
|
} catch (error) {
|
|
5596
|
-
|
|
5700
|
+
spinner16.stop(`\u2717 ${agent.name} failed`);
|
|
5597
5701
|
throw error;
|
|
5598
5702
|
}
|
|
5599
5703
|
await mergeWaveBranches([agent], projectPath);
|
|
@@ -5822,8 +5926,8 @@ Common fixes:
|
|
|
5822
5926
|
return JSON.parse(jsonMatch[0]);
|
|
5823
5927
|
}
|
|
5824
5928
|
async function runIntegrationPhase(anthropic, plan, projectPath) {
|
|
5825
|
-
const
|
|
5826
|
-
|
|
5929
|
+
const spinner16 = p17.spinner();
|
|
5930
|
+
spinner16.start("Running integration...");
|
|
5827
5931
|
const features = plan.waves.flatMap((w) => w.agents);
|
|
5828
5932
|
const response = await anthropic.messages.create({
|
|
5829
5933
|
model: "claude-sonnet-4-20250514",
|
|
@@ -5854,7 +5958,7 @@ Make sure all features are accessible and properly connected.`
|
|
|
5854
5958
|
await writeFilesFromResponse(text16, projectPath);
|
|
5855
5959
|
await execa9("git", ["add", "."], { cwd: projectPath });
|
|
5856
5960
|
await execa9("git", ["commit", "-m", "Integration: wire up all features"], { cwd: projectPath, reject: false });
|
|
5857
|
-
|
|
5961
|
+
spinner16.stop("Integration complete");
|
|
5858
5962
|
}
|
|
5859
5963
|
async function mergeWaveBranches(agents, projectPath) {
|
|
5860
5964
|
for (const agent of agents) {
|
|
@@ -5969,93 +6073,94 @@ import open2 from "open";
|
|
|
5969
6073
|
import { execa as execa10 } from "execa";
|
|
5970
6074
|
var INTEGRATIONS = [
|
|
5971
6075
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
5972
|
-
// AUTH
|
|
6076
|
+
// AUTH
|
|
5973
6077
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
5974
6078
|
{
|
|
5975
6079
|
id: "clerk",
|
|
5976
6080
|
name: "Clerk",
|
|
5977
|
-
description: "
|
|
6081
|
+
description: "User management & authentication",
|
|
5978
6082
|
category: "auth",
|
|
5979
6083
|
icon: "\u{1F510}",
|
|
5980
|
-
|
|
5981
|
-
oauthUrl: "https://dashboard.clerk.com/apps/new",
|
|
6084
|
+
dashboardUrl: "https://dashboard.clerk.com",
|
|
5982
6085
|
envVars: ["NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", "CLERK_SECRET_KEY"],
|
|
6086
|
+
envVarHints: {
|
|
6087
|
+
"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY": "pk_test_... or pk_live_...",
|
|
6088
|
+
"CLERK_SECRET_KEY": "sk_test_... or sk_live_..."
|
|
6089
|
+
},
|
|
5983
6090
|
packages: ["@clerk/nextjs"],
|
|
5984
|
-
docs: "https://clerk.com/docs"
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
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"
|
|
6091
|
+
docs: "https://clerk.com/docs",
|
|
6092
|
+
steps: [
|
|
6093
|
+
"Sign up or log in at dashboard.clerk.com",
|
|
6094
|
+
"Create a new application",
|
|
6095
|
+
"Go to API Keys in the sidebar",
|
|
6096
|
+
"Copy Publishable Key and Secret Key"
|
|
6097
|
+
]
|
|
6008
6098
|
},
|
|
6009
6099
|
{
|
|
6010
6100
|
id: "supabase-auth",
|
|
6011
6101
|
name: "Supabase Auth",
|
|
6012
|
-
description: "
|
|
6102
|
+
description: "Auth with email, social, magic links",
|
|
6013
6103
|
category: "auth",
|
|
6014
6104
|
icon: "\u26A1",
|
|
6015
|
-
|
|
6016
|
-
oauthUrl: "https://supabase.com/dashboard/projects",
|
|
6105
|
+
dashboardUrl: "https://supabase.com/dashboard",
|
|
6017
6106
|
envVars: ["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"],
|
|
6107
|
+
envVarHints: {
|
|
6108
|
+
"NEXT_PUBLIC_SUPABASE_URL": "https://xxx.supabase.co",
|
|
6109
|
+
"NEXT_PUBLIC_SUPABASE_ANON_KEY": "eyJ... (anon public key)"
|
|
6110
|
+
},
|
|
6018
6111
|
packages: ["@supabase/supabase-js", "@supabase/auth-helpers-nextjs"],
|
|
6019
|
-
docs: "https://supabase.com/docs/guides/auth"
|
|
6112
|
+
docs: "https://supabase.com/docs/guides/auth",
|
|
6113
|
+
steps: [
|
|
6114
|
+
"Sign in at supabase.com/dashboard",
|
|
6115
|
+
"Create or select a project",
|
|
6116
|
+
"Go to Settings > API",
|
|
6117
|
+
"Copy Project URL and anon/public key"
|
|
6118
|
+
]
|
|
6020
6119
|
},
|
|
6021
6120
|
{
|
|
6022
|
-
id: "
|
|
6023
|
-
name: "
|
|
6024
|
-
description: "
|
|
6121
|
+
id: "nextauth",
|
|
6122
|
+
name: "NextAuth.js",
|
|
6123
|
+
description: "Open source auth for Next.js",
|
|
6025
6124
|
category: "auth",
|
|
6026
|
-
icon: "\u{
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6125
|
+
icon: "\u{1F511}",
|
|
6126
|
+
dashboardUrl: "https://next-auth.js.org",
|
|
6127
|
+
envVars: ["NEXTAUTH_SECRET", "NEXTAUTH_URL"],
|
|
6128
|
+
envVarHints: {
|
|
6129
|
+
"NEXTAUTH_SECRET": "Run: openssl rand -base64 32",
|
|
6130
|
+
"NEXTAUTH_URL": "http://localhost:3000"
|
|
6131
|
+
},
|
|
6132
|
+
packages: ["next-auth"],
|
|
6133
|
+
docs: "https://next-auth.js.org",
|
|
6134
|
+
steps: [
|
|
6135
|
+
"No signup needed - open source!",
|
|
6136
|
+
"Generate secret: openssl rand -base64 32",
|
|
6137
|
+
"Set URL to your app (localhost for dev)"
|
|
6138
|
+
]
|
|
6032
6139
|
},
|
|
6033
6140
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6034
|
-
//
|
|
6141
|
+
// DATABASE
|
|
6035
6142
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6036
6143
|
{
|
|
6037
6144
|
id: "supabase",
|
|
6038
6145
|
name: "Supabase",
|
|
6039
|
-
description: "Postgres
|
|
6146
|
+
description: "Postgres + Realtime + Auth + Storage",
|
|
6040
6147
|
category: "database",
|
|
6041
6148
|
icon: "\u26A1",
|
|
6042
|
-
|
|
6043
|
-
oauthUrl: "https://supabase.com/dashboard/projects",
|
|
6149
|
+
dashboardUrl: "https://supabase.com/dashboard",
|
|
6044
6150
|
envVars: ["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY"],
|
|
6151
|
+
envVarHints: {
|
|
6152
|
+
"NEXT_PUBLIC_SUPABASE_URL": "https://xxx.supabase.co",
|
|
6153
|
+
"NEXT_PUBLIC_SUPABASE_ANON_KEY": "eyJ... (anon key)",
|
|
6154
|
+
"SUPABASE_SERVICE_ROLE_KEY": "eyJ... (service_role key - keep secret!)"
|
|
6155
|
+
},
|
|
6045
6156
|
packages: ["@supabase/supabase-js"],
|
|
6046
|
-
docs: "https://supabase.com/docs"
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
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"
|
|
6157
|
+
docs: "https://supabase.com/docs",
|
|
6158
|
+
steps: [
|
|
6159
|
+
"Sign in at supabase.com/dashboard",
|
|
6160
|
+
"Create a new project",
|
|
6161
|
+
"Go to Settings > API",
|
|
6162
|
+
"Copy URL, anon key, and service_role key"
|
|
6163
|
+
]
|
|
6059
6164
|
},
|
|
6060
6165
|
{
|
|
6061
6166
|
id: "neon",
|
|
@@ -6063,57 +6168,58 @@ var INTEGRATIONS = [
|
|
|
6063
6168
|
description: "Serverless Postgres",
|
|
6064
6169
|
category: "database",
|
|
6065
6170
|
icon: "\u{1F418}",
|
|
6066
|
-
|
|
6067
|
-
oauthUrl: "https://console.neon.tech",
|
|
6171
|
+
dashboardUrl: "https://console.neon.tech",
|
|
6068
6172
|
envVars: ["DATABASE_URL"],
|
|
6173
|
+
envVarHints: {
|
|
6174
|
+
"DATABASE_URL": "postgres://user:pass@xxx.neon.tech/db?sslmode=require"
|
|
6175
|
+
},
|
|
6069
6176
|
packages: ["@neondatabase/serverless"],
|
|
6070
|
-
docs: "https://neon.tech/docs"
|
|
6177
|
+
docs: "https://neon.tech/docs",
|
|
6178
|
+
steps: [
|
|
6179
|
+
"Sign in at console.neon.tech",
|
|
6180
|
+
"Create a project",
|
|
6181
|
+
"Go to Dashboard > Connection Details",
|
|
6182
|
+
"Copy the connection string"
|
|
6183
|
+
]
|
|
6071
6184
|
},
|
|
6072
6185
|
{
|
|
6073
|
-
id: "
|
|
6074
|
-
name: "
|
|
6075
|
-
description: "
|
|
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",
|
|
6186
|
+
id: "planetscale",
|
|
6187
|
+
name: "PlanetScale",
|
|
6188
|
+
description: "Serverless MySQL",
|
|
6088
6189
|
category: "database",
|
|
6089
|
-
icon: "\u{
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6190
|
+
icon: "\u{1FA90}",
|
|
6191
|
+
dashboardUrl: "https://app.planetscale.com",
|
|
6192
|
+
envVars: ["DATABASE_URL"],
|
|
6193
|
+
envVarHints: {
|
|
6194
|
+
"DATABASE_URL": "mysql://user:pass@xxx.psdb.cloud/db?sslaccept=strict"
|
|
6195
|
+
},
|
|
6196
|
+
packages: ["@planetscale/database"],
|
|
6197
|
+
docs: "https://planetscale.com/docs",
|
|
6198
|
+
steps: [
|
|
6199
|
+
"Sign in at app.planetscale.com",
|
|
6200
|
+
"Create a database",
|
|
6201
|
+
"Go to Connect > Create password",
|
|
6202
|
+
"Copy the connection string"
|
|
6203
|
+
]
|
|
6095
6204
|
},
|
|
6096
6205
|
{
|
|
6097
6206
|
id: "prisma",
|
|
6098
6207
|
name: "Prisma",
|
|
6099
|
-
description: "Type-safe ORM
|
|
6208
|
+
description: "Type-safe ORM",
|
|
6100
6209
|
category: "database",
|
|
6101
6210
|
icon: "\u{1F537}",
|
|
6102
|
-
|
|
6211
|
+
dashboardUrl: "https://prisma.io",
|
|
6103
6212
|
envVars: ["DATABASE_URL"],
|
|
6213
|
+
envVarHints: {
|
|
6214
|
+
"DATABASE_URL": "Your database connection string"
|
|
6215
|
+
},
|
|
6104
6216
|
packages: ["prisma", "@prisma/client"],
|
|
6105
|
-
docs: "https://
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
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"
|
|
6217
|
+
docs: "https://prisma.io/docs",
|
|
6218
|
+
steps: [
|
|
6219
|
+
"No account needed - Prisma is open source!",
|
|
6220
|
+
"Use your existing database URL",
|
|
6221
|
+
"Run: npx prisma init"
|
|
6222
|
+
]
|
|
6117
6223
|
},
|
|
6118
6224
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6119
6225
|
// PAYMENTS
|
|
@@ -6121,38 +6227,46 @@ var INTEGRATIONS = [
|
|
|
6121
6227
|
{
|
|
6122
6228
|
id: "stripe",
|
|
6123
6229
|
name: "Stripe",
|
|
6124
|
-
description: "
|
|
6230
|
+
description: "Payments & subscriptions",
|
|
6125
6231
|
category: "payments",
|
|
6126
6232
|
icon: "\u{1F4B3}",
|
|
6127
|
-
|
|
6128
|
-
oauthUrl: "https://dashboard.stripe.com/apikeys",
|
|
6233
|
+
dashboardUrl: "https://dashboard.stripe.com/apikeys",
|
|
6129
6234
|
envVars: ["STRIPE_SECRET_KEY", "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
6235
|
+
envVarHints: {
|
|
6236
|
+
"STRIPE_SECRET_KEY": "sk_test_... or sk_live_...",
|
|
6237
|
+
"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY": "pk_test_... or pk_live_...",
|
|
6238
|
+
"STRIPE_WEBHOOK_SECRET": "whsec_... (from Webhooks page)"
|
|
6239
|
+
},
|
|
6130
6240
|
packages: ["stripe", "@stripe/stripe-js"],
|
|
6131
|
-
docs: "https://stripe.com/docs"
|
|
6241
|
+
docs: "https://stripe.com/docs",
|
|
6242
|
+
steps: [
|
|
6243
|
+
"Sign in at dashboard.stripe.com",
|
|
6244
|
+
"Go to Developers > API keys",
|
|
6245
|
+
"Copy Publishable and Secret keys",
|
|
6246
|
+
"For webhooks: Developers > Webhooks > Add endpoint"
|
|
6247
|
+
]
|
|
6132
6248
|
},
|
|
6133
6249
|
{
|
|
6134
6250
|
id: "lemonsqueezy",
|
|
6135
6251
|
name: "Lemon Squeezy",
|
|
6136
|
-
description: "
|
|
6252
|
+
description: "Payments with tax handling (MoR)",
|
|
6137
6253
|
category: "payments",
|
|
6138
6254
|
icon: "\u{1F34B}",
|
|
6139
|
-
|
|
6140
|
-
oauthUrl: "https://app.lemonsqueezy.com/settings/api",
|
|
6255
|
+
dashboardUrl: "https://app.lemonsqueezy.com/settings/api",
|
|
6141
6256
|
envVars: ["LEMONSQUEEZY_API_KEY", "LEMONSQUEEZY_STORE_ID", "LEMONSQUEEZY_WEBHOOK_SECRET"],
|
|
6257
|
+
envVarHints: {
|
|
6258
|
+
"LEMONSQUEEZY_API_KEY": "From Settings > API",
|
|
6259
|
+
"LEMONSQUEEZY_STORE_ID": "Your store ID number",
|
|
6260
|
+
"LEMONSQUEEZY_WEBHOOK_SECRET": "From Settings > Webhooks"
|
|
6261
|
+
},
|
|
6142
6262
|
packages: ["@lemonsqueezy/lemonsqueezy.js"],
|
|
6143
|
-
docs: "https://docs.lemonsqueezy.com"
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
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"
|
|
6263
|
+
docs: "https://docs.lemonsqueezy.com",
|
|
6264
|
+
steps: [
|
|
6265
|
+
"Sign in at app.lemonsqueezy.com",
|
|
6266
|
+
"Go to Settings > API",
|
|
6267
|
+
"Create an API key",
|
|
6268
|
+
"Note your Store ID from the URL"
|
|
6269
|
+
]
|
|
6156
6270
|
},
|
|
6157
6271
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6158
6272
|
// EMAIL
|
|
@@ -6160,14 +6274,22 @@ var INTEGRATIONS = [
|
|
|
6160
6274
|
{
|
|
6161
6275
|
id: "resend",
|
|
6162
6276
|
name: "Resend",
|
|
6163
|
-
description: "Modern email API
|
|
6277
|
+
description: "Modern email API",
|
|
6164
6278
|
category: "email",
|
|
6165
6279
|
icon: "\u{1F4E7}",
|
|
6166
|
-
|
|
6167
|
-
oauthUrl: "https://resend.com/api-keys",
|
|
6280
|
+
dashboardUrl: "https://resend.com/api-keys",
|
|
6168
6281
|
envVars: ["RESEND_API_KEY"],
|
|
6282
|
+
envVarHints: {
|
|
6283
|
+
"RESEND_API_KEY": "re_..."
|
|
6284
|
+
},
|
|
6169
6285
|
packages: ["resend"],
|
|
6170
|
-
docs: "https://resend.com/docs"
|
|
6286
|
+
docs: "https://resend.com/docs",
|
|
6287
|
+
steps: [
|
|
6288
|
+
"Sign up at resend.com",
|
|
6289
|
+
"Go to API Keys",
|
|
6290
|
+
"Create a new API key",
|
|
6291
|
+
"Copy the key (only shown once!)"
|
|
6292
|
+
]
|
|
6171
6293
|
},
|
|
6172
6294
|
{
|
|
6173
6295
|
id: "sendgrid",
|
|
@@ -6175,46 +6297,19 @@ var INTEGRATIONS = [
|
|
|
6175
6297
|
description: "Email delivery service",
|
|
6176
6298
|
category: "email",
|
|
6177
6299
|
icon: "\u{1F4E8}",
|
|
6178
|
-
|
|
6179
|
-
oauthUrl: "https://app.sendgrid.com/settings/api_keys",
|
|
6300
|
+
dashboardUrl: "https://app.sendgrid.com/settings/api_keys",
|
|
6180
6301
|
envVars: ["SENDGRID_API_KEY"],
|
|
6302
|
+
envVarHints: {
|
|
6303
|
+
"SENDGRID_API_KEY": "SG.xxx..."
|
|
6304
|
+
},
|
|
6181
6305
|
packages: ["@sendgrid/mail"],
|
|
6182
|
-
docs: "https://docs.sendgrid.com"
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
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"
|
|
6306
|
+
docs: "https://docs.sendgrid.com",
|
|
6307
|
+
steps: [
|
|
6308
|
+
"Sign in at app.sendgrid.com",
|
|
6309
|
+
"Go to Settings > API Keys",
|
|
6310
|
+
"Create an API key with Mail Send permission",
|
|
6311
|
+
"Copy the key (only shown once!)"
|
|
6312
|
+
]
|
|
6218
6313
|
},
|
|
6219
6314
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6220
6315
|
// STORAGE
|
|
@@ -6225,11 +6320,20 @@ var INTEGRATIONS = [
|
|
|
6225
6320
|
description: "File uploads for Next.js",
|
|
6226
6321
|
category: "storage",
|
|
6227
6322
|
icon: "\u{1F4E4}",
|
|
6228
|
-
|
|
6229
|
-
oauthUrl: "https://uploadthing.com/dashboard",
|
|
6323
|
+
dashboardUrl: "https://uploadthing.com/dashboard",
|
|
6230
6324
|
envVars: ["UPLOADTHING_SECRET", "UPLOADTHING_APP_ID"],
|
|
6325
|
+
envVarHints: {
|
|
6326
|
+
"UPLOADTHING_SECRET": "sk_live_...",
|
|
6327
|
+
"UPLOADTHING_APP_ID": "Your app ID"
|
|
6328
|
+
},
|
|
6231
6329
|
packages: ["uploadthing", "@uploadthing/react"],
|
|
6232
|
-
docs: "https://docs.uploadthing.com"
|
|
6330
|
+
docs: "https://docs.uploadthing.com",
|
|
6331
|
+
steps: [
|
|
6332
|
+
"Sign in at uploadthing.com",
|
|
6333
|
+
"Create or select an app",
|
|
6334
|
+
"Go to API Keys",
|
|
6335
|
+
"Copy Secret and App ID"
|
|
6336
|
+
]
|
|
6233
6337
|
},
|
|
6234
6338
|
{
|
|
6235
6339
|
id: "cloudinary",
|
|
@@ -6237,85 +6341,20 @@ var INTEGRATIONS = [
|
|
|
6237
6341
|
description: "Image & video management",
|
|
6238
6342
|
category: "storage",
|
|
6239
6343
|
icon: "\u2601\uFE0F",
|
|
6240
|
-
|
|
6241
|
-
oauthUrl: "https://console.cloudinary.com/settings/api-keys",
|
|
6344
|
+
dashboardUrl: "https://console.cloudinary.com/settings/api-keys",
|
|
6242
6345
|
envVars: ["CLOUDINARY_CLOUD_NAME", "CLOUDINARY_API_KEY", "CLOUDINARY_API_SECRET"],
|
|
6346
|
+
envVarHints: {
|
|
6347
|
+
"CLOUDINARY_CLOUD_NAME": "Your cloud name",
|
|
6348
|
+
"CLOUDINARY_API_KEY": "Numeric API key",
|
|
6349
|
+
"CLOUDINARY_API_SECRET": "API Secret"
|
|
6350
|
+
},
|
|
6243
6351
|
packages: ["cloudinary"],
|
|
6244
|
-
docs: "https://cloudinary.com/documentation"
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
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"
|
|
6352
|
+
docs: "https://cloudinary.com/documentation",
|
|
6353
|
+
steps: [
|
|
6354
|
+
"Sign in at cloudinary.com",
|
|
6355
|
+
"Go to Settings > API Keys",
|
|
6356
|
+
"Copy Cloud Name, API Key, API Secret"
|
|
6357
|
+
]
|
|
6319
6358
|
},
|
|
6320
6359
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6321
6360
|
// AI
|
|
@@ -6323,14 +6362,22 @@ var INTEGRATIONS = [
|
|
|
6323
6362
|
{
|
|
6324
6363
|
id: "openai",
|
|
6325
6364
|
name: "OpenAI",
|
|
6326
|
-
description: "GPT
|
|
6365
|
+
description: "GPT-4, DALL-E, Whisper",
|
|
6327
6366
|
category: "ai",
|
|
6328
6367
|
icon: "\u{1F916}",
|
|
6329
|
-
|
|
6330
|
-
oauthUrl: "https://platform.openai.com/api-keys",
|
|
6368
|
+
dashboardUrl: "https://platform.openai.com/api-keys",
|
|
6331
6369
|
envVars: ["OPENAI_API_KEY"],
|
|
6370
|
+
envVarHints: {
|
|
6371
|
+
"OPENAI_API_KEY": "sk-..."
|
|
6372
|
+
},
|
|
6332
6373
|
packages: ["openai"],
|
|
6333
|
-
docs: "https://platform.openai.com/docs"
|
|
6374
|
+
docs: "https://platform.openai.com/docs",
|
|
6375
|
+
steps: [
|
|
6376
|
+
"Sign in at platform.openai.com",
|
|
6377
|
+
"Go to API Keys",
|
|
6378
|
+
"Create new secret key",
|
|
6379
|
+
"Copy it (only shown once!)"
|
|
6380
|
+
]
|
|
6334
6381
|
},
|
|
6335
6382
|
{
|
|
6336
6383
|
id: "anthropic",
|
|
@@ -6338,11 +6385,19 @@ var INTEGRATIONS = [
|
|
|
6338
6385
|
description: "Claude AI models",
|
|
6339
6386
|
category: "ai",
|
|
6340
6387
|
icon: "\u{1F9E0}",
|
|
6341
|
-
|
|
6342
|
-
oauthUrl: "https://console.anthropic.com/settings/keys",
|
|
6388
|
+
dashboardUrl: "https://console.anthropic.com/settings/keys",
|
|
6343
6389
|
envVars: ["ANTHROPIC_API_KEY"],
|
|
6390
|
+
envVarHints: {
|
|
6391
|
+
"ANTHROPIC_API_KEY": "sk-ant-..."
|
|
6392
|
+
},
|
|
6344
6393
|
packages: ["@anthropic-ai/sdk"],
|
|
6345
|
-
docs: "https://docs.anthropic.com"
|
|
6394
|
+
docs: "https://docs.anthropic.com",
|
|
6395
|
+
steps: [
|
|
6396
|
+
"Sign in at console.anthropic.com",
|
|
6397
|
+
"Go to API Keys",
|
|
6398
|
+
"Create a new key",
|
|
6399
|
+
"Copy it (only shown once!)"
|
|
6400
|
+
]
|
|
6346
6401
|
},
|
|
6347
6402
|
{
|
|
6348
6403
|
id: "replicate",
|
|
@@ -6350,72 +6405,83 @@ var INTEGRATIONS = [
|
|
|
6350
6405
|
description: "Run ML models in the cloud",
|
|
6351
6406
|
category: "ai",
|
|
6352
6407
|
icon: "\u{1F504}",
|
|
6353
|
-
|
|
6354
|
-
oauthUrl: "https://replicate.com/account/api-tokens",
|
|
6408
|
+
dashboardUrl: "https://replicate.com/account/api-tokens",
|
|
6355
6409
|
envVars: ["REPLICATE_API_TOKEN"],
|
|
6410
|
+
envVarHints: {
|
|
6411
|
+
"REPLICATE_API_TOKEN": "r8_..."
|
|
6412
|
+
},
|
|
6356
6413
|
packages: ["replicate"],
|
|
6357
|
-
docs: "https://replicate.com/docs"
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
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"
|
|
6414
|
+
docs: "https://replicate.com/docs",
|
|
6415
|
+
steps: [
|
|
6416
|
+
"Sign in at replicate.com",
|
|
6417
|
+
"Go to Account > API Tokens",
|
|
6418
|
+
"Create a token"
|
|
6419
|
+
]
|
|
6381
6420
|
},
|
|
6382
6421
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6383
|
-
//
|
|
6422
|
+
// ANALYTICS
|
|
6384
6423
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6385
6424
|
{
|
|
6386
|
-
id: "
|
|
6387
|
-
name: "
|
|
6388
|
-
description: "
|
|
6389
|
-
category: "
|
|
6390
|
-
icon: "\u{
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6425
|
+
id: "posthog",
|
|
6426
|
+
name: "PostHog",
|
|
6427
|
+
description: "Product analytics & feature flags",
|
|
6428
|
+
category: "analytics",
|
|
6429
|
+
icon: "\u{1F994}",
|
|
6430
|
+
dashboardUrl: "https://app.posthog.com/project/settings",
|
|
6431
|
+
envVars: ["NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST"],
|
|
6432
|
+
envVarHints: {
|
|
6433
|
+
"NEXT_PUBLIC_POSTHOG_KEY": "phc_...",
|
|
6434
|
+
"NEXT_PUBLIC_POSTHOG_HOST": "https://app.posthog.com (or your self-hosted URL)"
|
|
6435
|
+
},
|
|
6436
|
+
packages: ["posthog-js"],
|
|
6437
|
+
docs: "https://posthog.com/docs",
|
|
6438
|
+
steps: [
|
|
6439
|
+
"Sign up at posthog.com",
|
|
6440
|
+
"Create or select a project",
|
|
6441
|
+
"Go to Project Settings",
|
|
6442
|
+
"Copy Project API Key"
|
|
6443
|
+
]
|
|
6396
6444
|
},
|
|
6397
6445
|
{
|
|
6398
|
-
id: "
|
|
6399
|
-
name: "
|
|
6400
|
-
description: "
|
|
6401
|
-
category: "
|
|
6402
|
-
icon: "\
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6446
|
+
id: "vercel-analytics",
|
|
6447
|
+
name: "Vercel Analytics",
|
|
6448
|
+
description: "Web analytics by Vercel",
|
|
6449
|
+
category: "analytics",
|
|
6450
|
+
icon: "\u25B2",
|
|
6451
|
+
dashboardUrl: "https://vercel.com/dashboard",
|
|
6452
|
+
envVars: [],
|
|
6453
|
+
packages: ["@vercel/analytics"],
|
|
6454
|
+
docs: "https://vercel.com/docs/analytics",
|
|
6455
|
+
steps: [
|
|
6456
|
+
"No API key needed!",
|
|
6457
|
+
"Just install the package",
|
|
6458
|
+
"Add <Analytics /> to your layout",
|
|
6459
|
+
"Deploy to Vercel to see data"
|
|
6460
|
+
]
|
|
6408
6461
|
},
|
|
6462
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
6463
|
+
// MONITORING
|
|
6464
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
6409
6465
|
{
|
|
6410
|
-
id: "
|
|
6411
|
-
name: "
|
|
6412
|
-
description: "
|
|
6413
|
-
category: "
|
|
6414
|
-
icon: "\u{
|
|
6415
|
-
|
|
6416
|
-
envVars: ["
|
|
6417
|
-
|
|
6418
|
-
|
|
6466
|
+
id: "sentry",
|
|
6467
|
+
name: "Sentry",
|
|
6468
|
+
description: "Error tracking & performance",
|
|
6469
|
+
category: "monitoring",
|
|
6470
|
+
icon: "\u{1F41B}",
|
|
6471
|
+
dashboardUrl: "https://sentry.io/settings/account/api/auth-tokens/",
|
|
6472
|
+
envVars: ["SENTRY_DSN", "SENTRY_AUTH_TOKEN"],
|
|
6473
|
+
envVarHints: {
|
|
6474
|
+
"SENTRY_DSN": "https://xxx@xxx.ingest.sentry.io/xxx",
|
|
6475
|
+
"SENTRY_AUTH_TOKEN": "From Organization Auth Tokens"
|
|
6476
|
+
},
|
|
6477
|
+
packages: ["@sentry/nextjs"],
|
|
6478
|
+
docs: "https://docs.sentry.io",
|
|
6479
|
+
steps: [
|
|
6480
|
+
"Sign up at sentry.io",
|
|
6481
|
+
"Create a project (Next.js)",
|
|
6482
|
+
"Copy DSN from Project Settings > Client Keys",
|
|
6483
|
+
"Create auth token in Settings > Auth Tokens"
|
|
6484
|
+
]
|
|
6419
6485
|
},
|
|
6420
6486
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6421
6487
|
// MESSAGING
|
|
@@ -6426,11 +6492,21 @@ var INTEGRATIONS = [
|
|
|
6426
6492
|
description: "SMS, voice & WhatsApp",
|
|
6427
6493
|
category: "messaging",
|
|
6428
6494
|
icon: "\u{1F4F1}",
|
|
6429
|
-
|
|
6430
|
-
oauthUrl: "https://console.twilio.com/us1/account/keys-credentials/api-keys",
|
|
6495
|
+
dashboardUrl: "https://console.twilio.com",
|
|
6431
6496
|
envVars: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN", "TWILIO_PHONE_NUMBER"],
|
|
6497
|
+
envVarHints: {
|
|
6498
|
+
"TWILIO_ACCOUNT_SID": "AC... (from Console Dashboard)",
|
|
6499
|
+
"TWILIO_AUTH_TOKEN": "From Console Dashboard (click to reveal)",
|
|
6500
|
+
"TWILIO_PHONE_NUMBER": "+1234567890 (buy in Phone Numbers)"
|
|
6501
|
+
},
|
|
6432
6502
|
packages: ["twilio"],
|
|
6433
|
-
docs: "https://www.twilio.com/docs"
|
|
6503
|
+
docs: "https://www.twilio.com/docs",
|
|
6504
|
+
steps: [
|
|
6505
|
+
"Sign up at twilio.com",
|
|
6506
|
+
"Go to Console Dashboard",
|
|
6507
|
+
"Copy Account SID and Auth Token",
|
|
6508
|
+
"Buy a phone number in Phone Numbers > Manage"
|
|
6509
|
+
]
|
|
6434
6510
|
},
|
|
6435
6511
|
{
|
|
6436
6512
|
id: "pusher",
|
|
@@ -6438,62 +6514,23 @@ var INTEGRATIONS = [
|
|
|
6438
6514
|
description: "Realtime websockets",
|
|
6439
6515
|
category: "messaging",
|
|
6440
6516
|
icon: "\u{1F514}",
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6517
|
+
dashboardUrl: "https://dashboard.pusher.com",
|
|
6518
|
+
envVars: ["PUSHER_APP_ID", "PUSHER_KEY", "PUSHER_SECRET", "NEXT_PUBLIC_PUSHER_KEY", "PUSHER_CLUSTER"],
|
|
6519
|
+
envVarHints: {
|
|
6520
|
+
"PUSHER_APP_ID": "App ID from App Keys",
|
|
6521
|
+
"PUSHER_KEY": "Key from App Keys",
|
|
6522
|
+
"PUSHER_SECRET": "Secret from App Keys",
|
|
6523
|
+
"NEXT_PUBLIC_PUSHER_KEY": "Same as PUSHER_KEY",
|
|
6524
|
+
"PUSHER_CLUSTER": "e.g., us2, eu, ap1"
|
|
6525
|
+
},
|
|
6444
6526
|
packages: ["pusher", "pusher-js"],
|
|
6445
|
-
docs: "https://pusher.com/docs"
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
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"
|
|
6527
|
+
docs: "https://pusher.com/docs",
|
|
6528
|
+
steps: [
|
|
6529
|
+
"Sign up at pusher.com",
|
|
6530
|
+
"Create a Channels app",
|
|
6531
|
+
"Go to App Keys",
|
|
6532
|
+
"Copy all credentials"
|
|
6533
|
+
]
|
|
6497
6534
|
},
|
|
6498
6535
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6499
6536
|
// DEPLOYMENT
|
|
@@ -6504,11 +6541,19 @@ var INTEGRATIONS = [
|
|
|
6504
6541
|
description: "Deploy Next.js apps",
|
|
6505
6542
|
category: "deployment",
|
|
6506
6543
|
icon: "\u25B2",
|
|
6507
|
-
|
|
6508
|
-
oauthUrl: "https://vercel.com/account/tokens",
|
|
6544
|
+
dashboardUrl: "https://vercel.com/account/tokens",
|
|
6509
6545
|
envVars: ["VERCEL_TOKEN"],
|
|
6546
|
+
envVarHints: {
|
|
6547
|
+
"VERCEL_TOKEN": "From Account Settings > Tokens"
|
|
6548
|
+
},
|
|
6510
6549
|
packages: ["vercel"],
|
|
6511
|
-
docs: "https://vercel.com/docs"
|
|
6550
|
+
docs: "https://vercel.com/docs",
|
|
6551
|
+
steps: [
|
|
6552
|
+
"Sign in at vercel.com",
|
|
6553
|
+
"Go to Account Settings > Tokens",
|
|
6554
|
+
"Create a new token",
|
|
6555
|
+
"Copy it"
|
|
6556
|
+
]
|
|
6512
6557
|
},
|
|
6513
6558
|
{
|
|
6514
6559
|
id: "github",
|
|
@@ -6516,73 +6561,80 @@ var INTEGRATIONS = [
|
|
|
6516
6561
|
description: "Code hosting & CI/CD",
|
|
6517
6562
|
category: "deployment",
|
|
6518
6563
|
icon: "\u{1F419}",
|
|
6519
|
-
|
|
6520
|
-
oauthUrl: "https://github.com/settings/tokens",
|
|
6564
|
+
dashboardUrl: "https://github.com/settings/tokens",
|
|
6521
6565
|
envVars: ["GITHUB_TOKEN"],
|
|
6566
|
+
envVarHints: {
|
|
6567
|
+
"GITHUB_TOKEN": "ghp_... or github_pat_..."
|
|
6568
|
+
},
|
|
6522
6569
|
packages: ["octokit"],
|
|
6523
|
-
docs: "https://docs.github.com"
|
|
6570
|
+
docs: "https://docs.github.com",
|
|
6571
|
+
steps: [
|
|
6572
|
+
"Sign in at github.com",
|
|
6573
|
+
"Go to Settings > Developer settings > Personal access tokens",
|
|
6574
|
+
"Generate new token (classic)",
|
|
6575
|
+
"Select scopes: repo, user",
|
|
6576
|
+
"Copy the token"
|
|
6577
|
+
]
|
|
6524
6578
|
},
|
|
6525
6579
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6526
|
-
//
|
|
6580
|
+
// CMS
|
|
6527
6581
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
6528
6582
|
{
|
|
6529
|
-
id: "
|
|
6530
|
-
name: "
|
|
6531
|
-
description: "
|
|
6532
|
-
category: "
|
|
6533
|
-
icon: "\u{
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
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"
|
|
6583
|
+
id: "sanity",
|
|
6584
|
+
name: "Sanity",
|
|
6585
|
+
description: "Headless CMS",
|
|
6586
|
+
category: "cms",
|
|
6587
|
+
icon: "\u{1F4DD}",
|
|
6588
|
+
dashboardUrl: "https://www.sanity.io/manage",
|
|
6589
|
+
envVars: ["NEXT_PUBLIC_SANITY_PROJECT_ID", "NEXT_PUBLIC_SANITY_DATASET", "SANITY_API_TOKEN"],
|
|
6590
|
+
envVarHints: {
|
|
6591
|
+
"NEXT_PUBLIC_SANITY_PROJECT_ID": "From project settings",
|
|
6592
|
+
"NEXT_PUBLIC_SANITY_DATASET": 'Usually "production"',
|
|
6593
|
+
"SANITY_API_TOKEN": "From API > Tokens"
|
|
6594
|
+
},
|
|
6595
|
+
packages: ["@sanity/client", "next-sanity"],
|
|
6596
|
+
docs: "https://www.sanity.io/docs",
|
|
6597
|
+
steps: [
|
|
6598
|
+
"Sign in at sanity.io/manage",
|
|
6599
|
+
"Create or select a project",
|
|
6600
|
+
"Copy Project ID from settings",
|
|
6601
|
+
"Create API token in API > Tokens"
|
|
6602
|
+
]
|
|
6563
6603
|
}
|
|
6564
6604
|
];
|
|
6565
6605
|
async function integrateCommand(integrationId) {
|
|
6566
|
-
const config = new Config();
|
|
6567
6606
|
console.log(chalk19.cyan(`
|
|
6568
6607
|
\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
6608
|
\u2551 \u{1F50C} ONE-CLICK INTEGRATIONS \u2551
|
|
6570
6609
|
\u2551 \u2551
|
|
6571
|
-
\u2551 ${INTEGRATIONS.length}
|
|
6572
|
-
\u2551 Browser-based auth \u2022 Auto-install \u2022 Ready in seconds \u2551
|
|
6610
|
+
\u2551 ${INTEGRATIONS.length} services \u2022 Step-by-step guidance \u2022 Auto-setup \u2551
|
|
6573
6611
|
\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
6612
|
`));
|
|
6575
6613
|
if (integrationId) {
|
|
6576
6614
|
const integration2 = INTEGRATIONS.find((i) => i.id === integrationId);
|
|
6577
6615
|
if (!integration2) {
|
|
6578
6616
|
p18.log.error(`Unknown integration: ${integrationId}`);
|
|
6579
|
-
console.log(chalk19.dim(`
|
|
6617
|
+
console.log(chalk19.dim(`
|
|
6618
|
+
Available: ${INTEGRATIONS.map((i) => i.id).join(", ")}`));
|
|
6580
6619
|
return;
|
|
6581
6620
|
}
|
|
6582
|
-
await installIntegration(integration2
|
|
6621
|
+
await installIntegration(integration2);
|
|
6583
6622
|
return;
|
|
6584
6623
|
}
|
|
6585
6624
|
const categories = [...new Set(INTEGRATIONS.map((i) => i.category))];
|
|
6625
|
+
const categoryLabels = {
|
|
6626
|
+
auth: "\u{1F510} Authentication",
|
|
6627
|
+
database: "\u{1F5C4}\uFE0F Database",
|
|
6628
|
+
payments: "\u{1F4B3} Payments",
|
|
6629
|
+
email: "\u{1F4E7} Email",
|
|
6630
|
+
storage: "\u{1F4E6} Storage",
|
|
6631
|
+
ai: "\u{1F916} AI & ML",
|
|
6632
|
+
analytics: "\u{1F4CA} Analytics",
|
|
6633
|
+
monitoring: "\u{1F41B} Monitoring",
|
|
6634
|
+
messaging: "\u{1F4AC} Messaging",
|
|
6635
|
+
deployment: "\u{1F680} Deployment",
|
|
6636
|
+
cms: "\u{1F4DD} CMS"
|
|
6637
|
+
};
|
|
6586
6638
|
const category = await p18.select({
|
|
6587
6639
|
message: "Choose a category:",
|
|
6588
6640
|
options: [
|
|
@@ -6590,33 +6642,33 @@ async function integrateCommand(integrationId) {
|
|
|
6590
6642
|
{ value: "search", label: "\u{1F50D} Search by name" },
|
|
6591
6643
|
...categories.map((c) => ({
|
|
6592
6644
|
value: c,
|
|
6593
|
-
label:
|
|
6594
|
-
hint: `${INTEGRATIONS.filter((i) => i.category === c).length}
|
|
6645
|
+
label: categoryLabels[c] || c,
|
|
6646
|
+
hint: `${INTEGRATIONS.filter((i) => i.category === c).length} services`
|
|
6595
6647
|
}))
|
|
6596
6648
|
]
|
|
6597
6649
|
});
|
|
6598
6650
|
if (p18.isCancel(category)) return;
|
|
6599
|
-
let
|
|
6651
|
+
let filtered = INTEGRATIONS;
|
|
6600
6652
|
if (category === "search") {
|
|
6601
6653
|
const query = await p18.text({
|
|
6602
|
-
message: "Search
|
|
6603
|
-
placeholder: "stripe,
|
|
6654
|
+
message: "Search:",
|
|
6655
|
+
placeholder: "stripe, supabase, openai..."
|
|
6604
6656
|
});
|
|
6605
6657
|
if (p18.isCancel(query)) return;
|
|
6606
6658
|
const q = query.toLowerCase();
|
|
6607
|
-
|
|
6608
|
-
(i) => i.name.toLowerCase().includes(q) || i.description.toLowerCase().includes(q) || i.id.
|
|
6659
|
+
filtered = INTEGRATIONS.filter(
|
|
6660
|
+
(i) => i.name.toLowerCase().includes(q) || i.description.toLowerCase().includes(q) || i.id.includes(q)
|
|
6609
6661
|
);
|
|
6662
|
+
if (filtered.length === 0) {
|
|
6663
|
+
p18.log.warn("No integrations found");
|
|
6664
|
+
return;
|
|
6665
|
+
}
|
|
6610
6666
|
} else if (category !== "all") {
|
|
6611
|
-
|
|
6612
|
-
}
|
|
6613
|
-
if (filteredIntegrations.length === 0) {
|
|
6614
|
-
p18.log.warn("No integrations found");
|
|
6615
|
-
return;
|
|
6667
|
+
filtered = INTEGRATIONS.filter((i) => i.category === category);
|
|
6616
6668
|
}
|
|
6617
6669
|
const selected = await p18.select({
|
|
6618
|
-
message: "Select
|
|
6619
|
-
options:
|
|
6670
|
+
message: "Select integration:",
|
|
6671
|
+
options: filtered.map((i) => ({
|
|
6620
6672
|
value: i.id,
|
|
6621
6673
|
label: `${i.icon} ${i.name}`,
|
|
6622
6674
|
hint: i.description
|
|
@@ -6624,112 +6676,97 @@ async function integrateCommand(integrationId) {
|
|
|
6624
6676
|
});
|
|
6625
6677
|
if (p18.isCancel(selected)) return;
|
|
6626
6678
|
const integration = INTEGRATIONS.find((i) => i.id === selected);
|
|
6627
|
-
await installIntegration(integration
|
|
6679
|
+
await installIntegration(integration);
|
|
6628
6680
|
}
|
|
6629
|
-
async function installIntegration(integration
|
|
6681
|
+
async function installIntegration(integration) {
|
|
6630
6682
|
console.log(chalk19.cyan(`
|
|
6631
|
-
|
|
6683
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6684
|
+
\u2502 ${integration.icon} Installing ${integration.name}
|
|
6685
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6632
6686
|
`));
|
|
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 spinner17 = p18.spinner();
|
|
6642
6687
|
if (integration.packages && integration.packages.length > 0) {
|
|
6688
|
+
const spinner17 = p18.spinner();
|
|
6643
6689
|
spinner17.start(`Installing ${integration.packages.join(", ")}...`);
|
|
6644
6690
|
try {
|
|
6645
6691
|
await execa10("npm", ["install", ...integration.packages], {
|
|
6646
6692
|
cwd: process.cwd(),
|
|
6647
6693
|
reject: false
|
|
6648
6694
|
});
|
|
6649
|
-
spinner17.stop(
|
|
6695
|
+
spinner17.stop(chalk19.green("\u2713 Packages installed"));
|
|
6650
6696
|
} catch {
|
|
6651
|
-
spinner17.stop(
|
|
6697
|
+
spinner17.stop(chalk19.yellow("\u26A0 Package install had issues (may already be installed)"));
|
|
6652
6698
|
}
|
|
6653
6699
|
}
|
|
6654
6700
|
if (integration.envVars.length > 0) {
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6701
|
+
console.log(chalk19.cyan(`
|
|
6702
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6703
|
+
\u2502 \u{1F4CB} HOW TO GET YOUR API KEYS
|
|
6704
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6705
|
+
`));
|
|
6706
|
+
integration.steps.forEach((step, i) => {
|
|
6707
|
+
console.log(chalk19.white(` ${i + 1}. ${step}`));
|
|
6708
|
+
});
|
|
6709
|
+
console.log(chalk19.dim(`
|
|
6710
|
+
Dashboard: ${integration.dashboardUrl}
|
|
6711
|
+
Docs: ${integration.docs}
|
|
6659
6712
|
`));
|
|
6660
|
-
|
|
6661
|
-
|
|
6713
|
+
const openDashboard = await p18.confirm({
|
|
6714
|
+
message: `Open ${integration.name} dashboard in browser?`,
|
|
6715
|
+
initialValue: true
|
|
6716
|
+
});
|
|
6717
|
+
if (openDashboard && !p18.isCancel(openDashboard)) {
|
|
6718
|
+
await open2(integration.dashboardUrl);
|
|
6719
|
+
console.log(chalk19.dim(`
|
|
6720
|
+
\u2713 Browser opened! Get your keys and come back.
|
|
6721
|
+
`));
|
|
6722
|
+
await sleep2(2e3);
|
|
6662
6723
|
}
|
|
6724
|
+
console.log(chalk19.cyan(`
|
|
6725
|
+
Enter your credentials:
|
|
6726
|
+
`));
|
|
6663
6727
|
const credentials = {};
|
|
6664
6728
|
for (const envVar of integration.envVars) {
|
|
6729
|
+
const hint = integration.envVarHints?.[envVar] || "Paste here...";
|
|
6665
6730
|
const isPublic = envVar.startsWith("NEXT_PUBLIC_");
|
|
6666
|
-
const
|
|
6731
|
+
const label = isPublic ? chalk19.yellow("(public)") : chalk19.green("(secret)");
|
|
6667
6732
|
const value = await p18.text({
|
|
6668
|
-
message: `${envVar} ${
|
|
6669
|
-
placeholder:
|
|
6733
|
+
message: `${envVar} ${label}`,
|
|
6734
|
+
placeholder: hint,
|
|
6670
6735
|
validate: (v) => !v ? "Required" : void 0
|
|
6671
6736
|
});
|
|
6672
6737
|
if (p18.isCancel(value)) return;
|
|
6673
6738
|
credentials[envVar] = value;
|
|
6674
6739
|
}
|
|
6675
6740
|
await saveEnvVars(credentials);
|
|
6676
|
-
console.log(chalk19.green(`
|
|
6741
|
+
console.log(chalk19.green(`
|
|
6742
|
+
\u2713 Saved to .env.local
|
|
6677
6743
|
`));
|
|
6678
6744
|
}
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
await fs15.ensureDir(path14.dirname(file.path));
|
|
6684
|
-
await fs15.writeFile(file.path, file.content);
|
|
6685
|
-
}
|
|
6686
|
-
}
|
|
6687
|
-
spinner17.stop("\u2713 Setup complete");
|
|
6745
|
+
const spinner16 = p18.spinner();
|
|
6746
|
+
spinner16.start("Creating setup file...");
|
|
6747
|
+
await generateSetupCode(integration);
|
|
6748
|
+
spinner16.stop(chalk19.green("\u2713 Setup complete"));
|
|
6688
6749
|
console.log(chalk19.green(`
|
|
6689
6750
|
\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 \
|
|
6751
|
+
\u2551 \u2705 ${integration.name} installed!
|
|
6691
6752
|
\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
|
-
|
|
6694
|
-
|
|
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}
|
|
6753
|
+
\u2551 \u2551
|
|
6754
|
+
\u2551 Import: import { ... } from '@/lib/${integration.id}'
|
|
6755
|
+
\u2551 Docs: ${integration.docs.substring(0, 45).padEnd(45)}\u2551
|
|
6756
|
+
\u2551 \u2551
|
|
6701
6757
|
\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;
|
|
6758
|
+
`));
|
|
6720
6759
|
}
|
|
6721
6760
|
async function saveEnvVars(vars) {
|
|
6722
6761
|
const envPath = path14.join(process.cwd(), ".env.local");
|
|
6723
6762
|
let content = "";
|
|
6724
6763
|
if (await fs15.pathExists(envPath)) {
|
|
6725
6764
|
content = await fs15.readFile(envPath, "utf-8");
|
|
6726
|
-
if (!content.endsWith("\n"))
|
|
6727
|
-
content += "\n";
|
|
6728
|
-
}
|
|
6729
|
-
content += "\n# Added by CodeBakers\n";
|
|
6765
|
+
if (!content.endsWith("\n")) content += "\n";
|
|
6730
6766
|
}
|
|
6767
|
+
content += "\n# Added by CodeBakers\n";
|
|
6731
6768
|
for (const [key, value] of Object.entries(vars)) {
|
|
6732
|
-
const regex = new RegExp(`^${key}
|
|
6769
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
6733
6770
|
if (regex.test(content)) {
|
|
6734
6771
|
content = content.replace(regex, `${key}=${value}`);
|
|
6735
6772
|
} else {
|
|
@@ -6740,90 +6777,76 @@ async function saveEnvVars(vars) {
|
|
|
6740
6777
|
await fs15.writeFile(envPath, content);
|
|
6741
6778
|
}
|
|
6742
6779
|
async function generateSetupCode(integration) {
|
|
6743
|
-
const
|
|
6780
|
+
const libDir = path14.join(process.cwd(), "src", "lib");
|
|
6781
|
+
await fs15.ensureDir(libDir);
|
|
6782
|
+
let code = "";
|
|
6744
6783
|
switch (integration.id) {
|
|
6745
|
-
case "
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
content: `import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
6784
|
+
case "supabase":
|
|
6785
|
+
case "supabase-auth":
|
|
6786
|
+
code = `import { createClient } from '@supabase/supabase-js';
|
|
6749
6787
|
|
|
6750
|
-
|
|
6788
|
+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
6789
|
+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
6751
6790
|
|
|
6752
|
-
export const
|
|
6753
|
-
|
|
6754
|
-
};
|
|
6755
|
-
`
|
|
6756
|
-
});
|
|
6791
|
+
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
|
6792
|
+
`;
|
|
6757
6793
|
break;
|
|
6758
6794
|
case "stripe":
|
|
6759
|
-
|
|
6760
|
-
path: "src/lib/stripe.ts",
|
|
6761
|
-
content: `import Stripe from 'stripe';
|
|
6795
|
+
code = `import Stripe from 'stripe';
|
|
6762
6796
|
|
|
6763
6797
|
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
|
6764
6798
|
apiVersion: '2023-10-16',
|
|
6765
|
-
typescript: true,
|
|
6766
6799
|
});
|
|
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
|
-
});
|
|
6800
|
+
`;
|
|
6781
6801
|
break;
|
|
6782
6802
|
case "resend":
|
|
6783
|
-
|
|
6784
|
-
path: "src/lib/resend.ts",
|
|
6785
|
-
content: `import { Resend } from 'resend';
|
|
6803
|
+
code = `import { Resend } from 'resend';
|
|
6786
6804
|
|
|
6787
6805
|
export const resend = new Resend(process.env.RESEND_API_KEY);
|
|
6788
|
-
|
|
6789
|
-
});
|
|
6806
|
+
`;
|
|
6790
6807
|
break;
|
|
6791
6808
|
case "openai":
|
|
6792
|
-
|
|
6793
|
-
path: "src/lib/openai.ts",
|
|
6794
|
-
content: `import OpenAI from 'openai';
|
|
6809
|
+
code = `import OpenAI from 'openai';
|
|
6795
6810
|
|
|
6796
6811
|
export const openai = new OpenAI({
|
|
6797
6812
|
apiKey: process.env.OPENAI_API_KEY,
|
|
6798
6813
|
});
|
|
6799
|
-
|
|
6800
|
-
});
|
|
6814
|
+
`;
|
|
6801
6815
|
break;
|
|
6802
6816
|
case "anthropic":
|
|
6803
|
-
|
|
6804
|
-
path: "src/lib/anthropic.ts",
|
|
6805
|
-
content: `import Anthropic from '@anthropic-ai/sdk';
|
|
6817
|
+
code = `import Anthropic from '@anthropic-ai/sdk';
|
|
6806
6818
|
|
|
6807
6819
|
export const anthropic = new Anthropic({
|
|
6808
6820
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
6809
6821
|
});
|
|
6810
|
-
|
|
6811
|
-
});
|
|
6822
|
+
`;
|
|
6812
6823
|
break;
|
|
6813
|
-
case "
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
+
case "clerk":
|
|
6825
|
+
code = `// Clerk is configured via middleware
|
|
6826
|
+
// See: https://clerk.com/docs/quickstarts/nextjs
|
|
6827
|
+
|
|
6828
|
+
// In middleware.ts:
|
|
6829
|
+
// import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
6830
|
+
// export default clerkMiddleware();
|
|
6831
|
+
|
|
6832
|
+
// In layout.tsx:
|
|
6833
|
+
// import { ClerkProvider } from '@clerk/nextjs';
|
|
6834
|
+
// <ClerkProvider>{children}</ClerkProvider>
|
|
6835
|
+
|
|
6836
|
+
export {};
|
|
6837
|
+
`;
|
|
6824
6838
|
break;
|
|
6839
|
+
default:
|
|
6840
|
+
code = `// ${integration.name}
|
|
6841
|
+
// Docs: ${integration.docs}
|
|
6842
|
+
//
|
|
6843
|
+
// Environment variables:
|
|
6844
|
+
${integration.envVars.map((v) => `// - ${v}`).join("\n")}
|
|
6845
|
+
|
|
6846
|
+
export {};
|
|
6847
|
+
`;
|
|
6825
6848
|
}
|
|
6826
|
-
|
|
6849
|
+
await fs15.writeFile(path14.join(libDir, `${integration.id}.ts`), code);
|
|
6827
6850
|
}
|
|
6828
6851
|
function sleep2(ms) {
|
|
6829
6852
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -6945,12 +6968,27 @@ var TEMPLATES = [
|
|
|
6945
6968
|
async function websiteCommand() {
|
|
6946
6969
|
const config = new Config();
|
|
6947
6970
|
if (!config.isConfigured()) {
|
|
6948
|
-
|
|
6971
|
+
console.log(chalk20.yellow(`
|
|
6972
|
+
\u26A0\uFE0F CodeBakers isn't set up yet.
|
|
6973
|
+
|
|
6974
|
+
Run this first:
|
|
6975
|
+
${chalk20.cyan("codebakers setup")}
|
|
6976
|
+
`));
|
|
6949
6977
|
return;
|
|
6950
6978
|
}
|
|
6951
6979
|
const anthropicCreds = config.getCredentials("anthropic");
|
|
6952
6980
|
if (!anthropicCreds?.apiKey) {
|
|
6953
|
-
|
|
6981
|
+
console.log(chalk20.yellow(`
|
|
6982
|
+
\u26A0\uFE0F Anthropic API key not configured.
|
|
6983
|
+
|
|
6984
|
+
The website builder needs Claude AI to generate code.
|
|
6985
|
+
|
|
6986
|
+
Run this to add your API key:
|
|
6987
|
+
${chalk20.cyan("codebakers setup")}
|
|
6988
|
+
|
|
6989
|
+
Get an API key at:
|
|
6990
|
+
${chalk20.dim("https://console.anthropic.com/settings/keys")}
|
|
6991
|
+
`));
|
|
6954
6992
|
return;
|
|
6955
6993
|
}
|
|
6956
6994
|
console.log(chalk20.cyan(`
|
|
@@ -6989,11 +7027,11 @@ async function websiteCommand() {
|
|
|
6989
7027
|
console.log(chalk20.dim(` Style: ${websiteSpec.style}`));
|
|
6990
7028
|
console.log(chalk20.dim(` Sections: ${websiteSpec.sections.join(", ")}
|
|
6991
7029
|
`));
|
|
6992
|
-
const
|
|
7030
|
+
const confirm13 = await p19.confirm({
|
|
6993
7031
|
message: "Build this website?",
|
|
6994
7032
|
initialValue: true
|
|
6995
7033
|
});
|
|
6996
|
-
if (!
|
|
7034
|
+
if (!confirm13 || p19.isCancel(confirm13)) return;
|
|
6997
7035
|
await buildWebsite(anthropic, websiteSpec, config);
|
|
6998
7036
|
}
|
|
6999
7037
|
async function describeWebsite(anthropic) {
|
|
@@ -7007,8 +7045,8 @@ async function describeWebsite(anthropic) {
|
|
|
7007
7045
|
placeholder: "A landing page for..."
|
|
7008
7046
|
});
|
|
7009
7047
|
if (p19.isCancel(description)) return null;
|
|
7010
|
-
const
|
|
7011
|
-
|
|
7048
|
+
const spinner16 = p19.spinner();
|
|
7049
|
+
spinner16.start("Understanding your vision...");
|
|
7012
7050
|
const response = await anthropic.messages.create({
|
|
7013
7051
|
model: "claude-sonnet-4-20250514",
|
|
7014
7052
|
max_tokens: 2048,
|
|
@@ -7048,7 +7086,7 @@ Return JSON only:
|
|
|
7048
7086
|
Make the content compelling and professional. Use appropriate sections for the type of website.`
|
|
7049
7087
|
}]
|
|
7050
7088
|
});
|
|
7051
|
-
|
|
7089
|
+
spinner16.stop("Got it!");
|
|
7052
7090
|
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7053
7091
|
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7054
7092
|
if (!jsonMatch) {
|
|
@@ -7083,8 +7121,8 @@ async function templateWebsite(anthropic) {
|
|
|
7083
7121
|
placeholder: "We help startups with..., Our colors are blue and white..."
|
|
7084
7122
|
});
|
|
7085
7123
|
if (p19.isCancel(details)) return null;
|
|
7086
|
-
const
|
|
7087
|
-
|
|
7124
|
+
const spinner16 = p19.spinner();
|
|
7125
|
+
spinner16.start("Customizing template...");
|
|
7088
7126
|
const response = await anthropic.messages.create({
|
|
7089
7127
|
model: "claude-sonnet-4-20250514",
|
|
7090
7128
|
max_tokens: 2048,
|
|
@@ -7122,7 +7160,7 @@ Return JSON only:
|
|
|
7122
7160
|
Make the content specific and compelling for this business.`
|
|
7123
7161
|
}]
|
|
7124
7162
|
});
|
|
7125
|
-
|
|
7163
|
+
spinner16.stop("Template customized!");
|
|
7126
7164
|
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7127
7165
|
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7128
7166
|
if (!jsonMatch) {
|
|
@@ -7147,8 +7185,8 @@ async function cloneDesign(anthropic) {
|
|
|
7147
7185
|
placeholder: "A project management tool for designers"
|
|
7148
7186
|
});
|
|
7149
7187
|
if (p19.isCancel(purpose)) return null;
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7188
|
+
const spinner16 = p19.spinner();
|
|
7189
|
+
spinner16.start("Analyzing design inspiration...");
|
|
7152
7190
|
const response = await anthropic.messages.create({
|
|
7153
7191
|
model: "claude-sonnet-4-20250514",
|
|
7154
7192
|
max_tokens: 2048,
|
|
@@ -7183,7 +7221,7 @@ Return JSON only:
|
|
|
7183
7221
|
Capture the FEEL of the inspiration but make it original.`
|
|
7184
7222
|
}]
|
|
7185
7223
|
});
|
|
7186
|
-
|
|
7224
|
+
spinner16.stop("Design analyzed!");
|
|
7187
7225
|
const text16 = response.content[0].type === "text" ? response.content[0].text : "";
|
|
7188
7226
|
const jsonMatch = text16.match(/\{[\s\S]*\}/);
|
|
7189
7227
|
if (!jsonMatch) {
|
|
@@ -7205,8 +7243,8 @@ async function buildWebsite(anthropic, spec, config) {
|
|
|
7205
7243
|
console.log(chalk20.cyan(`
|
|
7206
7244
|
\u{1F3D7}\uFE0F Building ${spec.name}...
|
|
7207
7245
|
`));
|
|
7208
|
-
const
|
|
7209
|
-
|
|
7246
|
+
const spinner16 = p19.spinner();
|
|
7247
|
+
spinner16.start("Creating Next.js project...");
|
|
7210
7248
|
await execa11("npx", [
|
|
7211
7249
|
"create-next-app@latest",
|
|
7212
7250
|
spec.name,
|
|
@@ -7219,8 +7257,8 @@ async function buildWebsite(anthropic, spec, config) {
|
|
|
7219
7257
|
"@/*",
|
|
7220
7258
|
"--no-git"
|
|
7221
7259
|
], { cwd: process.cwd(), reject: false });
|
|
7222
|
-
|
|
7223
|
-
|
|
7260
|
+
spinner16.stop("Project created");
|
|
7261
|
+
spinner16.start("Setting up shadcn/ui...");
|
|
7224
7262
|
await execa11("npx", ["shadcn@latest", "init", "-y", "-d"], {
|
|
7225
7263
|
cwd: projectPath,
|
|
7226
7264
|
reject: false
|
|
@@ -7229,8 +7267,8 @@ async function buildWebsite(anthropic, spec, config) {
|
|
|
7229
7267
|
cwd: projectPath,
|
|
7230
7268
|
reject: false
|
|
7231
7269
|
});
|
|
7232
|
-
|
|
7233
|
-
|
|
7270
|
+
spinner16.stop("UI components ready");
|
|
7271
|
+
spinner16.start("Generating website code...");
|
|
7234
7272
|
const response = await anthropic.messages.create({
|
|
7235
7273
|
model: "claude-sonnet-4-20250514",
|
|
7236
7274
|
max_tokens: 16e3,
|
|
@@ -7295,12 +7333,12 @@ Make it production-quality and visually impressive.`
|
|
|
7295
7333
|
await fs16.writeFile(filePath, content);
|
|
7296
7334
|
fileCount++;
|
|
7297
7335
|
}
|
|
7298
|
-
|
|
7299
|
-
|
|
7336
|
+
spinner16.stop(`Generated ${fileCount} files`);
|
|
7337
|
+
spinner16.start("Initializing git...");
|
|
7300
7338
|
await execa11("git", ["init"], { cwd: projectPath, reject: false });
|
|
7301
7339
|
await execa11("git", ["add", "."], { cwd: projectPath, reject: false });
|
|
7302
7340
|
await execa11("git", ["commit", "-m", "Initial website build by CodeBakers"], { cwd: projectPath, reject: false });
|
|
7303
|
-
|
|
7341
|
+
spinner16.stop("Git initialized");
|
|
7304
7342
|
console.log(chalk20.green(`
|
|
7305
7343
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
7306
7344
|
\u2551 \u2705 Website built successfully! \u2551
|
|
@@ -7505,11 +7543,11 @@ async function clarifyCommand(parsed) {
|
|
|
7505
7543
|
return parsed;
|
|
7506
7544
|
}
|
|
7507
7545
|
if (parsed.confidence >= 0.5) {
|
|
7508
|
-
const
|
|
7546
|
+
const confirm13 = await p20.confirm({
|
|
7509
7547
|
message: `Did you mean: ${parsed.interpretation}?`,
|
|
7510
7548
|
initialValue: true
|
|
7511
7549
|
});
|
|
7512
|
-
if (
|
|
7550
|
+
if (confirm13 && !p20.isCancel(confirm13)) {
|
|
7513
7551
|
return { ...parsed, confidence: 1 };
|
|
7514
7552
|
}
|
|
7515
7553
|
}
|
|
@@ -7566,7 +7604,7 @@ async function clarifyDeployTarget() {
|
|
|
7566
7604
|
}
|
|
7567
7605
|
|
|
7568
7606
|
// src/index.ts
|
|
7569
|
-
var VERSION2 = "2.
|
|
7607
|
+
var VERSION2 = "2.2.1";
|
|
7570
7608
|
var logo = `
|
|
7571
7609
|
\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
|
|
7572
7610
|
\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
|
|
@@ -7707,27 +7745,34 @@ async function showMainMenu() {
|
|
|
7707
7745
|
}
|
|
7708
7746
|
function showHelp2() {
|
|
7709
7747
|
console.log(boxen(`
|
|
7710
|
-
${chalk21.bold("CodeBakers CLI")} \u2014 AI dev team that follows the rules
|
|
7711
|
-
|
|
7712
|
-
${chalk21.bold("
|
|
7713
|
-
${chalk21.cyan("codebakers")}
|
|
7714
|
-
${chalk21.cyan("codebakers init")} Create a new project
|
|
7715
|
-
${chalk21.cyan("codebakers
|
|
7716
|
-
${chalk21.cyan("codebakers
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
${chalk21.cyan("codebakers
|
|
7720
|
-
${chalk21.cyan("codebakers
|
|
7721
|
-
${chalk21.cyan("codebakers
|
|
7722
|
-
${chalk21.cyan("codebakers
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
${chalk21.
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
${chalk21.
|
|
7730
|
-
${chalk21.
|
|
7748
|
+
${chalk21.bold("CodeBakers CLI v" + VERSION2)} \u2014 AI dev team that follows the rules
|
|
7749
|
+
|
|
7750
|
+
${chalk21.bold.cyan("Getting Started:")}
|
|
7751
|
+
${chalk21.cyan("codebakers setup")} Connect your accounts (GitHub, Vercel, etc.)
|
|
7752
|
+
${chalk21.cyan("codebakers init")} Create a new project from scratch
|
|
7753
|
+
${chalk21.cyan("codebakers website")} Build a website by describing it
|
|
7754
|
+
${chalk21.cyan("codebakers build")} Build from PRD with parallel AI agents
|
|
7755
|
+
|
|
7756
|
+
${chalk21.bold.cyan("Daily Development:")}
|
|
7757
|
+
${chalk21.cyan("codebakers code")} Chat with AI to build features
|
|
7758
|
+
${chalk21.cyan("codebakers check")} Check code quality & patterns
|
|
7759
|
+
${chalk21.cyan("codebakers fix")} Auto-fix errors with AI
|
|
7760
|
+
${chalk21.cyan("codebakers deploy")} Deploy to Vercel
|
|
7761
|
+
|
|
7762
|
+
${chalk21.bold.cyan("Integrations:")}
|
|
7763
|
+
${chalk21.cyan("codebakers integrate")} 50+ one-click integrations (Stripe, Supabase, etc.)
|
|
7764
|
+
${chalk21.cyan("codebakers gateway")} Connect WhatsApp, Telegram, Discord, etc.
|
|
7765
|
+
|
|
7766
|
+
${chalk21.bold.cyan("Planning:")}
|
|
7767
|
+
${chalk21.cyan("codebakers prd-maker")} Create PRD through interview (voice supported)
|
|
7768
|
+
${chalk21.cyan("codebakers advisors")} Consult AI experts (CEO, CTO, Designer, etc.)
|
|
7769
|
+
|
|
7770
|
+
${chalk21.bold.cyan("Tips:")}
|
|
7771
|
+
\u2022 Type naturally: ${chalk21.dim('codebakers "add a contact form"')}
|
|
7772
|
+
\u2022 Voice input: Type ${chalk21.yellow('"v"')} at any prompt
|
|
7773
|
+
\u2022 Clipboard: Type ${chalk21.yellow('"clip"')} to paste from clipboard
|
|
7774
|
+
|
|
7775
|
+
${chalk21.bold("Docs:")} ${chalk21.dim("https://codebakers.dev/docs")}
|
|
7731
7776
|
`, { padding: 1, borderColor: "cyan", borderStyle: "round" }));
|
|
7732
7777
|
}
|
|
7733
7778
|
var program = new Command();
|