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