@codebakers/cli 3.9.16 → 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.
@@ -835,6 +835,15 @@ class CodeBakersServer {
835
835
  items: { type: 'string' },
836
836
  description: 'Files that were created/modified for this feature',
837
837
  },
838
+ envVarsAdded: {
839
+ type: 'array',
840
+ items: { type: 'string' },
841
+ description: 'New environment variables added during implementation (e.g., ["PAYPAL_CLIENT_ID", "PAYPAL_SECRET"])',
842
+ },
843
+ schemaModified: {
844
+ type: 'boolean',
845
+ description: 'Set to true if database schema (db/schema.ts) was modified',
846
+ },
838
847
  },
839
848
  required: ['feature'],
840
849
  },
@@ -1475,6 +1484,27 @@ class CodeBakersServer {
1475
1484
  },
1476
1485
  },
1477
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
+ },
1478
1508
  // Engineering workflow tools
1479
1509
  ...engineering_tools_js_1.ENGINEERING_TOOLS,
1480
1510
  ],
@@ -1609,6 +1639,8 @@ class CodeBakersServer {
1609
1639
  return this.handleProjectDashboardUrl();
1610
1640
  case 'resume_session':
1611
1641
  return this.handleResumeSession(args);
1642
+ case 'setup_services':
1643
+ return this.handleSetupServices(args);
1612
1644
  // Engineering workflow tools
1613
1645
  case 'engineering_start':
1614
1646
  case 'engineering_scope':
@@ -3587,6 +3619,177 @@ Just describe what you want to build! I'll automatically:
3587
3619
  }],
3588
3620
  };
3589
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
+ }
3590
3793
  handleRunTests(args) {
3591
3794
  const { filter, watch = false } = args;
3592
3795
  const cwd = process.cwd();
@@ -3922,12 +4125,21 @@ Just describe what you want to build! I'll automatically:
3922
4125
  * Runs local checks (tests, TypeScript), then validates with server
3923
4126
  */
3924
4127
  async handleValidateComplete(args) {
3925
- const { feature, files = [] } = args;
4128
+ const { feature, files = [], envVarsAdded = [], schemaModified: schemaModifiedArg } = args;
3926
4129
  const cwd = process.cwd();
3927
4130
  let testsExist = false;
3928
4131
  let testsPass = false;
3929
4132
  let typescriptPass = false;
3930
4133
  const testsWritten = [];
4134
+ // v3.9.17: Auto-detect schema modifications if not explicitly provided
4135
+ let schemaModified = schemaModifiedArg;
4136
+ if (schemaModified === undefined) {
4137
+ // Check if schema file was in the modified files list
4138
+ schemaModified = files.some(f => f.includes('schema.ts') ||
4139
+ f.includes('schema/') ||
4140
+ f.includes('db/schema') ||
4141
+ f.includes('drizzle/'));
4142
+ }
3931
4143
  // v6.1: Code analysis for compliance scoring
3932
4144
  const codeAnalysis = {};
3933
4145
  // Step 1: Get session token (from memory or state file)
@@ -4105,6 +4317,9 @@ Just describe what you want to build! I'll automatically:
4105
4317
  testsPassed: testsPass,
4106
4318
  typescriptPassed: typescriptPass,
4107
4319
  codeAnalysis, // v6.1: Send code analysis for compliance scoring
4320
+ // v3.9.17: Environment and schema validation
4321
+ envVarsAdded: envVarsAdded.length > 0 ? envVarsAdded : undefined,
4322
+ schemaModified,
4108
4323
  }),
4109
4324
  });
4110
4325
  const result = await response.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.9.16",
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
@@ -917,6 +917,15 @@ class CodeBakersServer {
917
917
  items: { type: 'string' },
918
918
  description: 'Files that were created/modified for this feature',
919
919
  },
920
+ envVarsAdded: {
921
+ type: 'array',
922
+ items: { type: 'string' },
923
+ description: 'New environment variables added during implementation (e.g., ["PAYPAL_CLIENT_ID", "PAYPAL_SECRET"])',
924
+ },
925
+ schemaModified: {
926
+ type: 'boolean',
927
+ description: 'Set to true if database schema (db/schema.ts) was modified',
928
+ },
920
929
  },
