@codebakers/cli 3.9.17 → 3.9.18

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.
@@ -1484,6 +1484,27 @@ class CodeBakersServer {
1484
1484
  },
1485
1485
  },
1486
1486
  },
1487
+ {
1488
+ name: 'setup_services',
1489
+ description: 'Help users configure external services (Supabase, OpenAI, Anthropic). Use at project start or when env vars are missing. Explains WHY each service is needed, guides users to get their own API keys, and validates the keys work. Does NOT assume users need all services - asks one at a time based on what they want to build.',
1490
+ inputSchema: {
1491
+ type: 'object',
1492
+ properties: {
1493
+ services: {
1494
+ type: 'array',
1495
+ items: {
1496
+ type: 'string',
1497
+ enum: ['supabase', 'openai', 'anthropic', 'all'],
1498
+ },
1499
+ description: 'Which services to help configure. Use "all" to check all services, or specify individual ones.',
1500
+ },
1501
+ checkOnly: {
1502
+ type: 'boolean',
1503
+ description: 'If true, only checks which services are missing without prompting for setup. Useful for initial detection.',
1504
+ },
1505
+ },
1506
+ },
1507
+ },
1487
1508
  // Engineering workflow tools
1488
1509
  ...engineering_tools_js_1.ENGINEERING_TOOLS,
1489
1510
  ],
@@ -1618,6 +1639,8 @@ class CodeBakersServer {
1618
1639
  return this.handleProjectDashboardUrl();
1619
1640
  case 'resume_session':
1620
1641
  return this.handleResumeSession(args);
1642
+ case 'setup_services':
1643
+ return this.handleSetupServices(args);
1621
1644
  // Engineering workflow tools
1622
1645
  case 'engineering_start':
1623
1646
  case 'engineering_scope':
@@ -3596,6 +3619,177 @@ Just describe what you want to build! I'll automatically:
3596
3619
  }],
3597
3620
  };
3598
3621
  }
