@codebakers/cli 2.8.0 → 3.0.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.
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getDeviceFingerprint = getDeviceFingerprint;
37
+ exports.canCreateFingerprint = canCreateFingerprint;
38
+ const os = __importStar(require("os"));
39
+ const crypto = __importStar(require("crypto"));
40
+ const child_process_1 = require("child_process");
41
+ /**
42
+ * Get a stable machine identifier based on OS
43
+ * - Windows: MachineGuid from registry
44
+ * - macOS: IOPlatformUUID from system
45
+ * - Linux: /etc/machine-id
46
+ */
47
+ function getMachineId() {
48
+ try {
49
+ const platform = os.platform();
50
+ if (platform === 'win32') {
51
+ // Windows: Use MachineGuid from registry
52
+ const output = (0, child_process_1.execSync)('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
53
+ const match = output.match(/MachineGuid\s+REG_SZ\s+(.+)/);
54
+ if (match && match[1]) {
55
+ return match[1].trim();
56
+ }
57
+ }
58
+ else if (platform === 'darwin') {
59
+ // macOS: Use hardware UUID
60
+ const output = (0, child_process_1.execSync)('ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
61
+ const match = output.match(/"IOPlatformUUID"\s*=\s*"(.+)"/);
62
+ if (match && match[1]) {
63
+ return match[1];
64
+ }
65
+ }
66
+ else {
67
+ // Linux: Use machine-id
68
+ const output = (0, child_process_1.execSync)('cat /etc/machine-id', {
69
+ encoding: 'utf-8',
70
+ stdio: ['pipe', 'pipe', 'pipe'],
71
+ });
72
+ return output.trim();
73
+ }
74
+ }
75
+ catch {
76
+ // Fallback handled below
77
+ }
78
+ // Fallback: Create a stable hash from hostname + username + home directory
79
+ // This is less reliable but works when we can't access system IDs
80
+ const fallbackData = [
81
+ os.hostname(),
82
+ os.userInfo().username,
83
+ os.homedir(),
84
+ os.platform(),
85
+ os.arch(),
86
+ ].join('|');
87
+ return crypto.createHash('sha256').update(fallbackData).digest('hex').slice(0, 36);
88
+ }
89
+ /**
90
+ * Get a complete device fingerprint
91
+ * The deviceHash is the primary identifier used for trial tracking
92
+ */
93
+ function getDeviceFingerprint() {
94
+ const machineId = getMachineId();
95
+ // Collect stable machine characteristics
96
+ const fingerprintData = {
97
+ machineId,
98
+ hostname: os.hostname(),
99
+ username: os.userInfo().username,
100
+ platform: os.platform(),
101
+ arch: os.arch(),
102
+ cpuModel: os.cpus()[0]?.model || 'unknown',
103
+ totalMemory: Math.floor(os.totalmem() / (1024 * 1024 * 1024)), // GB rounded
104
+ homeDir: os.homedir(),
105
+ };
106
+ // Create a stable hash from all characteristics
107
+ const deviceHash = crypto
108
+ .createHash('sha256')
109
+ .update(JSON.stringify(fingerprintData))
110
+ .digest('hex');
111
+ return {
112
+ machineId,
113
+ deviceHash,
114
+ platform: os.platform(),
115
+ hostname: os.hostname(),
116
+ };
117
+ }
118
+ /**
119
+ * Validate that we can create a fingerprint
120
+ * Used for diagnostics
121
+ */
122
+ function canCreateFingerprint() {
123
+ try {
124
+ const fp = getDeviceFingerprint();
125
+ if (fp.deviceHash && fp.deviceHash.length === 64) {
126
+ return { success: true };
127
+ }
128
+ return { success: false, error: 'Invalid fingerprint generated' };
129
+ }
130
+ catch (error) {
131
+ return {
132
+ success: false,
133
+ error: error instanceof Error ? error.message : 'Unknown error',
134
+ };
135
+ }
136
+ }
@@ -53,11 +53,15 @@ class CodeBakersServer {
53
53
  server;
54
54
  apiKey;
55
55
  apiUrl;
56
+ trialState;
57
+ authMode;
56
58
  autoUpdateChecked = false;
57
59
  autoUpdateInProgress = false;
58
60
  constructor() {
59
61
  this.apiKey = (0, config_js_1.getApiKey)();
60
62
  this.apiUrl = (0, config_js_1.getApiUrl)();
63
+ this.trialState = (0, config_js_1.getTrialState)();
64
+ this.authMode = (0, config_js_1.getAuthMode)();
61
65
  this.server = new index_js_1.Server({
62
66
  name: 'codebakers',
63
67
  version: '1.0.0',
@@ -72,12 +76,25 @@ class CodeBakersServer {
72
76
  // Silently ignore errors - don't interrupt user
73
77
  });
74
78
  }
79
+ /**
80
+ * Get authorization headers for API requests
81
+ * Supports both API key (paid users) and trial ID (free users)
82
+ */
83
+ getAuthHeaders() {
84
+ if (this.apiKey) {
85
+ return { 'Authorization': `Bearer ${this.apiKey}` };
86
+ }
87
+ if (this.trialState?.trialId) {
88
+ return { 'X-Trial-Id': this.trialState.trialId };
89
+ }
90
+ return {};
91
+ }
75
92
  /**
76
93
  * Automatically check for and apply pattern updates
77
94
  * Runs silently in background - no user intervention needed
78
95
  */
79
96
  async checkAndAutoUpdate() {
80
- if (this.autoUpdateChecked || this.autoUpdateInProgress || !this.apiKey) {
97
+ if (this.autoUpdateChecked || this.autoUpdateInProgress || this.authMode === 'none') {
81
98
  return;
82
99
  }
83
100
  this.autoUpdateInProgress = true;
@@ -110,7 +127,7 @@ class CodeBakersServer {
110
127
  }
111
128
  // Fetch latest version
112
129
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
113
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
130
+ headers: this.getAuthHeaders(),
114
131
  });
115
132
  if (!response.ok) {
116
133
  this.autoUpdateInProgress = false;
@@ -128,7 +145,7 @@ class CodeBakersServer {
128
145
  }
129
146
  // Fetch full content and update
130
147
  const contentResponse = await fetch(`${this.apiUrl}/api/content`, {
131
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
148
+ headers: this.getAuthHeaders(),
132
149
  });
133
150
  if (!contentResponse.ok) {
134
151
  this.autoUpdateInProgress = false;
@@ -362,7 +379,7 @@ class CodeBakersServer {
362
379
  let latest = null;
363
380
  try {
364
381
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
365
- headers: this.apiKey ? { 'Authorization': `Bearer ${this.apiKey}` } : {},
382
+ headers: this.getAuthHeaders(),
366
383
  });
367
384
  if (response.ok) {
368
385
  latest = await response.json();
@@ -509,7 +526,7 @@ class CodeBakersServer {
509
526
  },
510
527
  {
511
528
  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.',
529
+ 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
530
  inputSchema: {
514
531
  type: 'object',
515
532
  properties: {
@@ -521,6 +538,14 @@ class CodeBakersServer {
521
538
  type: 'string',
522
539
  description: 'Brief description of what the project is for (used in PRD.md)',
523
540
  },
541
+ fullDeploy: {
542
+ type: 'boolean',
543
+ description: 'If true, enables full deployment flow (GitHub + Supabase + Vercel). First call returns explanation for user confirmation.',
544
+ },
545
+ deployConfirmed: {
546
+ type: 'boolean',
547
+ description: 'Set to true AFTER user confirms they want full deployment. Only set this after showing user the explanation and getting their approval.',
548
+ },
524
549
  },
525
550
  required: ['projectName'],
526
551
  },
@@ -797,8 +822,26 @@ class CodeBakersServer {
797
822
  }));
798
823
  // Handle tool calls
799
824
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
800
- if (!this.apiKey) {
801
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not logged in. Run `codebakers login` first.');
825
+ // Check access: API key OR valid trial
826
+ if (this.authMode === 'none') {
827
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not logged in. Run `codebakers go` to start a free trial, or `codebakers setup` if you have an account.');
828
+ }
829
+ // Check if trial expired
830
+ if (this.authMode === 'trial' && (0, config_js_1.isTrialExpired)()) {
831
+ const trialState = (0, config_js_1.getTrialState)();
832
+ if (trialState?.stage === 'anonymous') {
833
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Trial expired. Run `codebakers extend` to add 7 more days with GitHub, or `codebakers billing` to upgrade.');
834
+ }
835
+ else {
836
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Trial expired. Run `codebakers billing` to upgrade to a paid plan.');
837
+ }
838
+ }
839
+ // Show warning if trial expiring soon
840
+ if (this.authMode === 'trial') {
841
+ const daysRemaining = (0, config_js_1.getTrialDaysRemaining)();
842
+ if (daysRemaining <= 2) {
843
+ console.error(`[CodeBakers] Trial expires in ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''}. Run 'codebakers extend' or 'codebakers billing'.`);
844
+ }
802
845
  }
803
846
  const { name, arguments: args } = request.params;
804
847
  switch (name) {
@@ -864,7 +907,7 @@ class CodeBakersServer {
864
907
  method: 'POST',
865
908
  headers: {
866
909
  'Content-Type': 'application/json',
867
- Authorization: `Bearer ${this.apiKey}`,
910
+ ...this.getAuthHeaders(),
868
911
  },
869
912
  body: JSON.stringify({
870
913
  prompt: userRequest,
@@ -973,9 +1016,7 @@ Show the user what their simple request was expanded into, then proceed with the
973
1016
  async handleListPatterns() {
974
1017
  const response = await fetch(`${this.apiUrl}/api/patterns`, {
975
1018
  method: 'GET',
976
- headers: {
977
- Authorization: `Bearer ${this.apiKey}`,
978
- },
1019
+ headers: this.getAuthHeaders(),
979
1020
  });
980
1021
  if (!response.ok) {
981
1022
  const error = await response.json().catch(() => ({}));
@@ -1018,7 +1059,7 @@ Show the user what their simple request was expanded into, then proceed with the
1018
1059
  method: 'POST',
1019
1060
  headers: {
1020
1061
  'Content-Type': 'application/json',
1021
- Authorization: `Bearer ${this.apiKey}`,
1062
+ ...this.getAuthHeaders(),
1022
1063
  },
1023
1064
  body: JSON.stringify({ patterns }),
1024
1065
  });
@@ -1035,7 +1076,7 @@ Show the user what their simple request was expanded into, then proceed with the
1035
1076
  method: 'POST',
1036
1077
  headers: {
1037
1078
  'Content-Type': 'application/json',
1038
- Authorization: `Bearer ${this.apiKey}`,
1079
+ ...this.getAuthHeaders(),
1039
1080
  },
1040
1081
  body: JSON.stringify({ query }),
1041
1082
  });
@@ -1204,8 +1245,12 @@ Show the user what their simple request was expanded into, then proceed with the
1204
1245
  };
1205
1246
  }
1206
1247
  async handleScaffoldProject(args) {
1207
- const { projectName, description } = args;
1248
+ const { projectName, description, fullDeploy, deployConfirmed } = args;
1208
1249
  const cwd = process.cwd();
1250
+ // If fullDeploy requested but not confirmed, show explanation and ask for confirmation
1251
+ if (fullDeploy && !deployConfirmed) {
1252
+ return this.showFullDeployExplanation(projectName, description);
1253
+ }
1209
1254
  // Check if directory has files
1210
1255
  const files = fs.readdirSync(cwd);
1211
1256
  const hasFiles = files.filter(f => !f.startsWith('.')).length > 0;
@@ -1279,7 +1324,7 @@ Show the user what their simple request was expanded into, then proceed with the
1279
1324
  results.push('\n## Installing CodeBakers Patterns...\n');
1280
1325
  const response = await fetch(`${this.apiUrl}/api/content`, {
1281
1326
  method: 'GET',
1282
- headers: { Authorization: `Bearer ${this.apiKey}` },
1327
+ headers: this.getAuthHeaders(),
1283
1328
  });
1284
1329
  if (response.ok) {
1285
1330
  const content = await response.json();
@@ -1344,14 +1389,22 @@ phase: setup
1344
1389
  }
1345
1390
  results.push('\n---\n');
1346
1391
  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"');
1392
+ // If fullDeploy is enabled and confirmed, proceed with cloud deployment
1393
+ if (fullDeploy && deployConfirmed) {
1394
+ results.push('## 🚀 Starting Full Deployment...\n');
1395
+ const deployResults = await this.executeFullDeploy(projectName, cwd, description);
1396
+ results.push(...deployResults);
1397
+ }
1398
+ else {
1399
+ results.push('### Next Steps:\n');
1400
+ results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
1401
+ results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
1402
+ results.push('3. **Start building:** Just tell me what features you want!\n');
1403
+ results.push('### Example:\n');
1404
+ results.push('> "Add user authentication with email/password"');
1405
+ results.push('> "Create a dashboard with stats cards"');
1406
+ results.push('> "Build a todo list with CRUD operations"');
1407
+ }
1355
1408
  }
1356
1409
  catch (error) {
1357
1410
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -1364,6 +1417,270 @@ phase: setup
1364
1417
  }],
1365
1418
  };
1366
1419
  }
1420
+ /**
1421
+ * Show explanation of what fullDeploy will do and ask for confirmation
1422
+ */
1423
+ showFullDeployExplanation(projectName, description) {
1424
+ const explanation = `# 🚀 Full Deployment: ${projectName}
1425
+
1426
+ ## What This Will Do
1427
+
1428
+ Full deployment creates a complete production-ready environment automatically:
1429
+
1430
+ ### 1. 📁 Local Project
1431
+ - Create Next.js + Supabase + Drizzle project
1432
+ - Install all dependencies
1433
+ - Set up CodeBakers patterns
1434
+
1435
+ ### 2. 🐙 GitHub Repository
1436
+ - Create a new private repository: \`${projectName}\`
1437
+ - Initialize git and push code
1438
+ - Set up .gitignore properly
1439
+
1440
+ ### 3. 🗄️ Supabase Project
1441
+ - Create a new Supabase project
1442
+ - Get database connection string
1443
+ - Get API keys (anon + service role)
1444
+ - Auto-configure .env.local
1445
+
1446
+ ### 4. 🔺 Vercel Deployment
1447
+ - Deploy to Vercel
1448
+ - Connect to GitHub for auto-deploys
1449
+ - Set all environment variables
1450
+ - Get your live URL
1451
+
1452
+ ---
1453
+
1454
+ ## Requirements
1455
+
1456
+ Make sure you have these CLIs installed and authenticated:
1457
+ - \`gh\` - GitHub CLI (run: \`gh auth login\`)
1458
+ - \`supabase\` - Supabase CLI (run: \`supabase login\`)
1459
+ - \`vercel\` - Vercel CLI (run: \`vercel login\`)
1460
+
1461
+ ---
1462
+
1463
+ ## 🎯 Result
1464
+
1465
+ After completion, you'll have:
1466
+ - ✅ GitHub repo with your code
1467
+ - ✅ Supabase project with database ready
1468
+ - ✅ Live URL on Vercel
1469
+ - ✅ Auto-deploys on every push
1470
+
1471
+ ---
1472
+
1473
+ **⚠️ IMPORTANT: Ask the user to confirm before proceeding.**
1474
+
1475
+ To proceed, call \`scaffold_project\` again with:
1476
+ \`\`\`json
1477
+ {
1478
+ "projectName": "${projectName}",
1479
+ "description": "${description || ''}",
1480
+ "fullDeploy": true,
1481
+ "deployConfirmed": true
1482
+ }
1483
+ \`\`\`
1484
+
1485
+ Or if user declines, call without fullDeploy:
1486
+ \`\`\`json
1487
+ {
1488
+ "projectName": "${projectName}",
1489
+ "description": "${description || ''}"
1490
+ }
1491
+ \`\`\`
1492
+ `;
1493
+ return {
1494
+ content: [{
1495
+ type: 'text',
1496
+ text: explanation,
1497
+ }],
1498
+ };
1499
+ }
1500
+ /**
1501
+ * Execute full cloud deployment (GitHub + Supabase + Vercel)
1502
+ */
1503
+ async executeFullDeploy(projectName, cwd, description) {
1504
+ const results = [];
1505
+ // Check for required CLIs
1506
+ const cliChecks = this.checkRequiredCLIs();
1507
+ if (cliChecks.missing.length > 0) {
1508
+ results.push('### ❌ Missing Required CLIs\n');
1509
+ results.push('The following CLIs are required for full deployment:\n');
1510
+ for (const cli of cliChecks.missing) {
1511
+ results.push(`- **${cli.name}**: ${cli.installCmd}`);
1512
+ }
1513
+ results.push('\nInstall the missing CLIs and try again.');
1514
+ return results;
1515
+ }
1516
+ results.push('✓ All required CLIs found\n');
1517
+ // Step 1: Initialize Git and create GitHub repo
1518
+ results.push('### Step 1: GitHub Repository\n');
1519
+ try {
1520
+ // Initialize git
1521
+ (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
1522
+ (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
1523
+ (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers"', { cwd, stdio: 'pipe' });
1524
+ results.push('✓ Initialized git repository');
1525
+ // Create GitHub repo
1526
+ const ghDescription = description || `${projectName} - Created with CodeBakers`;
1527
+ (0, child_process_1.execSync)(`gh repo create ${projectName} --private --source=. --push --description "${ghDescription}"`, { cwd, stdio: 'pipe' });
1528
+ results.push(`✓ Created GitHub repo: ${projectName}`);
1529
+ results.push(` → https://github.com/${this.getGitHubUsername()}/${projectName}\n`);
1530
+ }
1531
+ catch (error) {
1532
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1533
+ results.push(`⚠️ GitHub setup failed: ${msg}`);
1534
+ results.push(' You can create the repo manually: gh repo create\n');
1535
+ }
1536
+ // Step 2: Create Supabase project
1537
+ results.push('### Step 2: Supabase Project\n');
1538
+ try {
1539
+ // Create Supabase project (this may take a while)
1540
+ const orgId = this.getSupabaseOrgId();
1541
+ if (orgId) {
1542
+ (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 });
1543
+ results.push(`✓ Created Supabase project: ${projectName}`);
1544
+ // Get project credentials
1545
+ const projectsOutput = (0, child_process_1.execSync)('supabase projects list --output json', { cwd, encoding: 'utf-8' });
1546
+ const projects = JSON.parse(projectsOutput);
1547
+ const newProject = projects.find((p) => p.name === projectName);
1548
+ if (newProject) {
1549
+ // Update .env.local with Supabase credentials
1550
+ const envPath = path.join(cwd, '.env.local');
1551
+ let envContent = fs.readFileSync(envPath, 'utf-8');
1552
+ envContent = envContent.replace('your-supabase-url', `https://${newProject.id}.supabase.co`);
1553
+ envContent = envContent.replace('your-anon-key', newProject.anon_key || 'YOUR_ANON_KEY');
1554
+ fs.writeFileSync(envPath, envContent);
1555
+ results.push('✓ Updated .env.local with Supabase credentials\n');
1556
+ }
1557
+ }
1558
+ else {
1559
+ results.push('⚠️ Could not detect Supabase organization');
1560
+ results.push(' Run: supabase orgs list\n');
1561
+ }
1562
+ }
1563
+ catch (error) {
1564
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1565
+ results.push(`⚠️ Supabase setup failed: ${msg}`);
1566
+ results.push(' Create project manually at: https://supabase.com/dashboard\n');
1567
+ }
1568
+ // Step 3: Deploy to Vercel
1569
+ results.push('### Step 3: Vercel Deployment\n');
1570
+ try {
1571
+ // Link to Vercel (creates new project)
1572
+ (0, child_process_1.execSync)('vercel link --yes', { cwd, stdio: 'pipe' });
1573
+ results.push('✓ Linked to Vercel');
1574
+ // Set environment variables from .env.local
1575
+ const envPath = path.join(cwd, '.env.local');
1576
+ if (fs.existsSync(envPath)) {
1577
+ const envContent = fs.readFileSync(envPath, 'utf-8');
1578
+ const envVars = envContent.split('\n')
1579
+ .filter(line => line.includes('=') && !line.startsWith('#'))
1580
+ .map(line => {
1581
+ const [key, ...valueParts] = line.split('=');
1582
+ return { key: key.trim(), value: valueParts.join('=').trim() };
1583
+ });
1584
+ for (const { key, value } of envVars) {
1585
+ if (value && !value.includes('your-')) {
1586
+ try {
1587
+ (0, child_process_1.execSync)(`vercel env add ${key} production <<< "${value}"`, { cwd, stdio: 'pipe', shell: 'bash' });
1588
+ }
1589
+ catch {
1590
+ // Env var might already exist, try to update
1591
+ }
1592
+ }
1593
+ }
1594
+ results.push('✓ Set environment variables');
1595
+ }
1596
+ // Deploy to production
1597
+ const deployOutput = (0, child_process_1.execSync)('vercel --prod --yes', { cwd, encoding: 'utf-8' });
1598
+ const urlMatch = deployOutput.match(/https:\/\/[^\s]+\.vercel\.app/);
1599
+ const deployUrl = urlMatch ? urlMatch[0] : 'Check Vercel dashboard';
1600
+ results.push(`✓ Deployed to Vercel`);
1601
+ results.push(` → ${deployUrl}\n`);
1602
+ // Connect to GitHub for auto-deploys
1603
+ try {
1604
+ (0, child_process_1.execSync)('vercel git connect --yes', { cwd, stdio: 'pipe' });
1605
+ results.push('✓ Connected to GitHub for auto-deploys\n');
1606
+ }
1607
+ catch {
1608
+ results.push('⚠️ Could not auto-connect to GitHub\n');
1609
+ }
1610
+ }
1611
+ catch (error) {
1612
+ const msg = error instanceof Error ? error.message : 'Unknown error';
1613
+ results.push(`⚠️ Vercel deployment failed: ${msg}`);
1614
+ results.push(' Deploy manually: vercel --prod\n');
1615
+ }
1616
+ // Summary
1617
+ results.push('---\n');
1618
+ results.push('## 🎉 Full Deployment Complete!\n');
1619
+ results.push('Your project is now live with:');
1620
+ results.push('- GitHub repo with CI/CD ready');
1621
+ results.push('- Supabase database configured');
1622
+ results.push('- Vercel hosting with auto-deploys\n');
1623
+ results.push('**Start building features - every push auto-deploys!**');
1624
+ return results;
1625
+ }
1626
+ /**
1627
+ * Check if required CLIs are installed
1628
+ */
1629
+ checkRequiredCLIs() {
1630
+ const clis = [
1631
+ { name: 'gh', cmd: 'gh --version', installCmd: 'npm install -g gh' },
1632
+ { name: 'supabase', cmd: 'supabase --version', installCmd: 'npm install -g supabase' },
1633
+ { name: 'vercel', cmd: 'vercel --version', installCmd: 'npm install -g vercel' },
1634
+ ];
1635
+ const installed = [];
1636
+ const missing = [];
1637
+ for (const cli of clis) {
1638
+ try {
1639
+ (0, child_process_1.execSync)(cli.cmd, { stdio: 'pipe' });
1640
+ installed.push(cli.name);
1641
+ }
1642
+ catch {
1643
+ missing.push({ name: cli.name, installCmd: cli.installCmd });
1644
+ }
1645
+ }
1646
+ return { installed, missing };
1647
+ }
1648
+ /**
1649
+ * Get GitHub username from gh CLI
1650
+ */
1651
+ getGitHubUsername() {
1652
+ try {
1653
+ const output = (0, child_process_1.execSync)('gh api user --jq .login', { encoding: 'utf-8' });
1654
+ return output.trim();
1655
+ }
1656
+ catch {
1657
+ return 'YOUR_USERNAME';
1658
+ }
1659
+ }
1660
+ /**
1661
+ * Get Supabase organization ID
1662
+ */
1663
+ getSupabaseOrgId() {
1664
+ try {
1665
+ const output = (0, child_process_1.execSync)('supabase orgs list --output json', { encoding: 'utf-8' });
1666
+ const orgs = JSON.parse(output);
1667
+ return orgs[0]?.id || null;
1668
+ }
1669
+ catch {
1670
+ return null;
1671
+ }
1672
+ }
1673
+ /**
1674
+ * Generate a secure random password for Supabase
1675
+ */
1676
+ generatePassword() {
1677
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
1678
+ let password = '';
1679
+ for (let i = 0; i < 24; i++) {
1680
+ password += chars.charAt(Math.floor(Math.random() * chars.length));
1681
+ }
1682
+ return password;
1683
+ }
1367
1684
  async handleInitProject(args) {
1368
1685
  const cwd = process.cwd();
1369
1686
  const results = [];
@@ -1387,7 +1704,7 @@ phase: setup
1387
1704
  try {
1388
1705
  const response = await fetch(`${this.apiUrl}/api/content`, {
1389
1706
  method: 'GET',
1390
- headers: { Authorization: `Bearer ${this.apiKey}` },
1707
+ headers: this.getAuthHeaders(),
1391
1708
  });
1392
1709
  if (!response.ok) {
1393
1710
  throw new Error('Failed to fetch patterns from API');
@@ -2394,7 +2711,7 @@ Just describe what you want to build! I'll automatically:
2394
2711
  method: 'POST',
2395
2712
  headers: {
2396
2713
  'Content-Type': 'application/json',
2397
- Authorization: `Bearer ${this.apiKey}`,
2714
+ ...this.getAuthHeaders(),
2398
2715
  },
2399
2716
  body: JSON.stringify({
2400
2717
  category,
@@ -2441,7 +2758,7 @@ Just describe what you want to build! I'll automatically:
2441
2758
  method: 'POST',
2442
2759
  headers: {
2443
2760
  'Content-Type': 'application/json',
2444
- Authorization: `Bearer ${this.apiKey}`,
2761
+ ...this.getAuthHeaders(),
2445
2762
  },
2446
2763
  body: JSON.stringify({
2447
2764
  eventType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "2.8.0",
3
+ "version": "3.0.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {