@codebakers/cli 1.5.0 → 2.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.
@@ -46,6 +46,31 @@ const child_process_1 = require("child_process");
46
46
  const templates = __importStar(require("../templates/nextjs-supabase.js"));
47
47
  const config_js_1 = require("../config.js");
48
48
  const provision_js_1 = require("./provision.js");
49
+ /**
50
+ * Fetch ALL service keys from CodeBakers server
51
+ */
52
+ async function fetchServerKeys() {
53
+ const apiKey = (0, config_js_1.getApiKey)();
54
+ if (!apiKey)
55
+ return null;
56
+ try {
57
+ const apiUrl = (0, config_js_1.getApiUrl)();
58
+ const response = await fetch(`${apiUrl}/api/cli/service-keys`, {
59
+ headers: {
60
+ Authorization: `Bearer ${apiKey}`,
61
+ },
62
+ });
63
+ if (response.ok) {
64
+ const data = await response.json();
65
+ // Handle wrapped response { data: {...} } or direct response
66
+ return data.data || data;
67
+ }
68
+ }
69
+ catch {
70
+ // Server unreachable or error
71
+ }
72
+ return null;
73
+ }
49
74
  // Cursor IDE configuration templates
50
75
  const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
51
76
  # Zero-friction AI assistance - everything is automatic
@@ -285,6 +310,49 @@ async function confirm(question) {
285
310
  const answer = await prompt(`${question} (Y/n): `);
286
311
  return answer.toLowerCase() !== 'n';
287
312
  }
313
+ /**
314
+ * Display configured keys summary
315
+ */
316
+ function displayKeysSummary(_source) {
317
+ const configuredKeys = (0, config_js_1.getConfiguredServiceKeys)();
318
+ if (configuredKeys.length === 0) {
319
+ console.log(chalk_1.default.gray(' No service keys configured\n'));
320
+ return;
321
+ }
322
+ // Group by provisionable vs other
323
+ const provisionable = configuredKeys.filter(k => config_js_1.PROVISIONABLE_KEYS.includes(k));
324
+ const other = configuredKeys.filter(k => !config_js_1.PROVISIONABLE_KEYS.includes(k));
325
+ if (provisionable.length > 0) {
326
+ console.log(chalk_1.default.gray(' Infrastructure (can auto-provision):'));
327
+ for (const key of provisionable) {
328
+ console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
329
+ }
330
+ }
331
+ if (other.length > 0) {
332
+ console.log(chalk_1.default.gray(' Other services:'));
333
+ for (const key of other) {
334
+ console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
335
+ }
336
+ }
337
+ console.log('');
338
+ }
339
+ /**
340
+ * Display sync results
341
+ */
342
+ function displaySyncResults(result) {
343
+ if (result.added.length > 0) {
344
+ console.log(chalk_1.default.green(` + ${result.added.length} keys synced from server`));
345
+ for (const key of result.added) {
346
+ console.log(chalk_1.default.gray(` + ${config_js_1.SERVICE_KEY_LABELS[key]}`));
347
+ }
348
+ }
349
+ if (result.updated.length > 0) {
350
+ console.log(chalk_1.default.yellow(` ~ ${result.updated.length} keys updated from server`));
351
+ }
352
+ if (result.added.length === 0 && result.updated.length === 0) {
353
+ console.log(chalk_1.default.gray(' All keys already in sync'));
354
+ }
355
+ }
288
356
  /**
289
357
  * Scaffold a new project with full structure
290
358
  */
@@ -438,7 +506,6 @@ async function scaffold() {
438
506
  console.log(chalk_1.default.gray(' Run `npm install` manually.\n'));
439
507
  }
440
508
  }
441
- spinner.succeed('Project structure created!');
442
509
  // Auto-install CodeBakers patterns
443
510
  console.log(chalk_1.default.white('\n Installing CodeBakers patterns...\n'));
444
511
  const apiKey = (0, config_js_1.getApiKey)();
