@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,99 @@
1
+ import chalk from 'chalk';
2
+ import { execSync } from 'child_process';
3
+ import { getApiUrl, getApiKey, getTrialState, isTrialExpired, getTrialDaysRemaining } from '../config.js';
4
+
5
+ /**
6
+ * Open a URL in the default browser
7
+ */
8
+ function openBrowser(url: string): void {
9
+ const platform = process.platform;
10
+
11
+ try {
12
+ if (platform === 'win32') {
13
+ execSync(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
14
+ } else if (platform === 'darwin') {
15
+ execSync(`open "${url}"`, { stdio: 'ignore' });
16
+ } else {
17
+ execSync(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
18
+ stdio: 'ignore',
19
+ shell: '/bin/sh',
20
+ });
21
+ }
22
+ } catch {
23
+ console.log(chalk.yellow(`\n Could not open browser automatically.`));
24
+ console.log(chalk.gray(` Please open this URL manually:\n`));
25
+ console.log(chalk.cyan(` ${url}\n`));
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Open billing page for subscription management
31
+ */
32
+ export async function billing(): Promise<void> {
33
+ console.log(chalk.blue(`
34
+ ╔═══════════════════════════════════════════════════════════╗
35
+ ║ ║
36
+ ║ ${chalk.bold.white('CodeBakers Billing & Subscription')} ║
37
+ ║ ║
38
+ ╚═══════════════════════════════════════════════════════════╝
39
+ `));
40
+
41
+ // Check current status
42
+ const apiKey = getApiKey();
43
+ const trial = getTrialState();
44
+
45
+ if (apiKey) {
46
+ console.log(chalk.green(' ✓ You have an active subscription\n'));
47
+ console.log(chalk.gray(' Opening settings page to manage your subscription...\n'));
48
+
49
+ const apiUrl = getApiUrl();
50
+ const settingsUrl = `${apiUrl}/settings`;
51
+
52
+ openBrowser(settingsUrl);
53
+ console.log(chalk.gray(` ${settingsUrl}\n`));
54
+ return;
55
+ }
56
+
57
+ if (trial) {
58
+ if (isTrialExpired()) {
59
+ console.log(chalk.yellow(' ⚠️ Your trial has expired\n'));
60
+ } else {
61
+ const daysRemaining = getTrialDaysRemaining();
62
+ console.log(chalk.gray(` Trial: ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining\n`));
63
+ }
64
+ }
65
+
66
+ // Show pricing
67
+ console.log(chalk.white(' Choose your plan:\n'));
68
+
69
+ console.log(chalk.cyan(' Pro ') + chalk.white('- $49/month ') + chalk.gray('(1 seat)'));
70
+ console.log(chalk.gray(' All 40 pattern modules'));
71
+ console.log(chalk.gray(' Unlimited projects'));
72
+ console.log(chalk.gray(' Priority support\n'));
73
+
74
+ console.log(chalk.cyan(' Team ') + chalk.white('- $149/month ') + chalk.gray('(5 seats)'));
75
+ console.log(chalk.gray(' Everything in Pro'));
76
+ console.log(chalk.gray(' Team collaboration'));
77
+ console.log(chalk.gray(' Shared API keys\n'));
78
+
79
+ console.log(chalk.cyan(' Agency ') + chalk.white('- $349/month ') + chalk.gray('(unlimited seats)'));
80
+ console.log(chalk.gray(' Everything in Team'));
81
+ console.log(chalk.gray(' White-label option'));
82
+ console.log(chalk.gray(' Dedicated support\n'));
83
+
84
+ console.log(chalk.gray(' Enterprise pricing available for large teams.\n'));
85
+
86
+ // Open billing page
87
+ const apiUrl = getApiUrl();
88
+ const billingUrl = `${apiUrl}/billing`;
89
+
90
+ console.log(chalk.white(' Opening billing page...\n'));
91
+
92
+ openBrowser(billingUrl);
93
+
94
+ console.log(chalk.gray(` ${billingUrl}\n`));
95
+
96
+ console.log(chalk.gray(' After subscribing, run:'));
97
+ console.log(chalk.cyan(' codebakers setup\n'));
98
+ console.log(chalk.gray(' to configure your API key.\n'));
99
+ }
@@ -0,0 +1,157 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { execSync } from 'child_process';
4
+ import {
5
+ getTrialState,
6
+ setTrialState,
7
+ getApiUrl,
8
+ getApiKey,
9
+ isTrialExpired,
10
+ type TrialState,
11
+ } from '../config.js';
12
+
13
+ /**
14
+ * Open a URL in the default browser
15
+ */
16
+ function openBrowser(url: string): void {
17
+ const platform = process.platform;
18
+
19
+ try {
20
+ if (platform === 'win32') {
21
+ execSync(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
22
+ } else if (platform === 'darwin') {
23
+ execSync(`open "${url}"`, { stdio: 'ignore' });
24
+ } else {
25
+ // Linux - try common browsers
26
+ execSync(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
27
+ stdio: 'ignore',
28
+ shell: '/bin/sh',
29
+ });
30
+ }
31
+ } catch {
32
+ console.log(chalk.yellow(`\n Could not open browser automatically.`));
33
+ console.log(chalk.gray(` Please open this URL manually:\n`));
34
+ console.log(chalk.cyan(` ${url}\n`));
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Sleep for a specified number of milliseconds
40
+ */
41
+ function sleep(ms: number): Promise<void> {
42
+ return new Promise((resolve) => setTimeout(resolve, ms));
43
+ }
44
+
45
+ /**
46
+ * Extend trial by connecting GitHub account
47
+ */
48
+ export async function extend(): Promise<void> {
49
+ console.log(chalk.blue(`
50
+ ╔═══════════════════════════════════════════════════════════╗
51
+ ║ ║
52
+ ║ ${chalk.bold.white('Extend Your Trial with GitHub')} ║
53
+ ║ ║
54
+ ╚═══════════════════════════════════════════════════════════╝
55
+ `));
56
+
57
+ // Check if user already has an API key (paid user)
58
+ const apiKey = getApiKey();
59
+ if (apiKey) {
60
+ console.log(chalk.green(' ✓ You\'re already logged in with an API key!\n'));
61
+ console.log(chalk.gray(' You have unlimited access. No extension needed.\n'));
62
+ return;
63
+ }
64
+
65
+ // Check for existing trial
66
+ const trial = getTrialState();
67
+
68
+ if (!trial) {
69
+ console.log(chalk.yellow(' No trial found.\n'));
70
+ console.log(chalk.white(' Start your free trial first:\n'));
71
+ console.log(chalk.cyan(' codebakers go\n'));
72
+ return;
73
+ }
74
+
75
+ // Check if already extended
76
+ if (trial.stage === 'extended') {
77
+ console.log(chalk.yellow(' Your trial has already been extended.\n'));
78
+
79
+ if (isTrialExpired()) {
80
+ console.log(chalk.white(' Ready to upgrade? $49/month for unlimited access:\n'));
81
+ console.log(chalk.cyan(' codebakers upgrade\n'));
82
+ } else {
83
+ const expiresAt = new Date(trial.expiresAt);
84
+ const daysRemaining = Math.max(0, Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)));
85
+ console.log(chalk.gray(` ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining.\n`));
86
+ }
87
+ return;
88
+ }
89
+
90
+ // Check if converted
91
+ if (trial.stage === 'converted') {
92
+ console.log(chalk.green(' ✓ You\'ve upgraded to a paid plan!\n'));
93
+ console.log(chalk.gray(' Run ') + chalk.cyan('codebakers setup') + chalk.gray(' to configure your API key.\n'));
94
+ return;
95
+ }
96
+
97
+ // Open browser for GitHub OAuth
98
+ const apiUrl = getApiUrl();
99
+ const authUrl = `${apiUrl}/api/auth/github?trial_id=${trial.trialId}`;
100
+
101
+ console.log(chalk.white(' Opening browser for GitHub authorization...\n'));
102
+
103
+ openBrowser(authUrl);
104
+
105
+ console.log(chalk.gray(' Waiting for authorization...'));
106
+ console.log(chalk.gray(' (This may take a moment)\n'));
107
+
108
+ // Poll for completion
109
+ const spinner = ora('Checking authorization status...').start();
110
+ let extended = false;
111
+ let pollCount = 0;
112
+ const maxPolls = 60; // 2 minutes max
113
+
114
+ while (pollCount < maxPolls && !extended) {
115
+ await sleep(2000);
116
+ pollCount++;
117
+
118
+ try {
119
+ const response = await fetch(`${apiUrl}/api/trial/status?trialId=${trial.trialId}`);
120
+ const data = await response.json();
121
+
122
+ if (data.stage === 'extended') {
123
+ extended = true;
124
+
125
+ // Update local trial state
126
+ const updatedTrial: TrialState = {
127
+ ...trial,
128
+ stage: 'extended',
129
+ expiresAt: data.expiresAt,
130
+ extendedAt: new Date().toISOString(),
131
+ ...(data.githubUsername && { githubUsername: data.githubUsername }),
132
+ };
133
+
134
+ setTrialState(updatedTrial);
135
+ spinner.succeed('Trial extended!');
136
+ }
137
+ } catch {
138
+ // Ignore polling errors
139
+ }
140
+ }
141
+
142
+ if (extended) {
143
+ console.log(chalk.green(`
144
+ ╔═══════════════════════════════════════════════════════════╗
145
+ ║ ✅ Trial Extended! ║
146
+ ║ ║
147
+ ║ ${chalk.white('You have 7 more days to build with CodeBakers.')} ║
148
+ ║ ║
149
+ ║ ${chalk.gray('Keep building - your project is waiting!')} ║
150
+ ╚═══════════════════════════════════════════════════════════╝
151
+ `));
152
+ } else {
153
+ spinner.warn('Authorization timed out');
154
+ console.log(chalk.yellow('\n Please try again or authorize manually:\n'));
155
+ console.log(chalk.cyan(` ${authUrl}\n`));
156
+ }
157
+ }
@@ -0,0 +1,197 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { execSync } from 'child_process';
4
+ import {
5
+ getTrialState,
6
+ setTrialState,
7
+ getApiUrl,
8
+ getApiKey,
9
+ isTrialExpired,
10
+ getTrialDaysRemaining,
11
+ type TrialState,
12
+ } from '../config.js';
13
+ import { getDeviceFingerprint } from '../lib/fingerprint.js';
14
+
15
+ /**
16
+ * Zero-friction entry point - start using CodeBakers instantly
17
+ */
18
+ export async function go(): Promise<void> {
19
+ console.log(chalk.blue(`
20
+ ╔═══════════════════════════════════════════════════════════╗
21
+ ║ ║
22
+ ║ ${chalk.bold.white('CodeBakers - Zero Setup Required')} ║
23
+ ║ ║
24
+ ╚═══════════════════════════════════════════════════════════╝
25
+ `));
26
+
27
+ // Check if user already has an API key (paid user)
28
+ const apiKey = getApiKey();
29
+ if (apiKey) {
30
+ console.log(chalk.green(' ✓ You\'re already logged in with an API key!\n'));
31
+ console.log(chalk.gray(' Run ') + chalk.cyan('codebakers status') + chalk.gray(' to check your setup.\n'));
32
+ return;
33
+ }
34
+
35
+ // Check existing trial
36
+ const existingTrial = getTrialState();
37
+
38
+ if (existingTrial && !isTrialExpired()) {
39
+ const daysRemaining = getTrialDaysRemaining();
40
+ console.log(chalk.green(` ✓ Trial active (${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining)\n`));
41
+
42
+ if (existingTrial.stage === 'anonymous' && daysRemaining <= 2) {
43
+ console.log(chalk.yellow(' ⚠️ Trial expiring soon! Extend with GitHub:\n'));
44
+ console.log(chalk.cyan(' codebakers extend\n'));
45
+ }
46
+
47
+ await configureMCP();
48
+ return;
49
+ }
50
+
51
+ // Check if trial expired
52
+ if (existingTrial && isTrialExpired()) {
53
+ console.log(chalk.yellow(' ⚠️ Your trial has expired.\n'));
54
+
55
+ if (existingTrial.stage === 'anonymous') {
56
+ console.log(chalk.white(' Extend your trial for 7 more days with GitHub:\n'));
57
+ console.log(chalk.cyan(' codebakers extend\n'));
58
+ console.log(chalk.gray(' Or upgrade to Pro ($49/month):\n'));
59
+ console.log(chalk.cyan(' codebakers upgrade\n'));
60
+ } else {
61
+ console.log(chalk.white(' Ready to upgrade? $49/month for unlimited access:\n'));
62
+ console.log(chalk.cyan(' codebakers upgrade\n'));
63
+ }
64
+ return;
65
+ }
66
+
67
+ // Start new trial
68
+ const spinner = ora('Starting your free trial...').start();
69
+
70
+ try {
71
+ const fingerprint = getDeviceFingerprint();
72
+ const apiUrl = getApiUrl();
73
+
74
+ const response = await fetch(`${apiUrl}/api/trial/start`, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify({
78
+ deviceHash: fingerprint.deviceHash,
79
+ machineId: fingerprint.machineId,
80
+ platform: fingerprint.platform,
81
+ hostname: fingerprint.hostname,
82
+ }),
83
+ });
84
+
85
+ const data = await response.json();
86
+
87
+ if (data.error === 'trial_not_available') {
88
+ spinner.fail('Trial not available');
89
+ console.log(chalk.yellow(`
90
+ It looks like you've already used a CodeBakers trial.
91
+
92
+ Ready to upgrade? $49/month for unlimited access.
93
+
94
+ ${chalk.cyan('codebakers upgrade')} or visit ${chalk.underline('https://codebakers.ai/pricing')}
95
+ `));
96
+ return;
97
+ }
98
+
99
+ if (!response.ok) {
100
+ throw new Error(data.error || 'Failed to start trial');
101
+ }
102
+
103
+ // Check if returning existing trial
104
+ if (data.stage === 'expired') {
105
+ spinner.warn('Your previous trial has expired');
106
+ console.log('');
107
+
108
+ if (data.canExtend) {
109
+ console.log(chalk.white(' Extend your trial for 7 more days with GitHub:\n'));
110
+ console.log(chalk.cyan(' codebakers extend\n'));
111
+ } else {
112
+ console.log(chalk.white(' Ready to upgrade? $49/month for unlimited access:\n'));
113
+ console.log(chalk.cyan(' codebakers upgrade\n'));
114
+ }
115
+ return;
116
+ }
117
+
118
+ // Save trial state
119
+ const trialState: TrialState = {
120
+ trialId: data.trialId,
121
+ stage: data.stage,
122
+ deviceHash: fingerprint.deviceHash,
123
+ expiresAt: data.expiresAt,
124
+ startedAt: data.startedAt,
125
+ ...(data.githubUsername && { githubUsername: data.githubUsername }),
126
+ ...(data.projectId && { projectId: data.projectId }),
127
+ ...(data.projectName && { projectName: data.projectName }),
128
+ };
129
+
130
+ setTrialState(trialState);
131
+
132
+ spinner.succeed(`Trial started (${data.daysRemaining} days free)`);
133
+ console.log('');
134
+
135
+ // Configure MCP
136
+ await configureMCP();
137
+
138
+ // Show success message
139
+ console.log(chalk.green(`
140
+ ╔═══════════════════════════════════════════════════════════╗
141
+ ║ ✅ CodeBakers is ready! ║
142
+ ║ ║
143
+ ║ ${chalk.white('Your 7-day free trial has started.')} ║
144
+ ║ ║
145
+ ║ ${chalk.gray('Try: "Build me a todo app with authentication"')} ║
146
+ ╚═══════════════════════════════════════════════════════════╝
147
+ `));
148
+
149
+ // Attempt auto-restart Claude Code
150
+ await attemptAutoRestart();
151
+
152
+ } catch (error) {
153
+ spinner.fail('Failed to start trial');
154
+
155
+ if (error instanceof Error) {
156
+ if (error.message.includes('fetch') || error.message.includes('network')) {
157
+ console.log(chalk.red('\n Could not connect to CodeBakers server.'));
158
+ console.log(chalk.gray(' Check your internet connection and try again.\n'));
159
+ } else {
160
+ console.log(chalk.red(`\n ${error.message}\n`));
161
+ }
162
+ } else {
163
+ console.log(chalk.red('\n An unexpected error occurred.\n'));
164
+ }
165
+ }
166
+ }
167
+
168
+ async function configureMCP(): Promise<void> {
169
+ const spinner = ora('Configuring Claude Code integration...').start();
170
+ const isWindows = process.platform === 'win32';
171
+
172
+ const mcpCmd = isWindows
173
+ ? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
174
+ : 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
175
+
176
+ try {
177
+ execSync(mcpCmd, { stdio: 'pipe' });
178
+ spinner.succeed('CodeBakers connected to Claude Code');
179
+ } catch (error) {
180
+ const errorMessage = error instanceof Error ? error.message : String(error);
181
+ if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
182
+ spinner.succeed('CodeBakers already connected to Claude Code');
183
+ } else {
184
+ spinner.warn('Could not auto-configure Claude Code');
185
+ console.log(chalk.gray('\n Run this command manually:\n'));
186
+ console.log(chalk.cyan(` ${mcpCmd}\n`));
187
+ }
188
+ }
189
+ }
190
+
191
+ async function attemptAutoRestart(): Promise<void> {
192
+ console.log(chalk.yellow('\n ⚠️ RESTART REQUIRED\n'));
193
+ console.log(chalk.gray(' Close this terminal and open a new one to activate CodeBakers.\n'));
194
+
195
+ // Note: Auto-restart is risky and could lose user work
196
+ // We show instructions instead of forcibly restarting
197
+ }
package/src/config.ts CHANGED
@@ -94,12 +94,28 @@ type ServiceKeys = {
94
94
  [K in ServiceName]: string | null;
95
95
  };
