@codebakers/cli 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64,7 +64,7 @@ const program = new commander_1.Command();
64
64
  program
65
65
  .name('codebakers')
66
66
  .description('CodeBakers CLI - Production patterns for AI-assisted development')
67
- .version('2.8.0');
67
+ .version('2.9.0');
68
68
  // Primary command - one-time setup
69
69
  program
70
70
  .command('setup')
@@ -509,7 +509,7 @@ class CodeBakersServer {
509
509
  },
510
510
  {
511
511
  name: 'scaffold_project',
512
- description: 'Create a new project from scratch with Next.js + Supabase + Drizzle. Use this when user wants to build something new and no project exists yet. Creates all files, installs dependencies, and sets up CodeBakers patterns automatically.',
512
+ description: 'Create a new project from scratch with Next.js + Supabase + Drizzle. Use this when user wants to build something new and no project exists yet. Creates all files, installs dependencies, and sets up CodeBakers patterns automatically. Set fullDeploy=true for seamless idea-to-deployment (creates GitHub repo, Supabase project, and deploys to Vercel). When fullDeploy=true, first call returns explanation - then call again with deployConfirmed=true after user confirms.',
513
513
  inputSchema: {
514
514
  type: 'object',
515
515
  properties: {
@@ -521,6 +521,14 @@ class CodeBakersServer {
521
521
  type: 'string',
522
522
  description: 'Brief description of what the project is for (used in PRD.md)',
523
523
  },
524
+ fullDeploy: {
525
+ type: 'boolean',
526
+ description: 'If true, enables full deployment flow (GitHub + Supabase + Vercel). First call returns explanation for user confirmation.',
527
+ },
528
+ deployConfirmed: {
529
+ type: 'boolean',
530
+ description: 'Set to true AFTER user confirms they want full deployment. Only set this after showing user the explanation and getting their approval.',
531
+ },
524
532
  },
525
533
  required: ['projectName'],
526
534
  },
@@ -1204,8 +1212,12 @@ Show the user what their simple request was expanded into, then proceed with the
1204
1212
  };
1205
1213
  }
1206
1214
  async handleScaffoldProject(args) {
1207
- const { projectName, description } = args;
1215
+ const { projectName, description, fullDeploy, deployConfirmed } = args;
1208
1216
  const cwd = process.cwd();
1217
+ // If fullDeploy requested but not confirmed, show explanation and ask for confirmation
1218
+ if (fullDeploy && !deployConfirmed) {
1219
+ return this.showFullDeployExplanation(projectName, description);
1220
+ }
1209
1221
  // Check if directory has files
1210
1222
  const files = fs.readdirSync(cwd);
1211
1223
  const hasFiles = files.filter(f => !f.startsWith('.')).length > 0;
@@ -1344,14 +1356,22 @@ phase: setup
1344
1356
  }
1345
1357
  results.push('\n---\n');
1346
1358
  results.push('## ✅ Project Created Successfully!\n');
1347
- results.push('### Next Steps:\n');
1348
- results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
1349
- results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
1350
- results.push('3. **Start building:** Just tell me what features you want!\n');
1351
- results.push('### Example:\n');
1352
- results.push('> "Add user authentication with email/password"');
1353
- results.push('> "Create a dashboard with stats cards"');
1354
- results.push('> "Build a todo list with CRUD operations"');
1359
+ // If fullDeploy is enabled and confirmed, proceed with cloud deployment
1360
+ if (fullDeploy && deployConfirmed) {
1361
+ results.push('## 🚀 Starting Full Deployment...\n');
1362
+ const deployResults = await this.executeFullDeploy(projectName, cwd, description);
1363
+ results.push(...deployResults);
1364
+ }
1365
+ else {
1366
+ results.push('### Next Steps:\n');
1367
+ results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
1368
+ results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
1369
+ results.push('3. **Start building:** Just tell me what features you want!\n');
1370
+ results.push('### Example:\n');
1371
+ results.push('> "Add user authentication with email/password"');
1372
+ results.push('> "Create a dashboard with stats cards"');
1373
+ results.push('> "Build a todo list with CRUD operations"');
1374
+ }
1355
1375
  }
1356
1376
  catch (error) {
1357
1377
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -1364,6 +1384,270 @@ phase: setup
1364
1384
  }],
1365
1385
  };
1366
1386
  }
1387
+ /**
1388
+ * Show explanation of what fullDeploy will do and ask for confirmation
1389
+ */
1390
+ showFullDeployExplanation(projectName, description) {
1391
+ const explanation = `# 🚀 Full Deployment: ${projectName}
1392
+
1393
+ ## What This Will Do
1394
+
1395
+ Full deployment creates a complete production-ready environment automatically:
1396
+
1397
+ ### 1. 📁 Local Project
1398
+ - Create Next.js + Supabase + Drizzle project
1399
+ - Install all dependencies
1400
+ - Set up CodeBakers patterns
1401
+
1402
+ ### 2. 🐙 GitHub Repository
1403
+ - Create a new private repository: \`${projectName}\`
1404
+ - Initialize git and push code
1405
+ - Set up .gitignore properly
1406
+
1407
+ ### 3. 🗄️ Supabase Project
1408
+ - Create a new Supabase project
1409
+ - Get database connection string
1410
+ - Get API keys (anon + service role)
1411
+ - Auto-configure .env.local
1412
+
1413
+ ### 4. 🔺 Vercel Deployment
1414
+ - Deploy to Vercel
1415
+ - Connect to GitHub for auto-deploys
1416
+ - Set all environment variables
1417
+ - Get your live URL
1418
+
1419
+ ---
1420
+
1421
+ ## Requirements
1422
+
1423
+ Make sure you have these CLIs installed and authenticated:
1424
+ - \`gh\` - GitHub CLI (run: \`gh auth login\`)
1425
+ - \`supabase\` - Supabase CLI (run: \`supabase login\`)
1426
+ - \`vercel\` - Vercel CLI (run: \`vercel login\`)
1427
+
1428
+ ---
1429
+
1430
+ ## 🎯 Result
1431
+
1432
+ After completion, you'll have:
1433
+ - ✅ GitHub repo with your code
1434
+ - ✅ Supabase project with database ready
1435
+ - ✅ Live URL on Vercel
1436
+ - ✅ Auto-deploys on every push
1437
+
1438
+ ---
1439
+
1440
+ **⚠️ IMPORTANT: Ask the user to confirm before proceeding.**
1441
+
1442
+ To proceed, call \`scaffold_project\` again with:
1443
+ \`\`\`json
1444
+ {
1445
+ "projectName": "${projectName}",
1446
+ "description": "${description || ''}",
1447
+ "fullDeploy": true,
1448
+ "deployConfirmed": true
1449
+ }
1450
+ \`\`\`
1451
+
1452
+ Or if user declines, call without fullDeploy:
1453
+ \`\`\`json
1454
+ {
1455
+ "projectName": "${projectName}",
1456
+ "description": "${description || ''}"
1457
+ }
1458
+ \`\`\`
1459
+ `;
1460
+ return {
1461
+ content: [{
1462
+ type: 'text',
1463
+ text: explanation,
1464
+ }],
1465
+ };
1466
+ }
1467
+ /**
1468
+ * Execute full cloud deployment (GitHub + Supabase + Vercel)
1469
+ */
1470
+ async executeFullDeploy(projectName, cwd, description) {
1471
+ const results = [];
1472
+ // Check for required CLIs
1473
+ const cliChecks = this.checkRequiredCLIs();
1474
+ if (cliChecks.missing.length > 0) {
1475
+ results.push('### ❌ Missing Required CLIs\n');
1476
+ results.push('The following CLIs are required for full deployment:\n');
1477
+ for (const cli of cliChecks.missing) {
1478
+ results.push(`- **${cli.name}**: ${cli.installCmd}`);
1479
+ }
1480
+ results.push('\nInstall the missing CLIs and try again.');
1481
+ return results;
1482
+ }
1483
+ results.push('✓ All required CLIs found\n');
1484
+ // Step 1: Initialize Git and create GitHub repo
1485
+ results.push('### Step 1: GitHub Repository\n');
1486
+ try {
1487
+ // Initialize git
1488
+ (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
1489
+ (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
1490
+ (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers"', { cwd, stdio: 'pipe' });
1491
+ results.push('✓ Initialized git repository');
1492
+ // Create GitHub repo
1493
+ const ghDescription = description || `${projectName} - Created with CodeBakers`;
1494
+ (0, child_process_1.execSync)(`gh repo create ${projectName} --private --source=. --push --description "${ghDescription}"`, { cwd, stdio: 'pipe' });
1495
+ results.push(`✓ Created GitHub repo: ${projectName}`);
1496
+ results.push(` → https://github.com/${this.getGitHubUsername()}/${projectName}\n`);
1497
+ }
1498
+ catch (error) {
1499
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1500
+ results.push(`⚠️ GitHub setup failed: ${msg}`);
1501
+ results.push(' You can create the repo manually: gh repo create\n');
1502
+ }
1503
+ // Step 2: Create Supabase project
1504
+ results.push('### Step 2: Supabase Project\n');
1505
+ try {
1506
+ // Create Supabase project (this may take a while)
1507
+ const orgId = this.getSupabaseOrgId();
1508
+ if (orgId) {
1509
+ (0, child_process_1.execSync)(`supabase projects create ${projectName} --org-id ${orgId} --region us-east-1 --db-password "${this.generatePassword()}"`, { cwd, stdio: 'pipe', timeout: 120000 });
1510
+ results.push(`✓ Created Supabase project: ${projectName}`);
1511
+ // Get project credentials
1512
+ const projectsOutput = (0, child_process_1.execSync)('supabase projects list --output json', { cwd, encoding: 'utf-8' });
1513
+ const projects = JSON.parse(projectsOutput);
1514
+ const newProject = projects.find((p) => p.name === projectName);
1515
+ if (newProject) {
1516
+ // Update .env.local with Supabase credentials
1517
+ const envPath = path.join(cwd, '.env.local');
1518
+ let envContent = fs.readFileSync(envPath, 'utf-8');
1519
+ envContent = envContent.replace('your-supabase-url', `https://${newProject.id}.supabase.co`);
1520
+ envContent = envContent.replace('your-anon-key', newProject.anon_key || 'YOUR_ANON_KEY');
1521
+ fs.writeFileSync(envPath, envContent);
1522
+ results.push('✓ Updated .env.local with Supabase credentials\n');
1523
+ }
1524
+ }
1525
+ else {
1526
+ results.push('⚠️ Could not detect Supabase organization');
1527
+ results.push(' Run: supabase orgs list\n');
1528
+ }
1529
+ }
1530
+ catch (error) {
1531
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1532
+ results.push(`⚠️ Supabase setup failed: ${msg}`);
1533
+ results.push(' Create project manually at: https://supabase.com/dashboard\n');
1534
+ }
1535
+ // Step 3: Deploy to Vercel
1536
+ results.push('### Step 3: Vercel Deployment\n');
1537
+ try {
1538
+ // Link to Vercel (creates new project)
1539
+ (0, child_process_1.execSync)('vercel link --yes', { cwd, stdio: 'pipe' });
1540
+ results.push('✓ Linked to Vercel');
1541
+ // Set environment variables from .env.local
1542
+ const envPath = path.join(cwd, '.env.local');
1543
+ if (fs.existsSync(envPath)) {
1544
+ const envContent = fs.readFileSync(envPath, 'utf-8');
1545
+ const envVars = envContent.split('\n')
1546
+ .filter(line => line.includes('=') && !line.startsWith('#'))
1547
+ .map(line => {
1548
+ const [key, ...valueParts] = line.split('=');
1549
+ return { key: key.trim(), value: valueParts.join('=').trim() };
1550
+ });
1551
+ for (const { key, value } of envVars) {
1552
+ if (value && !value.includes('your-')) {
1553
+ try {
1554
+ (0, child_process_1.execSync)(`vercel env add ${key} production <<< "${value}"`, { cwd, stdio: 'pipe', shell: 'bash' });
1555
+ }
1556
+ catch {
1557
+ // Env var might already exist, try to update
1558
+ }
1559
+ }
1560
+ }
1561
+ results.push('✓ Set environment variables');
1562
+ }
1563
+ // Deploy to production
1564
+ const deployOutput = (0, child_process_1.execSync)('vercel --prod --yes', { cwd, encoding: 'utf-8' });
1565
+ const urlMatch = deployOutput.match(/https:\/\/[^\s]+\.vercel\.app/);
1566
+ const deployUrl = urlMatch ? urlMatch[0] : 'Check Vercel dashboard';
1567
+ results.push(`✓ Deployed to Vercel`);
1568
+ results.push(` → ${deployUrl}\n`);
1569
+ // Connect to GitHub for auto-deploys
1570
+ try {
1571
+ (0, child_process_1.execSync)('vercel git connect --yes', { cwd, stdio: 'pipe' });
1572
+ results.push('✓ Connected to GitHub for auto-deploys\n');
1573
+ }
1574
+ catch {
1575
+ results.push('⚠️ Could not auto-connect to GitHub\n');
1576
+ }
1577
+ }
1578
+ catch (error) {
1579
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1580
+ results.push(`⚠️ Vercel deployment failed: ${msg}`);
1581
+ results.push(' Deploy manually: vercel --prod\n');
1582
+ }
1583
+ // Summary
1584
+ results.push('---\n');
1585
+ results.push('## 🎉 Full Deployment Complete!\n');
1586
+ results.push('Your project is now live with:');
1587
+ results.push('- GitHub repo with CI/CD ready');
1588
+ results.push('- Supabase database configured');
1589
+ results.push('- Vercel hosting with auto-deploys\n');
1590
+ results.push('**Start building features - every push auto-deploys!**');
1591
+ return results;
1592
+ }
1593
+ /**
1594
+ * Check if required CLIs are installed
1595
+ */
1596
+ checkRequiredCLIs() {
1597
+ const clis = [
1598
+ { name: 'gh', cmd: 'gh --version', installCmd: 'npm install -g gh' },
1599
+ { name: 'supabase', cmd: 'supabase --version', installCmd: 'npm install -g supabase' },
1600
+ { name: 'vercel', cmd: 'vercel --version', installCmd: 'npm install -g vercel' },
1601
+ ];
1602
+ const installed = [];
1603
+ const missing = [];
1604
+ for (const cli of clis) {
1605
+ try {
1606
+ (0, child_process_1.execSync)(cli.cmd, { stdio: 'pipe' });
1607
+ installed.push(cli.name);
1608
+ }
1609
+ catch {
1610
+ missing.push({ name: cli.name, installCmd: cli.installCmd });
1611
+ }
1612
+ }
1613
+ return { installed, missing };
1614
+ }
1615
+ /**
1616
+ * Get GitHub username from gh CLI
1617
+ */
1618
+ getGitHubUsername() {
1619
+ try {
1620
+ const output = (0, child_process_1.execSync)('gh api user --jq .login', { encoding: 'utf-8' });
1621
+ return output.trim();
1622
+ }
1623
+ catch {
1624
+ return 'YOUR_USERNAME';
1625
+ }
1626
+ }
1627
+ /**
1628
+ * Get Supabase organization ID
1629
+ */
1630
+ getSupabaseOrgId() {
1631
+ try {
1632
+ const output = (0, child_process_1.execSync)('supabase orgs list --output json', { encoding: 'utf-8' });
1633
+ const orgs = JSON.parse(output);
1634
+ return orgs[0]?.id || null;
1635
+ }
1636
+ catch {
1637
+ return null;
1638
+ }
1639
+ }
1640
+ /**
1641
+ * Generate a secure random password for Supabase
1642
+ */
1643
+ generatePassword() {
1644
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
1645
+ let password = '';
1646
+ for (let i = 0; i < 24; i++) {
1647
+ password += chars.charAt(Math.floor(Math.random() * chars.length));
1648
+ }
1649
+ return password;
1650
+ }
1367
1651
  async handleInitProject(args) {
1368
1652
  const cwd = process.cwd();
1369
1653
  const results = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -69,7 +69,7 @@ const program = new Command();
69
69
  program
70
70
  .name('codebakers')
71
71
  .description('CodeBakers CLI - Production patterns for AI-assisted development')
72
- .version('2.8.0');
72
+ .version('2.9.0');
73
73
 
74
74
  // Primary command - one-time setup
75
75
  program
package/src/mcp/server.ts CHANGED
@@ -566,7 +566,7 @@ class CodeBakersServer {
566
566
  {
567
567
  name: 'scaffold_project',
568
568
  description:
569
- 'Create a new project from scratch with Next.js + Supabase + Drizzle. Use this when user wants to build something new and no project exists yet. Creates all files, installs dependencies, and sets up CodeBakers patterns automatically.',
569
+ 'Create a new project from scratch with Next.js + Supabase + Drizzle. Use this when user wants to build something new and no project exists yet. Creates all files, installs dependencies, and sets up CodeBakers patterns automatically. Set fullDeploy=true for seamless idea-to-deployment (creates GitHub repo, Supabase project, and deploys to Vercel). When fullDeploy=true, first call returns explanation - then call again with deployConfirmed=true after user confirms.',
570
570
  inputSchema: {
571
571
  type: 'object' as const,
572
572
  properties: {
@@ -578,6 +578,14 @@ class CodeBakersServer {
578
578
  type: 'string',
579
579
  description: 'Brief description of what the project is for (used in PRD.md)',
580
580
  },
581
+ fullDeploy: {
582
+ type: 'boolean',
583
+ description: 'If true, enables full deployment flow (GitHub + Supabase + Vercel). First call returns explanation for user confirmation.',
584
+ },
585
+ deployConfirmed: {
586
+ type: 'boolean',
587
+ description: 'Set to true AFTER user confirms they want full deployment. Only set this after showing user the explanation and getting their approval.',
588
+ },
581
589
  },
582
590
  required: ['projectName'],
583
591
  },
@@ -900,7 +908,7 @@ class CodeBakersServer {
900
908
  return this.handleGetPatternSection(args as { pattern: string; section: string });
901
909
 
902
910
  case 'scaffold_project':
903
- return this.handleScaffoldProject(args as { projectName: string; description?: string });
911
+ return this.handleScaffoldProject(args as { projectName: string; description?: string; fullDeploy?: boolean; deployConfirmed?: boolean });
904
912
 
905
913
  case 'init_project':
906
914
  return this.handleInitProject(args as { projectName?: string });
@@ -1376,10 +1384,15 @@ Show the user what their simple request was expanded into, then proceed with the
1376
1384
  };
1377
1385
  }
1378
1386
 
1379
- private async handleScaffoldProject(args: { projectName: string; description?: string }) {
1380
- const { projectName, description } = args;
1387
+ private async handleScaffoldProject(args: { projectName: string; description?: string; fullDeploy?: boolean; deployConfirmed?: boolean }) {
1388
+ const { projectName, description, fullDeploy, deployConfirmed } = args;
1381
1389
  const cwd = process.cwd();
1382
1390
 
1391
+ // If fullDeploy requested but not confirmed, show explanation and ask for confirmation
1392
+ if (fullDeploy && !deployConfirmed) {
1393
+ return this.showFullDeployExplanation(projectName, description);
1394
+ }
1395
+
1383
1396
  // Check if directory has files
1384
1397
  const files = fs.readdirSync(cwd);
1385
1398
  const hasFiles = files.filter(f => !f.startsWith('.')).length > 0;
@@ -1533,14 +1546,22 @@ phase: setup
1533
1546
 
1534
1547
  results.push('\n---\n');
1535
1548
  results.push('## ✅ Project Created Successfully!\n');
1536
- results.push('### Next Steps:\n');
1537
- results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
1538
- results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
1539
- results.push('3. **Start building:** Just tell me what features you want!\n');
1540
- results.push('### Example:\n');
1541
- results.push('> "Add user authentication with email/password"');
1542
- results.push('> "Create a dashboard with stats cards"');
1543
- results.push('> "Build a todo list with CRUD operations"');
1549
+
1550
+ // If fullDeploy is enabled and confirmed, proceed with cloud deployment
1551
+ if (fullDeploy && deployConfirmed) {
1552
+ results.push('## 🚀 Starting Full Deployment...\n');
1553
+ const deployResults = await this.executeFullDeploy(projectName, cwd, description);
1554
+ results.push(...deployResults);
1555
+ } else {
1556
+ results.push('### Next Steps:\n');
1557
+ results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
1558
+ results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
1559
+ results.push('3. **Start building:** Just tell me what features you want!\n');
1560
+ results.push('### Example:\n');
1561
+ results.push('> "Add user authentication with email/password"');
1562
+ results.push('> "Create a dashboard with stats cards"');
1563
+ results.push('> "Build a todo list with CRUD operations"');
1564
+ }
1544
1565
 
1545
1566
  } catch (error) {
1546
1567
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -1555,6 +1576,285 @@ phase: setup
1555
1576
  };
1556
1577
  }
1557
1578
 
1579
+ /**
1580
+ * Show explanation of what fullDeploy will do and ask for confirmation
1581
+ */
1582
+ private showFullDeployExplanation(projectName: string, description?: string) {
1583
+ const explanation = `# 🚀 Full Deployment: ${projectName}
1584
+
1585
+ ## What This Will Do
1586
+
1587
+ Full deployment creates a complete production-ready environment automatically:
1588
+
1589
+ ### 1. 📁 Local Project
1590
+ - Create Next.js + Supabase + Drizzle project
1591
+ - Install all dependencies
1592
+ - Set up CodeBakers patterns
1593
+
1594
+ ### 2. 🐙 GitHub Repository
1595
+ - Create a new private repository: \`${projectName}\`
1596
+ - Initialize git and push code
1597
+ - Set up .gitignore properly
1598
+
1599
+ ### 3. 🗄️ Supabase Project
1600
+ - Create a new Supabase project
1601
+ - Get database connection string
1602
+ - Get API keys (anon + service role)
1603
+ - Auto-configure .env.local
1604
+
1605
+ ### 4. 🔺 Vercel Deployment
1606
+ - Deploy to Vercel
1607
+ - Connect to GitHub for auto-deploys
1608
+ - Set all environment variables
1609
+ - Get your live URL
1610
+
1611
+ ---
1612
+
1613
+ ## Requirements
1614
+
1615
+ Make sure you have these CLIs installed and authenticated:
1616
+ - \`gh\` - GitHub CLI (run: \`gh auth login\`)
1617
+ - \`supabase\` - Supabase CLI (run: \`supabase login\`)
1618
+ - \`vercel\` - Vercel CLI (run: \`vercel login\`)
1619
+
1620
+ ---
1621
+
1622
+ ## 🎯 Result
1623
+
1624
+ After completion, you'll have:
1625
+ - ✅ GitHub repo with your code
1626
+ - ✅ Supabase project with database ready
1627
+ - ✅ Live URL on Vercel
1628
+ - ✅ Auto-deploys on every push
1629
+
1630
+ ---
1631
+
1632
+ **⚠️ IMPORTANT: Ask the user to confirm before proceeding.**
1633
+
1634
+ To proceed, call \`scaffold_project\` again with:
1635
+ \`\`\`json
1636
+ {
1637
+ "projectName": "${projectName}",
1638
+ "description": "${description || ''}",
1639
+ "fullDeploy": true,
1640
+ "deployConfirmed": true
1641
+ }
1642
+ \`\`\`
1643
+
1644
+ Or if user declines, call without fullDeploy:
1645
+ \`\`\`json
1646
+ {
1647
+ "projectName": "${projectName}",
1648
+ "description": "${description || ''}"
1649
+ }
1650
+ \`\`\`
1651
+ `;
1652
+
1653
+ return {
1654
+ content: [{
1655
+ type: 'text' as const,
1656
+ text: explanation,
1657
+ }],
1658
+ };
1659
+ }
1660
+
1661
+ /**
1662
+ * Execute full cloud deployment (GitHub + Supabase + Vercel)
1663
+ */
1664
+ private async executeFullDeploy(projectName: string, cwd: string, description?: string): Promise<string[]> {
1665
+ const results: string[] = [];
1666
+
1667
+ // Check for required CLIs
1668
+ const cliChecks = this.checkRequiredCLIs();
1669
+ if (cliChecks.missing.length > 0) {
1670
+ results.push('### ❌ Missing Required CLIs\n');
1671
+ results.push('The following CLIs are required for full deployment:\n');
1672
+ for (const cli of cliChecks.missing) {
1673
+ results.push(`- **${cli.name}**: ${cli.installCmd}`);
1674
+ }
1675
+ results.push('\nInstall the missing CLIs and try again.');
1676
+ return results;
1677
+ }
1678
+ results.push('✓ All required CLIs found\n');
1679
+
1680
+ // Step 1: Initialize Git and create GitHub repo
1681
+ results.push('### Step 1: GitHub Repository\n');
1682
+ try {
1683
+ // Initialize git
1684
+ execSync('git init', { cwd, stdio: 'pipe' });
1685
+ execSync('git add .', { cwd, stdio: 'pipe' });
1686
+ execSync('git commit -m "Initial commit from CodeBakers"', { cwd, stdio: 'pipe' });
1687
+ results.push('✓ Initialized git repository');
1688
+
1689
+ // Create GitHub repo
1690
+ const ghDescription = description || `${projectName} - Created with CodeBakers`;
1691
+ execSync(`gh repo create ${projectName} --private --source=. --push --description "${ghDescription}"`, { cwd, stdio: 'pipe' });
1692
+ results.push(`✓ Created GitHub repo: ${projectName}`);
1693
+ results.push(` → https://github.com/${this.getGitHubUsername()}/${projectName}\n`);
1694
+ } catch (error) {
1695
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1696
+ results.push(`⚠️ GitHub setup failed: ${msg}`);
1697
+ results.push(' You can create the repo manually: gh repo create\n');
1698
+ }
1699
+
1700
+ // Step 2: Create Supabase project
1701
+ results.push('### Step 2: Supabase Project\n');
1702
+ try {
1703
+ // Create Supabase project (this may take a while)
1704
+ const orgId = this.getSupabaseOrgId();
1705
+ if (orgId) {
1706
+ execSync(`supabase projects create ${projectName} --org-id ${orgId} --region us-east-1 --db-password "${this.generatePassword()}"`, { cwd, stdio: 'pipe', timeout: 120000 });
1707
+ results.push(`✓ Created Supabase project: ${projectName}`);
1708
+
1709
+ // Get project credentials
1710
+ const projectsOutput = execSync('supabase projects list --output json', { cwd, encoding: 'utf-8' });
1711
+ const projects = JSON.parse(projectsOutput);
1712
+ const newProject = projects.find((p: { name: string }) => p.name === projectName);
1713
+
1714
+ if (newProject) {
1715
+ // Update .env.local with Supabase credentials
1716
+ const envPath = path.join(cwd, '.env.local');
1717
+ let envContent = fs.readFileSync(envPath, 'utf-8');
1718
+ envContent = envContent.replace('your-supabase-url', `https://${newProject.id}.supabase.co`);
1719
+ envContent = envContent.replace('your-anon-key', newProject.anon_key || 'YOUR_ANON_KEY');
1720
+ fs.writeFileSync(envPath, envContent);
1721
+ results.push('✓ Updated .env.local with Supabase credentials\n');
1722
+ }
1723
+ } else {
1724
+ results.push('⚠️ Could not detect Supabase organization');
1725
+ results.push(' Run: supabase orgs list\n');
1726
+ }
1727
+ } catch (error) {
1728
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1729
+ results.push(`⚠️ Supabase setup failed: ${msg}`);
1730
+ results.push(' Create project manually at: https://supabase.com/dashboard\n');
1731
+ }
1732
+
1733
+ // Step 3: Deploy to Vercel
1734
+ results.push('### Step 3: Vercel Deployment\n');
1735
+ try {
1736
+ // Link to Vercel (creates new project)
1737
+ execSync('vercel link --yes', { cwd, stdio: 'pipe' });
1738
+ results.push('✓ Linked to Vercel');
1739
+
1740
+ // Set environment variables from .env.local
1741
+ const envPath = path.join(cwd, '.env.local');
1742
+ if (fs.existsSync(envPath)) {
1743
+ const envContent = fs.readFileSync(envPath, 'utf-8');
1744
+ const envVars = envContent.split('\n')
1745
+ .filter(line => line.includes('=') && !line.startsWith('#'))
1746
+ .map(line => {
1747
+ const [key, ...valueParts] = line.split('=');
1748
+ return { key: key.trim(), value: valueParts.join('=').trim() };
1749
+ });
1750
+
1751
+ for (const { key, value } of envVars) {
1752
+ if (value && !value.includes('your-')) {
1753
+ try {
1754
+ execSync(`vercel env add ${key} production <<< "${value}"`, { cwd, stdio: 'pipe', shell: 'bash' });
1755
+ } catch {
1756
+ // Env var might already exist, try to update
1757
+ }
1758
+ }
1759
+ }
1760
+ results.push('✓ Set environment variables');
1761
+ }
1762
+
1763
+ // Deploy to production
1764
+ const deployOutput = execSync('vercel --prod --yes', { cwd, encoding: 'utf-8' });
1765
+ const urlMatch = deployOutput.match(/https:\/\/[^\s]+\.vercel\.app/);
1766
+ const deployUrl = urlMatch ? urlMatch[0] : 'Check Vercel dashboard';
1767
+ results.push(`✓ Deployed to Vercel`);
1768
+ results.push(` → ${deployUrl}\n`);
1769
+
1770
+ // Connect to GitHub for auto-deploys
1771
+ try {
1772
+ execSync('vercel git connect --yes', { cwd, stdio: 'pipe' });
1773
+ results.push('✓ Connected to GitHub for auto-deploys\n');
1774
+ } catch {
1775
+ results.push('⚠️ Could not auto-connect to GitHub\n');
1776
+ }
1777
+
1778
+ } catch (error) {
1779
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1780
+ results.push(`⚠️ Vercel deployment failed: ${msg}`);
1781
+ results.push(' Deploy manually: vercel --prod\n');
1782
+ }
1783
+
1784
+ // Summary
1785
+ results.push('---\n');
1786
+ results.push('## 🎉 Full Deployment Complete!\n');
1787
+ results.push('Your project is now live with:');
1788
+ results.push('- GitHub repo with CI/CD ready');
1789
+ results.push('- Supabase database configured');
1790
+ results.push('- Vercel hosting with auto-deploys\n');
1791
+ results.push('**Start building features - every push auto-deploys!**');
1792
+
1793
+ return results;
1794
+ }
1795
+
1796
+ /**
1797
+ * Check if required CLIs are installed
1798
+ */
1799
+ private checkRequiredCLIs(): { installed: string[]; missing: { name: string; installCmd: string }[] } {
1800
+ const clis = [
1801
+ { name: 'gh', cmd: 'gh --version', installCmd: 'npm install -g gh' },
1802
+ { name: 'supabase', cmd: 'supabase --version', installCmd: 'npm install -g supabase' },
1803
+ { name: 'vercel', cmd: 'vercel --version', installCmd: 'npm install -g vercel' },
1804
+ ];
1805
+
1806
+ const installed: string[] = [];
1807
+ const missing: { name: string; installCmd: string }[] = [];
1808
+
1809
+ for (const cli of clis) {
1810
+ try {
1811
+ execSync(cli.cmd, { stdio: 'pipe' });
1812
+ installed.push(cli.name);
1813
+ } catch {
1814
+ missing.push({ name: cli.name, installCmd: cli.installCmd });
1815
+ }
1816
+ }
1817
+
1818
+ return { installed, missing };
1819
+ }
1820
+
1821
+ /**
1822
+ * Get GitHub username from gh CLI
1823
+ */
1824
+ private getGitHubUsername(): string {
1825
+ try {
1826
+ const output = execSync('gh api user --jq .login', { encoding: 'utf-8' });
1827
+ return output.trim();
1828
+ } catch {
1829
+ return 'YOUR_USERNAME';
1830
+ }
1831
+ }
1832
+
1833
+ /**
1834
+ * Get Supabase organization ID
1835
+ */
1836
+ private getSupabaseOrgId(): string | null {
1837
+ try {
1838
+ const output = execSync('supabase orgs list --output json', { encoding: 'utf-8' });
1839
+ const orgs = JSON.parse(output);
1840
+ return orgs[0]?.id || null;
1841
+ } catch {
1842
+ return null;
1843
+ }
1844
+ }
1845
+
1846
+ /**
1847
+ * Generate a secure random password for Supabase
1848
+ */
1849
+ private generatePassword(): string {
1850
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
1851
+ let password = '';
1852
+ for (let i = 0; i < 24; i++) {
1853
+ password += chars.charAt(Math.floor(Math.random() * chars.length));
1854
+ }
1855
+ return password;
1856
+ }
1857
+
1558
1858
  private async handleInitProject(args: { projectName?: string }) {
1559
1859
  const cwd = process.cwd();
1560
1860
  const results: string[] = [];