@@ -515,26 +582,113 @@ async function scaffold() {
515
582
  const wantProvision = await confirm(' Auto-provision services?');
516
583
  let provisionResult = {};
517
584
  if (wantProvision) {
518
- // Initialize git first if not already
519
- try {
520
- (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
521
- (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
522
- (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
585
+ // Check for saved keys in CodeBakers back office
586
+ const serverKeys = await fetchServerKeys();
587
+ const serverKeyCount = serverKeys ? Object.values(serverKeys).filter(v => v).length : 0;
588
+ const localKeyCount = (0, config_js_1.getConfiguredServiceKeys)().length;
589
+ if (serverKeyCount > 0 || localKeyCount > 0) {
590
+ // Show which keys are available
591
+ console.log(chalk_1.default.white('\n Available service keys:\n'));
592
+ if (serverKeyCount > 0) {
593
+ console.log(chalk_1.default.gray(` From CodeBakers account (${serverKeyCount} keys):`));
594
+ for (const key of config_js_1.SERVICE_KEYS) {
595
+ if (serverKeys && serverKeys[key]) {
596
+ console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
597
+ }
598
+ }
599
+ }
600
+ if (localKeyCount > 0) {
601
+ console.log(chalk_1.default.gray(` Stored locally (${localKeyCount} keys):`));
602
+ displayKeysSummary('local');
603
+ }
604
+ console.log('');
605
+ // Ask which keys to use
606
+ console.log(chalk_1.default.white(' Which keys would you like to use?\n'));
607
+ console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('Use saved keys') + chalk_1.default.gray(' - Use keys from your account/local storage'));
608
+ console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('Start fresh') + chalk_1.default.gray(' - Clear local keys, enter new ones (for client projects)'));
609
+ console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('Skip') + chalk_1.default.gray(' - Don\'t provision, I\'ll do it manually\n'));
610
+ let keyChoice = '';
611
+ while (!['1', '2', '3'].includes(keyChoice)) {
612
+ keyChoice = await prompt(' Enter 1, 2, or 3: ');
613
+ }
614
+ if (keyChoice === '3') {
615
+ console.log(chalk_1.default.gray('\n Skipping auto-provisioning.\n'));
616
+ }
617
+ else if (keyChoice === '2') {
618
+ // FIX: Actually clear keys for client projects
619
+ console.log(chalk_1.default.yellow('\n Clearing local keys for fresh start...\n'));
620
+ (0, config_js_1.clearAllServiceKeys)();
621
+ console.log(chalk_1.default.green(' ✓ Local keys cleared'));
622
+ console.log(chalk_1.default.gray(' You\'ll be prompted to enter keys for each service.\n'));
623
+ // Initialize git first
624
+ try {
625
+ (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
626
+ (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
627
+ (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
628
+ }
629
+ catch {
630
+ // Git might already be initialized
631
+ }
632
+ provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
633
+ }
634
+ else {
635
+ // Option 1: Use saved keys
636
+ if (serverKeyCount > 0 && serverKeys) {
637
+ // Sync server keys to local storage
638
+ console.log(chalk_1.default.white('\n Syncing keys from CodeBakers account...\n'));
639
+ const syncResult = (0, config_js_1.syncServiceKeys)(serverKeys);
640
+ displaySyncResults(syncResult);
641
+ console.log(chalk_1.default.green('\n ✓ Keys synced from CodeBakers account\n'));
642
+ }
643
+ else {
644
+ console.log(chalk_1.default.green('\n ✓ Using locally stored keys\n'));
645
+ }
646
+ // Initialize git first
647
+ try {
648
+ (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
649
+ (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
650
+ (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
651
+ }
652
+ catch {
653
+ // Git might already be initialized
654
+ }
655
+ provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
656
+ }
523
657
  }
524
- catch {
525
- // Git might already be initialized or have issues
658
+ else {
659
+ // No saved keys - proceed with provisioning (will prompt for keys)
660
+ console.log(chalk_1.default.gray('\n No saved keys found. You\'ll be prompted to enter keys for each service.\n'));
661
+ console.log(chalk_1.default.gray(' Tip: Save keys in your CodeBakers dashboard to auto-provision future projects!\n'));
662
+ // Initialize git first
663
+ try {
664
+ (0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
665
+ (0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
666
+ (0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
667
+ }
668
+ catch {
669
+ // Git might already be initialized
670
+ }
671
+ provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
526
672
  }
527
- provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
528
- // Update .env.local with Supabase credentials if available
673
+ // Write ALL service keys to .env.local
674
+ const additionalVars = {};
675
+ // Add Supabase project-specific vars if provisioned
529
676
  if (provisionResult.supabase) {
530
- const envPath = (0, path_1.join)(cwd, '.env.local');
531
- let envContent = (0, fs_1.existsSync)(envPath) ? (0, fs_1.readFileSync)(envPath, 'utf-8') : '';
532
- // Replace placeholder values with actual credentials
533
- envContent = envContent
534
- .replace('your-project-id.supabase.co', provisionResult.supabase.apiUrl.replace('https://', ''))
535
- .replace('your-anon-key', provisionResult.supabase.anonKey || 'your-anon-key');
536
- (0, fs_1.writeFileSync)(envPath, envContent);
537
- console.log(chalk_1.default.green(' ✅ Updated .env.local with Supabase credentials!\n'));
677
+ additionalVars['NEXT_PUBLIC_SUPABASE_URL'] = provisionResult.supabase.apiUrl;
678
+ additionalVars['NEXT_PUBLIC_SUPABASE_ANON_KEY'] = provisionResult.supabase.anonKey;
679
+ additionalVars['SUPABASE_PROJECT_ID'] = provisionResult.supabase.projectId;
680
+ }
681
+ // Add Vercel project-specific vars if provisioned
682
+ if (provisionResult.vercel) {
683
+ additionalVars['VERCEL_PROJECT_ID'] = provisionResult.vercel.projectId;
684
+ }
685
+ // Write all keys to .env.local
686
+ const { written } = (0, config_js_1.writeKeysToEnvFile)(cwd, {
687
+ includeEmpty: false,
688
+ additionalVars,
689
+ });
690
+ if (written > 0 || Object.keys(additionalVars).length > 0) {
691
+ console.log(chalk_1.default.green(`\n ✅ Wrote ${written} service keys and ${Object.keys(additionalVars).length} project configs to .env.local\n`));
538
692
  }
539
693
  }
540
694
  // Success message
@@ -574,32 +728,58 @@ async function scaffold() {
574
728
  }
575
729
  console.log('');
576
730
  console.log(chalk_1.default.white(' Next steps:\n'));
731
+ // Show appropriate next steps based on what was provisioned
732
+ let stepNum = 1;
733
+ if (!provisionResult.supabase) {
734
+ if (isBeginnerMode) {
735
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Set up Supabase (free database + login):'));
736
+ console.log(chalk_1.default.gray(' Go to https://supabase.com → Create free account → New Project'));
737
+ console.log('');
738
+ stepNum++;
739
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Connect your project:'));
740
+ console.log(chalk_1.default.gray(' Open .env.local file and paste your Supabase credentials'));
741
+ console.log(chalk_1.default.gray(' (Found in Supabase: Settings → API)'));
742
+ console.log('');
743
+ stepNum++;
744
+ }
745
+ else {
746
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.gray('Create Supabase project at https://supabase.com'));
747
+ stepNum++;
748
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.gray('Update .env.local with Supabase credentials'));
749
+ stepNum++;
750
+ }
751
+ }
752
+ // CRITICAL: Add db:push step
753
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Push database schema:'));
754
+ console.log(chalk_1.default.gray(' npx drizzle-kit db:push'));
755
+ console.log('');
756
+ stepNum++;
757
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Start your app:'));
758
+ console.log(chalk_1.default.gray(' npm run dev'));
577
759
  if (isBeginnerMode) {
578
- console.log(chalk_1.default.cyan(' 1. ') + chalk_1.default.white('Set up Supabase (free database + login):'));
579
- console.log(chalk_1.default.gray(' Go to https://supabase.com → Create free account → New Project'));
580
- console.log('');
581
- console.log(chalk_1.default.cyan(' 2. ') + chalk_1.default.white('Connect your project:'));
582
- console.log(chalk_1.default.gray(' Open .env.local file and paste your Supabase credentials'));
583
- console.log(chalk_1.default.gray(' (Found in Supabase: Settings → API)'));
584
- console.log('');
585
- console.log(chalk_1.default.cyan(' 3. ') + chalk_1.default.white('Start your app:'));
586
- console.log(chalk_1.default.gray(' Run: npm run dev'));
587
760
  console.log(chalk_1.default.gray(' Open: http://localhost:3000 in your browser'));
588
- console.log('');
589
- console.log(chalk_1.default.cyan(' 4. ') + chalk_1.default.white('Start building!'));
590
- console.log(chalk_1.default.gray(' Tell your AI: "Build me a [feature]"'));
591
- console.log(chalk_1.default.gray(' The AI already has all the patterns loaded!\n'));
592
761
  }
593
- else {
594
- console.log(chalk_1.default.cyan(' 1. ') + chalk_1.default.gray('Update .env.local with your Supabase credentials'));
595
- console.log(chalk_1.default.cyan(' 2. ') + chalk_1.default.gray('Run `npm run dev` to start development'));
596
- console.log(chalk_1.default.cyan(' 3. ') + chalk_1.default.gray('Tell your AI what to build - patterns are already loaded!\n'));
597
- console.log(chalk_1.default.white(' Supabase setup:\n'));
598
- console.log(chalk_1.default.gray(' 1. Create a project at https://supabase.com'));
599
- console.log(chalk_1.default.gray(' 2. Go to Settings → API'));
600
- console.log(chalk_1.default.gray(' 3. Copy URL and anon key to .env.local'));
601
- console.log(chalk_1.default.gray(' 4. Go to Settings Database Connection string'));
602
- console.log(chalk_1.default.gray(' 5. Copy DATABASE_URL to .env.local\n'));
762
+ console.log('');
763
+ stepNum++;
764
+ console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Start building!'));
765
+ console.log(chalk_1.default.gray(' Tell your AI: "Build me a [feature]"'));
766
+ if (isBeginnerMode) {
767
+ console.log(chalk_1.default.gray(' The AI already has all the patterns loaded!'));
768
+ }
769
+ console.log('');
770
+ // Show provisioning summary if any services were created
771
+ if (provisionResult.github || provisionResult.supabase || provisionResult.vercel) {
772
+ console.log(chalk_1.default.white(' Provisioned services:\n'));
773
+ if (provisionResult.github) {
774
+ console.log(chalk_1.default.green(' ✅ GitHub: ') + chalk_1.default.gray(provisionResult.github.repoUrl));
775
+ }
776
+ if (provisionResult.supabase) {
777
+ console.log(chalk_1.default.green(' ✅ Supabase: ') + chalk_1.default.gray(provisionResult.supabase.projectUrl));
778
+ }
779
+ if (provisionResult.vercel) {
780
+ console.log(chalk_1.default.green(' ✅ Vercel: ') + chalk_1.default.gray(provisionResult.vercel.projectUrl));
781
+ }
782
+ console.log('');
603
783
  }
604
784
  }
605
785
  catch (error) {
@@ -9,6 +9,7 @@ const ora_1 = __importDefault(require("ora"));
9
9
  const readline_1 = require("readline");
10
10
  const child_process_1 = require("child_process");
11
11
  const config_js_1 = require("../config.js");
12
+ const api_js_1 = require("../lib/api.js");
12
13
  function prompt(question) {
13
14
  const rl = (0, readline_1.createInterface)({
14
15
  input: process.stdin,
@@ -25,6 +26,15 @@ async function setup() {
25
26
  console.log(chalk_1.default.blue('\n ╔══════════════════════════════════════╗'));
26
27
  console.log(chalk_1.default.blue(' ║') + chalk_1.default.white(' CodeBakers One-Time Setup ') + chalk_1.default.blue('║'));
27
28
  console.log(chalk_1.default.blue(' ╚══════════════════════════════════════╝\n'));
29
+ // Check CLI version
30
+ const version = (0, api_js_1.getCliVersion)();
31
+ console.log(chalk_1.default.gray(` CLI Version: ${version}\n`));
32
+ // Check for updates
33
+ const updateInfo = await (0, api_js_1.checkForUpdates)();
34
+ if (updateInfo?.updateAvailable) {
35
+ console.log(chalk_1.default.yellow(` ⚠️ Update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
36
+ console.log(chalk_1.default.gray(' Run: npm install -g @codebakers/cli@latest\n'));
37
+ }
28
38
  // Check if already set up
29
39
  const existingKey = (0, config_js_1.getApiKey)();
30
40
  if (existingKey) {
@@ -44,39 +54,70 @@ async function setup() {
44
54
  console.log(chalk_1.default.red('\n API key is required.\n'));
45
55
  process.exit(1);
46
56
  }
47
- // Validate API key
57
+ // Validate API key using shared validation
48
58
  const spinner = (0, ora_1.default)('Validating API key...').start();
49
59
  try {
50
- const apiUrl = (0, config_js_1.getApiUrl)();
51
- const response = await fetch(`${apiUrl}/api/patterns`, {
52
- method: 'GET',
53
- headers: {
54
- Authorization: `Bearer ${apiKey}`,
55
- },
56
- });
57
- if (!response.ok) {
58
- spinner.fail('Invalid API key');
59
- const error = await response.json().catch(() => ({}));
60
- console.log(chalk_1.default.red(`\n ${error.error || 'API key validation failed'}\n`));
61
- process.exit(1);
62
- }
60
+ await (0, api_js_1.validateApiKey)(apiKey);
63
61
  spinner.succeed('API key validated');
64
62
  }
65
- catch {
66
- spinner.fail('Could not connect to CodeBakers');
67
- console.log(chalk_1.default.red('\n Check your internet connection and try again.\n'));
63
+ catch (error) {
64
+ spinner.fail('Invalid API key');
65
+ if (error && typeof error === 'object' && 'recoverySteps' in error) {
66
+ console.log(chalk_1.default.red(`\n ${(0, api_js_1.formatApiError)(error)}\n`));
67
+ }
68
+ else {
69
+ const message = error instanceof Error ? error.message : 'API key validation failed';
70
+ console.log(chalk_1.default.red(`\n ${message}\n`));
71
+ }
68
72
  process.exit(1);
69
73
  }
70
74
  // Save API key
71
75
  (0, config_js_1.setApiKey)(apiKey);
72
76
  console.log(chalk_1.default.green(' ✓ API key saved\n'));
77
+ // Step 2: Sync service keys from server
78
+ console.log(chalk_1.default.white(' Step 2: Syncing service keys...\n'));
79
+ const syncSpinner = (0, ora_1.default)('Fetching service keys from your account...').start();
80
+ try {
81
+ const apiUrl = (0, config_js_1.getApiUrl)();
82
+ const response = await fetch(`${apiUrl}/api/cli/service-keys`, {
83
+ headers: { 'Authorization': `Bearer ${apiKey}` },
84
+ });
85
+ if (response.ok) {
86
+ const data = await response.json();
87
+ const serverKeys = data.data || data;
88
+ const result = (0, config_js_1.syncServiceKeys)(serverKeys);
89
+ const totalSynced = result.added.length + result.updated.length;
90
+ if (totalSynced > 0) {
91
+ syncSpinner.succeed(`Synced ${totalSynced} service keys`);
92
+ // Show which keys were synced
93
+ for (const keyName of [...result.added, ...result.updated]) {
94
+ console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[keyName]}`));
95
+ }
96
+ console.log('');
97
+ }
98
+ else if (result.unchanged.length > 0) {
99
+ syncSpinner.succeed(`${result.unchanged.length} service keys already in sync`);
100
+ }
101
+ else {
102
+ syncSpinner.succeed('No service keys configured in your account');
103
+ console.log(chalk_1.default.gray(' Tip: Add keys at https://codebakers.ai/settings\n'));
104
+ }
105
+ }
106
+ else {
107
+ syncSpinner.warn('Could not sync service keys');
108
+ console.log(chalk_1.default.gray(' You can add keys later in the scaffold wizard.\n'));
109
+ }
110
+ }
111
+ catch {
112
+ syncSpinner.warn('Could not sync service keys');
113
+ console.log(chalk_1.default.gray(' You can add keys later in the scaffold wizard.\n'));
114
+ }
73
115
  showFinalInstructions();
74
116
  }
75
117
  function showFinalInstructions() {
76
118
  const isWindows = process.platform === 'win32';
77
- console.log(chalk_1.default.green('\n ✅ API key saved!\n'));
78
119
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
79
- console.log(chalk_1.default.white.bold('\n STEP 2: Connecting CodeBakers to Claude...\n'));
120
+ console.log(chalk_1.default.white.bold('\n STEP 3: Connecting CodeBakers to Claude...\n'));
80
121
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
81
122
  // Auto-install MCP server
82
123
  const mcpCmd = isWindows
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Upgrade CodeBakers patterns to the latest version
3
+ */
4
+ export declare function upgrade(): Promise<void>;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.upgrade = upgrade;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
11
+ const config_js_1 = require("../config.js");
12
+ const api_js_1 = require("../lib/api.js");
13
+ /**
14
+ * Upgrade CodeBakers patterns to the latest version
15
+ */
16
+ async function upgrade() {
17
+ console.log(chalk_1.default.blue('\n CodeBakers Upgrade\n'));
18
+ const cwd = process.cwd();
19
+ const claudeMdPath = (0, path_1.join)(cwd, 'CLAUDE.md');
20
+ const claudeDir = (0, path_1.join)(cwd, '.claude');
21
+ // Check if this is a CodeBakers project
22
+ if (!(0, fs_1.existsSync)(claudeMdPath) && !(0, fs_1.existsSync)(claudeDir)) {
23
+ console.log(chalk_1.default.yellow(' No CodeBakers installation found in this directory.\n'));
24
+ console.log(chalk_1.default.gray(' Run `codebakers install` to set up patterns first.\n'));
25
+ return;
26
+ }
27
+ // Check for CLI updates
28
+ console.log(chalk_1.default.gray(' Checking for CLI updates...\n'));
29
+ const updateInfo = await (0, api_js_1.checkForUpdates)();
30
+ if (updateInfo?.updateAvailable) {
31
+ console.log(chalk_1.default.yellow(` ⚠️ CLI update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
32
+ console.log(chalk_1.default.gray(' Run: npm install -g @codebakers/cli@latest\n'));
33
+ }
34
+ else {
35
+ console.log(chalk_1.default.green(` ✓ CLI is up to date (v${(0, api_js_1.getCliVersion)()})\n`));
36
+ }
37
+ // Check API key
38
+ const apiKey = (0, config_js_1.getApiKey)();
39
+ if (!apiKey) {
40
+ console.log(chalk_1.default.yellow(' Not logged in. Run `codebakers setup` first.\n'));
41
+ return;
42
+ }
43
+ // Fetch latest patterns
44
+ const spinner = (0, ora_1.default)('Fetching latest patterns...').start();
45
+ try {
46
+ const apiUrl = (0, config_js_1.getApiUrl)();
47
+ const response = await fetch(`${apiUrl}/api/content`, {
48
+ method: 'GET',
49
+ headers: {
50
+ 'Authorization': `Bearer ${apiKey}`,
51
+ },
52
+ });
53
+ if (!response.ok) {
54
+ const error = await response.json().catch(() => ({}));
55
+ throw new Error(error.error || 'Failed to fetch patterns');
56
+ }
57
+ const content = await response.json();
58
+ spinner.succeed(`Patterns v${content.version} downloaded`);
59
+ // Count what we're updating
60
+ const moduleCount = Object.keys(content.modules).length;
61
+ console.log(chalk_1.default.gray(` Updating ${moduleCount} modules...\n`));
62
+ // Update CLAUDE.md
63
+ if (content.router) {
64
+ (0, fs_1.writeFileSync)(claudeMdPath, content.router);
65
+ console.log(chalk_1.default.green(' ✓ Updated CLAUDE.md'));
66
+ }
67
+ // Update pattern modules
68
+ if (content.modules && Object.keys(content.modules).length > 0) {
69
+ if (!(0, fs_1.existsSync)(claudeDir)) {
70
+ (0, fs_1.mkdirSync)(claudeDir, { recursive: true });
71
+ }
72
+ for (const [name, data] of Object.entries(content.modules)) {
73
+ (0, fs_1.writeFileSync)((0, path_1.join)(claudeDir, name), data);
74
+ }
75
+ console.log(chalk_1.default.green(` ✓ Updated ${moduleCount} modules in .claude/`));
76
+ }
77
+ console.log(chalk_1.default.green(`\n ✅ Upgraded to patterns v${content.version}!\n`));
78
+ // Show what's new if available
79
+ console.log(chalk_1.default.gray(' Changes take effect in your next AI session.\n'));
80
+ }
81
+ catch (error) {
82
+ spinner.fail('Upgrade failed');
83
+ const message = error instanceof Error ? error.message : 'Unknown error';
84
+ console.log(chalk_1.default.red(`\n Error: ${message}\n`));
85
+ if (message.includes('401') || message.includes('Invalid')) {
86
+ console.log(chalk_1.default.gray(' Your API key may have expired. Run `codebakers setup` to reconfigure.\n'));
87
+ }
88
+ process.exit(1);
89
+ }
90
+ }
package/dist/config.d.ts CHANGED
@@ -1,8 +1,35 @@
1
1
  export type ExperienceLevel = 'beginner' | 'intermediate' | 'advanced';
2
- interface ServiceKeys {
3
- github: string | null;
4
- supabase: string | null;
5
- vercel: string | null;
2
+ /**
3
+ * Canonical list of ALL supported service keys
4
+ * This must match the server contract at src/lib/contracts/service-keys.ts
5
+ */
6
+ export declare const SERVICE_KEYS: readonly ["github", "supabase", "vercel", "openai", "anthropic", "stripe", "twilio_sid", "twilio_auth", "resend", "vapi", "sentry", "cloudinary", "pexels", "midjourney"];
7
+ export type ServiceName = typeof SERVICE_KEYS[number];
8
+ /**
9
+ * Map service key names to environment variable names
10
+ */
11
+ export declare const ENV_VAR_NAMES: Record<ServiceName, string>;
12
+ /**
13
+ * Service key categories for display grouping
14
+ */
15
+ export declare const SERVICE_KEY_CATEGORIES: Record<string, ServiceName[]>;
16
+ /**
17
+ * Service key labels for display
18
+ */
19
+ export declare const SERVICE_KEY_LABELS: Record<ServiceName, string>;
20
+ /**
21
+ * Keys that can be used for auto-provisioning
22
+ */
23
+ export declare const PROVISIONABLE_KEYS: ServiceName[];
24
+ type ServiceKeys = {
25
+ [K in ServiceName]: string | null;
26
+ };
27
+ interface ConfigSchema {
28
+ apiKey: string | null;
29
+ apiUrl: string;
30
+ experienceLevel: ExperienceLevel;
31
+ serviceKeys: ServiceKeys;
32
+ lastKeySync: string | null;
6
33
  }
7
34
  export declare function getApiKey(): string | null;
8
35
  export declare function setApiKey(key: string): void;
@@ -11,9 +38,38 @@ export declare function getApiUrl(): string;
11
38
  export declare function setApiUrl(url: string): void;
12
39
  export declare function getExperienceLevel(): ExperienceLevel;
13
40
  export declare function setExperienceLevel(level: ExperienceLevel): void;
14
- export type ServiceName = 'github' | 'supabase' | 'vercel';
15
41
  export declare function getServiceKey(service: ServiceName): string | null;
16
42
  export declare function setServiceKey(service: ServiceName, key: string): void;
17
43
  export declare function clearServiceKey(service: ServiceName): void;
44
+ export declare function clearAllServiceKeys(): void;
18
45
  export declare function getAllServiceKeys(): ServiceKeys;
46
+ export declare function getConfiguredServiceKeys(): ServiceName[];
47
+ export declare function getLastKeySync(): Date | null;
48
+ export declare function setLastKeySync(date: Date): void;
49
+ export interface SyncResult {
50
+ added: ServiceName[];
51
+ updated: ServiceName[];
52
+ unchanged: ServiceName[];
53
+ total: number;
54
+ }
55
+ /**
56
+ * Sync keys from server response to local storage
57
+ */
58
+ export declare function syncServiceKeys(serverKeys: Partial<ServiceKeys>): SyncResult;
59
+ /**
60
+ * Write service keys to a .env.local file
61
+ */
62
+ export declare function writeKeysToEnvFile(projectPath: string, options?: {
63
+ includeEmpty?: boolean;
64
+ additionalVars?: Record<string, string>;
65
+ }): {
66
+ written: number;
67
+ path: string;
68
+ };
69
+ /**
70
+ * Check if a key value appears valid (basic format check)
71
+ */
72
+ export declare function validateKeyFormat(name: ServiceName, value: string): boolean;
73
+ export declare function getConfigPath(): string;
74
+ export declare function getConfigStore(): ConfigSchema;
19
75
  export {};