3622
+ handleSetupServices(args) {
3623
+ const { services = ['all'], checkOnly = false } = args;
3624
+ const cwd = process.cwd();
3625
+ // Define service explanations - WHY users need each service
3626
+ const SERVICE_INFO = {
3627
+ supabase: {
3628
+ name: 'Supabase',
3629
+ why: `**Why you might need Supabase:**
3630
+ - 📦 **Database** - Store your users, products, orders, etc.
3631
+ - 🔐 **Authentication** - Login, signup, OAuth (Google, GitHub, etc.)
3632
+ - ⚡ **Real-time** - Live updates without refreshing the page
3633
+ - 📁 **Storage** - File uploads (images, documents)
3634
+
3635
+ If your app needs to save ANY data or have user accounts, you need this.`,
3636
+ envVars: ['NEXT_PUBLIC_SUPABASE_URL', 'NEXT_PUBLIC_SUPABASE_ANON_KEY', 'SUPABASE_SERVICE_ROLE_KEY'],
3637
+ howToGet: `**How to get your Supabase keys:**
3638
+
3639
+ 1. Go to https://supabase.com and create a free account (or sign in)
3640
+ 2. Click "New project" and create a project
3641
+ 3. Wait ~2 minutes for the project to initialize
3642
+ 4. Go to **Project Settings** → **API** (in the left sidebar)
3643
+ 5. Copy these values:
3644
+
3645
+ - **Project URL** → use for \`NEXT_PUBLIC_SUPABASE_URL\`
3646
+ - **anon/public key** → use for \`NEXT_PUBLIC_SUPABASE_ANON_KEY\`
3647
+ - **service_role key** → use for \`SUPABASE_SERVICE_ROLE_KEY\` (keep this SECRET!)
3648
+
3649
+ ⚠️ The service_role key has FULL access - never expose it in client code.`,
3650
+ },
3651
+ openai: {
3652
+ name: 'OpenAI',
3653
+ why: `**Why you might need OpenAI:**
3654
+ - 🤖 **GPT Models** - Generate text, answer questions, chat
3655
+ - 🔍 **Embeddings** - Semantic search, finding similar content
3656
+ - 🎨 **DALL-E** - Generate images from text
3657
+
3658
+ If you want AI features like chatbots, content generation, or smart search, you need this.`,
3659
+ envVars: ['OPENAI_API_KEY'],
3660
+ howToGet: `**How to get your OpenAI API key:**
3661
+
3662
+ 1. Go to https://platform.openai.com and sign up (or sign in)
3663
+ 2. Click your profile icon → "View API keys"
3664
+ 3. Click "Create new secret key"
3665
+ 4. Give it a name (e.g., "My App")
3666
+ 5. Copy the key immediately (you won't see it again!)
3667
+
3668
+ - Use this for \`OPENAI_API_KEY\`
3669
+
3670
+ 💰 **Pricing Note:** OpenAI charges per token (roughly per word).
3671
+ - GPT-4: ~$0.03/1K input, ~$0.06/1K output
3672
+ - GPT-3.5: ~$0.0005/1K input, ~$0.0015/1K output
3673
+
3674
+ Start with GPT-3.5-turbo for development to save costs.`,
3675
+ },
3676
+ anthropic: {
3677
+ name: 'Anthropic (Claude)',
3678
+ why: `**Why you might need Anthropic:**
3679
+ - 🧠 **Claude Models** - Often better at following complex instructions
3680
+ - 💻 **Coding Tasks** - Claude excels at code generation and review
3681
+ - 📝 **Long Documents** - Handles very long context windows
3682
+
3683
+ If you want AI features and prefer Claude over GPT (or want both as fallback).`,
3684
+ envVars: ['ANTHROPIC_API_KEY'],
3685
+ howToGet: `**How to get your Anthropic API key:**
3686
+
3687
+ 1. Go to https://console.anthropic.com and sign up (or sign in)
3688
+ 2. Go to "API Keys" in the left sidebar
3689
+ 3. Click "Create Key"
3690
+ 4. Give it a name and copy the key
3691
+
3692
+ - Use this for \`ANTHROPIC_API_KEY\`
3693
+
3694
+ 💰 **Pricing Note:** Anthropic charges per token.
3695
+ - Claude 3 Opus: ~$15/M input, ~$75/M output (most capable)
3696
+ - Claude 3 Sonnet: ~$3/M input, ~$15/M output (balanced)
3697
+ - Claude 3 Haiku: ~$0.25/M input, ~$1.25/M output (fastest/cheapest)`,
3698
+ },
3699
+ };
3700
+ // Check which services to process
3701
+ const servicesToCheck = services.includes('all')
3702
+ ? ['supabase', 'openai', 'anthropic']
3703
+ : services.filter(s => s !== 'all');
3704
+ // Check .env file for existing vars
3705
+ const envPath = path.join(cwd, '.env');
3706
+ const envLocalPath = path.join(cwd, '.env.local');
3707
+ let envContent = '';
3708
+ if (fs.existsSync(envLocalPath)) {
3709
+ envContent = fs.readFileSync(envLocalPath, 'utf-8');
3710
+ }
3711
+ else if (fs.existsSync(envPath)) {
3712
+ envContent = fs.readFileSync(envPath, 'utf-8');
3713
+ }
3714
+ // Parse existing env vars
3715
+ const existingVars = new Set();
3716
+ for (const line of envContent.split('\n')) {
3717
+ const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
3718
+ if (match) {
3719
+ existingVars.add(match[1]);
3720
+ }
3721
+ }
3722
+ // Check each service
3723
+ const results = [];
3724
+ for (const serviceKey of servicesToCheck) {
3725
+ const info = SERVICE_INFO[serviceKey];
3726
+ if (!info)
3727
+ continue;
3728
+ const missingVars = info.envVars.filter(v => !existingVars.has(v));
3729
+ results.push({
3730
+ service: serviceKey,
3731
+ configured: missingVars.length === 0,
3732
+ missingVars,
3733
+ info,
3734
+ });
3735
+ }
3736
+ // Build response
3737
+ let response = `# 🔧 Service Configuration Check\n\n`;
3738
+ const configured = results.filter(r => r.configured);
3739
+ const missing = results.filter(r => !r.configured);
3740
+ if (configured.length > 0) {
3741
+ response += `## ✅ Already Configured\n`;
3742
+ for (const r of configured) {
3743
+ response += `- **${r.info.name}** - All env vars present\n`;
3744
+ }
3745
+ response += `\n`;
3746
+ }
3747
+ if (missing.length === 0) {
3748
+ response += `All requested services are configured! 🎉\n\n`;
3749
+ response += `If you need to reconfigure any service, add the specific service name (e.g., \`setup_services({ services: ['supabase'] })\`).\n`;
3750
+ }
3751
+ else {
3752
+ response += `## ⚠️ Missing Configuration\n\n`;
3753
+ for (const r of missing) {
3754
+ response += `### ${r.info.name}\n\n`;
3755
+ response += `**Missing:** \`${r.missingVars.join('`, `')}\`\n\n`;
3756
+ if (checkOnly) {
3757
+ // Just show what's missing
3758
+ response += `---\n\n`;
3759
+ }
3760
+ else {
3761
+ // Show full explanation
3762
+ response += `${r.info.why}\n\n`;
3763
+ response += `${r.info.howToGet}\n\n`;
3764
+ response += `---\n\n`;
3765
+ }
3766
+ }
3767
+ if (checkOnly) {
3768
+ response += `\nRun \`setup_services({ checkOnly: false })\` to get setup instructions for each service.\n`;
3769
+ }
3770
+ else {
3771
+ response += `## Next Steps\n\n`;
3772
+ response += `1. Decide which services you actually need for your project\n`;
3773
+ response += `2. Create accounts and get API keys for those services\n`;
3774
+ response += `3. Add the keys to your \`.env.local\` file:\n\n`;
3775
+ response += `\`\`\`bash\n`;
3776
+ for (const r of missing) {
3777
+ for (const v of r.missingVars) {
3778
+ response += `${v}=your_key_here\n`;
3779
+ }
3780
+ }
3781
+ response += `\`\`\`\n\n`;
3782
+ response += `4. Restart your dev server after adding keys\n\n`;
3783
+ response += `**Don't need a service?** That's fine! Only configure what you'll actually use.\n`;
3784
+ }
3785
+ }
3786
+ return {
3787
+ content: [{
3788
+ type: 'text',
3789
+ text: response,
3790
+ }],
3791
+ };
3792
+ }
3599
3793
  handleRunTests(args) {
3600
3794
  const { filter, watch = false } = args;
3601
3795
  const cwd = process.cwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.9.17",
3
+ "version": "3.9.18",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/mcp/server.ts CHANGED
@@ -1598,6 +1598,28 @@ class CodeBakersServer {
1598
1598
  },
1599
1599
  },
