@codebakers/cli 3.8.1 → 3.8.3

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.
@@ -5,8 +5,8 @@ import { join } from 'path';
5
5
  import { getApiKey } from '../config.js';
6
6
  import { getCliVersion } from '../lib/api.js';
7
7
 
8
- // v6.0 Bootstrap files - patterns come from server via MCP
9
- const V6_CLAUDE_MD = `# CodeBakers v6.0
8
+ // Bootstrap files - patterns come from server via MCP
9
+ const CLAUDE_MD_BOOTSTRAP = `# CodeBakers
10
10
 
11
11
  **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
12
12
 
@@ -21,7 +21,7 @@ The server will return:
21
21
  **You cannot write code without calling this tool first. The server tracks compliance.**
22
22
  `;
23
23
 
24
- const V6_CURSORRULES = `# CodeBakers v6.0
24
+ const CURSORRULES_BOOTSTRAP = `# CodeBakers
25
25
 
26
26
  **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
27
27
 
@@ -37,7 +37,7 @@ The server will return:
37
37
  `;
38
38
 
39
39
  export async function install(): Promise<void> {
40
- console.log(chalk.blue('\n CodeBakers Install (v6.0)\n'));
40
+ console.log(chalk.blue('\n CodeBakers Install\n'));
41
41
 
42
42
  const apiKey = getApiKey();
43
43
  if (!apiKey) {
@@ -45,7 +45,7 @@ export async function install(): Promise<void> {
45
45
  process.exit(1);
46
46
  }
47
47
 
48
- const spinner = ora('Installing CodeBakers v6.0...').start();
48
+ const spinner = ora('Installing CodeBakers...').start();
49
49
 
50
50
  try {
51
51
  const cwd = process.cwd();
@@ -53,15 +53,15 @@ export async function install(): Promise<void> {
53
53
  const cursorRulesPath = join(cwd, '.cursorrules');
54
54
  const claudeDir = join(cwd, '.claude');
55
55
 
56
- // Check for existing v5 installation and migrate
56
+ // Remove old .claude folder if it exists (patterns now server-side)
57
57
  if (existsSync(claudeDir)) {
58
- spinner.text = 'Migrating from v5 to v6...';
58
+ spinner.text = 'Removing old pattern files...';
59
59
  rmSync(claudeDir, { recursive: true, force: true });
60
60
  }
61
61
 
62
- // Write v6 bootstrap files
63
- writeFileSync(claudeMdPath, V6_CLAUDE_MD);
64
- writeFileSync(cursorRulesPath, V6_CURSORRULES);
62
+ // Write bootstrap files
63
+ writeFileSync(claudeMdPath, CLAUDE_MD_BOOTSTRAP);
64
+ writeFileSync(cursorRulesPath, CURSORRULES_BOOTSTRAP);
65
65
 
66
66
  // Add .cursorrules to .gitignore if not present
67
67
  const gitignorePath = join(cwd, '.gitignore');
@@ -73,17 +73,16 @@ export async function install(): Promise<void> {
73
73
  }
74
74
  }
75
75
 
76
- spinner.succeed('CodeBakers v6.0 installed!');
76
+ spinner.succeed('CodeBakers installed!');
77
77
 
78
78
  console.log(chalk.gray('\n Files created:'));
79
79
  console.log(chalk.gray(' - CLAUDE.md (Claude Code gateway)'));
80
80
  console.log(chalk.gray(' - .cursorrules (Cursor IDE gateway)'));
81
81
 
82
- console.log(chalk.cyan('\n What\'s new in v6.0:'));
83
- console.log(chalk.gray(' - Patterns are now server-side'));
82
+ console.log(chalk.cyan('\n How it works:'));
83
+ console.log(chalk.gray(' - Patterns are fetched from server in real-time'));
84
84
  console.log(chalk.gray(' - AI calls discover_patterns before coding'));
85
- console.log(chalk.gray(' - Real-time pattern updates'));
86
- console.log(chalk.gray(' - Usage tracking & compliance'));
85
+ console.log(chalk.gray(' - Always up-to-date, no manual updates needed'));
87
86
 
88
87
  console.log(chalk.gray(`\n CLI version: ${getCliVersion()}`));
89
88
  console.log(chalk.blue('\n Start building! AI will fetch patterns via MCP.\n'));
@@ -1,89 +1,12 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
- import { existsSync, writeFileSync, mkdirSync, readFileSync, rmSync, readdirSync, copyFileSync, chmodSync } from 'fs';
3
+ import { existsSync, writeFileSync, readFileSync, rmSync } from 'fs';
4
4
  import { join } from 'path';
5
- import { getApiKey, getApiUrl } from '../config.js';
5
+ import { getApiKey } from '../config.js';
6
6
  import { checkForUpdates, getCliVersion } from '../lib/api.js';
7
7
 
8
- // Pre-commit hook script for session enforcement
9
- const PRE_COMMIT_SCRIPT = `#!/bin/sh
10
- # CodeBakers Pre-Commit Hook - Session Enforcement
11
- # Blocks commits unless AI called discover_patterns and validate_complete
12
- node "$(dirname "$0")/validate-session.js"
13
- exit $?
14
- `;
15
-
16
- const VALIDATE_SESSION_SCRIPT = `#!/usr/bin/env node
17
- const fs = require('fs');
18
- const path = require('path');
19
- const RED = '\\x1b[31m', GREEN = '\\x1b[32m', YELLOW = '\\x1b[33m', CYAN = '\\x1b[36m', RESET = '\\x1b[0m';
20
- function log(c, m) { console.log(c + m + RESET); }
21
-
22
- async function validate() {
23
- const stateFile = path.join(process.cwd(), '.codebakers.json');
24
- if (!fs.existsSync(stateFile)) return { valid: true, reason: 'not-codebakers' };
25
-
26
- let state;
27
- try { state = JSON.parse(fs.readFileSync(stateFile, 'utf-8')); }
28
- catch { return { valid: false, reason: 'invalid-state' }; }
29
-
30
- if (!state.serverEnforced) return { valid: true, reason: 'legacy' };
31
-
32
- const v = state.lastValidation;
33
- if (!v) return { valid: false, reason: 'no-validation', msg: 'AI must call validate_complete before commit' };
34
- if (!v.passed) return { valid: false, reason: 'failed', msg: 'Validation failed - fix issues first' };
35
-
36
- const age = Date.now() - new Date(v.timestamp).getTime();
37
- if (age > 30 * 60 * 1000) return { valid: false, reason: 'stale', msg: 'Validation expired - call validate_complete again' };
38
-
39
- return { valid: true, reason: 'ok' };
40
- }
41
-
42
- async function main() {
43
- console.log(''); log(CYAN, ' 🍪 CodeBakers Pre-Commit');
44
- const r = await validate();
45
- if (r.valid) { log(GREEN, ' ✓ Commit allowed'); console.log(''); process.exit(0); }
46
- else { log(RED, ' ✗ Blocked: ' + r.reason); if (r.msg) log(YELLOW, ' ' + r.msg);
47
- console.log(''); log(YELLOW, ' Bypass: git commit --no-verify'); console.log(''); process.exit(1); }
48
- }
49
- main().catch(e => { log(RED, ' Error: ' + e.message); process.exit(1); });
50
- `;
51
-
52
- /**
53
- * Install pre-commit hook for session enforcement
54
- */
55
- function installPrecommitHook(cwd: string): void {
56
- const gitDir = join(cwd, '.git');
57
- if (!existsSync(gitDir)) {
58
- console.log(chalk.gray(' ⏭️ Skipping pre-commit hook (not a git repo)'));
59
- return;
60
- }
61
-
62
- const hooksDir = join(gitDir, 'hooks');
63
- if (!existsSync(hooksDir)) {
64
- mkdirSync(hooksDir, { recursive: true });
65
- }
66
-
67
- // Write pre-commit hook
68
- const preCommitPath = join(hooksDir, 'pre-commit');
69
- writeFileSync(preCommitPath, PRE_COMMIT_SCRIPT);
70
- try { chmodSync(preCommitPath, '755'); } catch { /* Windows */ }
71
-
72
- // Write validation script
73
- const validatePath = join(hooksDir, 'validate-session.js');
74
- writeFileSync(validatePath, VALIDATE_SESSION_SCRIPT);
75
-
76
- console.log(chalk.green(' ✓ Installed pre-commit hook (blocks commits without validation)'));
77
- }
78
-
79
- interface ContentResponse {
80
- version: string;
81
- router: string;
82
- modules: Record<string, string>;
83
- }
84
-
85
- // Ultra-minimal CLAUDE.md for v6.0 - ALL instructions come from server
86
- const MINIMAL_CLAUDE_MD = `# CodeBakers v6.0
8
+ // Bootstrap files - patterns come from server via MCP
9
+ const CLAUDE_MD_BOOTSTRAP = `# CodeBakers
87
10
 
88
11
  **MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
89
12
 
@@ -98,13 +21,9 @@ The server will return:
98
21
  - Validation instructions
99
22
 
100
23
  **You cannot write code without calling this tool first. The server tracks compliance.**
101
-
102
- ---
103
- *CodeBakers v6.0 - Server-Enforced*
104
24
  `;
105
25
 
106
- // Ultra-minimal .cursorrules for v6.0 - ALL instructions come from server
107
- const MINIMAL_CURSORRULES = `# CodeBakers v6.0
26
+ const CURSORRULES_BOOTSTRAP = `# CodeBakers
108
27
 
109
28
  MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
110
29
 
@@ -116,145 +35,49 @@ You cannot write code without calling this tool first.
116
35
  `;
117
36
 
118
37
  /**
119
- * Compare semantic versions (returns true if v1 < v2)
38
+ * Check if project is using server-enforced mode
120
39
  */
121
- function isVersionLessThan(v1: string, v2: string): boolean {
122
- const parts1 = v1.split('.').map(Number);
123
- const parts2 = v2.split('.').map(Number);
124
- for (let i = 0; i < 3; i++) {
125
- const p1 = parts1[i] || 0;
126
- const p2 = parts2[i] || 0;
127
- if (p1 < p2) return true;
128
- if (p1 > p2) return false;
129
- }
130
- return false;
131
- }
132
-
133
- interface ConfirmData {
134
- version: string;
135
- moduleCount: number;
136
- cliVersion: string;
137
- command: string;
138
- projectName?: string;
139
- }
140
-
141
- /**
142
- * Confirm download to server (non-blocking, fire-and-forget)
143
- */
144
- async function confirmDownload(apiUrl: string, apiKey: string, data: ConfirmData): Promise<void> {
145
- try {
146
- await fetch(`${apiUrl}/api/content/confirm`, {
147
- method: 'POST',
148
- headers: {
149
- 'Content-Type': 'application/json',
150
- 'Authorization': `Bearer ${apiKey}`,
151
- },
152
- body: JSON.stringify(data),
153
- });
154
- } catch {
155
- // Silently ignore - this is just for analytics
156
- }
157
- }
158
-
159
- /**
160
- * Get current installed version from .claude/.version.json
161
- */
162
- function getCurrentVersion(cwd: string): string | null {
163
- const versionFile = join(cwd, '.claude', '.version.json');
164
- const codebakersFile = join(cwd, '.codebakers.json');
165
-
166
- try {
167
- if (existsSync(versionFile)) {
168
- const data = JSON.parse(readFileSync(versionFile, 'utf-8'));
169
- return data.version || null;
170
- }
171
- if (existsSync(codebakersFile)) {
172
- const data = JSON.parse(readFileSync(codebakersFile, 'utf-8'));
173
- return data.version || null;
174
- }
175
- } catch {
176
- // Ignore errors
177
- }
178
- return null;
179
- }
180
-
181
- /**
182
- * Backup old files before migration
183
- */
184
- function backupOldFiles(cwd: string): void {
185
- const backupDir = join(cwd, '.codebakers', 'backup', new Date().toISOString().replace(/[:.]/g, '-'));
186
- mkdirSync(backupDir, { recursive: true });
187
-
188
- // Backup CLAUDE.md
189
- const claudeMd = join(cwd, 'CLAUDE.md');
190
- if (existsSync(claudeMd)) {
191
- copyFileSync(claudeMd, join(backupDir, 'CLAUDE.md'));
192
- }
193
-
194
- // Backup .cursorrules
195
- const cursorrules = join(cwd, '.cursorrules');
196
- if (existsSync(cursorrules)) {
197
- copyFileSync(cursorrules, join(backupDir, '.cursorrules'));
198
- }
199
-
200
- // Backup .claude folder
201
- const claudeDir = join(cwd, '.claude');
202
- if (existsSync(claudeDir)) {
203
- const claudeBackup = join(backupDir, '.claude');
204
- mkdirSync(claudeBackup, { recursive: true });
205
- const files = readdirSync(claudeDir);
206
- for (const file of files) {
207
- const src = join(claudeDir, file);
208
- const dest = join(claudeBackup, file);
209
- try {
210
- copyFileSync(src, dest);
211
- } catch {
212
- // Ignore copy errors
213
- }
40
+ function isServerEnforced(cwd: string): boolean {
41
+ const stateFile = join(cwd, '.codebakers.json');
42
+ if (existsSync(stateFile)) {
43
+ try {
44
+ const state = JSON.parse(readFileSync(stateFile, 'utf-8'));
45
+ return state.serverEnforced === true;
46
+ } catch {
47
+ // Ignore parse errors
214
48
  }
215
49
  }
216
-
217
- console.log(chalk.gray(` Backup saved to: ${backupDir}`));
50
+ return false;
218
51
  }
219
52
 
220
53
  /**
221
- * Migrate to v6.0 server-enforced patterns
54
+ * Migrate project to server-enforced mode
222
55
  */
223
- function migrateToV6(cwd: string): void {
224
- console.log(chalk.yellow('\n 📦 Migrating to v6.0 Server-Enforced Patterns...\n'));
225
-
226
- // Backup old files
227
- console.log(chalk.gray(' Backing up old files...'));
228
- backupOldFiles(cwd);
56
+ function migrateToServerEnforced(cwd: string): void {
57
+ console.log(chalk.yellow('\n 📦 Upgrading to server-enforced patterns...\n'));
229
58
 
230
- // Replace CLAUDE.md with minimal version
231
- const claudeMd = join(cwd, 'CLAUDE.md');
232
- writeFileSync(claudeMd, MINIMAL_CLAUDE_MD);
233
- console.log(chalk.green(' ✓ Updated CLAUDE.md (minimal server-enforced version)'));
59
+ // Update CLAUDE.md
60
+ const claudeMdPath = join(cwd, 'CLAUDE.md');
61
+ writeFileSync(claudeMdPath, CLAUDE_MD_BOOTSTRAP);
62
+ console.log(chalk.green(' ✓ Updated CLAUDE.md'));
234
63
 
235
- // Replace .cursorrules with minimal version
64
+ // Update .cursorrules
236
65
  const cursorrules = join(cwd, '.cursorrules');
237
- writeFileSync(cursorrules, MINIMAL_CURSORRULES);
238
- console.log(chalk.green(' ✓ Updated .cursorrules (minimal server-enforced version)'));
66
+ writeFileSync(cursorrules, CURSORRULES_BOOTSTRAP);
67
+ console.log(chalk.green(' ✓ Updated .cursorrules'));
239
68
 
240
- // Delete .claude folder (patterns now come from server)
69
+ // Remove old .claude folder if it exists (patterns now come from server)
241
70
  const claudeDir = join(cwd, '.claude');
242
71
  if (existsSync(claudeDir)) {
243
72
  try {
244
73
  rmSync(claudeDir, { recursive: true, force: true });
245
74
  console.log(chalk.green(' ✓ Removed .claude/ folder (patterns now server-side)'));
246
- } catch (error) {
75
+ } catch {
247
76
  console.log(chalk.yellow(' ⚠️ Could not remove .claude/ folder - please delete manually'));
248
77
  }
249
78
  }
250
79
 
251
- // Create .codebakers directory if it doesn't exist
252
- const codebakersDir = join(cwd, '.codebakers');
253
- if (!existsSync(codebakersDir)) {
254
- mkdirSync(codebakersDir, { recursive: true });
255
- }
256
-
257
- // Update version in .codebakers.json
80
+ // Update .codebakers.json
258
81
  const stateFile = join(cwd, '.codebakers.json');
259
82
  let state: Record<string, unknown> = {};
260
83
  if (existsSync(stateFile)) {
@@ -264,200 +87,68 @@ function migrateToV6(cwd: string): void {
264
87
  // Ignore errors
265
88
  }
266
89
  }
267
- state.version = '6.0';
268
90
  state.migratedAt = new Date().toISOString();
269
91
  state.serverEnforced = true;
270
92
  writeFileSync(stateFile, JSON.stringify(state, null, 2));
271
93
 
272
- // Auto-install pre-commit hook for enforcement
273
- installPrecommitHook(cwd);
274
-
275
- console.log(chalk.green('\n ✅ Migration to v6.0 complete!\n'));
94
+ console.log(chalk.green('\n ✅ Upgrade complete!\n'));
276
95
  console.log(chalk.cyan(' What changed:'));
277
96
  console.log(chalk.gray(' - Patterns are now fetched from server in real-time'));
278
- console.log(chalk.gray(' - discover_patterns creates a server-tracked session'));
279
- console.log(chalk.gray(' - validate_complete verifies with server before completion'));
280
- console.log(chalk.gray(' - Pre-commit hook blocks commits without validation'));
281
- console.log(chalk.gray(' - No local pattern files needed\n'));
97
+ console.log(chalk.gray(' - AI calls discover_patterns before coding'));
98
+ console.log(chalk.gray(' - No local pattern files needed'));
99
+ console.log(chalk.gray(' - Always up-to-date patterns\n'));
282
100
  }
283
101
 
284
102
  /**
285
- * Upgrade CodeBakers patterns to the latest version
103
+ * Upgrade CodeBakers - checks for CLI updates and ensures server-enforced setup
286
104
  */
287
105
  export async function upgrade(): Promise<void> {
288
106
  console.log(chalk.blue('\n CodeBakers Upgrade\n'));
289
107
 
290
108
  const cwd = process.cwd();
291
109
  const claudeMdPath = join(cwd, 'CLAUDE.md');
292
- const claudeDir = join(cwd, '.claude');
293
110
  const codebakersJson = join(cwd, '.codebakers.json');
294
111
 
295
112
  // Check if this is a CodeBakers project
296
- if (!existsSync(claudeMdPath) && !existsSync(claudeDir) && !existsSync(codebakersJson)) {
113
+ if (!existsSync(claudeMdPath) && !existsSync(codebakersJson)) {
297
114
  console.log(chalk.yellow(' No CodeBakers installation found in this directory.\n'));
298
- console.log(chalk.gray(' Run `codebakers install` to set up patterns first.\n'));
115
+ console.log(chalk.gray(' Run `codebakers init` to set up CodeBakers first.\n'));
299
116
  return;
300
117
  }
301
118
 
302
119
  // Check for CLI updates
303
- console.log(chalk.gray(' Checking for CLI updates...\n'));
304
- const updateInfo = await checkForUpdates();
120
+ const spinner = ora('Checking for CLI updates...').start();
305
121
 
306
- if (updateInfo?.updateAvailable) {
307
- console.log(chalk.yellow(` ⚠️ CLI update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
308
- console.log(chalk.gray(' Run: npm install -g @codebakers/cli@latest\n'));
309
- } else {
310
- console.log(chalk.green(` ✓ CLI is up to date (v${getCliVersion()})\n`));
122
+ try {
123
+ const updateInfo = await checkForUpdates();
124
+
125
+ if (updateInfo?.updateAvailable) {
126
+ spinner.succeed('CLI update available!');
127
+ console.log(chalk.yellow(`\n ⚠️ New CLI version: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
128
+ console.log(chalk.cyan(' Run: npm install -g @codebakers/cli@latest\n'));
129
+ } else {
130
+ spinner.succeed(`CLI is up to date (v${getCliVersion()})`);
131
+ }
132
+ } catch {
133
+ spinner.warn('Could not check for CLI updates');
311
134
  }
312
135
 
313
136
  // Check API key
314
137
  const apiKey = getApiKey();
315
138
  if (!apiKey) {
316
- console.log(chalk.yellow(' Not logged in. Run `codebakers setup` first.\n'));
317
- return;
139
+ console.log(chalk.yellow('\n Not logged in. Run `codebakers login` to authenticate.\n'));
140
+ } else {
141
+ console.log(chalk.green(' ✓ Logged in\n'));
318
142
  }
319
143
 
320
- // Check current version and determine if migration is needed
321
- const currentVersion = getCurrentVersion(cwd);
322
- const spinner = ora('Checking server for latest version...').start();
323
-
324
- try {
325
- const apiUrl = getApiUrl();
326
-
327
- // Check latest version from server
328
- const versionResponse = await fetch(`${apiUrl}/api/content/version`, {
329
- method: 'GET',
330
- headers: {
331
- 'Authorization': `Bearer ${apiKey}`,
332
- },
333
- });
334
-
335
- if (!versionResponse.ok) {
336
- throw new Error('Failed to check version');
337
- }
338
-
339
- const versionData = await versionResponse.json();
340
- const latestVersion = versionData.version;
341
-
342
- spinner.succeed(`Latest version: v${latestVersion}`);
343
-
344
- // Check if we need to migrate to v6.0
345
- const needsV6Migration = currentVersion && isVersionLessThan(currentVersion, '6.0') &&
346
- !isVersionLessThan(latestVersion, '6.0');
347
-
348
- if (needsV6Migration || (!currentVersion && !isVersionLessThan(latestVersion, '6.0'))) {
349
- // Need to migrate to v6.0 server-enforced patterns
350
- migrateToV6(cwd);
351
-
352
- // Confirm migration to server
353
- confirmDownload(apiUrl, apiKey, {
354
- version: '6.0',
355
- moduleCount: 0, // No local modules in v6
356
- cliVersion: getCliVersion(),
357
- command: 'upgrade-v6-migration',
358
- }).catch(() => {});
359
-
360
- return;
361
- }
362
-
363
- // For v6.0+, just confirm the installation is up to date
364
- const stateFile = join(cwd, '.codebakers.json');
365
- let state: Record<string, unknown> = {};
366
- if (existsSync(stateFile)) {
367
- try {
368
- state = JSON.parse(readFileSync(stateFile, 'utf-8'));
369
- } catch {
370
- // Ignore
371
- }
372
- }
373
-
374
- if (state.serverEnforced) {
375
- // Already on v6.0+ server-enforced mode
376
- console.log(chalk.green('\n ✅ Already using v6.0 server-enforced patterns!\n'));
377
- console.log(chalk.gray(' Patterns are fetched from server in real-time.'));
378
- console.log(chalk.gray(' No local updates needed.\n'));
379
- return;
380
- }
381
-
382
- // Legacy upgrade for pre-6.0 versions (fetch full content)
383
- const contentSpinner = ora('Fetching latest patterns...').start();
384
-
385
- const response = await fetch(`${apiUrl}/api/content`, {
386
- method: 'GET',
387
- headers: {
388
- 'Authorization': `Bearer ${apiKey}`,
389
- },
390
- });
391
-
392
- if (!response.ok) {
393
- const error = await response.json().catch(() => ({}));
394
- throw new Error(error.error || 'Failed to fetch patterns');
395
- }
396
-
397
- const content: ContentResponse = await response.json();
398
-
399
- contentSpinner.succeed(`Patterns v${content.version} downloaded`);
400
-
401
- // Count what we're updating
402
- const moduleCount = Object.keys(content.modules).length;
403
-
404
- console.log(chalk.gray(` Updating ${moduleCount} modules...\n`));
405
-
406
- // Update CLAUDE.md
407
- if (content.router) {
408
- writeFileSync(claudeMdPath, content.router);
409
- console.log(chalk.green(' ✓ Updated CLAUDE.md'));
410
- }
411
-
412
- // Update pattern modules
413
- if (content.modules && Object.keys(content.modules).length > 0) {
414
- if (!existsSync(claudeDir)) {
415
- mkdirSync(claudeDir, { recursive: true });
416
- }
417
-
418
- for (const [name, data] of Object.entries(content.modules)) {
419
- writeFileSync(join(claudeDir, name), data);
420
- }
421
-
422
- console.log(chalk.green(` ✓ Updated ${moduleCount} modules in .claude/`));
423
- }
424
-
425
- // Write version file for tracking
426
- const versionInfo = {
427
- version: content.version,
428
- moduleCount,
429
- updatedAt: new Date().toISOString(),
430
- cliVersion: getCliVersion(),
431
- };
432
-
433
- if (!existsSync(claudeDir)) {
434
- mkdirSync(claudeDir, { recursive: true });
435
- }
436
- writeFileSync(join(claudeDir, '.version.json'), JSON.stringify(versionInfo, null, 2));
437
- console.log(chalk.green(' ✓ Version info saved'));
438
-
439
- // Confirm download to server (non-blocking)
440
- confirmDownload(apiUrl, apiKey, {
441
- version: content.version,
442
- moduleCount,
443
- cliVersion: getCliVersion(),
444
- command: 'upgrade',
445
- }).catch(() => {}); // Silently ignore confirmation failures
446
-
447
- console.log(chalk.green(`\n ✅ Upgraded to patterns v${content.version}!\n`));
448
-
449
- // Show what's new if available
450
- console.log(chalk.gray(' Changes take effect in your next AI session.\n'));
451
-
452
- } catch (error) {
453
- spinner.fail('Upgrade failed');
454
- const message = error instanceof Error ? error.message : 'Unknown error';
455
- console.log(chalk.red(`\n Error: ${message}\n`));
456
-
457
- if (message.includes('401') || message.includes('Invalid')) {
458
- console.log(chalk.gray(' Your API key may have expired. Run `codebakers setup` to reconfigure.\n'));
459
- }
460
-
461
- process.exit(1);
144
+ // Check if already using server-enforced patterns
145
+ if (isServerEnforced(cwd)) {
146
+ console.log(chalk.green(' Already using server-enforced patterns!\n'));
147
+ console.log(chalk.gray(' Patterns are fetched from server in real-time.'));
148
+ console.log(chalk.gray(' AI calls discover_patterns before coding - always up to date.\n'));
149
+ return;
462
150
  }
151
+
152
+ // Migrate to server-enforced mode
153
+ migrateToServerEnforced(cwd);
463
154
  }