@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.
- package/dist/commands/init.js +29 -0
- package/dist/commands/setup.js +52 -19
- package/dist/config.d.ts +17 -0
- package/dist/config.js +48 -1
- package/dist/index.js +65 -2
- package/package.json +1 -1
- package/src/commands/init.ts +30 -0
- package/src/commands/setup.ts +55 -20
- package/src/config.ts +52 -1
- package/src/index.ts +73 -3
package/dist/commands/init.js
CHANGED
|
@@ -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)');
|
package/dist/commands/setup.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
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(' ✅
|
|
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(' ✅
|
|
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(' ⚠️
|
|
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
|
|
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('
|
|
170
|
-
console.log(chalk_1.default.
|
|
171
|
-
console.log(chalk_1.default.
|
|
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.
|
|
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.
|
|
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
|
|
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
package/src/commands/init.ts
CHANGED
|
@@ -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
|
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
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(' ✅
|
|
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(' ✅
|
|
160
|
+
console.log(chalk.green(' ✅ Claude Code MCP server already installed!\n'));
|
|
161
|
+
claudeCodeInstalled = true;
|
|
157
162
|
} else {
|
|
158
|
-
console.log(chalk.yellow(' ⚠️
|
|
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
|
|
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('
|
|
186
|
-
console.log(chalk.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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(),
|