1600
1600
  },
1601
+ {
1602
+ name: 'setup_services',
1603
+ description:
1604
+ 'Help users configure external services (Supabase, OpenAI, Anthropic). Use at project start or when env vars are missing. Explains WHY each service is needed, guides users to get their own API keys, and validates the keys work. Does NOT assume users need all services - asks one at a time based on what they want to build.',
1605
+ inputSchema: {
1606
+ type: 'object' as const,
1607
+ properties: {
1608
+ services: {
1609
+ type: 'array',
1610
+ items: {
1611
+ type: 'string',
1612
+ enum: ['supabase', 'openai', 'anthropic', 'all'],
1613
+ },
1614
+ description: 'Which services to help configure. Use "all" to check all services, or specify individual ones.',
1615
+ },
1616
+ checkOnly: {
1617
+ type: 'boolean',
1618
+ description: 'If true, only checks which services are missing without prompting for setup. Useful for initial detection.',
1619
+ },
1620
+ },
1621
+ },
1622
+ },
1601
1623
  // Engineering workflow tools
1602
1624
  ...ENGINEERING_TOOLS,
1603
1625
  ],
@@ -1849,6 +1871,9 @@ class CodeBakersServer {
1849
1871
  case 'resume_session':
1850
1872
  return this.handleResumeSession(args as { reason?: string });
1851
1873
 
1874
+ case 'setup_services':
1875
+ return this.handleSetupServices(args as { services?: string[]; checkOnly?: boolean });
1876
+
1852
1877
  // Engineering workflow tools
1853
1878
  case 'engineering_start':
1854
1879
  case 'engineering_scope':
@@ -4041,6 +4066,200 @@ Just describe what you want to build! I'll automatically:
4041
4066
  };
4042
4067
  }
4043
4068
 
