@codebakers/cli 3.3.4 → 3.3.6

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.
@@ -656,6 +656,34 @@ async function init() {
656
656
  (0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorrules'), CURSORRULES_TEMPLATE);
657
657
  // Write .cursorignore
658
658
  (0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
659
+ // Create .cursor/mcp.json for MCP server configuration
660
+ const cursorDir = (0, path_1.join)(cwd, '.cursor');
661
+ if (!(0, fs_1.existsSync)(cursorDir)) {
662
+ (0, fs_1.mkdirSync)(cursorDir, { recursive: true });
663
+ }
664
+ const mcpConfigPath = (0, path_1.join)(cursorDir, 'mcp.json');
665
+ const isWindows = process.platform === 'win32';
666
+ const mcpConfig = {
667
+ mcpServers: {
668
+ codebakers: isWindows
669
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
670
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
671
+ }
672
+ };
673
+ // Merge with existing MCP config if present
674
+ if ((0, fs_1.existsSync)(mcpConfigPath)) {
675
+ try {
676
+ const existing = JSON.parse((0, fs_1.readFileSync)(mcpConfigPath, 'utf-8'));
677
+ existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
678
+ (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(existing, null, 2));
679
+ }
680
+ catch {
681
+ (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
682
+ }
683
+ }
684
+ else {
685
+ (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
686
+ }
659
687
  // Create/merge .vscode/settings.json
660
688
  const vscodeDir = (0, path_1.join)(cwd, '.vscode');
661
689
  if (!(0, fs_1.existsSync)(vscodeDir)) {
@@ -671,6 +699,7 @@ async function init() {
671
699
  (0, fs_1.writeFileSync)(existingSettingsPath, JSON.stringify(VSCODE_SETTINGS_TEMPLATE, null, 2));
672
700
  }
673
701
  cursorSpinner.succeed('Cursor configuration installed!');
702
+ console.log(chalk_1.default.green(' ✓ MCP server configured (.cursor/mcp.json)'));
674
703
  }
675
704
  catch (error) {
676
705
  cursorSpinner.warn('Could not install Cursor files (continuing anyway)');
@@ -8,6 +8,8 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const ora_1 = __importDefault(require("ora"));
9
9
  const readline_1 = require("readline");
10
10
  const child_process_1 = require("child_process");
11
+ const fs_1 = require("fs");
12
+ const path_1 = require("path");
11
13
  const config_js_1 = require("../config.js");
12
14
  const api_js_1 = require("../lib/api.js");
13
15
  const progress_js_1 = require("../lib/progress.js");
@@ -127,47 +129,78 @@ async function setup() {
127
129
  }
128
130
  function showFinalInstructions() {
129
131
  const isWindows = process.platform === 'win32';
132
+ const cwd = process.cwd();
130
133
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
131
- console.log(chalk_1.default.white.bold('\n STEP 3: Connecting CodeBakers to Claude...\n'));
134
+ console.log(chalk_1.default.white.bold('\n STEP 3: Connecting CodeBakers to your IDE...\n'));
132
135
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
133
- // Auto-install MCP server
136
+ // Install MCP for Claude Code CLI
134
137
  const mcpCmd = isWindows
135
138
  ? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
136
139
  : 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
140
+ let claudeCodeInstalled = false;
137
141
  try {
138
142
  (0, child_process_1.execSync)(mcpCmd, { stdio: 'pipe' });
139
- console.log(chalk_1.default.green(' ✅ CodeBakers MCP server installed!\n'));
143
+ console.log(chalk_1.default.green(' ✅ Claude Code MCP server installed!\n'));
144
+ claudeCodeInstalled = true;
140
145
  }
141
146
  catch (error) {
142
- // Check if it's already installed (command might fail if already exists)
143
147
  const errorMessage = error instanceof Error ? error.message : String(error);
144
148
  if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
145
- console.log(chalk_1.default.green(' ✅ CodeBakers MCP server already installed!\n'));
149
+ console.log(chalk_1.default.green(' ✅ Claude Code MCP server already installed!\n'));
150
+ claudeCodeInstalled = true;
146
151
  }
147
152
  else {
148
- console.log(chalk_1.default.yellow(' ⚠️ Could not auto-install MCP server.\n'));
149
- console.log(chalk_1.default.white(' Run this command manually in your terminal:\n'));
150
- console.log(chalk_1.default.bgBlue.white('\n ' + mcpCmd + ' \n'));
151
- console.log(chalk_1.default.yellow('\n ⚠️ Then close this terminal and open a new one in your project.\n'));
152
- return;
153
+ console.log(chalk_1.default.yellow(' ⚠️ Claude Code not detected (that\'s OK if using Cursor)\n'));
153
154
  }
154
155
  }
156
+ // Install MCP for Cursor IDE (create .cursor/mcp.json)
157
+ const cursorDir = (0, path_1.join)(cwd, '.cursor');
158
+ const mcpConfigPath = (0, path_1.join)(cursorDir, 'mcp.json');
159
+ const mcpConfig = {
160
+ mcpServers: {
161
+ codebakers: isWindows
162
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
163
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
164
+ }
165
+ };
166
+ try {
167
+ if (!(0, fs_1.existsSync)(cursorDir)) {
168
+ (0, fs_1.mkdirSync)(cursorDir, { recursive: true });
169
+ }
170
+ // Merge with existing MCP config if present
171
+ if ((0, fs_1.existsSync)(mcpConfigPath)) {
172
+ const existing = JSON.parse((0, fs_1.readFileSync)(mcpConfigPath, 'utf-8'));
173
+ existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
174
+ (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(existing, null, 2));
175
+ }
176
+ else {
177
+ (0, fs_1.writeFileSync)(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
178
+ }
179
+ console.log(chalk_1.default.green(' ✅ Cursor MCP server configured! (.cursor/mcp.json)\n'));
180
+ }
181
+ catch (error) {
182
+ console.log(chalk_1.default.yellow(' ⚠️ Could not create Cursor MCP config\n'));
183
+ }
155
184
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
156
185
  console.log(chalk_1.default.white.bold('\n 🎉 Setup Complete!\n'));
157
186
  console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
158
- console.log(chalk_1.default.yellow.bold(' ⚠️ RESTART REQUIRED - Follow these steps:\n'));
159
- console.log(chalk_1.default.white(' For Claude Code:\n'));
160
- console.log(chalk_1.default.gray(' 1. Close this terminal completely (type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(')'));
161
- console.log(chalk_1.default.gray(' 2. Open a NEW terminal window'));
162
- console.log(chalk_1.default.gray(' 3. Navigate to your project folder'));
163
- console.log(chalk_1.default.gray(' 4. Run ') + chalk_1.default.cyan('claude') + chalk_1.default.gray(' to start Claude Code\n'));
187
+ console.log(chalk_1.default.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
164
188
  console.log(chalk_1.default.white(' For Cursor:\n'));
165
189
  console.log(chalk_1.default.gray(' 1. Close Cursor completely (Cmd+Q / Alt+F4)'));
166
190
  console.log(chalk_1.default.gray(' 2. Reopen Cursor'));
167
191
  console.log(chalk_1.default.gray(' 3. Open Composer (Cmd+I / Ctrl+I)\n'));
192
+ if (claudeCodeInstalled) {
193
+ console.log(chalk_1.default.white(' For Claude Code:\n'));
194
+ console.log(chalk_1.default.gray(' 1. Close this terminal completely (type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(')'));
195
+ console.log(chalk_1.default.gray(' 2. Open a NEW terminal window'));
196
+ console.log(chalk_1.default.gray(' 3. Navigate to your project folder'));
197
+ console.log(chalk_1.default.gray(' 4. Run ') + chalk_1.default.cyan('claude') + chalk_1.default.gray(' to start Claude Code\n'));
198
+ }
168
199
  console.log(chalk_1.default.blue(' ──────────────────────────────────────────────────────────\n'));
169
- console.log(chalk_1.default.white(' To verify CodeBakers is working, ask the AI:\n'));
170
- console.log(chalk_1.default.green(' "Are you using CodeBakers?"\n'));
171
- console.log(chalk_1.default.gray(' The AI should respond with its CodeBakers status.'));
200
+ console.log(chalk_1.default.white(' Next step: Initialize patterns in your project:\n'));
201
+ console.log(chalk_1.default.cyan(' codebakers init\n'));
202
+ console.log(chalk_1.default.white(' To verify CodeBakers is working, ask the AI:\n'));
203
+ console.log(chalk_1.default.green(' "update codebakers patterns"\n'));
204
+ console.log(chalk_1.default.gray(' The AI should call the update_patterns MCP tool.'));
172
205
  console.log(chalk_1.default.gray(' Need help? https://codebakers.ai/docs\n'));
173
206
  }
package/dist/config.d.ts CHANGED
@@ -139,4 +139,21 @@ export declare function setCachedPatternInfo(latestVersion: string): void;
139
139
  * Clear pattern cache to force a fresh check
140
140
  */
141
141
  export declare function clearPatternCache(): void;
142
+ /**
143
+ * Check if we should attempt CLI auto-update
144
+ * Returns false if we recently attempted and failed
145
+ */
146
+ export declare function shouldAttemptCliUpdate(): boolean;
147
+ /**
148
+ * Record a CLI update attempt
149
+ */
150
+ export declare function setCliUpdateAttempt(targetVersion: string, success: boolean): void;
151
+ /**
152
+ * Check if CLI auto-update is disabled
153
+ */
154
+ export declare function isCliAutoUpdateDisabled(): boolean;
155
+ /**
156
+ * Disable/enable CLI auto-update
157
+ */
158
+ export declare function setCliAutoUpdateDisabled(disabled: boolean): void;
142
159
  export {};
package/dist/config.js CHANGED
@@ -38,6 +38,10 @@ exports.getCliVersion = getCliVersion;
38
38
  exports.getCachedPatternInfo = getCachedPatternInfo;
39
39
  exports.setCachedPatternInfo = setCachedPatternInfo;
40
40
  exports.clearPatternCache = clearPatternCache;
41
+ exports.shouldAttemptCliUpdate = shouldAttemptCliUpdate;
42
+ exports.setCliUpdateAttempt = setCliUpdateAttempt;
43
+ exports.isCliAutoUpdateDisabled = isCliAutoUpdateDisabled;
44
+ exports.setCliAutoUpdateDisabled = setCliAutoUpdateDisabled;
41
45
  const conf_1 = __importDefault(require("conf"));
42
46
  const fs_1 = require("fs");
43
47
  const path_1 = require("path");
@@ -458,7 +462,7 @@ function setCachedUpdateInfo(latestVersion) {
458
462
  * Get the current CLI version from package.json
459
463
  */
460
464
  function getCliVersion() {
461
- return '3.3.0'; // Keep in sync with package.json
465
+ return '3.3.5'; // Keep in sync with package.json
462
466
  }
463
467
  // ============================================
464
468
  // Pattern Auto-Update Cache
@@ -491,3 +495,46 @@ function clearPatternCache() {
491
495
  config.set('lastPatternCheck', null);
492
496
  config.set('latestPatternVersion', null);
493
497
  }
498
+ // ============================================
499
+ // CLI Auto-Update Tracking
500
+ // ============================================
501
+ const CLI_UPDATE_COOLDOWN_HOURS = 24; // Don't retry auto-update for 24 hours after failure
502
+ /**
503
+ * Check if we should attempt CLI auto-update
504
+ * Returns false if we recently attempted and failed
505
+ */
506
+ function shouldAttemptCliUpdate() {
507
+ const lastAttempt = config.get('lastCliUpdateAttempt');
508
+ const lastAttemptVersion = config.get('lastCliUpdateAttemptVersion');
509
+ if (!lastAttempt || typeof lastAttempt !== 'string')
510
+ return true;
511
+ const hoursSinceAttempt = (Date.now() - new Date(lastAttempt).getTime()) / (1000 * 60 * 60);
512
+ // If we already successfully updated to this version, don't retry
513
+ const currentVersion = getCliVersion();
514
+ if (lastAttemptVersion === currentVersion)
515
+ return false;
516
+ // If cooldown hasn't passed, don't retry
517
+ if (hoursSinceAttempt < CLI_UPDATE_COOLDOWN_HOURS)
518
+ return false;
519
+ return true;
520
+ }
521
+ /**
522
+ * Record a CLI update attempt
523
+ */
524
+ function setCliUpdateAttempt(targetVersion, success) {
525
+ config.set('lastCliUpdateAttempt', new Date().toISOString());
526
+ config.set('lastCliUpdateAttemptVersion', targetVersion);
527
+ config.set('lastCliUpdateSuccess', success);
528
+ }
529
+ /**
530
+ * Check if CLI auto-update is disabled
531
+ */
532
+ function isCliAutoUpdateDisabled() {
533
+ return config.get('disableCliAutoUpdate') === true;
534
+ }
535
+ /**
536
+ * Disable/enable CLI auto-update
537
+ */
538
+ function setCliAutoUpdateDisabled(disabled) {
539
+ config.set('disableCliAutoUpdate', disabled);
540
+ }
package/dist/index.js CHANGED
@@ -27,13 +27,14 @@ const go_js_1 = require("./commands/go.js");
27
27
  const extend_js_1 = require("./commands/extend.js");
28
28
  const billing_js_1 = require("./commands/billing.js");
29
29
  const config_js_2 = require("./config.js");
30
+ const child_process_1 = require("child_process");
30
31
  const api_js_1 = require("./lib/api.js");
31
32
  const fs_1 = require("fs");
32
33
  const path_1 = require("path");
33
34
  // ============================================
34
35
  // Automatic Update Notification
35
36
  // ============================================
36
- const CURRENT_VERSION = '3.3.1';
37
+ const CURRENT_VERSION = '3.3.6';
37
38
  async function checkForUpdatesInBackground() {
38
39
  // Check if we have a valid cached result first (fast path)
39
40
  const cached = (0, config_js_2.getCachedUpdateInfo)();
@@ -93,6 +94,65 @@ function showUpdateBanner(currentVersion, latestVersion, isRecommended) {
93
94
  ╰─────────────────────────────────────────────────────────╯
94
95
  `));
95
96
  }
97
+ // ============================================
98
+ // CLI Auto-Update
99
+ // ============================================
100
+ async function autoUpdateCli() {
101
+ // Check if auto-update is disabled
102
+ if ((0, config_js_2.isCliAutoUpdateDisabled)())
103
+ return;
104
+ // Check if we should attempt update (cooldown, etc.)
105
+ if (!(0, config_js_2.shouldAttemptCliUpdate)())
106
+ return;
107
+ // Check for available updates
108
+ try {
109
+ const updateInfo = await (0, api_js_1.checkForUpdates)();
110
+ if (!updateInfo || !updateInfo.updateAvailable)
111
+ return;
112
+ // Don't auto-update blocked versions - show warning instead
113
+ if (updateInfo.isBlocked)
114
+ return;
115
+ const targetVersion = updateInfo.latestVersion;
116
+ const currentVersion = CURRENT_VERSION;
117
+ // Only auto-update if the version has auto-update enabled from server
118
+ if (!updateInfo.autoUpdateEnabled)
119
+ return;
120
+ console.log(chalk_1.default.blue(`\n 🔄 Auto-updating CLI: ${chalk_1.default.gray(currentVersion)} → ${chalk_1.default.green(targetVersion)}...\n`));
121
+ try {
122
+ // Run npm install globally
123
+ (0, child_process_1.execSync)('npm install -g @codebakers/cli@latest', {
124
+ stdio: 'inherit',
125
+ timeout: 60000, // 60 second timeout
126
+ });
127
+ (0, config_js_2.setCliUpdateAttempt)(targetVersion, true);
128
+ console.log(chalk_1.default.green(`\n ✓ CLI updated to v${targetVersion}!\n`));
129
+ console.log(chalk_1.default.gray(' The update will take effect on your next command.\n'));
130
+ }
131
+ catch (installError) {
132
+ (0, config_js_2.setCliUpdateAttempt)(targetVersion, false);
133
+ // Check if it's a permission error
134
+ const errorMessage = installError instanceof Error ? installError.message : String(installError);
135
+ if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
136
+ console.log(chalk_1.default.yellow(`
137
+ ⚠️ Auto-update failed (permission denied)
138
+
139
+ Run manually with: ${chalk_1.default.cyan('sudo npm i -g @codebakers/cli@latest')}
140
+ Or disable auto-update: ${chalk_1.default.cyan('codebakers config set disableCliAutoUpdate true')}
141
+ `));
142
+ }
143
+ else {
144
+ console.log(chalk_1.default.yellow(`
145
+ ⚠️ Auto-update failed
146
+
147
+ Run manually: ${chalk_1.default.cyan('npm i -g @codebakers/cli@latest')}
148
+ `));
149
+ }
150
+ }
151
+ }
152
+ catch {
153
+ // Silently fail - don't block CLI for update check
154
+ }
155
+ }
96
156
  function getLocalPatternVersion() {
97
157
  const cwd = process.cwd();
98
158
  const versionFile = (0, path_1.join)(cwd, '.claude', '.version.json');
@@ -406,7 +466,10 @@ program
406
466
  .action(mcp_config_js_1.mcpUninstall);
407
467
  // Add update check hook (runs before every command)
408
468
  program.hook('preAction', async () => {
409
- // Run CLI update check and pattern auto-update in parallel
469
+ // Run CLI auto-update first (if enabled and conditions met)
470
+ // Then run pattern auto-update in parallel with update banner check
471
+ await autoUpdateCli();
472
+ // Run pattern auto-update and update banner check in parallel
410
473
  await Promise.all([
411
474
  checkForUpdatesInBackground(),
412
475
  autoUpdatePatterns(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.3.4",
3
+ "version": "3.3.6",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -700,6 +700,35 @@ export async function init(): Promise<void> {
700
700
  // Write .cursorignore
701
701
  writeFileSync(join(cwd, '.cursorignore'), CURSORIGNORE_TEMPLATE);
702
702
 
703
+ // Create .cursor/mcp.json for MCP server configuration
704
+ const cursorDir = join(cwd, '.cursor');
705
+ if (!existsSync(cursorDir)) {
706
+ mkdirSync(cursorDir, { recursive: true });
707
+ }
708
+
709
+ const mcpConfigPath = join(cursorDir, 'mcp.json');
710
+ const isWindows = process.platform === 'win32';
711
+ const mcpConfig = {
712
+ mcpServers: {
713
+ codebakers: isWindows
714
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
715
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
716
+ }
717
+ };
718
+
719
+ // Merge with existing MCP config if present
720
+ if (existsSync(mcpConfigPath)) {
721
+ try {
722
+ const existing = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
723
+ existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
724
+ writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2));
725
+ } catch {
726
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
727
+ }
728
+ } else {
729
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
730
+ }
731
+
703
732
  // Create/merge .vscode/settings.json
704
733
  const vscodeDir = join(cwd, '.vscode');
705
734
  if (!existsSync(vscodeDir)) {
@@ -717,6 +746,7 @@ export async function init(): Promise<void> {
717
746
  }
718
747
 
719
748
  cursorSpinner.succeed('Cursor configuration installed!');
749
+ console.log(chalk.green(' ✓ MCP server configured (.cursor/mcp.json)'));
720
750
  } catch (error) {
721
751
  cursorSpinner.warn('Could not install Cursor files (continuing anyway)');
722
752
  }
@@ -2,6 +2,8 @@ import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { createInterface } from 'readline';
4
4
  import { execSync } from 'child_process';
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
6
+ import { join } from 'path';
5
7
  import { setApiKey, getApiKey, getApiUrl, syncServiceKeys, SERVICE_KEY_LABELS, type ServiceName } from '../config.js';
6
8
  import { validateApiKey, formatApiError, checkForUpdates, getCliVersion, type ApiError } from '../lib/api.js';
7
9
  import { showQuickStartGuide, formatFriendlyError, getNetworkError, getAuthError } from '../lib/progress.js';
@@ -136,55 +138,88 @@ export async function setup(): Promise<void> {
136
138
 
137
139
  function showFinalInstructions(): void {
138
140
  const isWindows = process.platform === 'win32';
141
+ const cwd = process.cwd();
139
142
 
140
143
  console.log(chalk.blue(' ══════════════════════════════════════════════════════════'));
141
- console.log(chalk.white.bold('\n STEP 3: Connecting CodeBakers to Claude...\n'));
144
+ console.log(chalk.white.bold('\n STEP 3: Connecting CodeBakers to your IDE...\n'));
142
145
  console.log(chalk.blue(' ══════════════════════════════════════════════════════════\n'));
143
146
 
144
- // Auto-install MCP server
147
+ // Install MCP for Claude Code CLI
145
148
  const mcpCmd = isWindows
146
149
  ? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
147
150
  : 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
148
151
 
152
+ let claudeCodeInstalled = false;
149
153
  try {
150
154
  execSync(mcpCmd, { stdio: 'pipe' });
151
- console.log(chalk.green(' ✅ CodeBakers MCP server installed!\n'));
155
+ console.log(chalk.green(' ✅ Claude Code MCP server installed!\n'));
156
+ claudeCodeInstalled = true;
152
157
  } catch (error) {
153
- // Check if it's already installed (command might fail if already exists)
154
158
  const errorMessage = error instanceof Error ? error.message : String(error);
155
159
  if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
156
- console.log(chalk.green(' ✅ CodeBakers MCP server already installed!\n'));
160
+ console.log(chalk.green(' ✅ Claude Code MCP server already installed!\n'));
161
+ claudeCodeInstalled = true;
157
162
  } else {
158
- console.log(chalk.yellow(' ⚠️ Could not auto-install MCP server.\n'));
159
- console.log(chalk.white(' Run this command manually in your terminal:\n'));
160
- console.log(chalk.bgBlue.white('\n ' + mcpCmd + ' \n'));
161
- console.log(chalk.yellow('\n ⚠️ Then close this terminal and open a new one in your project.\n'));
162
- return;
163
+ console.log(chalk.yellow(' ⚠️ Claude Code not detected (that\'s OK if using Cursor)\n'));
163
164
  }
164
165
  }
165
166
 
167
+ // Install MCP for Cursor IDE (create .cursor/mcp.json)
168
+ const cursorDir = join(cwd, '.cursor');
169
+ const mcpConfigPath = join(cursorDir, 'mcp.json');
170
+ const mcpConfig = {
171
+ mcpServers: {
172
+ codebakers: isWindows
173
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@codebakers/cli', 'serve'] }
174
+ : { command: 'npx', args: ['-y', '@codebakers/cli', 'serve'] }
175
+ }
176
+ };
177
+
178
+ try {
179
+ if (!existsSync(cursorDir)) {
180
+ mkdirSync(cursorDir, { recursive: true });
181
+ }
182
+
183
+ // Merge with existing MCP config if present
184
+ if (existsSync(mcpConfigPath)) {
185
+ const existing = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
186
+ existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
187
+ writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2));
188
+ } else {
189
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
190
+ }
191
+ console.log(chalk.green(' ✅ Cursor MCP server configured! (.cursor/mcp.json)\n'));
192
+ } catch (error) {
193
+ console.log(chalk.yellow(' ⚠️ Could not create Cursor MCP config\n'));
194
+ }
195
+
166
196
  console.log(chalk.blue(' ══════════════════════════════════════════════════════════'));
167
197
  console.log(chalk.white.bold('\n 🎉 Setup Complete!\n'));
168
198
  console.log(chalk.blue(' ══════════════════════════════════════════════════════════\n'));
169
199
 
170
- console.log(chalk.yellow.bold(' ⚠️ RESTART REQUIRED - Follow these steps:\n'));
171
-
172
- console.log(chalk.white(' For Claude Code:\n'));
173
- console.log(chalk.gray(' 1. Close this terminal completely (type ') + chalk.cyan('exit') + chalk.gray(')'));
174
- console.log(chalk.gray(' 2. Open a NEW terminal window'));
175
- console.log(chalk.gray(' 3. Navigate to your project folder'));
176
- console.log(chalk.gray(' 4. Run ') + chalk.cyan('claude') + chalk.gray(' to start Claude Code\n'));
200
+ console.log(chalk.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
177
201
 
178
202
  console.log(chalk.white(' For Cursor:\n'));
179
203
  console.log(chalk.gray(' 1. Close Cursor completely (Cmd+Q / Alt+F4)'));
180
204
  console.log(chalk.gray(' 2. Reopen Cursor'));
181
205
  console.log(chalk.gray(' 3. Open Composer (Cmd+I / Ctrl+I)\n'));
182
206
 
207
+ if (claudeCodeInstalled) {
208
+ console.log(chalk.white(' For Claude Code:\n'));
209
+ console.log(chalk.gray(' 1. Close this terminal completely (type ') + chalk.cyan('exit') + chalk.gray(')'));
210
+ console.log(chalk.gray(' 2. Open a NEW terminal window'));
211
+ console.log(chalk.gray(' 3. Navigate to your project folder'));
212
+ console.log(chalk.gray(' 4. Run ') + chalk.cyan('claude') + chalk.gray(' to start Claude Code\n'));
213
+ }
214
+
183
215
  console.log(chalk.blue(' ──────────────────────────────────────────────────────────\n'));
184
216
 
185
- console.log(chalk.white(' To verify CodeBakers is working, ask the AI:\n'));
186
- console.log(chalk.green(' "Are you using CodeBakers?"\n'));
217
+ console.log(chalk.white(' Next step: Initialize patterns in your project:\n'));
218
+ console.log(chalk.cyan(' codebakers init\n'));
219
+
220
+ console.log(chalk.white(' To verify CodeBakers is working, ask the AI:\n'));
221
+ console.log(chalk.green(' "update codebakers patterns"\n'));
187
222
 
188
- console.log(chalk.gray(' The AI should respond with its CodeBakers status.'));
223
+ console.log(chalk.gray(' The AI should call the update_patterns MCP tool.'));
189
224
  console.log(chalk.gray(' Need help? https://codebakers.ai/docs\n'));
190
225
  }
package/src/config.ts CHANGED
@@ -542,7 +542,7 @@ export function setCachedUpdateInfo(latestVersion: string): void {
542
542
  * Get the current CLI version from package.json
543
543
  */
544
544
  export function getCliVersion(): string {
545
- return '3.3.0'; // Keep in sync with package.json
545
+ return '3.3.5'; // Keep in sync with package.json
546
546
  }
547
547
 
548
548
  // ============================================
@@ -581,3 +581,54 @@ export function clearPatternCache(): void {
581
581
  config.set('lastPatternCheck', null);
582
582
  config.set('latestPatternVersion', null);
583
583
  }
584
+
585
+ // ============================================
586
+ // CLI Auto-Update Tracking
587
+ // ============================================
588
+
589
+ const CLI_UPDATE_COOLDOWN_HOURS = 24; // Don't retry auto-update for 24 hours after failure
590
+
591
+ /**
592
+ * Check if we should attempt CLI auto-update
593
+ * Returns false if we recently attempted and failed
594
+ */
595
+ export function shouldAttemptCliUpdate(): boolean {
596
+ const lastAttempt = config.get('lastCliUpdateAttempt') as string | undefined;
597
+ const lastAttemptVersion = config.get('lastCliUpdateAttemptVersion') as string | undefined;
598
+
599
+ if (!lastAttempt || typeof lastAttempt !== 'string') return true;
600
+
601
+ const hoursSinceAttempt = (Date.now() - new Date(lastAttempt).getTime()) / (1000 * 60 * 60);
602
+
603
+ // If we already successfully updated to this version, don't retry
604
+ const currentVersion = getCliVersion();
605
+ if (lastAttemptVersion === currentVersion) return false;
606
+
607
+ // If cooldown hasn't passed, don't retry
608
+ if (hoursSinceAttempt < CLI_UPDATE_COOLDOWN_HOURS) return false;
609
+
610
+ return true;
611
+ }
612
+
613
+ /**
614
+ * Record a CLI update attempt
615
+ */
616
+ export function setCliUpdateAttempt(targetVersion: string, success: boolean): void {
617
+ config.set('lastCliUpdateAttempt', new Date().toISOString());
618
+ config.set('lastCliUpdateAttemptVersion', targetVersion);
619
+ config.set('lastCliUpdateSuccess', success);
620
+ }
621
+
622
+ /**
623
+ * Check if CLI auto-update is disabled
624
+ */
625
+ export function isCliAutoUpdateDisabled(): boolean {
626
+ return config.get('disableCliAutoUpdate') === true;
627
+ }
628
+
629
+ /**
630
+ * Disable/enable CLI auto-update
631
+ */
632
+ export function setCliAutoUpdateDisabled(disabled: boolean): void {
633
+ config.set('disableCliAutoUpdate', disabled);
634
+ }
package/src/index.ts CHANGED
@@ -22,7 +22,8 @@ import { pushPatterns, pushPatternsInteractive } from './commands/push-patterns.
22
22
  import { go } from './commands/go.js';
23
23
  import { extend } from './commands/extend.js';
24
24
  import { billing } from './commands/billing.js';
25
- import { getCachedUpdateInfo, setCachedUpdateInfo, getCliVersion, getCachedPatternInfo, setCachedPatternInfo, getApiKey, getApiUrl, getTrialState, hasValidAccess } from './config.js';
25
+ import { getCachedUpdateInfo, setCachedUpdateInfo, getCliVersion, getCachedPatternInfo, setCachedPatternInfo, getApiKey, getApiUrl, getTrialState, hasValidAccess, shouldAttemptCliUpdate, setCliUpdateAttempt, isCliAutoUpdateDisabled } from './config.js';
26
+ import { execSync } from 'child_process';
26
27
  import { checkForUpdates } from './lib/api.js';
27
28
  import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
28
29
  import { join } from 'path';
@@ -31,7 +32,7 @@ import { join } from 'path';
31
32
  // Automatic Update Notification
32
33
  // ============================================
33
34
 
34
- const CURRENT_VERSION = '3.3.1';
35
+ const CURRENT_VERSION = '3.3.6';
35
36
 
36
37
  async function checkForUpdatesInBackground(): Promise<void> {
37
38
  // Check if we have a valid cached result first (fast path)
@@ -97,6 +98,71 @@ function showUpdateBanner(currentVersion: string, latestVersion: string, isRecom
97
98
  `));
98
99
  }
99
100
 
101
+ // ============================================
102
+ // CLI Auto-Update
103
+ // ============================================
104
+
105
+ async function autoUpdateCli(): Promise<void> {
106
+ // Check if auto-update is disabled
107
+ if (isCliAutoUpdateDisabled()) return;
108
+
109
+ // Check if we should attempt update (cooldown, etc.)
110
+ if (!shouldAttemptCliUpdate()) return;
111
+
112
+ // Check for available updates
113
+ try {
114
+ const updateInfo = await checkForUpdates();
115
+
116
+ if (!updateInfo || !updateInfo.updateAvailable) return;
117
+
118
+ // Don't auto-update blocked versions - show warning instead
119
+ if (updateInfo.isBlocked) return;
120
+
121
+ const targetVersion = updateInfo.latestVersion;
122
+ const currentVersion = CURRENT_VERSION;
123
+
124
+ // Only auto-update if the version has auto-update enabled from server
125
+ if (!updateInfo.autoUpdateEnabled) return;
126
+
127
+ console.log(chalk.blue(`\n 🔄 Auto-updating CLI: ${chalk.gray(currentVersion)} → ${chalk.green(targetVersion)}...\n`));
128
+
129
+ try {
130
+ // Run npm install globally
131
+ execSync('npm install -g @codebakers/cli@latest', {
132
+ stdio: 'inherit',
133
+ timeout: 60000, // 60 second timeout
134
+ });
135
+
136
+ setCliUpdateAttempt(targetVersion, true);
137
+
138
+ console.log(chalk.green(`\n ✓ CLI updated to v${targetVersion}!\n`));
139
+ console.log(chalk.gray(' The update will take effect on your next command.\n'));
140
+
141
+ } catch (installError) {
142
+ setCliUpdateAttempt(targetVersion, false);
143
+
144
+ // Check if it's a permission error
145
+ const errorMessage = installError instanceof Error ? installError.message : String(installError);
146
+ if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
147
+ console.log(chalk.yellow(`
148
+ ⚠️ Auto-update failed (permission denied)
149
+
150
+ Run manually with: ${chalk.cyan('sudo npm i -g @codebakers/cli@latest')}
151
+ Or disable auto-update: ${chalk.cyan('codebakers config set disableCliAutoUpdate true')}
152
+ `));
153
+ } else {
154
+ console.log(chalk.yellow(`
155
+ ⚠️ Auto-update failed
156
+
157
+ Run manually: ${chalk.cyan('npm i -g @codebakers/cli@latest')}
158
+ `));
159
+ }
160
+ }
161
+ } catch {
162
+ // Silently fail - don't block CLI for update check
163
+ }
164
+ }
165
+
100
166
  // ============================================
101
167
  // Automatic Pattern Updates
102
168
  // ============================================
@@ -476,7 +542,11 @@ program
476
542
 
477
543
  // Add update check hook (runs before every command)
478
544
  program.hook('preAction', async () => {
479
- // Run CLI update check and pattern auto-update in parallel
545
+ // Run CLI auto-update first (if enabled and conditions met)
546
+ // Then run pattern auto-update in parallel with update banner check
547
+ await autoUpdateCli();
548
+
549
+ // Run pattern auto-update and update banner check in parallel
480
550
  await Promise.all([
481
551
  checkForUpdatesInBackground(),
482
552
  autoUpdatePatterns(),