@codebakers/cli 3.3.18 → 3.4.1

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.
@@ -3,6 +3,7 @@ interface GoOptions {
3
3
  }
4
4
  /**
5
5
  * Zero-friction entry point - start using CodeBakers instantly
6
+ * Single command for both trial and paid users
6
7
  */
7
8
  export declare function go(options?: GoOptions): Promise<void>;
8
9
  export {};
@@ -11,6 +11,7 @@ const fs_1 = require("fs");
11
11
  const path_1 = require("path");
12
12
  const readline_1 = require("readline");
13
13
  const config_js_1 = require("../config.js");
14
+ const api_js_1 = require("../lib/api.js");
14
15
  const fingerprint_js_1 = require("../lib/fingerprint.js");
15
16
  function prompt(question) {
16
17
  const rl = (0, readline_1.createInterface)({
@@ -68,6 +69,7 @@ function log(message, options) {
68
69
  }
69
70
  /**
70
71
  * Zero-friction entry point - start using CodeBakers instantly
72
+ * Single command for both trial and paid users
71
73
  */
72
74
  async function go(options = {}) {
73
75
  log('Starting go command...', options);
@@ -76,19 +78,20 @@ async function go(options = {}) {
76
78
  console.log(chalk_1.default.blue(`
77
79
  ╔═══════════════════════════════════════════════════════════╗
78
80
  ║ ║
79
- ║ ${chalk_1.default.bold.white('CodeBakers - Zero Setup Required')}
81
+ ║ ${chalk_1.default.bold.white('CodeBakers - Get Started')}
80
82
  ║ ║
81
83
  ╚═══════════════════════════════════════════════════════════╝
82
84
  `));
83
85
  // Check if user already has an API key (paid user)
84
86
  log('Checking for existing API key...', options);
85
- const apiKey = (0, config_js_1.getApiKey)();
86
- if (apiKey) {
87
- log(`Found API key: ${apiKey.substring(0, 8)}...`, options);
88
- console.log(chalk_1.default.green(' ✓ You\'re already logged in with an API key!\n'));
89
- // Still install patterns if not already installed
90
- await installPatternsWithApiKey(apiKey, options);
87
+ const existingApiKey = (0, config_js_1.getApiKey)();
88
+ if (existingApiKey) {
89
+ log(`Found API key: ${existingApiKey.substring(0, 8)}...`, options);
90
+ console.log(chalk_1.default.green(' ✓ You\'re already logged in!\n'));
91
+ // Install patterns if not already installed
92
+ await installPatternsWithApiKey(existingApiKey, options);
91
93
  await configureMCP(options);
94
+ await showSuccessAndRestart();
92
95
  return;
93
96
  }
94
97
  log('No API key found, checking trial state...', options);
@@ -104,21 +107,33 @@ async function go(options = {}) {
104
107
  // Install patterns if not already installed
105
108
  await installPatterns(existingTrial.trialId, options);
106
109
  await configureMCP(options);
110
+ await showSuccessAndRestart();
107
111
  return;
108
112
  }
109
113
  // Check if trial expired
110
114
  if (existingTrial && (0, config_js_1.isTrialExpired)()) {
111
115
  console.log(chalk_1.default.yellow(' ⚠️ Your trial has expired.\n'));
112
- if (existingTrial.stage === 'anonymous') {
113
- console.log(chalk_1.default.white(' Extend your trial for 7 more days with GitHub:\n'));
114
- console.log(chalk_1.default.cyan(' codebakers extend\n'));
115
- console.log(chalk_1.default.gray(' Or upgrade to Pro ($49/month):\n'));
116
- console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
116
+ // Offer to login with API key or extend
117
+ console.log(chalk_1.default.white(' Options:\n'));
118
+ console.log(chalk_1.default.cyan(' [1] Login with API key') + chalk_1.default.gray(' (I have an account)'));
119
+ console.log(chalk_1.default.cyan(' [2] Extend trial') + chalk_1.default.gray(' (7 more days with GitHub)\n'));
120
+ const choice = await prompt(chalk_1.default.gray(' Enter 1 or 2: '));
121
+ if (choice === '1') {
122
+ await handleApiKeyLogin(options);
123
+ return;
117
124
  }
118
125
  else {
119
- console.log(chalk_1.default.white(' Ready to upgrade? $49/month for unlimited access:\n'));
120
- console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
126
+ console.log(chalk_1.default.cyan('\n Run: codebakers extend\n'));
127
+ return;
121
128
  }
129
+ }
130
+ // New user - ask how they want to proceed
131
+ console.log(chalk_1.default.white(' How would you like to get started?\n'));
132
+ console.log(chalk_1.default.cyan(' [1] Start free 7-day trial') + chalk_1.default.gray(' (no signup required)'));
133
+ console.log(chalk_1.default.cyan(' [2] Login with API key') + chalk_1.default.gray(' (I have an account)\n'));
134
+ const choice = await prompt(chalk_1.default.gray(' Enter 1 or 2: '));
135
+ if (choice === '2') {
136
+ await handleApiKeyLogin(options);
122
137
  return;
123
138
  }
124
139
  // Start new trial
@@ -183,18 +198,8 @@ async function go(options = {}) {
183
198
  await installPatterns(data.trialId, options);
184
199
  // Configure MCP
185
200
  await configureMCP(options);
186
- // Show success message
187
- console.log(chalk_1.default.green(`
188
- ╔═══════════════════════════════════════════════════════════╗
189
- ║ ✅ CodeBakers is ready! ║
190
- ║ ║
191
- ║ ${chalk_1.default.white('Your 7-day free trial has started.')} ║
192
- ║ ║
193
- ║ ${chalk_1.default.gray('Try: "Build me a todo app with authentication"')} ║
194
- ╚═══════════════════════════════════════════════════════════╝
195
- `));
196
- // Attempt auto-restart Claude Code
197
- await attemptAutoRestart();
201
+ // Show success and restart
202
+ await showSuccessAndRestart();
198
203
  }
199
204
  catch (error) {
200
205
  spinner.fail('Failed to start trial');
@@ -235,9 +240,50 @@ async function configureMCP(options = {}) {
235
240
  }
236
241
  }
237
242
  }
238
- async function attemptAutoRestart() {
243
+ /**
244
+ * Handle API key login flow (for paid users)
245
+ */
246
+ async function handleApiKeyLogin(options = {}) {
247
+ console.log(chalk_1.default.white('\n Enter your API key\n'));
248
+ console.log(chalk_1.default.gray(' Find it at: https://codebakers.ai/dashboard\n'));
249
+ const apiKey = await prompt(chalk_1.default.cyan(' API Key: '));
250
+ if (!apiKey) {
251
+ console.log(chalk_1.default.red('\n API key is required.\n'));
252
+ return;
253
+ }
254
+ const spinner = (0, ora_1.default)('Validating API key...').start();
255
+ try {
256
+ await (0, api_js_1.validateApiKey)(apiKey);
257
+ spinner.succeed('API key validated');
258
+ // Save API key
259
+ (0, config_js_1.setApiKey)(apiKey);
260
+ console.log(chalk_1.default.green(' ✓ Logged in successfully!\n'));
261
+ // Install patterns
262
+ await installPatternsWithApiKey(apiKey, options);
263
+ // Configure MCP
264
+ await configureMCP(options);
265
+ // Show success
266
+ await showSuccessAndRestart();
267
+ }
268
+ catch (error) {
269
+ spinner.fail('Invalid API key');
270
+ console.log(chalk_1.default.red('\n Could not validate API key.'));
271
+ console.log(chalk_1.default.gray(' Check your key at: https://codebakers.ai/dashboard\n'));
272
+ }
273
+ }
274
+ /**
275
+ * Show success message and offer to restart
276
+ */
277
+ async function showSuccessAndRestart() {
239
278
  const cwd = process.cwd();
240
- console.log(chalk_1.default.yellow('\n ⚠️ RESTART REQUIRED\n'));
279
+ console.log(chalk_1.default.green(`
280
+ ╔═══════════════════════════════════════════════════════════╗
281
+ ║ ✅ CodeBakers is ready! ║
282
+ ║ ║
283
+ ║ ${chalk_1.default.gray('Try: "Build me a todo app with authentication"')} ║
284
+ ╚═══════════════════════════════════════════════════════════╝
285
+ `));
286
+ console.log(chalk_1.default.yellow(' ⚠️ RESTART REQUIRED\n'));
241
287
  console.log(chalk_1.default.gray(' Claude Code needs to restart to load CodeBakers.\n'));
242
288
  const answer = await prompt(chalk_1.default.cyan(' Restart Claude Code now? (Y/n): '));
243
289
  if (answer === 'n' || answer === 'no') {
@@ -249,7 +295,6 @@ async function attemptAutoRestart() {
249
295
  try {
250
296
  const isWindows = process.platform === 'win32';
251
297
  if (isWindows) {
252
- // On Windows, spawn a new Claude process detached and exit
253
298
  (0, child_process_1.spawn)('cmd', ['/c', 'start', 'claude'], {
254
299
  cwd,
255
300
  detached: true,
@@ -258,7 +303,6 @@ async function attemptAutoRestart() {
258
303
  }).unref();
259
304
  }
260
305
  else {
261
- // On Mac/Linux, spawn claude in new terminal
262
306
  (0, child_process_1.spawn)('claude', [], {
263
307
  cwd,
264
308
  detached: true,
@@ -268,12 +312,10 @@ async function attemptAutoRestart() {
268
312
  }
269
313
  console.log(chalk_1.default.green(' ✓ Claude Code is restarting...\n'));
270
314
  console.log(chalk_1.default.gray(' This terminal will close. Claude Code will open in a new window.\n'));
271
- // Give the spawn a moment to start
272
315
  await new Promise(resolve => setTimeout(resolve, 1000));
273
- // Exit this process
274
316
  process.exit(0);
275
317
  }
276
- catch (error) {
318
+ catch {
277
319
  console.log(chalk_1.default.yellow(' Could not auto-restart. Please restart Claude Code manually.\n'));
278
320
  }
279
321
  }
@@ -0,0 +1 @@
1
+ export declare function installPrecommit(): Promise<void>;
@@ -0,0 +1,229 @@
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.installPrecommit = installPrecommit;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const PRE_COMMIT_SCRIPT = `#!/bin/sh
11
+ # CodeBakers Pre-Commit Hook - Session Enforcement
12
+ # Blocks commits unless AI called discover_patterns and validate_complete
13
+
14
+ # Run the validation script
15
+ node "$(dirname "$0")/validate-session.js"
16
+ exit $?
17
+ `;
18
+ const VALIDATE_SESSION_SCRIPT = `#!/usr/bin/env node
19
+ /**
20
+ * CodeBakers Pre-Commit Validation
21
+ * Blocks commits unless a valid session exists
22
+ */
23
+
24
+ const fs = require('fs');
25
+ const path = require('path');
26
+
27
+ const RED = '\\x1b[31m';
28
+ const GREEN = '\\x1b[32m';
29
+ const YELLOW = '\\x1b[33m';
30
+ const CYAN = '\\x1b[36m';
31
+ const RESET = '\\x1b[0m';
32
+
33
+ function log(color, message) {
34
+ console.log(color + message + RESET);
35
+ }
36
+
37
+ async function validateSession() {
38
+ const cwd = process.cwd();
39
+ const stateFile = path.join(cwd, '.codebakers.json');
40
+
41
+ // Check if this is a CodeBakers project
42
+ if (!fs.existsSync(stateFile)) {
43
+ return { valid: true, reason: 'not-codebakers-project' };
44
+ }
45
+
46
+ let state;
47
+ try {
48
+ state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
49
+ } catch (error) {
50
+ return { valid: false, reason: 'invalid-state-file' };
51
+ }
52
+
53
+ // Check if v6.0 server-enforced mode
54
+ if (!state.serverEnforced) {
55
+ return { valid: true, reason: 'legacy-project' };
56
+ }
57
+
58
+ // Check for session token (means discover_patterns was called)
59
+ const sessionToken = state.currentSessionToken;
60
+ if (!sessionToken) {
61
+ // Check if there's a recent passed validation
62
+ const lastValidation = state.lastValidation;
63
+ if (!lastValidation || !lastValidation.passed) {
64
+ return {
65
+ valid: false,
66
+ reason: 'no-session',
67
+ message: 'No active CodeBakers session.\\nAI must call discover_patterns before writing code.'
68
+ };
69
+ }
70
+ }
71
+
72
+ // Check session expiry
73
+ const sessionExpiry = state.sessionExpiresAt;
74
+ if (sessionExpiry && new Date(sessionExpiry) < new Date()) {
75
+ return {
76
+ valid: false,
77
+ reason: 'session-expired',
78
+ message: 'CodeBakers session has expired.\\nAI must call discover_patterns again.'
79
+ };
80
+ }
81
+
82
+ // Check if validation was completed
83
+ const lastValidation = state.lastValidation;
84
+ if (!lastValidation) {
85
+ return {
86
+ valid: false,
87
+ reason: 'no-validation',
88
+ message: 'No validation completed.\\nAI must call validate_complete before committing.'
89
+ };
90
+ }
91
+
92
+ // Check if validation passed
93
+ if (!lastValidation.passed) {
94
+ const issues = lastValidation.issues?.map(i => i.message || i).join(', ') || 'Unknown issues';
95
+ return {
96
+ valid: false,
97
+ reason: 'validation-failed',
98
+ message: 'Validation failed: ' + issues + '\\nAI must fix issues and call validate_complete again.'
99
+ };
100
+ }
101
+
102
+ // Check if validation is recent (within last 30 minutes)
103
+ const validationTime = new Date(lastValidation.timestamp);
104
+ const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
105
+ if (validationTime < thirtyMinutesAgo) {
106
+ return {
107
+ valid: false,
108
+ reason: 'validation-stale',
109
+ message: 'Validation is stale (older than 30 minutes).\\nAI must call validate_complete again.'
110
+ };
111
+ }
112
+
113
+ return { valid: true, reason: 'session-valid' };
114
+ }
115
+
116
+ async function main() {
117
+ console.log('');
118
+ log(CYAN, ' 🍪 CodeBakers Pre-Commit Validation');
119
+ console.log('');
120
+
121
+ const result = await validateSession();
122
+
123
+ if (result.valid) {
124
+ if (result.reason === 'not-codebakers-project') {
125
+ log(GREEN, ' ✓ Not a CodeBakers project - commit allowed');
126
+ } else if (result.reason === 'legacy-project') {
127
+ log(GREEN, ' ✓ Legacy project (pre-6.0) - commit allowed');
128
+ } else {
129
+ log(GREEN, ' ✓ Valid CodeBakers session - commit allowed');
130
+ }
131
+ console.log('');
132
+ process.exit(0);
133
+ } else {
134
+ log(RED, ' ✗ Commit blocked: ' + result.reason);
135
+ console.log('');
136
+ if (result.message) {
137
+ log(YELLOW, ' ' + result.message.split('\\n').join('\\n '));
138
+ }
139
+ console.log('');
140
+ log(CYAN, ' How to fix:');
141
+ log(RESET, ' 1. AI must call discover_patterns before writing code');
142
+ log(RESET, ' 2. AI must call validate_complete before saying "done"');
143
+ log(RESET, ' 3. Both tools must pass for commits to be allowed');
144
+ console.log('');
145
+ log(YELLOW, ' To bypass (not recommended): git commit --no-verify');
146
+ console.log('');
147
+ process.exit(1);
148
+ }
149
+ }
150
+
151
+ main().catch(error => {
152
+ log(RED, ' Error: ' + error.message);
153
+ process.exit(1);
154
+ });
155
+ `;
156
+ async function installPrecommit() {
157
+ console.log(chalk_1.default.blue('\n CodeBakers Pre-Commit Hook Installation\n'));
158
+ const cwd = process.cwd();
159
+ // Check if this is a git repository
160
+ const gitDir = (0, path_1.join)(cwd, '.git');
161
+ if (!(0, fs_1.existsSync)(gitDir)) {
162
+ console.log(chalk_1.default.red(' ✗ Not a git repository'));
163
+ console.log(chalk_1.default.gray(' Initialize git first: git init\n'));
164
+ process.exit(1);
165
+ }
166
+ // Check if this is a CodeBakers project
167
+ const stateFile = (0, path_1.join)(cwd, '.codebakers.json');
168
+ if (!(0, fs_1.existsSync)(stateFile)) {
169
+ console.log(chalk_1.default.yellow(' ⚠️ No .codebakers.json found'));
170
+ console.log(chalk_1.default.gray(' Run codebakers upgrade first to enable server enforcement\n'));
171
+ }
172
+ // Create hooks directory if it doesn't exist
173
+ const hooksDir = (0, path_1.join)(gitDir, 'hooks');
174
+ if (!(0, fs_1.existsSync)(hooksDir)) {
175
+ (0, fs_1.mkdirSync)(hooksDir, { recursive: true });
176
+ }
177
+ // Write the pre-commit hook
178
+ const preCommitPath = (0, path_1.join)(hooksDir, 'pre-commit');
179
+ (0, fs_1.writeFileSync)(preCommitPath, PRE_COMMIT_SCRIPT);
180
+ // Make it executable (Unix only, Windows ignores this)
181
+ try {
182
+ (0, fs_1.chmodSync)(preCommitPath, '755');
183
+ }
184
+ catch {
185
+ // Windows doesn't support chmod
186
+ }
187
+ console.log(chalk_1.default.green(' ✓ Created pre-commit hook'));
188
+ // Write the validation script
189
+ const validatePath = (0, path_1.join)(hooksDir, 'validate-session.js');
190
+ (0, fs_1.writeFileSync)(validatePath, VALIDATE_SESSION_SCRIPT);
191
+ console.log(chalk_1.default.green(' ✓ Created validation script'));
192
+ // Check if husky is being used
193
+ const huskyDir = (0, path_1.join)(cwd, '.husky');
194
+ if ((0, fs_1.existsSync)(huskyDir)) {
195
+ // Also install in husky
196
+ const huskyPreCommit = (0, path_1.join)(huskyDir, 'pre-commit');
197
+ let huskyContent = '';
198
+ if ((0, fs_1.existsSync)(huskyPreCommit)) {
199
+ huskyContent = (0, fs_1.readFileSync)(huskyPreCommit, 'utf-8');
200
+ if (!huskyContent.includes('validate-session')) {
201
+ huskyContent += '\n# CodeBakers session enforcement\nnode .git/hooks/validate-session.js\n';
202
+ (0, fs_1.writeFileSync)(huskyPreCommit, huskyContent);
203
+ console.log(chalk_1.default.green(' ✓ Added to existing husky pre-commit'));
204
+ }
205
+ else {
206
+ console.log(chalk_1.default.gray(' ✓ Husky hook already configured'));
207
+ }
208
+ }
209
+ else {
210
+ huskyContent = '#!/usr/bin/env sh\n. "$(dirname -- "$0")/_/husky.sh"\n\n# CodeBakers session enforcement\nnode .git/hooks/validate-session.js\n';
211
+ (0, fs_1.writeFileSync)(huskyPreCommit, huskyContent);
212
+ try {
213
+ (0, fs_1.chmodSync)(huskyPreCommit, '755');
214
+ }
215
+ catch {
216
+ // Windows
217
+ }
218
+ console.log(chalk_1.default.green(' ✓ Created husky pre-commit hook'));
219
+ }
220
+ }
221
+ console.log(chalk_1.default.green('\n ✅ Pre-commit hook installed!\n'));
222
+ console.log(chalk_1.default.cyan(' What this does:'));
223
+ console.log(chalk_1.default.gray(' - Blocks commits unless AI called discover_patterns'));
224
+ console.log(chalk_1.default.gray(' - Blocks commits unless AI called validate_complete'));
225
+ console.log(chalk_1.default.gray(' - Requires validation to pass before committing'));
226
+ console.log(chalk_1.default.gray(' - Validation expires after 30 minutes\n'));
227
+ console.log(chalk_1.default.yellow(' To bypass (not recommended):'));
228
+ console.log(chalk_1.default.gray(' git commit --no-verify\n'));
229
+ }