921
930
  required: ['feature'],
922
931
  },
@@ -1589,6 +1598,28 @@ class CodeBakersServer {
1589
1598
  },
1590
1599
  },
1591
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
+ },
1592
1623
  // Engineering workflow tools
1593
1624
  ...ENGINEERING_TOOLS,
1594
1625
  ],
@@ -1686,7 +1717,7 @@ class CodeBakersServer {
1686
1717
  return this.handleGenerateTests(args as { file?: string; feature?: string; testType?: 'unit' | 'integration' | 'e2e' });
1687
1718
 
1688
1719
  case 'validate_complete':
1689
- return this.handleValidateComplete(args as { feature: string; files?: string[] });
1720
+ return this.handleValidateComplete(args as { feature: string; files?: string[]; envVarsAdded?: string[]; schemaModified?: boolean });
1690
1721
 
1691
1722
  case 'discover_patterns':
1692
1723
  return this.handleDiscoverPatterns(args as { task: string; files?: string[]; keywords?: string[] });
@@ -1840,6 +1871,9 @@ class CodeBakersServer {
1840
1871
  case 'resume_session':
1841
1872
  return this.handleResumeSession(args as { reason?: string });
1842
1873
 
1874
+ case 'setup_services':
1875
+ return this.handleSetupServices(args as { services?: string[]; checkOnly?: boolean });
1876
+
1843
1877
  // Engineering workflow tools
1844
1878
  case 'engineering_start':
1845
1879
  case 'engineering_scope':
@@ -4032,6 +4066,200 @@ Just describe what you want to build! I'll automatically:
4032
4066
  };
4033
4067
  }
4034
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
+
4035
4263
  private handleRunTests(args: { filter?: string; watch?: boolean }) {
4036
4264
  const { filter, watch = false } = args;
4037
4265
  const cwd = process.cwd();
@@ -4384,14 +4612,26 @@ Just describe what you want to build! I'll automatically:
4384
4612
  * MANDATORY: Validate that a feature is complete before AI can say "done" (v6.0 Server-Side)
4385
4613
  * Runs local checks (tests, TypeScript), then validates with server
4386
4614
  */
4387
- private async handleValidateComplete(args: { feature: string; files?: string[] }) {
4388
- const { feature, files = [] } = args;
4615
+ private async handleValidateComplete(args: { feature: string; files?: string[]; envVarsAdded?: string[]; schemaModified?: boolean }) {
4616
+ const { feature, files = [], envVarsAdded = [], schemaModified: schemaModifiedArg } = args;
4389
4617
  const cwd = process.cwd();
4390
4618
  let testsExist = false;
4391
4619
  let testsPass = false;
4392
4620
  let typescriptPass = false;
4393
4621
  const testsWritten: string[] = [];
4394
4622
 
4623
+ // v3.9.17: Auto-detect schema modifications if not explicitly provided
4624
+ let schemaModified = schemaModifiedArg;
4625
+ if (schemaModified === undefined) {
4626
+ // Check if schema file was in the modified files list
4627
+ schemaModified = files.some(f =>
4628
+ f.includes('schema.ts') ||
4629
+ f.includes('schema/') ||
4630
+ f.includes('db/schema') ||
4631
+ f.includes('drizzle/')
4632
+ );
4633
+ }
4634
+
4395
4635
  // v6.1: Code analysis for compliance scoring
4396
4636
  const codeAnalysis: {
4397
4637
  hasErrorHandling?: boolean;
@@ -4584,6 +4824,9 @@ Just describe what you want to build! I'll automatically:
4584
4824
  testsPassed: testsPass,
4585
4825
  typescriptPassed: typescriptPass,
4586
4826
  codeAnalysis, // v6.1: Send code analysis for compliance scoring
4827
+ // v3.9.17: Environment and schema validation
4828
+ envVarsAdded: envVarsAdded.length > 0 ? envVarsAdded : undefined,
4829
+ schemaModified,
4587
4830
  }),
4588
4831
  });
4589
4832