codebakers 2.3.0 → 2.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  import { Command } from "commander";
14
14
  import * as p21 from "@clack/prompts";
15
15
  import chalk21 from "chalk";
16
- import boxen from "boxen";
16
+ import boxen2 from "boxen";
17
17
  import gradient from "gradient-string";
18
18
  import * as path16 from "path";
19
19
 
@@ -43,19 +43,26 @@ async function checkForUpdates() {
43
43
  // src/commands/setup.ts
44
44
  import * as p from "@clack/prompts";
45
45
  import chalk2 from "chalk";
46
+ import boxen from "boxen";
46
47
  import open from "open";
47
48
  var SERVICES = {
48
49
  anthropic: {
49
50
  key: "anthropic",
50
51
  name: "Anthropic (Claude)",
51
52
  description: "Powers the AI coding agent",
52
- whyNeeded: "Without this, CodeBakers cannot generate any code. This is the brain of the system.",
53
+ whyNeeded: "This is REQUIRED. Without it, CodeBakers cannot generate any code.",
53
54
  url: "https://console.anthropic.com/settings/keys",
54
- urlDescription: "Create an API key",
55
+ steps: [
56
+ "Sign up or log in to Anthropic Console",
57
+ 'Click "Create Key"',
58
+ 'Give it a name like "CodeBakers"',
59
+ "Copy the key (starts with sk-ant-)"
60
+ ],
55
61
  fields: [{
56
62
  key: "apiKey",
57
- label: "Anthropic API Key",
63
+ label: "Paste your Anthropic API Key",
58
64
  placeholder: "sk-ant-...",
65
+ hint: "Starts with sk-ant-",
59
66
  validate: (v) => {
60
67
  if (!v) return "API key is required";
61
68
  if (!v.startsWith("sk-ant-")) return "Should start with sk-ant-";
@@ -67,13 +74,19 @@ var SERVICES = {
67
74
  key: "github",
68
75
  name: "GitHub",
69
76
  description: "Create repos and push code",
70
- whyNeeded: "Without this, CodeBakers cannot create repositories or save your code to GitHub.",
77
+ whyNeeded: "Needed to create repositories and save your code. Skip if you'll manage git manually.",
71
78
  url: "https://github.com/settings/tokens/new?scopes=repo,user&description=CodeBakers",
72
- urlDescription: 'Create a token with "repo" and "user" scopes checked',
79
+ steps: [
80
+ "Log in to GitHub",
81
+ 'The "repo" and "user" boxes should already be checked',
82
+ 'Scroll down and click "Generate token"',
83
+ "Copy the token (starts with ghp_)"
84
+ ],
73
85
  fields: [{
74
86
  key: "token",
75
- label: "GitHub Token",
76
- placeholder: "ghp_... or github_pat_...",
87
+ label: "Paste your GitHub Token",
88
+ placeholder: "ghp_...",
89
+ hint: "Starts with ghp_ or github_pat_",
77
90
  validate: (v) => {
78
91
  if (!v) return "Token is required";
79
92
  if (!v.startsWith("ghp_") && !v.startsWith("github_pat_")) {
@@ -87,38 +100,55 @@ var SERVICES = {
87
100
  key: "vercel",
88
101
  name: "Vercel",
89
102
  description: "Deploy your apps to production",
90
- whyNeeded: "Without this, CodeBakers cannot deploy your apps. You'll have to deploy manually.",
103
+ whyNeeded: "Needed to deploy apps automatically. Skip if you'll deploy manually.",
91
104
  url: "https://vercel.com/account/tokens",
92
- urlDescription: 'Click "Create" to generate a new token',
105
+ steps: [
106
+ "Log in to Vercel",
107
+ 'Click "Create" button',
108
+ 'Name it "CodeBakers"',
109
+ 'Click "Create Token"',
110
+ "Copy the token"
111
+ ],
93
112
  fields: [{
94
113
  key: "token",
95
- label: "Vercel Token",
96
- placeholder: "vercel_..."
114
+ label: "Paste your Vercel Token",
115
+ placeholder: "vercel_...",
116
+ hint: "The token you just created"
97
117
  }]
98
118
  },
99
119
  supabase: {
100
120
  key: "supabase",
101
121
  name: "Supabase",
102
122
  description: "Database and authentication",
103
- whyNeeded: "Without this, CodeBakers cannot create databases for your apps. You'll need to set up databases manually.",
123
+ whyNeeded: "Needed for database features. Skip if you'll set up databases manually.",
104
124
  url: "https://supabase.com/dashboard/account/tokens",
105
- urlDescription: "Generate a new access token",
125
+ steps: [
126
+ "Log in to Supabase",
127
+ 'Click "Generate new token"',
128
+ 'Name it "CodeBakers"',
129
+ "Copy the token"
130
+ ],
106
131
  fields: [{
107
132
  key: "accessToken",
108
- label: "Supabase Access Token",
109
- placeholder: "sbp_..."
133
+ label: "Paste your Supabase Token",
134
+ placeholder: "sbp_...",
135
+ hint: "Starts with sbp_"
110
136
  }]
111
137
  },
112
138
  openai: {
113
139
  key: "openai",
114
140
  name: "OpenAI",
115
141
  description: "GPT models, embeddings, DALL-E",
116
- whyNeeded: "Optional. Only needed if you want to use OpenAI models instead of or alongside Claude.",
142
+ whyNeeded: "Optional. Only if you want to use OpenAI alongside Claude.",
117
143
  url: "https://platform.openai.com/api-keys",
118
- urlDescription: "Create a new API key",
144
+ steps: [
145
+ "Log in to OpenAI",
146
+ 'Click "Create new secret key"',
147
+ "Copy the key"
148
+ ],
119
149
  fields: [{
120
150
  key: "apiKey",
121
- label: "OpenAI API Key",
151
+ label: "Paste your OpenAI API Key",
122
152
  placeholder: "sk-..."
123
153
  }]
124
154
  },
@@ -126,12 +156,15 @@ var SERVICES = {
126
156
  key: "stripe",
127
157
  name: "Stripe",
128
158
  description: "Payment processing",
129
- whyNeeded: "Optional. Only needed if your app accepts payments.",
159
+ whyNeeded: "Optional. Only if your app accepts payments.",
130
160
  url: "https://dashboard.stripe.com/apikeys",
131
- urlDescription: "Copy your Secret key (sk_live or sk_test)",
161
+ steps: [
162
+ "Log in to Stripe",
163
+ "Copy your Secret key (starts with sk_)"
164
+ ],
132
165
  fields: [{
133
166
  key: "secretKey",
134
- label: "Stripe Secret Key",
167
+ label: "Paste your Stripe Secret Key",
135
168
  placeholder: "sk_live_... or sk_test_..."
136
169
  }]
137
170
  },
@@ -139,9 +172,13 @@ var SERVICES = {
139
172
  key: "twilio",
140
173
  name: "Twilio",
141
174
  description: "SMS, voice calls, WhatsApp",
142
- whyNeeded: "Optional. Only needed if your app sends SMS or makes calls.",
175
+ whyNeeded: "Optional. Only if your app sends SMS or makes calls.",
143
176
  url: "https://console.twilio.com/",
144
- urlDescription: "Find Account SID and Auth Token on the dashboard",
177
+ steps: [
178
+ "Log in to Twilio",
179
+ "Find Account SID on the dashboard",
180
+ "Find Auth Token on the dashboard"
181
+ ],
145
182
  fields: [
146
183
  {
147
184
  key: "accountSid",
@@ -159,12 +196,16 @@ var SERVICES = {
159
196
  key: "resend",
160
197
  name: "Resend",
161
198
  description: "Email sending",
162
- whyNeeded: "Optional. Only needed if your app sends emails.",
199
+ whyNeeded: "Optional. Only if your app sends emails.",
163
200
  url: "https://resend.com/api-keys",
164
- urlDescription: "Create an API key",
201
+ steps: [
202
+ "Log in to Resend",
203
+ 'Click "Create API Key"',
204
+ "Copy the key"
205
+ ],
165
206
  fields: [{
166
207
  key: "apiKey",
167
- label: "Resend API Key",
208
+ label: "Paste your Resend API Key",
168
209
  placeholder: "re_..."
169
210
  }]
170
211
  },
@@ -172,12 +213,16 @@ var SERVICES = {
172
213
  key: "vapi",
173
214
  name: "VAPI",
174
215
  description: "Voice AI agents",
175
- whyNeeded: "Optional. Only needed if you're building voice agents.",
216
+ whyNeeded: "Optional. Only if you're building voice agents.",
176
217
  url: "https://dashboard.vapi.ai/",
177
- urlDescription: "Copy your API key from the dashboard",
218
+ steps: [
219
+ "Log in to VAPI",
220
+ "Go to Settings > API Keys",
221
+ "Copy your API key"
222
+ ],
178
223
  fields: [{
179
224
  key: "apiKey",
180
- label: "VAPI API Key",
225
+ label: "Paste your VAPI API Key",
181
226
  placeholder: "..."
182
227
  }]
183
228
  },
@@ -185,12 +230,16 @@ var SERVICES = {
185
230
  key: "elevenlabs",
186
231
  name: "ElevenLabs",
187
232
  description: "AI voice generation",
188
- whyNeeded: "Optional. Only needed if you want AI-generated voices.",
233
+ whyNeeded: "Optional. Only if you want AI-generated voices.",
189
234
  url: "https://elevenlabs.io/app/settings/api-keys",
190
- urlDescription: "Create an API key",
235
+ steps: [
236
+ "Log in to ElevenLabs",
237
+ 'Click "Create API Key"',
238
+ "Copy the key"
239
+ ],
191
240
  fields: [{
192
241
  key: "apiKey",
193
- label: "ElevenLabs API Key",
242
+ label: "Paste your ElevenLabs API Key",
194
243
  placeholder: "..."
195
244
  }]
196
245
  }
@@ -199,171 +248,210 @@ var CORE_SERVICES = ["anthropic", "github", "vercel", "supabase"];
199
248
  var OPTIONAL_SERVICES = ["openai", "stripe", "twilio", "resend", "vapi", "elevenlabs"];
200
249
  async function setupCommand() {
201
250
  const config = new Config();
202
- p.intro(chalk2.bgCyan.black(" CodeBakers Setup "));
203
- if (config.isConfigured()) {
204
- const action = await p.select({
205
- message: "CodeBakers is already configured. What do you want to do?",
206
- options: [
207
- { value: "view", label: "\u{1F440} View connected services" },
208
- { value: "add", label: "\u2795 Add another service" },
209
- { value: "update", label: "\u{1F504} Update a service" },
210
- { value: "reset", label: "\u{1F5D1}\uFE0F Reset all configuration" },
211
- { value: "back", label: "\u2190 Back" }
212
- ]
251
+ const hasAnthropic = !!config.getCredentials("anthropic")?.apiKey;
252
+ if (hasAnthropic) {
253
+ await showManagementMenu(config);
254
+ return;
255
+ }
256
+ console.log(boxen(
257
+ chalk2.bold.cyan("Welcome to CodeBakers Setup!\n\n") + chalk2.white("We need to connect a few services to get started.\n\n") + chalk2.dim("Your credentials are stored locally on your computer\n") + chalk2.dim("in ~/.codebakers/ and never sent to our servers.\n\n") + chalk2.yellow("Required: ") + chalk2.white("Anthropic API key (for AI)\n") + chalk2.dim("Optional: GitHub, Vercel, Supabase (for full features)"),
258
+ { padding: 1, borderColor: "cyan", borderStyle: "round" }
259
+ ));
260
+ console.log(chalk2.bold.cyan("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
261
+ console.log(chalk2.bold.cyan(" STEP 1 of 4: Anthropic API Key (Required)"));
262
+ console.log(chalk2.bold.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
263
+ console.log(chalk2.white(" Anthropic (Claude)"));
264
+ console.log(chalk2.dim(" Powers the AI coding agent\n"));
265
+ const hasAccount = await p.select({
266
+ message: "Do you have an Anthropic account?",
267
+ options: [
268
+ { value: "yes", label: "\u2713 Yes, I have an API key ready" },
269
+ { value: "no", label: "\u2717 No, I need to sign up (free)" }
270
+ ]
271
+ });
272
+ if (p.isCancel(hasAccount)) {
273
+ return;
274
+ }
275
+ if (hasAccount === "no") {
276
+ console.log(chalk2.yellow("\n No problem! Let's get you signed up.\n"));
277
+ console.log(chalk2.white(" Steps:"));
278
+ console.log(chalk2.cyan(" 1. ") + 'Click "Sign Up" on the page that opens');
279
+ console.log(chalk2.cyan(" 2. ") + "Create your account (email + password)");
280
+ console.log(chalk2.cyan(" 3. ") + "Once logged in, go to Settings \u2192 API Keys");
281
+ console.log(chalk2.cyan(" 4. ") + 'Click "Create Key"');
282
+ console.log(chalk2.cyan(" 5. ") + "Copy the key and paste it here\n");
283
+ const openSignup = await p.confirm({
284
+ message: "Open Anthropic signup page?",
285
+ initialValue: true
213
286
  });
214
- if (p.isCancel(action) || action === "back") {
215
- return;
287
+ if (openSignup && !p.isCancel(openSignup)) {
288
+ await open("https://console.anthropic.com/");
289
+ console.log(chalk2.dim("\n Take your time - come back when you have your API key.\n"));
216
290
  }
217
- switch (action) {
218
- case "view":
219
- showConnectedServices(config);
220
- return;
221
- case "add":
222
- await addService(config);
223
- return;
224
- case "update":
225
- await updateService(config);
226
- return;
227
- case "reset":
228
- await resetConfig(config);
229
- return;
291
+ } else {
292
+ console.log(chalk2.bold.white("\n Here's what to do:\n"));
293
+ console.log(chalk2.cyan(" 1. ") + "Log in to Anthropic Console");
294
+ console.log(chalk2.cyan(" 2. ") + "Go to Settings \u2192 API Keys");
295
+ console.log(chalk2.cyan(" 3. ") + 'Click "Create Key"');
296
+ console.log(chalk2.cyan(" 4. ") + "Copy the key (starts with sk-ant-)");
297
+ console.log("");
298
+ const openBrowser = await p.confirm({
299
+ message: "Open Anthropic Console?",
300
+ initialValue: true
301
+ });
302
+ if (openBrowser && !p.isCancel(openBrowser)) {
303
+ await open("https://console.anthropic.com/settings/keys");
230
304
  }
231
305
  }
232
- console.log(chalk2.cyan(`
233
- Welcome to CodeBakers! Let's connect your services.
234
-
235
- ${chalk2.dim("Your credentials are stored locally in ~/.codebakers/")}
236
- ${chalk2.dim("and are never sent to our servers.")}
237
-
238
- ${chalk2.bold("Core Services (Recommended):")}
239
- ${chalk2.dim("These power the main CodeBakers features.")}
240
-
241
- `));
242
- let skippedCore = [];
243
- for (const serviceKey of CORE_SERVICES) {
244
- const service = SERVICES[serviceKey];
245
- const connected = await connectService(config, service);
246
- if (!connected) {
247
- skippedCore.push(service.name);
306
+ console.log("");
307
+ const apiKey = await p.text({
308
+ message: "Paste your Anthropic API Key:",
309
+ placeholder: "sk-ant-...",
310
+ validate: (v) => {
311
+ if (!v) return "API key is required";
312
+ if (!v.startsWith("sk-ant-")) return "Should start with sk-ant-";
313
+ return void 0;
248
314
  }
249
- }
250
- if (skippedCore.length > 0) {
251
- console.log(chalk2.yellow(`
252
- \u26A0\uFE0F You skipped: ${skippedCore.join(", ")}
253
-
254
- Some features won't work without these services.
255
- Run ${chalk2.bold("codebakers setup")} anytime to add them.
256
- `));
257
- }
258
- const addOptional = await p.confirm({
259
- message: "Add optional services? (Stripe, Twilio, Resend, etc.)",
260
- initialValue: false
261
315
  });
262
- if (addOptional && !p.isCancel(addOptional)) {
263
- const selected = await p.multiselect({
264
- message: "Select services to add:",
265
- options: OPTIONAL_SERVICES.map((key) => ({
266
- value: key,
267
- label: `${SERVICES[key].name} - ${SERVICES[key].description}`
268
- })),
269
- required: false
270
- });
271
- if (!p.isCancel(selected) && Array.isArray(selected)) {
272
- for (const serviceKey of selected) {
273
- await connectService(config, SERVICES[serviceKey]);
274
- }
275
- }
316
+ if (p.isCancel(apiKey) || !apiKey) {
317
+ console.log(chalk2.red("\n \u274C Anthropic API key is required to use CodeBakers."));
318
+ console.log(chalk2.dim(" Run `codebakers setup` when you're ready to try again.\n"));
319
+ return;
276
320
  }
277
- const connectedCount = [...CORE_SERVICES, ...OPTIONAL_SERVICES].filter(
278
- (key) => config.getCredentials(key)
279
- ).length;
280
- console.log(chalk2.green(`
281
- \u2713 ${connectedCount} services connected!
282
- `));
321
+ config.setCredentials("anthropic", { apiKey });
322
+ console.log(chalk2.green("\n \u2713 Anthropic connected successfully!\n"));
323
+ console.log(chalk2.bold.cyan("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
324
+ console.log(chalk2.bold.cyan(" STEP 2 of 4: GitHub Token (Recommended)"));
325
+ console.log(chalk2.bold.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
326
+ await connectServiceWithInstructions(config, SERVICES.github, false);
327
+ console.log(chalk2.bold.cyan("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
328
+ console.log(chalk2.bold.cyan(" STEP 3 of 4: Vercel Token (Recommended)"));
329
+ console.log(chalk2.bold.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
330
+ await connectServiceWithInstructions(config, SERVICES.vercel, false);
331
+ console.log(chalk2.bold.cyan("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
332
+ console.log(chalk2.bold.cyan(" STEP 4 of 4: Supabase Token (Optional)"));
333
+ console.log(chalk2.bold.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
334
+ await connectServiceWithInstructions(config, SERVICES.supabase, false);
335
+ const connectedServices = CORE_SERVICES.filter((key) => config.getCredentials(key)).map((key) => SERVICES[key].name);
336
+ console.log(boxen(
337
+ chalk2.bold.green("\u2713 Setup Complete!\n\n") + chalk2.white("Connected: ") + chalk2.cyan(connectedServices.join(", ")) + "\n\n" + chalk2.bold.white("What to do next:\n\n") + chalk2.cyan("1. ") + chalk2.white("Open a terminal and navigate to where you want to build:\n") + chalk2.dim(" cd C:\\dev\\my-project\n") + chalk2.dim(" (or any folder where you want to create your project)\n\n") + chalk2.cyan("2. ") + chalk2.white("Run CodeBakers:\n") + chalk2.dim(" codebakers\n\n") + chalk2.bold.white("Or try these commands directly:\n\n") + chalk2.dim(" codebakers website ") + chalk2.white("Build a website by describing it\n") + chalk2.dim(" codebakers init ") + chalk2.white("Create a new project from scratch\n") + chalk2.dim(" codebakers help ") + chalk2.white("See all available commands"),
338
+ { padding: 1, borderColor: "green", borderStyle: "round" }
339
+ ));
283
340
  }
284
- async function connectService(config, service) {
285
- console.log("");
286
- console.log(chalk2.bold(` ${service.name}`));
287
- console.log(chalk2.dim(` ${service.description}`));
288
- console.log("");
341
+ async function connectServiceWithInstructions(config, service, required) {
342
+ console.log(chalk2.white(` ${service.name}`));
343
+ console.log(chalk2.dim(` ${service.description}
344
+ `));
345
+ if (!required) {
346
+ console.log(chalk2.yellow(` ${service.whyNeeded}
347
+ `));
348
+ }
289
349
  const action = await p.select({
290
350
  message: `Connect ${service.name}?`,
291
- options: [
292
- { value: "connect", label: "\u2713 Yes, connect now" },
293
- { value: "skip", label: "\u2192 Skip for now" },
294
- { value: "why", label: "? Why do I need this?" }
351
+ options: required ? [
352
+ { value: "connect", label: "\u2713 Yes, let's connect it" }
353
+ ] : [
354
+ { value: "connect", label: "\u2713 Yes, connect it" },
355
+ { value: "skip", label: "\u2192 Skip for now" }
295
356
  ]
296
357
  });
297
- if (p.isCancel(action)) return false;
298
- if (action === "why") {
299
- console.log(chalk2.yellow(`
300
- ${service.whyNeeded}
358
+ if (p.isCancel(action) || action === "skip") {
359
+ if (!required) {
360
+ console.log(chalk2.dim(`
361
+ Skipped ${service.name}. You can add it later with: codebakers setup
301
362
  `));
302
- const proceed = await p.confirm({
303
- message: `Connect ${service.name}?`,
304
- initialValue: true
305
- });
306
- if (!proceed || p.isCancel(proceed)) {
307
- console.log(chalk2.dim(` Skipped ${service.name}`));
308
- return false;
309
363
  }
310
- } else if (action === "skip") {
311
- console.log(chalk2.dim(` Skipped ${service.name}`));
312
- console.log(chalk2.dim(` ${service.whyNeeded}`));
313
364
  return false;
314
365
  }
366
+ console.log(chalk2.bold.white("\n Here's what to do:\n"));
367
+ service.steps.forEach((step, i) => {
368
+ console.log(chalk2.cyan(` ${i + 1}. `) + chalk2.white(step));
369
+ });
370
+ console.log("");
315
371
  const openBrowser = await p.confirm({
316
- message: "Open browser to get credentials?",
372
+ message: "Open the website in your browser?",
317
373
  initialValue: true
318
374
  });
319
375
  if (openBrowser && !p.isCancel(openBrowser)) {
320
376
  console.log(chalk2.dim(`
321
- Opening: ${service.url}`));
322
- console.log(chalk2.dim(` ${service.urlDescription}
377
+ Opening: ${service.url}
323
378
  `));
324
379
  await open(service.url);
380
+ console.log(chalk2.dim(" Complete the steps above, then paste your key below.\n"));
325
381
  }
326
382
  const credentials = {};
327
383
  for (const field of service.fields) {
328
384
  const value = await p.text({
329
- message: `${field.label}:`,
385
+ message: field.label + (field.hint ? chalk2.dim(` (${field.hint})`) : "") + ":",
330
386
  placeholder: field.placeholder,
331
387
  validate: field.validate
332
388
  });
333
389
  if (p.isCancel(value)) return false;
334
390
  if (!value) {
335
- console.log(chalk2.dim(` Skipped ${service.name} (no ${field.label} provided)`));
391
+ if (required) {
392
+ console.log(chalk2.red(`
393
+ ${field.label} is required.
394
+ `));
395
+ return false;
396
+ }
397
+ console.log(chalk2.dim(`
398
+ Skipped ${service.name}.
399
+ `));
336
400
  return false;
337
401
  }
338
402
  credentials[field.key] = value;
339
403
  }
340
404
  config.setCredentials(service.key, credentials);
341
- console.log(chalk2.green(` \u2713 ${service.name} connected!`));
405
+ console.log(chalk2.green(`
406
+ \u2713 ${service.name} connected successfully!
407
+ `));
342
408
  return true;
343
409
  }
344
- function showConnectedServices(config) {
345
- console.log(chalk2.bold("\n Connected Services:\n"));
410
+ async function showManagementMenu(config) {
411
+ console.log(chalk2.bold.cyan("\n CodeBakers Settings\n"));
346
412
  const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
347
- let connectedCount = 0;
348
- for (const key of allServices) {
349
- const service = SERVICES[key];
350
- const creds = config.getCredentials(key);
351
- if (creds) {
352
- console.log(chalk2.green(` \u2713 ${service.name}`));
353
- connectedCount++;
354
- } else {
355
- console.log(chalk2.dim(` \u25CB ${service.name} (not connected)`));
356
- }
413
+ const connected = allServices.filter((key) => config.getCredentials(key));
414
+ const notConnected = allServices.filter((key) => !config.getCredentials(key));
415
+ console.log(chalk2.green(" Connected:"));
416
+ connected.forEach((key) => {
417
+ console.log(chalk2.green(` \u2713 ${SERVICES[key].name}`));
418
+ });
419
+ if (notConnected.length > 0) {
420
+ console.log(chalk2.dim("\n Not connected:"));
421
+ notConnected.forEach((key) => {
422
+ console.log(chalk2.dim(` \u25CB ${SERVICES[key].name}`));
423
+ });
424
+ }
425
+ console.log("");
426
+ const action = await p.select({
427
+ message: "What would you like to do?",
428
+ options: [
429
+ { value: "add", label: "\u2795 Add a service" },
430
+ { value: "update", label: "\u{1F504} Update a service" },
431
+ { value: "reset", label: "\u{1F5D1}\uFE0F Reset all settings" },
432
+ { value: "back", label: "\u2190 Back" }
433
+ ]
434
+ });
435
+ if (p.isCancel(action) || action === "back") {
436
+ return;
437
+ }
438
+ switch (action) {
439
+ case "add":
440
+ await addService(config);
441
+ break;
442
+ case "update":
443
+ await updateService(config);
444
+ break;
445
+ case "reset":
446
+ await resetConfig(config);
447
+ break;
357
448
  }
358
- console.log(chalk2.dim(`
359
- ${connectedCount}/${allServices.length} services connected
360
- `));
361
449
  }
362
450
  async function addService(config) {
363
451
  const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
364
452
  const unconnected = allServices.filter((key) => !config.getCredentials(key));
365
453
  if (unconnected.length === 0) {
366
- p.log.info("All services are already connected!");
454
+ console.log(chalk2.green("\n All services are already connected!\n"));
367
455
  return;
368
456
  }
369
457
  const selected = await p.select({
@@ -374,13 +462,13 @@ async function addService(config) {
374
462
  }))
375
463
  });
376
464
  if (p.isCancel(selected)) return;
377
- await connectService(config, SERVICES[selected]);
465
+ await connectServiceWithInstructions(config, SERVICES[selected], false);
378
466
  }
379
467
  async function updateService(config) {
380
468
  const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
381
469
  const connected = allServices.filter((key) => config.getCredentials(key));
382
470
  if (connected.length === 0) {
383
- p.log.info("No services connected yet. Run setup first.");
471
+ console.log(chalk2.yellow("\n No services connected yet.\n"));
384
472
  return;
385
473
  }
386
474
  const selected = await p.select({
@@ -391,19 +479,24 @@ async function updateService(config) {
391
479
  }))
392
480
  });
393
481
  if (p.isCancel(selected)) return;
394
- await connectService(config, SERVICES[selected]);
482
+ await connectServiceWithInstructions(config, SERVICES[selected], false);
395
483
  }
396
484
  async function resetConfig(config) {
485
+ console.log(chalk2.yellow("\n \u26A0\uFE0F This will remove ALL your saved API keys.\n"));
397
486
  const confirm13 = await p.confirm({
398
- message: "Are you sure? This will remove ALL credentials.",
487
+ message: "Are you sure?",
399
488
  initialValue: false
400
489
  });
401
- if (!confirm13 || p.isCancel(confirm13)) return;
490
+ if (!confirm13 || p.isCancel(confirm13)) {
491
+ console.log(chalk2.dim("\n Cancelled.\n"));
492
+ return;
493
+ }
402
494
  const allServices = [...CORE_SERVICES, ...OPTIONAL_SERVICES];
403
495
  for (const key of allServices) {
404
496
  config.setCredentials(key, null);
405
497
  }
406
- p.log.success("Configuration reset. Run `codebakers setup` to reconfigure.");
498
+ console.log(chalk2.green("\n \u2713 All settings reset.\n"));
499
+ console.log(chalk2.dim(" Run `codebakers setup` to configure again.\n"));
407
500
  }
408
501
 
409
502
  // src/commands/init.ts
@@ -7539,7 +7632,7 @@ If unclear between multiple commands, use the most likely one with lower confide
7539
7632
  }
7540
7633
 
7541
7634
  // src/index.ts
7542
- var VERSION2 = "2.3.0";
7635
+ var VERSION2 = "2.3.5";
7543
7636
  var logo = `
7544
7637
  \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
7545
7638
  \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
@@ -7555,7 +7648,7 @@ async function showMainMenu() {
7555
7648
  `));
7556
7649
  const hasAnthropic = !!config.getCredentials("anthropic")?.apiKey;
7557
7650
  if (!hasAnthropic) {
7558
- console.log(boxen(
7651
+ console.log(boxen2(
7559
7652
  chalk21.yellow("Welcome to CodeBakers!\n\n") + chalk21.white("Let's connect your Anthropic API key so the AI can work.\n") + chalk21.dim("(Takes about 1 minute)"),
7560
7653
  { padding: 1, borderColor: "yellow", borderStyle: "round" }
7561
7654
  ));
@@ -7581,55 +7674,85 @@ async function showMainMenu() {
7581
7674
  }
7582
7675
  }
7583
7676
  async function showStartMenu(config) {
7584
- const action = await p21.select({
7585
- message: "What would you like to do?",
7586
- options: [
7587
- { value: "website", label: "\u{1F310} Build a website", hint: "Describe it \u2192 AI builds it" },
7588
- { value: "new", label: "\u{1F195} Create new project", hint: "From scratch with options" },
7589
- { value: "prd-maker", label: "\u270F\uFE0F Create a PRD", hint: "Plan before building" },
7590
- { value: "build", label: "\u{1F3D7}\uFE0F Build from PRD", hint: "Have a PRD? Build it now" },
7591
- { value: "advisors", label: "\u{1F31F} Consult advisors", hint: "AI experts help you plan" },
7592
- { value: "divider1", label: chalk21.dim("\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") },
7593
- { value: "integrate", label: "\u{1F50C} Add integration", hint: "Stripe, Supabase, etc." },
7594
- { value: "settings", label: "\u2699\uFE0F Settings", hint: "API keys & preferences" },
7595
- { value: "help", label: "\u2753 Help" }
7596
- ]
7597
- });
7598
- if (p21.isCancel(action)) {
7599
- p21.cancel("Goodbye!");
7600
- process.exit(0);
7601
- }
7602
- if (action === "divider1") {
7603
- await showStartMenu(config);
7604
- return;
7677
+ console.log(chalk21.cyan("\n \u2139\uFE0F This folder doesn't have a project yet."));
7678
+ console.log(chalk21.dim(" Use the arrow keys to select an option, then press Enter.\n"));
7679
+ let keepRunning = true;
7680
+ while (keepRunning) {
7681
+ const action = await p21.select({
7682
+ message: "What would you like to do?",
7683
+ options: [
7684
+ { value: "website", label: "\u{1F310} Build a website", hint: "Describe it, AI builds it" },
7685
+ { value: "new", label: "\u{1F195} Create new project", hint: "Start with Next.js, React, etc." },
7686
+ { value: "prd-maker", label: "\u270F\uFE0F Plan my project", hint: "Create a detailed plan first" },
7687
+ { value: "build", label: "\u{1F3D7}\uFE0F Build from plan", hint: "I already have a PRD file" },
7688
+ { value: "advisors", label: "\u{1F31F} Get expert advice", hint: "AI consultants help you decide" },
7689
+ { value: "integrate", label: "\u{1F50C} Add a service", hint: "Stripe, Supabase, Auth, etc." },
7690
+ { value: "settings", label: "\u2699\uFE0F Settings", hint: "API keys & preferences" },
7691
+ { value: "help", label: "\u2753 Help", hint: "Learn how CodeBakers works" },
7692
+ { value: "exit", label: "\u{1F6AA} Return to terminal", hint: "Go back to command line" }
7693
+ ]
7694
+ });
7695
+ if (p21.isCancel(action) || action === "exit") {
7696
+ console.log("");
7697
+ console.log(chalk21.cyan(" \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"));
7698
+ console.log(chalk21.white(" You're back in the terminal."));
7699
+ console.log("");
7700
+ console.log(chalk21.dim(" To start CodeBakers again, type:"));
7701
+ console.log(chalk21.green(" codebakers"));
7702
+ console.log("");
7703
+ console.log(chalk21.dim(" Quick commands you can run directly:"));
7704
+ console.log(chalk21.dim(" codebakers website") + chalk21.gray(" - Build a website"));
7705
+ console.log(chalk21.dim(" codebakers code") + chalk21.gray(" - Code with AI"));
7706
+ console.log(chalk21.dim(" codebakers help") + chalk21.gray(" - See all commands"));
7707
+ console.log(chalk21.cyan(" \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"));
7708
+ console.log("");
7709
+ keepRunning = false;
7710
+ break;
7711
+ }
7712
+ await handleAction(action, config);
7713
+ console.log("");
7605
7714
  }
7606
- await handleAction(action, config);
7607
7715
  }
7608
7716
  async function showProjectMenu(config) {
7609
- const action = await p21.select({
7610
- message: "What would you like to do?",
7611
- options: [
7612
- { value: "code", label: "\u{1F4AC} Code with AI", hint: "Build features, fix bugs" },
7613
- { value: "deploy", label: "\u{1F680} Deploy", hint: "Push to production" },
7614
- { value: "check", label: "\u{1F50D} Check code", hint: "Quality & patterns" },
7615
- { value: "fix", label: "\u{1F527} Fix errors", hint: "Auto-fix with AI" },
7616
- { value: "integrate", label: "\u{1F50C} Add integration", hint: "Stripe, Supabase, etc." },
7617
- { value: "generate", label: "\u26A1 Generate", hint: "Components, pages" },
7618
- { value: "divider1", label: chalk21.dim("\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") },
7619
- { value: "new", label: "\u{1F195} Start new project", hint: "Create something else" },
7620
- { value: "settings", label: "\u2699\uFE0F Settings" },
7621
- { value: "help", label: "\u2753 Help" }
7622
- ]
7623
- });
7624
- if (p21.isCancel(action)) {
7625
- p21.cancel("Goodbye!");
7626
- process.exit(0);
7627
- }
7628
- if (action === "divider1") {
7629
- await showProjectMenu(config);
7630
- return;
7717
+ console.log(chalk21.cyan("\n \u2139\uFE0F I found an existing project in this folder."));
7718
+ console.log(chalk21.dim(" Use the arrow keys to select an option, then press Enter.\n"));
7719
+ let keepRunning = true;
7720
+ while (keepRunning) {
7721
+ const action = await p21.select({
7722
+ message: "What would you like to do with this project?",
7723
+ options: [
7724
+ { value: "code", label: "\u{1F4AC} Code with AI", hint: "Tell AI what to build or fix" },
7725
+ { value: "deploy", label: "\u{1F680} Deploy to production", hint: "Make your site live" },
7726
+ { value: "check", label: "\u{1F50D} Check my code", hint: "Find issues & improvements" },
7727
+ { value: "fix", label: "\u{1F527} Fix errors for me", hint: "AI repairs broken code" },
7728
+ { value: "integrate", label: "\u{1F50C} Add a service", hint: "Stripe, Supabase, Auth, etc." },
7729
+ { value: "generate", label: "\u26A1 Create new files", hint: "Components, pages, APIs" },
7730
+ { value: "new", label: "\u{1F195} Start fresh project", hint: "Begin something new" },
7731
+ { value: "settings", label: "\u2699\uFE0F Settings", hint: "API keys & preferences" },
7732
+ { value: "help", label: "\u2753 Help", hint: "Learn how CodeBakers works" },
7733
+ { value: "exit", label: "\u{1F6AA} Return to terminal", hint: "Go back to command line" }
7734
+ ]
7735
+ });
7736
+ if (p21.isCancel(action) || action === "exit") {
7737
+ console.log("");
7738
+ console.log(chalk21.cyan(" \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"));
7739
+ console.log(chalk21.white(" You're back in the terminal."));
7740
+ console.log("");
7741
+ console.log(chalk21.dim(" To start CodeBakers again, type:"));
7742
+ console.log(chalk21.green(" codebakers"));
7743
+ console.log("");
7744
+ console.log(chalk21.dim(" Quick commands you can run directly:"));
7745
+ console.log(chalk21.dim(" codebakers code") + chalk21.gray(" - Code with AI"));
7746
+ console.log(chalk21.dim(" codebakers deploy") + chalk21.gray(" - Deploy your project"));
7747
+ console.log(chalk21.dim(" codebakers help") + chalk21.gray(" - See all commands"));
7748
+ console.log(chalk21.cyan(" \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"));
7749
+ console.log("");
7750
+ keepRunning = false;
7751
+ break;
7752
+ }
7753
+ await handleAction(action, config);
7754
+ console.log("");
7631
7755
  }
7632
- await handleAction(action, config);
7633
7756
  }
7634
7757
  async function handleAction(action, config) {
7635
7758
  switch (action) {
@@ -7686,14 +7809,14 @@ async function handleAction(action, config) {
7686
7809
  }
7687
7810
  }
7688
7811
  function showPostSetupInstructions() {
7689
- console.log(boxen(
7812
+ console.log(boxen2(
7690
7813
  chalk21.green.bold("\u2713 Setup complete!\n\n") + chalk21.white("What's next?\n\n") + chalk21.cyan("1. ") + "Navigate to where you want to build:\n" + chalk21.dim(" cd C:\\dev\\my-project\n\n") + chalk21.cyan("2. ") + "Run CodeBakers:\n" + chalk21.dim(" codebakers\n\n") + chalk21.white("Or build a website right now:\n") + chalk21.dim(" codebakers website"),
7691
7814
  { padding: 1, borderColor: "green", borderStyle: "round" }
7692
7815
  ));
7693
7816
  }
7694
7817
  function showPostBuildInstructions(projectName, projectPath) {
7695
7818
  const displayPath = projectPath || projectName;
7696
- console.log(boxen(
7819
+ console.log(boxen2(
7697
7820
  chalk21.green.bold(`\u2713 ${projectName} created!
7698
7821
 
7699
7822
  `) + chalk21.white("Next steps:\n\n") + chalk21.cyan("1. ") + "Go to your project:\n" + chalk21.dim(` cd ${displayPath}
@@ -7703,7 +7826,7 @@ function showPostBuildInstructions(projectName, projectPath) {
7703
7826
  ));
7704
7827
  }
7705
7828
  function showHelp2() {
7706
- console.log(boxen(`
7829
+ console.log(boxen2(`
7707
7830
  ${chalk21.bold("CodeBakers CLI v" + VERSION2)} \u2014 AI dev team that follows the rules
7708
7831
 
7709
7832
  ${chalk21.bold.cyan("Getting Started:")}