@codebakers/cli 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +17 -0
- package/dist/config.js +47 -2
- package/dist/index.js +147 -3
- package/package.json +1 -1
- package/src/config.ts +54 -3
- package/src/index.ts +185 -4
package/dist/config.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ interface ConfigSchema {
|
|
|
45
45
|
trial: TrialState | null;
|
|
46
46
|
lastUpdateCheck: string | null;
|
|
47
47
|
latestKnownVersion: string | null;
|
|
48
|
+
lastPatternCheck: string | null;
|
|
49
|
+
latestPatternVersion: string | null;
|
|
48
50
|
}
|
|
49
51
|
export declare function getApiKey(): string | null;
|
|
50
52
|
export declare function setApiKey(key: string): void;
|
|
@@ -122,4 +124,19 @@ export declare function setCachedUpdateInfo(latestVersion: string): void;
|
|
|
122
124
|
* Get the current CLI version from package.json
|
|
123
125
|
*/
|
|
124
126
|
export declare function getCliVersion(): string;
|
|
127
|
+
/**
|
|
128
|
+
* Get cached pattern version info if still valid (within 6 hours)
|
|
129
|
+
*/
|
|
130
|
+
export declare function getCachedPatternInfo(): {
|
|
131
|
+
latestVersion: string;
|
|
132
|
+
checkedAt: string;
|
|
133
|
+
} | null;
|
|
134
|
+
/**
|
|
135
|
+
* Cache the latest pattern version from server
|
|
136
|
+
*/
|
|
137
|
+
export declare function setCachedPatternInfo(latestVersion: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* Clear pattern cache to force a fresh check
|
|
140
|
+
*/
|
|
141
|
+
export declare function clearPatternCache(): void;
|
|
125
142
|
export {};
|
package/dist/config.js
CHANGED
|
@@ -35,6 +35,9 @@ exports.getAuthMode = getAuthMode;
|
|
|
35
35
|
exports.getCachedUpdateInfo = getCachedUpdateInfo;
|
|
36
36
|
exports.setCachedUpdateInfo = setCachedUpdateInfo;
|
|
37
37
|
exports.getCliVersion = getCliVersion;
|
|
38
|
+
exports.getCachedPatternInfo = getCachedPatternInfo;
|
|
39
|
+
exports.setCachedPatternInfo = setCachedPatternInfo;
|
|
40
|
+
exports.clearPatternCache = clearPatternCache;
|
|
38
41
|
const conf_1 = __importDefault(require("conf"));
|
|
39
42
|
const fs_1 = require("fs");
|
|
40
43
|
const path_1 = require("path");
|
|
@@ -121,7 +124,7 @@ exports.PROVISIONABLE_KEYS = ['github', 'supabase', 'vercel'];
|
|
|
121
124
|
const defaultServiceKeys = Object.fromEntries(exports.SERVICE_KEYS.map(key => [key, null]));
|
|
122
125
|
const config = new conf_1.default({
|
|
123
126
|
projectName: 'codebakers',
|
|
124
|
-
projectVersion: '1.
|
|
127
|
+
projectVersion: '1.9.0',
|
|
125
128
|
defaults: {
|
|
126
129
|
apiKey: null,
|
|
127
130
|
apiUrl: 'https://codebakers.ai',
|
|
@@ -131,6 +134,8 @@ const config = new conf_1.default({
|
|
|
131
134
|
trial: null,
|
|
132
135
|
lastUpdateCheck: null,
|
|
133
136
|
latestKnownVersion: null,
|
|
137
|
+
lastPatternCheck: null,
|
|
138
|
+
latestPatternVersion: null,
|
|
134
139
|
},
|
|
135
140
|
// Migration to add new keys when upgrading from old version
|
|
136
141
|
migrations: {
|
|
@@ -151,6 +156,15 @@ const config = new conf_1.default({
|
|
|
151
156
|
store.set('trial', null);
|
|
152
157
|
}
|
|
153
158
|
},
|
|
159
|
+
'1.9.0': (store) => {
|
|
160
|
+
// Add pattern auto-update fields if not present
|
|
161
|
+
if (!store.has('lastPatternCheck')) {
|
|
162
|
+
store.set('lastPatternCheck', null);
|
|
163
|
+
}
|
|
164
|
+
if (!store.has('latestPatternVersion')) {
|
|
165
|
+
store.set('latestPatternVersion', null);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
154
168
|
},
|
|
155
169
|
});
|
|
156
170
|
// ============================================================
|
|
@@ -444,5 +458,36 @@ function setCachedUpdateInfo(latestVersion) {
|
|
|
444
458
|
* Get the current CLI version from package.json
|
|
445
459
|
*/
|
|
446
460
|
function getCliVersion() {
|
|
447
|
-
return '3.
|
|
461
|
+
return '3.3.0'; // Keep in sync with package.json
|
|
462
|
+
}
|
|
463
|
+
// ============================================
|
|
464
|
+
// Pattern Auto-Update Cache
|
|
465
|
+
// ============================================
|
|
466
|
+
const PATTERN_CHECK_INTERVAL_HOURS = 6; // Check more frequently than CLI
|
|
467
|
+
/**
|
|
468
|
+
* Get cached pattern version info if still valid (within 6 hours)
|
|
469
|
+
*/
|
|
470
|
+
function getCachedPatternInfo() {
|
|
471
|
+
const lastCheck = config.get('lastPatternCheck');
|
|
472
|
+
const latestVersion = config.get('latestPatternVersion');
|
|
473
|
+
if (!lastCheck || !latestVersion)
|
|
474
|
+
return null;
|
|
475
|
+
const hoursSinceCheck = (Date.now() - new Date(lastCheck).getTime()) / (1000 * 60 * 60);
|
|
476
|
+
if (hoursSinceCheck > PATTERN_CHECK_INTERVAL_HOURS)
|
|
477
|
+
return null;
|
|
478
|
+
return { latestVersion, checkedAt: lastCheck };
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Cache the latest pattern version from server
|
|
482
|
+
*/
|
|
483
|
+
function setCachedPatternInfo(latestVersion) {
|
|
484
|
+
config.set('lastPatternCheck', new Date().toISOString());
|
|
485
|
+
config.set('latestPatternVersion', latestVersion);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Clear pattern cache to force a fresh check
|
|
489
|
+
*/
|
|
490
|
+
function clearPatternCache() {
|
|
491
|
+
config.set('lastPatternCheck', null);
|
|
492
|
+
config.set('latestPatternVersion', null);
|
|
448
493
|
}
|
package/dist/index.js
CHANGED
|
@@ -27,10 +27,12 @@ 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 fs_1 = require("fs");
|
|
31
|
+
const path_1 = require("path");
|
|
30
32
|
// ============================================
|
|
31
33
|
// Automatic Update Notification
|
|
32
34
|
// ============================================
|
|
33
|
-
const CURRENT_VERSION = '3.
|
|
35
|
+
const CURRENT_VERSION = '3.3.0';
|
|
34
36
|
async function checkForUpdatesInBackground() {
|
|
35
37
|
// Check if we have a valid cached result first (fast path)
|
|
36
38
|
const cached = (0, config_js_2.getCachedUpdateInfo)();
|
|
@@ -73,6 +75,144 @@ function showUpdateBanner(currentVersion, latestVersion) {
|
|
|
73
75
|
╰─────────────────────────────────────────────────────────╯
|
|
74
76
|
`));
|
|
75
77
|
}
|
|
78
|
+
function getLocalPatternVersion() {
|
|
79
|
+
const cwd = process.cwd();
|
|
80
|
+
const versionFile = (0, path_1.join)(cwd, '.claude', '.version.json');
|
|
81
|
+
if (!(0, fs_1.existsSync)(versionFile))
|
|
82
|
+
return null;
|
|
83
|
+
try {
|
|
84
|
+
const content = (0, fs_1.readFileSync)(versionFile, 'utf-8');
|
|
85
|
+
const info = JSON.parse(content);
|
|
86
|
+
return info.version;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function isCodeBakersProject() {
|
|
93
|
+
const cwd = process.cwd();
|
|
94
|
+
return (0, fs_1.existsSync)((0, path_1.join)(cwd, 'CLAUDE.md')) || (0, fs_1.existsSync)((0, path_1.join)(cwd, '.claude'));
|
|
95
|
+
}
|
|
96
|
+
async function autoUpdatePatterns() {
|
|
97
|
+
// Only auto-update if this is a CodeBakers project
|
|
98
|
+
if (!isCodeBakersProject())
|
|
99
|
+
return;
|
|
100
|
+
// Only auto-update if user has valid access
|
|
101
|
+
if (!(0, config_js_2.hasValidAccess)())
|
|
102
|
+
return;
|
|
103
|
+
const localVersion = getLocalPatternVersion();
|
|
104
|
+
// Check if we have a valid cached result first (fast path)
|
|
105
|
+
const cached = (0, config_js_2.getCachedPatternInfo)();
|
|
106
|
+
if (cached) {
|
|
107
|
+
// If local matches latest, nothing to do
|
|
108
|
+
if (localVersion === cached.latestVersion)
|
|
109
|
+
return;
|
|
110
|
+
// If we know there's an update but haven't updated yet, do it now
|
|
111
|
+
if (localVersion !== cached.latestVersion) {
|
|
112
|
+
await performPatternUpdate(cached.latestVersion);
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Fetch from server to check for updates (with timeout)
|
|
117
|
+
try {
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
120
|
+
const apiUrl = (0, config_js_2.getApiUrl)();
|
|
121
|
+
const apiKey = (0, config_js_2.getApiKey)();
|
|
122
|
+
const trial = (0, config_js_2.getTrialState)();
|
|
123
|
+
// Build authorization header
|
|
124
|
+
let authHeader = '';
|
|
125
|
+
if (apiKey) {
|
|
126
|
+
authHeader = `Bearer ${apiKey}`;
|
|
127
|
+
}
|
|
128
|
+
else if (trial?.trialId) {
|
|
129
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
130
|
+
}
|
|
131
|
+
if (!authHeader)
|
|
132
|
+
return;
|
|
133
|
+
// First, check the version endpoint (lightweight)
|
|
134
|
+
const versionResponse = await fetch(`${apiUrl}/api/content/version`, {
|
|
135
|
+
method: 'GET',
|
|
136
|
+
headers: {
|
|
137
|
+
'Authorization': authHeader,
|
|
138
|
+
},
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
});
|
|
141
|
+
clearTimeout(timeout);
|
|
142
|
+
if (versionResponse.ok) {
|
|
143
|
+
const versionData = await versionResponse.json();
|
|
144
|
+
const serverVersion = versionData.version;
|
|
145
|
+
// Cache the version info
|
|
146
|
+
(0, config_js_2.setCachedPatternInfo)(serverVersion);
|
|
147
|
+
// If local version is different, update
|
|
148
|
+
if (localVersion !== serverVersion) {
|
|
149
|
+
await performPatternUpdate(serverVersion);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Silently fail - don't block CLI for pattern check
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function performPatternUpdate(targetVersion) {
|
|
158
|
+
const cwd = process.cwd();
|
|
159
|
+
const claudeMdPath = (0, path_1.join)(cwd, 'CLAUDE.md');
|
|
160
|
+
const claudeDir = (0, path_1.join)(cwd, '.claude');
|
|
161
|
+
try {
|
|
162
|
+
const apiUrl = (0, config_js_2.getApiUrl)();
|
|
163
|
+
const apiKey = (0, config_js_2.getApiKey)();
|
|
164
|
+
const trial = (0, config_js_2.getTrialState)();
|
|
165
|
+
let authHeader = '';
|
|
166
|
+
if (apiKey) {
|
|
167
|
+
authHeader = `Bearer ${apiKey}`;
|
|
168
|
+
}
|
|
169
|
+
else if (trial?.trialId) {
|
|
170
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
171
|
+
}
|
|
172
|
+
if (!authHeader)
|
|
173
|
+
return;
|
|
174
|
+
const controller = new AbortController();
|
|
175
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
176
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
177
|
+
method: 'GET',
|
|
178
|
+
headers: {
|
|
179
|
+
'Authorization': authHeader,
|
|
180
|
+
},
|
|
181
|
+
signal: controller.signal,
|
|
182
|
+
});
|
|
183
|
+
clearTimeout(timeout);
|
|
184
|
+
if (!response.ok)
|
|
185
|
+
return;
|
|
186
|
+
const content = await response.json();
|
|
187
|
+
// Update CLAUDE.md
|
|
188
|
+
if (content.router) {
|
|
189
|
+
(0, fs_1.writeFileSync)(claudeMdPath, content.router);
|
|
190
|
+
}
|
|
191
|
+
// Update pattern modules
|
|
192
|
+
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
193
|
+
if (!(0, fs_1.existsSync)(claudeDir)) {
|
|
194
|
+
(0, fs_1.mkdirSync)(claudeDir, { recursive: true });
|
|
195
|
+
}
|
|
196
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
197
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(claudeDir, name), data);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Write version file
|
|
201
|
+
const moduleCount = Object.keys(content.modules || {}).length;
|
|
202
|
+
const versionInfo = {
|
|
203
|
+
version: content.version,
|
|
204
|
+
moduleCount,
|
|
205
|
+
updatedAt: new Date().toISOString(),
|
|
206
|
+
cliVersion: (0, config_js_2.getCliVersion)(),
|
|
207
|
+
};
|
|
208
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(claudeDir, '.version.json'), JSON.stringify(versionInfo, null, 2));
|
|
209
|
+
// Show subtle notification
|
|
210
|
+
console.log(chalk_1.default.green(` ✓ Patterns auto-updated to v${content.version} (${moduleCount} modules)\n`));
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Silently fail - don't block the user
|
|
214
|
+
}
|
|
215
|
+
}
|
|
76
216
|
// Show welcome message when no command is provided
|
|
77
217
|
function showWelcome() {
|
|
78
218
|
console.log(chalk_1.default.blue(`
|
|
@@ -114,7 +254,7 @@ const program = new commander_1.Command();
|
|
|
114
254
|
program
|
|
115
255
|
.name('codebakers')
|
|
116
256
|
.description('CodeBakers CLI - Production patterns for AI-assisted development')
|
|
117
|
-
.version('3.
|
|
257
|
+
.version('3.3.0');
|
|
118
258
|
// Zero-friction trial entry (no signup required)
|
|
119
259
|
program
|
|
120
260
|
.command('go')
|
|
@@ -248,7 +388,11 @@ program
|
|
|
248
388
|
.action(mcp_config_js_1.mcpUninstall);
|
|
249
389
|
// Add update check hook (runs before every command)
|
|
250
390
|
program.hook('preAction', async () => {
|
|
251
|
-
|
|
391
|
+
// Run CLI update check and pattern auto-update in parallel
|
|
392
|
+
await Promise.all([
|
|
393
|
+
checkForUpdatesInBackground(),
|
|
394
|
+
autoUpdatePatterns(),
|
|
395
|
+
]);
|
|
252
396
|
});
|
|
253
397
|
// Show welcome if no command provided
|
|
254
398
|
if (process.argv.length <= 2) {
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -116,9 +116,12 @@ interface ConfigSchema {
|
|
|
116
116
|
lastKeySync: string | null; // ISO date of last sync with server
|
|
117
117
|
// Trial state (for zero-friction onboarding)
|
|
118
118
|
trial: TrialState | null;
|
|
119
|
-
// Update notification cache
|
|
119
|
+
// Update notification cache (CLI updates)
|
|
120
120
|
lastUpdateCheck: string | null; // ISO date of last npm registry check
|
|
121
121
|
latestKnownVersion: string | null; // Cached latest version from npm
|
|
122
|
+
// Pattern auto-update cache
|
|
123
|
+
lastPatternCheck: string | null; // ISO date of last pattern version check
|
|
124
|
+
latestPatternVersion: string | null; // Cached latest pattern version from server
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// Create default service keys object with all keys set to null
|
|
@@ -128,7 +131,7 @@ const defaultServiceKeys: ServiceKeys = Object.fromEntries(
|
|
|
128
131
|
|
|
129
132
|
const config = new Conf<ConfigSchema>({
|
|
130
133
|
projectName: 'codebakers',
|
|
131
|
-
projectVersion: '1.
|
|
134
|
+
projectVersion: '1.9.0',
|
|
132
135
|
defaults: {
|
|
133
136
|
apiKey: null,
|
|
134
137
|
apiUrl: 'https://codebakers.ai',
|
|
@@ -138,6 +141,8 @@ const config = new Conf<ConfigSchema>({
|
|
|
138
141
|
trial: null,
|
|
139
142
|
lastUpdateCheck: null,
|
|
140
143
|
latestKnownVersion: null,
|
|
144
|
+
lastPatternCheck: null,
|
|
145
|
+
latestPatternVersion: null,
|
|
141
146
|
},
|
|
142
147
|
// Migration to add new keys when upgrading from old version
|
|
143
148
|
migrations: {
|
|
@@ -160,6 +165,15 @@ const config = new Conf<ConfigSchema>({
|
|
|
160
165
|
store.set('trial', null);
|
|
161
166
|
}
|
|
162
167
|
},
|
|
168
|
+
'1.9.0': (store) => {
|
|
169
|
+
// Add pattern auto-update fields if not present
|
|
170
|
+
if (!store.has('lastPatternCheck')) {
|
|
171
|
+
store.set('lastPatternCheck', null);
|
|
172
|
+
}
|
|
173
|
+
if (!store.has('latestPatternVersion')) {
|
|
174
|
+
store.set('latestPatternVersion', null);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
163
177
|
},
|
|
164
178
|
});
|
|
165
179
|
|
|
@@ -528,5 +542,42 @@ export function setCachedUpdateInfo(latestVersion: string): void {
|
|
|
528
542
|
* Get the current CLI version from package.json
|
|
529
543
|
*/
|
|
530
544
|
export function getCliVersion(): string {
|
|
531
|
-
return '3.
|
|
545
|
+
return '3.3.0'; // Keep in sync with package.json
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ============================================
|
|
549
|
+
// Pattern Auto-Update Cache
|
|
550
|
+
// ============================================
|
|
551
|
+
|
|
552
|
+
const PATTERN_CHECK_INTERVAL_HOURS = 6; // Check more frequently than CLI
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Get cached pattern version info if still valid (within 6 hours)
|
|
556
|
+
*/
|
|
557
|
+
export function getCachedPatternInfo(): { latestVersion: string; checkedAt: string } | null {
|
|
558
|
+
const lastCheck = config.get('lastPatternCheck');
|
|
559
|
+
const latestVersion = config.get('latestPatternVersion');
|
|
560
|
+
|
|
561
|
+
if (!lastCheck || !latestVersion) return null;
|
|
562
|
+
|
|
563
|
+
const hoursSinceCheck = (Date.now() - new Date(lastCheck).getTime()) / (1000 * 60 * 60);
|
|
564
|
+
if (hoursSinceCheck > PATTERN_CHECK_INTERVAL_HOURS) return null;
|
|
565
|
+
|
|
566
|
+
return { latestVersion, checkedAt: lastCheck };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Cache the latest pattern version from server
|
|
571
|
+
*/
|
|
572
|
+
export function setCachedPatternInfo(latestVersion: string): void {
|
|
573
|
+
config.set('lastPatternCheck', new Date().toISOString());
|
|
574
|
+
config.set('latestPatternVersion', latestVersion);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Clear pattern cache to force a fresh check
|
|
579
|
+
*/
|
|
580
|
+
export function clearPatternCache(): void {
|
|
581
|
+
config.set('lastPatternCheck', null);
|
|
582
|
+
config.set('latestPatternVersion', null);
|
|
532
583
|
}
|
package/src/index.ts
CHANGED
|
@@ -22,13 +22,15 @@ 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 } from './config.js';
|
|
25
|
+
import { getCachedUpdateInfo, setCachedUpdateInfo, getCliVersion, getCachedPatternInfo, setCachedPatternInfo, getApiKey, getApiUrl, getTrialState, hasValidAccess } from './config.js';
|
|
26
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
|
|
27
|
+
import { join } from 'path';
|
|
26
28
|
|
|
27
29
|
// ============================================
|
|
28
30
|
// Automatic Update Notification
|
|
29
31
|
// ============================================
|
|
30
32
|
|
|
31
|
-
const CURRENT_VERSION = '3.
|
|
33
|
+
const CURRENT_VERSION = '3.3.0';
|
|
32
34
|
|
|
33
35
|
async function checkForUpdatesInBackground(): Promise<void> {
|
|
34
36
|
// Check if we have a valid cached result first (fast path)
|
|
@@ -78,6 +80,181 @@ function showUpdateBanner(currentVersion: string, latestVersion: string): void {
|
|
|
78
80
|
`));
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
// ============================================
|
|
84
|
+
// Automatic Pattern Updates
|
|
85
|
+
// ============================================
|
|
86
|
+
|
|
87
|
+
interface PatternVersionInfo {
|
|
88
|
+
version: string;
|
|
89
|
+
moduleCount: number;
|
|
90
|
+
updatedAt: string;
|
|
91
|
+
cliVersion: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface ContentResponse {
|
|
95
|
+
version: string;
|
|
96
|
+
router: string;
|
|
97
|
+
modules: Record<string, string>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getLocalPatternVersion(): string | null {
|
|
101
|
+
const cwd = process.cwd();
|
|
102
|
+
const versionFile = join(cwd, '.claude', '.version.json');
|
|
103
|
+
|
|
104
|
+
if (!existsSync(versionFile)) return null;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const content = readFileSync(versionFile, 'utf-8');
|
|
108
|
+
const info: PatternVersionInfo = JSON.parse(content);
|
|
109
|
+
return info.version;
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isCodeBakersProject(): boolean {
|
|
116
|
+
const cwd = process.cwd();
|
|
117
|
+
return existsSync(join(cwd, 'CLAUDE.md')) || existsSync(join(cwd, '.claude'));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function autoUpdatePatterns(): Promise<void> {
|
|
121
|
+
// Only auto-update if this is a CodeBakers project
|
|
122
|
+
if (!isCodeBakersProject()) return;
|
|
123
|
+
|
|
124
|
+
// Only auto-update if user has valid access
|
|
125
|
+
if (!hasValidAccess()) return;
|
|
126
|
+
|
|
127
|
+
const localVersion = getLocalPatternVersion();
|
|
128
|
+
|
|
129
|
+
// Check if we have a valid cached result first (fast path)
|
|
130
|
+
const cached = getCachedPatternInfo();
|
|
131
|
+
if (cached) {
|
|
132
|
+
// If local matches latest, nothing to do
|
|
133
|
+
if (localVersion === cached.latestVersion) return;
|
|
134
|
+
// If we know there's an update but haven't updated yet, do it now
|
|
135
|
+
if (localVersion !== cached.latestVersion) {
|
|
136
|
+
await performPatternUpdate(cached.latestVersion);
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fetch from server to check for updates (with timeout)
|
|
142
|
+
try {
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
145
|
+
|
|
146
|
+
const apiUrl = getApiUrl();
|
|
147
|
+
const apiKey = getApiKey();
|
|
148
|
+
const trial = getTrialState();
|
|
149
|
+
|
|
150
|
+
// Build authorization header
|
|
151
|
+
let authHeader = '';
|
|
152
|
+
if (apiKey) {
|
|
153
|
+
authHeader = `Bearer ${apiKey}`;
|
|
154
|
+
} else if (trial?.trialId) {
|
|
155
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!authHeader) return;
|
|
159
|
+
|
|
160
|
+
// First, check the version endpoint (lightweight)
|
|
161
|
+
const versionResponse = await fetch(`${apiUrl}/api/content/version`, {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
headers: {
|
|
164
|
+
'Authorization': authHeader,
|
|
165
|
+
},
|
|
166
|
+
signal: controller.signal,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
clearTimeout(timeout);
|
|
170
|
+
|
|
171
|
+
if (versionResponse.ok) {
|
|
172
|
+
const versionData = await versionResponse.json();
|
|
173
|
+
const serverVersion = versionData.version;
|
|
174
|
+
|
|
175
|
+
// Cache the version info
|
|
176
|
+
setCachedPatternInfo(serverVersion);
|
|
177
|
+
|
|
178
|
+
// If local version is different, update
|
|
179
|
+
if (localVersion !== serverVersion) {
|
|
180
|
+
await performPatternUpdate(serverVersion);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
// Silently fail - don't block CLI for pattern check
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function performPatternUpdate(targetVersion: string): Promise<void> {
|
|
189
|
+
const cwd = process.cwd();
|
|
190
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
191
|
+
const claudeDir = join(cwd, '.claude');
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const apiUrl = getApiUrl();
|
|
195
|
+
const apiKey = getApiKey();
|
|
196
|
+
const trial = getTrialState();
|
|
197
|
+
|
|
198
|
+
let authHeader = '';
|
|
199
|
+
if (apiKey) {
|
|
200
|
+
authHeader = `Bearer ${apiKey}`;
|
|
201
|
+
} else if (trial?.trialId) {
|
|
202
|
+
authHeader = `Trial ${trial.trialId}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!authHeader) return;
|
|
206
|
+
|
|
207
|
+
const controller = new AbortController();
|
|
208
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
209
|
+
|
|
210
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
211
|
+
method: 'GET',
|
|
212
|
+
headers: {
|
|
213
|
+
'Authorization': authHeader,
|
|
214
|
+
},
|
|
215
|
+
signal: controller.signal,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
clearTimeout(timeout);
|
|
219
|
+
|
|
220
|
+
if (!response.ok) return;
|
|
221
|
+
|
|
222
|
+
const content: ContentResponse = await response.json();
|
|
223
|
+
|
|
224
|
+
// Update CLAUDE.md
|
|
225
|
+
if (content.router) {
|
|
226
|
+
writeFileSync(claudeMdPath, content.router);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Update pattern modules
|
|
230
|
+
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
231
|
+
if (!existsSync(claudeDir)) {
|
|
232
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
236
|
+
writeFileSync(join(claudeDir, name), data);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Write version file
|
|
241
|
+
const moduleCount = Object.keys(content.modules || {}).length;
|
|
242
|
+
const versionInfo: PatternVersionInfo = {
|
|
243
|
+
version: content.version,
|
|
244
|
+
moduleCount,
|
|
245
|
+
updatedAt: new Date().toISOString(),
|
|
246
|
+
cliVersion: getCliVersion(),
|
|
247
|
+
};
|
|
248
|
+
writeFileSync(join(claudeDir, '.version.json'), JSON.stringify(versionInfo, null, 2));
|
|
249
|
+
|
|
250
|
+
// Show subtle notification
|
|
251
|
+
console.log(chalk.green(` ✓ Patterns auto-updated to v${content.version} (${moduleCount} modules)\n`));
|
|
252
|
+
|
|
253
|
+
} catch {
|
|
254
|
+
// Silently fail - don't block the user
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
81
258
|
// Show welcome message when no command is provided
|
|
82
259
|
function showWelcome(): void {
|
|
83
260
|
console.log(chalk.blue(`
|
|
@@ -127,7 +304,7 @@ const program = new Command();
|
|
|
127
304
|
program
|
|
128
305
|
.name('codebakers')
|
|
129
306
|
.description('CodeBakers CLI - Production patterns for AI-assisted development')
|
|
130
|
-
.version('3.
|
|
307
|
+
.version('3.3.0');
|
|
131
308
|
|
|
132
309
|
// Zero-friction trial entry (no signup required)
|
|
133
310
|
program
|
|
@@ -282,7 +459,11 @@ program
|
|
|
282
459
|
|
|
283
460
|
// Add update check hook (runs before every command)
|
|
284
461
|
program.hook('preAction', async () => {
|
|
285
|
-
|
|
462
|
+
// Run CLI update check and pattern auto-update in parallel
|
|
463
|
+
await Promise.all([
|
|
464
|
+
checkForUpdatesInBackground(),
|
|
465
|
+
autoUpdatePatterns(),
|
|
466
|
+
]);
|
|
286
467
|
});
|
|
287
468
|
|
|
288
469
|
// Show welcome if no command provided
|