96
96
 
97
+ export type TrialStage = 'anonymous' | 'extended' | 'expired' | 'converted';
98
+
99
+ export interface TrialState {
100
+ trialId: string;
101
+ stage: TrialStage;
102
+ deviceHash: string;
103
+ expiresAt: string; // ISO date
104
+ startedAt: string; // ISO date
105
+ extendedAt?: string; // ISO date
106
+ githubUsername?: string;
107
+ projectId?: string;
108
+ projectName?: string;
109
+ }
110
+
97
111
  interface ConfigSchema {
98
112
  apiKey: string | null;
99
113
  apiUrl: string;
100
114
  experienceLevel: ExperienceLevel;
101
115
  serviceKeys: ServiceKeys;
102
116
  lastKeySync: string | null; // ISO date of last sync with server
117
+ // Trial state (for zero-friction onboarding)
118
+ trial: TrialState | null;
103
119
  }
104
120
 
105
121
  // Create default service keys object with all keys set to null
@@ -109,13 +125,14 @@ const defaultServiceKeys: ServiceKeys = Object.fromEntries(
109
125
 
110
126
  const config = new Conf<ConfigSchema>({
111
127
  projectName: 'codebakers',
112
- projectVersion: '1.7.0',
128
+ projectVersion: '1.8.0',
113
129
  defaults: {
114
130
  apiKey: null,
115
131
  apiUrl: 'https://codebakers.ai',
116
132
  experienceLevel: 'intermediate',
117
133
  serviceKeys: defaultServiceKeys,
118
134
  lastKeySync: null,
135
+ trial: null,
119
136
  },
120
137
  // Migration to add new keys when upgrading from old version
121
138
  migrations: {
@@ -132,6 +149,12 @@ const config = new Conf<ConfigSchema>({
132
149
 
133
150
  store.set('serviceKeys', newKeys);
134
151
  },
152
+ '1.8.0': (store) => {
153
+ // Add trial field if not present
154
+ if (!store.has('trial')) {
155
+ store.set('trial', null);
156
+ }
157
+ },
135
158
  },
136
159
  });
137
160
 
@@ -389,3 +412,80 @@ export function getConfigPath(): string {
389
412
  export function getConfigStore(): ConfigSchema {
390
413
  return config.store;
391
414
  }
415
+
416
+ // ============================================================
417
+ // Trial State Management (Zero-Friction Onboarding)
418
+ // ============================================================
419
+
420
+ export function getTrialState(): TrialState | null {
421
+ return config.get('trial');
422
+ }
423
+
424
+ export function setTrialState(trial: TrialState): void {
425
+ config.set('trial', trial);
426
+ }
427
+
428
+ export function clearTrialState(): void {
429
+ config.set('trial', null);
430
+ }
431
+
432
+ export function updateTrialState(updates: Partial<TrialState>): void {
433
+ const current = config.get('trial');
434
+ if (current) {
435
+ config.set('trial', { ...current, ...updates });
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Check if the current trial has expired
441
+ */
442
+ export function isTrialExpired(): boolean {
443
+ const trial = config.get('trial');
444
+ if (!trial) return true;
445
+
446
+ const expiresAt = new Date(trial.expiresAt);
447
+ return new Date() > expiresAt;
448
+ }
449
+
450
+ /**
451
+ * Get the number of days remaining in the trial
452
+ */
453
+ export function getTrialDaysRemaining(): number {
454
+ const trial = config.get('trial');
455
+ if (!trial) return 0;
456
+
457
+ const expiresAt = new Date(trial.expiresAt);
458
+ const now = new Date();
459
+ const diffMs = expiresAt.getTime() - now.getTime();
460
+ const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
461
+
462
+ return Math.max(0, diffDays);
463
+ }
464
+
465
+ /**
466
+ * Check if user has any valid access (API key OR active trial)
467
+ */
468
+ export function hasValidAccess(): boolean {
469
+ // Paid users always have access
470
+ const apiKey = config.get('apiKey');
471
+ if (apiKey) return true;
472
+
473
+ // Check trial
474
+ const trial = config.get('trial');
475
+ if (!trial) return false;
476
+
477
+ return !isTrialExpired();
478
+ }
479
+
480
+ /**
481
+ * Get authentication mode: 'apiKey', 'trial', or 'none'
482
+ */
483
+ export function getAuthMode(): 'apiKey' | 'trial' | 'none' {
484
+ const apiKey = config.get('apiKey');
485
+ if (apiKey) return 'apiKey';
486
+
487
+ const trial = config.get('trial');
488
+ if (trial && !isTrialExpired()) return 'trial';
489
+
490
+ return 'none';
491
+ }
package/src/index.ts CHANGED
@@ -19,6 +19,9 @@ import { config } from './commands/config.js';
19
19
  import { audit } from './commands/audit.js';
20
20
  import { heal, healWatch } from './commands/heal.js';
21
21
  import { pushPatterns, pushPatternsInteractive } from './commands/push-patterns.js';
22
+ import { go } from './commands/go.js';
23
+ import { extend } from './commands/extend.js';
24
+ import { billing } from './commands/billing.js';
22
25
 
23
26
  // Show welcome message when no command is provided
24
27
  function showWelcome(): void {
@@ -33,7 +36,7 @@ function showWelcome(): void {
33
36
  `));
34
37
 
35
38
  console.log(chalk.white(' Getting Started:\n'));
36
- console.log(chalk.cyan(' codebakers setup') + chalk.gray(' One-time setup (recommended first step)'));
39
+ console.log(chalk.cyan(' codebakers go') + chalk.gray(' Start free trial instantly (no signup!)'));
37
40
  console.log(chalk.cyan(' codebakers scaffold') + chalk.gray(' Create a new project from scratch'));
38
41
  console.log(chalk.cyan(' codebakers init') + chalk.gray(' Add patterns to existing project\n'));
39
42
 
@@ -57,9 +60,9 @@ function showWelcome(): void {
57
60
  console.log(chalk.cyan(' codebakers doctor') + chalk.gray(' Check CodeBakers setup\n'));
58
61
 
59
62
  console.log(chalk.white(' All Commands:\n'));
60
- console.log(chalk.gray(' setup, scaffold, init, generate, upgrade, status, audit, heal, doctor, config, login'));
61
- console.log(chalk.gray(' install, uninstall, install-hook, uninstall-hook'));
62
- console.log(chalk.gray(' serve, mcp-config, mcp-uninstall\n'));
63
+ console.log(chalk.gray(' go, extend, billing, setup, scaffold, init, generate, upgrade, status'));
64
+ console.log(chalk.gray(' audit, heal, doctor, config, login, install, uninstall'));
65
+ console.log(chalk.gray(' install-hook, uninstall-hook, serve, mcp-config, mcp-uninstall\n'));
63
66
 
64
67
  console.log(chalk.gray(' Run ') + chalk.cyan('codebakers <command> --help') + chalk.gray(' for more info\n'));
65
68
  }
@@ -69,9 +72,27 @@ const program = new Command();
69
72
  program
70
73
  .name('codebakers')
71
74
  .description('CodeBakers CLI - Production patterns for AI-assisted development')
72
- .version('2.8.0');
75
+ .version('2.9.0');
73
76
 
74
- // Primary command - one-time setup
77
+ // Zero-friction trial entry (no signup required)
78
+ program
79
+ .command('go')
80
+ .alias('start')
81
+ .description('Start using CodeBakers instantly (no signup required)')
82
+ .action(go);
83
+
84
+ program
85
+ .command('extend')
86
+ .description('Extend your free trial with GitHub')
87
+ .action(extend);
88
+
89
+ program
90
+ .command('billing')
91
+ .alias('subscribe')
92
+ .description('Manage subscription or upgrade to paid plan')
93
+ .action(billing);
94
+
95
+ // Primary command - one-time setup (for paid users)
75
96
  program
76
97
  .command('setup')
77
98
  .description('One-time setup: login + configure Claude Code (recommended)')