4069
+ private handleSetupServices(args: { services?: string[]; checkOnly?: boolean }) {
4070
+ const { services = ['all'], checkOnly = false } = args;
4071
+ const cwd = process.cwd();
4072
+
4073
+ // Define service explanations - WHY users need each service
4074
+ const SERVICE_INFO: Record<string, {
4075
+ name: string;
4076
+ why: string;
4077
+ envVars: string[];
4078
+ howToGet: string;
4079
+ validateUrl?: string;
4080
+ }> = {
4081
+ supabase: {
4082
+ name: 'Supabase',
4083
+ why: `**Why you might need Supabase:**
4084
+ - 📦 **Database** - Store your users, products, orders, etc.
4085
+ - 🔐 **Authentication** - Login, signup, OAuth (Google, GitHub, etc.)
4086
+ - ⚡ **Real-time** - Live updates without refreshing the page
4087
+ - 📁 **Storage** - File uploads (images, documents)
4088
+
4089
+ If your app needs to save ANY data or have user accounts, you need this.`,
4090
+ envVars: ['NEXT_PUBLIC_SUPABASE_URL', 'NEXT_PUBLIC_SUPABASE_ANON_KEY', 'SUPABASE_SERVICE_ROLE_KEY'],
4091
+ howToGet: `**How to get your Supabase keys:**
4092
+
4093
+ 1. Go to https://supabase.com and create a free account (or sign in)
4094
+ 2. Click "New project" and create a project
4095
+ 3. Wait ~2 minutes for the project to initialize
4096
+ 4. Go to **Project Settings** → **API** (in the left sidebar)
4097
+ 5. Copy these values:
4098
+
4099
+ - **Project URL** → use for \`NEXT_PUBLIC_SUPABASE_URL\`
4100
+ - **anon/public key** → use for \`NEXT_PUBLIC_SUPABASE_ANON_KEY\`
4101
+ - **service_role key** → use for \`SUPABASE_SERVICE_ROLE_KEY\` (keep this SECRET!)
4102
+
4103
+ ⚠️ The service_role key has FULL access - never expose it in client code.`,
4104
+ },
4105
+ openai: {
4106
+ name: 'OpenAI',
4107
+ why: `**Why you might need OpenAI:**
4108
+ - 🤖 **GPT Models** - Generate text, answer questions, chat
4109
+ - 🔍 **Embeddings** - Semantic search, finding similar content
4110
+ - 🎨 **DALL-E** - Generate images from text
4111
+
4112
+ If you want AI features like chatbots, content generation, or smart search, you need this.`,
4113
+ envVars: ['OPENAI_API_KEY'],
4114
+ howToGet: `**How to get your OpenAI API key:**
4115
+
4116
+ 1. Go to https://platform.openai.com and sign up (or sign in)
4117
+ 2. Click your profile icon → "View API keys"
4118
+ 3. Click "Create new secret key"
4119
+ 4. Give it a name (e.g., "My App")
4120
+ 5. Copy the key immediately (you won't see it again!)
4121
+
4122
+ - Use this for \`OPENAI_API_KEY\`
4123
+
4124
+ 💰 **Pricing Note:** OpenAI charges per token (roughly per word).
4125
+ - GPT-4: ~$0.03/1K input, ~$0.06/1K output
4126
+ - GPT-3.5: ~$0.0005/1K input, ~$0.0015/1K output
4127
+
4128
+ Start with GPT-3.5-turbo for development to save costs.`,
4129
+ },
4130
+ anthropic: {
4131
+ name: 'Anthropic (Claude)',
4132
+ why: `**Why you might need Anthropic:**
4133
+ - 🧠 **Claude Models** - Often better at following complex instructions
4134
+ - 💻 **Coding Tasks** - Claude excels at code generation and review
4135
+ - 📝 **Long Documents** - Handles very long context windows
4136
+
4137
+ If you want AI features and prefer Claude over GPT (or want both as fallback).`,
4138
+ envVars: ['ANTHROPIC_API_KEY'],
4139
+ howToGet: `**How to get your Anthropic API key:**
4140
+
4141
+ 1. Go to https://console.anthropic.com and sign up (or sign in)
4142
+ 2. Go to "API Keys" in the left sidebar
4143
+ 3. Click "Create Key"
4144
+ 4. Give it a name and copy the key
4145
+
4146
+ - Use this for \`ANTHROPIC_API_KEY\`
4147
+
4148
+ 💰 **Pricing Note:** Anthropic charges per token.
4149
+ - Claude 3 Opus: ~$15/M input, ~$75/M output (most capable)
4150
+ - Claude 3 Sonnet: ~$3/M input, ~$15/M output (balanced)
4151
+ - Claude 3 Haiku: ~$0.25/M input, ~$1.25/M output (fastest/cheapest)`,
4152
+ },
4153
+ };
4154
+
4155
+ // Check which services to process
4156
+ const servicesToCheck = services.includes('all')
4157
+ ? ['supabase', 'openai', 'anthropic']
4158
+ : services.filter(s => s !== 'all');
4159
+
4160
+ // Check .env file for existing vars
4161
+ const envPath = path.join(cwd, '.env');
4162
+ const envLocalPath = path.join(cwd, '.env.local');
4163
+ let envContent = '';
4164
+
4165
+ if (fs.existsSync(envLocalPath)) {
4166
+ envContent = fs.readFileSync(envLocalPath, 'utf-8');
4167
+ } else if (fs.existsSync(envPath)) {
4168
+ envContent = fs.readFileSync(envPath, 'utf-8');
4169
+ }
4170
+
4171
+ // Parse existing env vars
4172
+ const existingVars = new Set<string>();
4173
+ for (const line of envContent.split('\n')) {
4174
+ const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
4175
+ if (match) {
4176
+ existingVars.add(match[1]);
4177
+ }
4178
+ }
4179
+
4180
+ // Check each service
4181
+ const results: Array<{
4182
+ service: string;
4183
+ configured: boolean;
4184
+ missingVars: string[];
4185
+ info: typeof SERVICE_INFO[string];
4186
+ }> = [];
4187
+
4188
+ for (const serviceKey of servicesToCheck) {
4189
+ const info = SERVICE_INFO[serviceKey];
4190
+ if (!info) continue;
4191
+
4192
+ const missingVars = info.envVars.filter(v => !existingVars.has(v));
4193
+ results.push({
4194
+ service: serviceKey,
4195
+ configured: missingVars.length === 0,
4196
+ missingVars,
4197
+ info,
4198
+ });
4199
+ }
4200
+
4201
+ // Build response
4202
+ let response = `# 🔧 Service Configuration Check\n\n`;
4203
+
4204
+ const configured = results.filter(r => r.configured);
4205
+ const missing = results.filter(r => !r.configured);
4206
+
4207
+ if (configured.length > 0) {
4208
+ response += `## ✅ Already Configured\n`;
4209
+ for (const r of configured) {
4210
+ response += `- **${r.info.name}** - All env vars present\n`;
4211
+ }
4212
+ response += `\n`;
4213
+ }
4214
+
4215
+ if (missing.length === 0) {
4216
+ response += `All requested services are configured! 🎉\n\n`;
4217
+ response += `If you need to reconfigure any service, add the specific service name (e.g., \`setup_services({ services: ['supabase'] })\`).\n`;
4218
+ } else {
4219
+ response += `## ⚠️ Missing Configuration\n\n`;
4220
+
4221
+ for (const r of missing) {
4222
+ response += `### ${r.info.name}\n\n`;
4223
+ response += `**Missing:** \`${r.missingVars.join('`, `')}\`\n\n`;
4224
+
4225
+ if (checkOnly) {
4226
+ // Just show what's missing
4227
+ response += `---\n\n`;
4228
+ } else {
4229
+ // Show full explanation
4230
+ response += `${r.info.why}\n\n`;
4231
+ response += `${r.info.howToGet}\n\n`;
4232
+ response += `---\n\n`;
4233
+ }
4234
+ }
4235
+
4236
+ if (checkOnly) {
4237
+ response += `\nRun \`setup_services({ checkOnly: false })\` to get setup instructions for each service.\n`;
4238
+ } else {
4239
+ response += `## Next Steps\n\n`;
4240
+ response += `1. Decide which services you actually need for your project\n`;
4241
+ response += `2. Create accounts and get API keys for those services\n`;
4242
+ response += `3. Add the keys to your \`.env.local\` file:\n\n`;
4243
+ response += `\`\`\`bash\n`;
4244
+ for (const r of missing) {
4245
+ for (const v of r.missingVars) {
4246
+ response += `${v}=your_key_here\n`;
4247
+ }
4248
+ }
4249
+ response += `\`\`\`\n\n`;
4250
+ response += `4. Restart your dev server after adding keys\n\n`;
4251
+ response += `**Don't need a service?** That's fine! Only configure what you'll actually use.\n`;
4252
+ }
4253
+ }
4254
+
4255
+ return {
4256
+ content: [{
4257
+ type: 'text' as const,
4258
+ text: response,
4259
+ }],
4260
+ };
4261
+ }
4262
+
4044
4263
  private handleRunTests(args: { filter?: string; watch?: boolean }) {
4045
4264
  const { filter, watch = false } = args;
4046
4265
  const cwd = process.cwd();