@codebakers/cli 3.3.0 → 3.3.2
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/doctor.js +4 -3
- package/dist/commands/go.js +54 -6
- package/dist/commands/install.js +2 -1
- package/dist/commands/upgrade.js +25 -0
- package/dist/index.js +36 -18
- package/dist/lib/api.d.ts +10 -0
- package/dist/lib/api.js +73 -4
- package/dist/lib/stats.d.ts +9 -0
- package/dist/lib/stats.js +12 -0
- package/dist/mcp/server.js +227 -0
- package/nul +1 -0
- package/package.json +3 -2
- package/scripts/register-version.js +79 -0
- package/src/commands/doctor.ts +4 -3
- package/src/commands/go.ts +75 -6
- package/src/commands/install.ts +2 -1
- package/src/commands/upgrade.ts +34 -0
- package/src/index.ts +36 -19
- package/src/lib/api.ts +83 -4
- package/src/lib/stats.ts +10 -0
- package/src/mcp/server.ts +262 -0
package/dist/mcp/server.js
CHANGED
|
@@ -89,6 +89,30 @@ class CodeBakersServer {
|
|
|
89
89
|
}
|
|
90
90
|
return {};
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Confirm download to server (non-blocking analytics)
|
|
94
|
+
*/
|
|
95
|
+
async confirmDownload(version, moduleCount) {
|
|
96
|
+
try {
|
|
97
|
+
const headers = {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
...this.getAuthHeaders(),
|
|
100
|
+
};
|
|
101
|
+
await fetch(`${this.apiUrl}/api/content/confirm`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers,
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
version,
|
|
106
|
+
moduleCount,
|
|
107
|
+
cliVersion: (0, api_js_1.getCliVersion)(),
|
|
108
|
+
command: 'auto-update',
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Silently ignore - this is just for analytics
|
|
114
|
+
}
|
|
115
|
+
}
|
|
92
116
|
/**
|
|
93
117
|
* Automatically check for and apply pattern updates
|
|
94
118
|
* Runs silently in background - no user intervention needed
|
|
@@ -178,6 +202,19 @@ class CodeBakersServer {
|
|
|
178
202
|
cliVersion: (0, api_js_1.getCliVersion)(),
|
|
179
203
|
};
|
|
180
204
|
fs.writeFileSync(versionPath, JSON.stringify(versionInfo, null, 2));
|
|
205
|
+
// Write notification file for AI to read and show to user
|
|
206
|
+
const notificationPath = path.join(claudeDir, '.update-notification.json');
|
|
207
|
+
const notification = {
|
|
208
|
+
type: 'patterns_updated',
|
|
209
|
+
previousVersion: installed?.version || 'unknown',
|
|
210
|
+
newVersion: content.version,
|
|
211
|
+
moduleCount,
|
|
212
|
+
updatedAt: new Date().toISOString(),
|
|
213
|
+
message: `CodeBakers patterns have been automatically updated from v${installed?.version || 'unknown'} to v${content.version} (${moduleCount} modules). Your AI tools now have the latest production patterns.`,
|
|
214
|
+
};
|
|
215
|
+
fs.writeFileSync(notificationPath, JSON.stringify(notification, null, 2));
|
|
216
|
+
// Confirm to server (non-blocking, fire-and-forget)
|
|
217
|
+
this.confirmDownload(content.version, moduleCount).catch(() => { });
|
|
181
218
|
this.autoUpdateChecked = true;
|
|
182
219
|
this.autoUpdateInProgress = false;
|
|
183
220
|
// Log success (visible in MCP logs)
|
|
@@ -902,6 +939,27 @@ class CodeBakersServer {
|
|
|
902
939
|
required: ['request'],
|
|
903
940
|
},
|
|
904
941
|
},
|
|
942
|
+
{
|
|
943
|
+
name: 'check_update_notification',
|
|
944
|
+
description: 'ALWAYS CALL THIS AT THE START OF EACH SESSION. Checks if CodeBakers patterns were recently auto-updated and returns a notification message to show the user. If an update occurred, tell the user about it with the returned message. After showing, the notification is cleared.',
|
|
945
|
+
inputSchema: {
|
|
946
|
+
type: 'object',
|
|
947
|
+
properties: {},
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
name: 'update_patterns',
|
|
952
|
+
description: 'Download and update CodeBakers pattern files from the server. Use when user says "upgrade codebakers", "update patterns", "download latest patterns", "sync codebakers", or when patterns are missing or outdated. This tool fetches the latest CLAUDE.md router and all .claude/ module files from the server and writes them to disk.',
|
|
953
|
+
inputSchema: {
|
|
954
|
+
type: 'object',
|
|
955
|
+
properties: {
|
|
956
|
+
force: {
|
|
957
|
+
type: 'boolean',
|
|
958
|
+
description: 'Force update even if already at latest version (default: false)',
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
},
|
|
905
963
|
],
|
|
906
964
|
}));
|
|
907
965
|
// Handle tool calls
|
|
@@ -987,6 +1045,10 @@ class CodeBakersServer {
|
|
|
987
1045
|
return this.handleAddPage(args);
|
|
988
1046
|
case 'add_api_route':
|
|
989
1047
|
return this.handleAddApiRoute(args);
|
|
1048
|
+
case 'check_update_notification':
|
|
1049
|
+
return this.handleCheckUpdateNotification();
|
|
1050
|
+
case 'update_patterns':
|
|
1051
|
+
return this.handleUpdatePatterns(args);
|
|
990
1052
|
default:
|
|
991
1053
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
992
1054
|
}
|
|
@@ -4266,6 +4328,40 @@ export default ${asyncKeyword}function ${pageName}Page() {${authCheck}
|
|
|
4266
4328
|
}],
|
|
4267
4329
|
};
|
|
4268
4330
|
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Check for update notifications and return message to show user
|
|
4333
|
+
*/
|
|
4334
|
+
async handleCheckUpdateNotification() {
|
|
4335
|
+
const cwd = process.cwd();
|
|
4336
|
+
const notificationPath = path.join(cwd, '.claude', '.update-notification.json');
|
|
4337
|
+
try {
|
|
4338
|
+
if (!fs.existsSync(notificationPath)) {
|
|
4339
|
+
return {
|
|
4340
|
+
content: [{
|
|
4341
|
+
type: 'text',
|
|
4342
|
+
text: 'No update notification.',
|
|
4343
|
+
}],
|
|
4344
|
+
};
|
|
4345
|
+
}
|
|
4346
|
+
const notification = JSON.parse(fs.readFileSync(notificationPath, 'utf-8'));
|
|
4347
|
+
// Delete the notification file after reading (so it only shows once)
|
|
4348
|
+
fs.unlinkSync(notificationPath);
|
|
4349
|
+
return {
|
|
4350
|
+
content: [{
|
|
4351
|
+
type: 'text',
|
|
4352
|
+
text: `🍪 **CodeBakers Update**\n\n${notification.message}\n\n**Previous version:** ${notification.previousVersion}\n**New version:** ${notification.newVersion}\n**Modules:** ${notification.moduleCount}\n**Updated:** ${new Date(notification.updatedAt).toLocaleString()}`,
|
|
4353
|
+
}],
|
|
4354
|
+
};
|
|
4355
|
+
}
|
|
4356
|
+
catch {
|
|
4357
|
+
return {
|
|
4358
|
+
content: [{
|
|
4359
|
+
type: 'text',
|
|
4360
|
+
text: 'No update notification.',
|
|
4361
|
+
}],
|
|
4362
|
+
};
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4269
4365
|
parseApiRouteRequest(request) {
|
|
4270
4366
|
const lower = request.toLowerCase();
|
|
4271
4367
|
// Detect webhook
|
|
@@ -4451,6 +4547,137 @@ export async function POST(request: NextRequest) {
|
|
|
4451
4547
|
${handlers.join('\n')}
|
|
4452
4548
|
`;
|
|
4453
4549
|
}
|
|
4550
|
+
/**
|
|
4551
|
+
* Download and update CodeBakers patterns from server
|
|
4552
|
+
* This is the MCP equivalent of the `codebakers upgrade` CLI command
|
|
4553
|
+
*/
|
|
4554
|
+
async handleUpdatePatterns(args) {
|
|
4555
|
+
const { force = false } = args;
|
|
4556
|
+
const cwd = process.cwd();
|
|
4557
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
4558
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
4559
|
+
const versionPath = path.join(claudeDir, '.version.json');
|
|
4560
|
+
let response = `# 🔄 CodeBakers Pattern Update\n\n`;
|
|
4561
|
+
try {
|
|
4562
|
+
// Check current version
|
|
4563
|
+
let currentVersion = null;
|
|
4564
|
+
let currentModuleCount = 0;
|
|
4565
|
+
if (fs.existsSync(versionPath)) {
|
|
4566
|
+
try {
|
|
4567
|
+
const versionInfo = JSON.parse(fs.readFileSync(versionPath, 'utf-8'));
|
|
4568
|
+
currentVersion = versionInfo.version;
|
|
4569
|
+
currentModuleCount = versionInfo.moduleCount || 0;
|
|
4570
|
+
}
|
|
4571
|
+
catch {
|
|
4572
|
+
// Ignore parse errors
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
// Count current modules
|
|
4576
|
+
if (fs.existsSync(claudeDir)) {
|
|
4577
|
+
try {
|
|
4578
|
+
const files = fs.readdirSync(claudeDir).filter(f => f.endsWith('.md'));
|
|
4579
|
+
currentModuleCount = files.length;
|
|
4580
|
+
}
|
|
4581
|
+
catch {
|
|
4582
|
+
// Ignore read errors
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
response += `## Current Status\n`;
|
|
4586
|
+
response += `- Version: ${currentVersion || 'Unknown'}\n`;
|
|
4587
|
+
response += `- Modules: ${currentModuleCount}\n\n`;
|
|
4588
|
+
// Fetch latest version info first
|
|
4589
|
+
const versionResponse = await fetch(`${this.apiUrl}/api/content/version`, {
|
|
4590
|
+
headers: this.getAuthHeaders(),
|
|
4591
|
+
});
|
|
4592
|
+
if (!versionResponse.ok) {
|
|
4593
|
+
throw new Error('Failed to check version from server');
|
|
4594
|
+
}
|
|
4595
|
+
const latestInfo = await versionResponse.json();
|
|
4596
|
+
const latestVersion = latestInfo.version;
|
|
4597
|
+
const latestModuleCount = latestInfo.moduleCount || 0;
|
|
4598
|
+
response += `## Server Status\n`;
|
|
4599
|
+
response += `- Latest Version: ${latestVersion}\n`;
|
|
4600
|
+
response += `- Available Modules: ${latestModuleCount}\n\n`;
|
|
4601
|
+
// Check if update needed
|
|
4602
|
+
const needsUpdate = force || !currentVersion || currentVersion !== latestVersion || currentModuleCount < latestModuleCount;
|
|
4603
|
+
if (!needsUpdate) {
|
|
4604
|
+
response += `✅ **Already up to date!**\n\n`;
|
|
4605
|
+
response += `Your patterns are current (v${latestVersion} with ${latestModuleCount} modules).\n`;
|
|
4606
|
+
response += `Use \`force: true\` to re-download anyway.\n`;
|
|
4607
|
+
return {
|
|
4608
|
+
content: [{
|
|
4609
|
+
type: 'text',
|
|
4610
|
+
text: response,
|
|
4611
|
+
}],
|
|
4612
|
+
};
|
|
4613
|
+
}
|
|
4614
|
+
response += `## Downloading Updates...\n\n`;
|
|
4615
|
+
// Fetch full content
|
|
4616
|
+
const contentResponse = await fetch(`${this.apiUrl}/api/content`, {
|
|
4617
|
+
headers: this.getAuthHeaders(),
|
|
4618
|
+
});
|
|
4619
|
+
if (!contentResponse.ok) {
|
|
4620
|
+
const error = await contentResponse.json().catch(() => ({}));
|
|
4621
|
+
throw new Error(error.error || error.message || 'Failed to fetch patterns');
|
|
4622
|
+
}
|
|
4623
|
+
const content = await contentResponse.json();
|
|
4624
|
+
const moduleCount = content.modules ? Object.keys(content.modules).length : 0;
|
|
4625
|
+
// Create .claude directory if needed
|
|
4626
|
+
if (!fs.existsSync(claudeDir)) {
|
|
4627
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
4628
|
+
response += `✓ Created .claude/ directory\n`;
|
|
4629
|
+
}
|
|
4630
|
+
// Update CLAUDE.md router
|
|
4631
|
+
if (content.router) {
|
|
4632
|
+
fs.writeFileSync(claudeMdPath, content.router);
|
|
4633
|
+
response += `✓ Updated CLAUDE.md (router)\n`;
|
|
4634
|
+
}
|
|
4635
|
+
// Update all modules
|
|
4636
|
+
if (content.modules && moduleCount > 0) {
|
|
4637
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
4638
|
+
fs.writeFileSync(path.join(claudeDir, name), data);
|
|
4639
|
+
}
|
|
4640
|
+
response += `✓ Updated ${moduleCount} modules in .claude/\n`;
|
|
4641
|
+
}
|
|
4642
|
+
// Save version info
|
|
4643
|
+
const newVersionInfo = {
|
|
4644
|
+
version: content.version || latestVersion,
|
|
4645
|
+
moduleCount,
|
|
4646
|
+
installedAt: currentVersion ? undefined : new Date().toISOString(),
|
|
4647
|
+
updatedAt: new Date().toISOString(),
|
|
4648
|
+
cliVersion: (0, api_js_1.getCliVersion)(),
|
|
4649
|
+
};
|
|
4650
|
+
fs.writeFileSync(versionPath, JSON.stringify(newVersionInfo, null, 2));
|
|
4651
|
+
response += `✓ Saved version info\n`;
|
|
4652
|
+
// Confirm download to server (non-blocking analytics)
|
|
4653
|
+
this.confirmDownload(content.version || latestVersion, moduleCount).catch(() => { });
|
|
4654
|
+
response += `\n## ✅ Update Complete!\n\n`;
|
|
4655
|
+
response += `- **From:** v${currentVersion || 'none'} (${currentModuleCount} modules)\n`;
|
|
4656
|
+
response += `- **To:** v${content.version || latestVersion} (${moduleCount} modules)\n\n`;
|
|
4657
|
+
if (moduleCount > currentModuleCount) {
|
|
4658
|
+
response += `🆕 **${moduleCount - currentModuleCount} new modules added!**\n\n`;
|
|
4659
|
+
}
|
|
4660
|
+
response += `Your patterns are now up to date. The new patterns will be used in your next response.\n`;
|
|
4661
|
+
}
|
|
4662
|
+
catch (error) {
|
|
4663
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
4664
|
+
response += `\n## ❌ Update Failed\n\n`;
|
|
4665
|
+
response += `Error: ${message}\n\n`;
|
|
4666
|
+
if (message.includes('401') || message.includes('Invalid') || message.includes('expired')) {
|
|
4667
|
+
response += `Your API key may be invalid or expired.\n`;
|
|
4668
|
+
response += `Run \`codebakers setup\` in terminal to reconfigure.\n`;
|
|
4669
|
+
}
|
|
4670
|
+
else {
|
|
4671
|
+
response += `Please try again or run \`codebakers upgrade\` in terminal.\n`;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
return {
|
|
4675
|
+
content: [{
|
|
4676
|
+
type: 'text',
|
|
4677
|
+
text: response,
|
|
4678
|
+
}],
|
|
4679
|
+
};
|
|
4680
|
+
}
|
|
4454
4681
|
async run() {
|
|
4455
4682
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
4456
4683
|
await this.server.connect(transport);
|
package/nul
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vitest.config.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codebakers/cli",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "CodeBakers CLI - Production patterns for AI-assisted development",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"mcp": "tsx src/mcp/server.ts",
|
|
16
16
|
"test": "vitest run",
|
|
17
17
|
"test:watch": "vitest",
|
|
18
|
-
"test:coverage": "vitest run --coverage"
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
19
|
+
"postpublish": "node scripts/register-version.js"
|
|
19
20
|
},
|
|
20
21
|
"keywords": [
|
|
21
22
|
"codebakers",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Automatically registers a new CLI version with the CodeBakers server
|
|
5
|
+
* Called automatically after npm publish via postpublish hook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const API_URL = process.env.CODEBAKERS_API_URL || 'https://codebakers.ai';
|
|
12
|
+
const ADMIN_API_KEY = process.env.CODEBAKERS_ADMIN_KEY;
|
|
13
|
+
|
|
14
|
+
async function registerVersion() {
|
|
15
|
+
// Read version from package.json
|
|
16
|
+
const packagePath = path.join(__dirname, '..', 'package.json');
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
|
|
18
|
+
const version = pkg.version;
|
|
19
|
+
|
|
20
|
+
console.log(`\n📦 Registering CLI version ${version} with CodeBakers server...\n`);
|
|
21
|
+
|
|
22
|
+
if (!ADMIN_API_KEY) {
|
|
23
|
+
console.log('⚠️ CODEBAKERS_ADMIN_KEY not set - skipping auto-registration');
|
|
24
|
+
console.log(' You can manually add this version in Admin → CLI Versions\n');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Read changelog if it exists
|
|
30
|
+
let changelog = '';
|
|
31
|
+
const changelogPath = path.join(__dirname, '..', 'CHANGELOG.md');
|
|
32
|
+
if (fs.existsSync(changelogPath)) {
|
|
33
|
+
const content = fs.readFileSync(changelogPath, 'utf-8');
|
|
34
|
+
// Extract the latest version's changes
|
|
35
|
+
const match = content.match(/## \[?\d+\.\d+\.\d+\]?[^\n]*\n([\s\S]*?)(?=## \[?\d+\.\d+\.\d+|$)/);
|
|
36
|
+
if (match) {
|
|
37
|
+
changelog = match[1].trim().slice(0, 2000); // Limit length
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const response = await fetch(`${API_URL}/api/cli/register-version`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
'Authorization': `Bearer ${ADMIN_API_KEY}`,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
version,
|
|
49
|
+
npmTag: 'latest',
|
|
50
|
+
changelog,
|
|
51
|
+
minNodeVersion: '18',
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (response.ok) {
|
|
56
|
+
const data = await response.json();
|
|
57
|
+
console.log(`✅ Version ${version} registered successfully!`);
|
|
58
|
+
console.log(` Status: ${data.data?.version?.status || 'draft'}`);
|
|
59
|
+
console.log(`\n Next steps:`);
|
|
60
|
+
console.log(` 1. Test the version`);
|
|
61
|
+
console.log(` 2. Go to Admin → CLI Versions`);
|
|
62
|
+
console.log(` 3. Promote to "testing" then "stable"`);
|
|
63
|
+
console.log(` 4. Enable auto-update when ready\n`);
|
|
64
|
+
} else {
|
|
65
|
+
const error = await response.json().catch(() => ({}));
|
|
66
|
+
if (error.error?.includes('already exists')) {
|
|
67
|
+
console.log(`ℹ️ Version ${version} already registered\n`);
|
|
68
|
+
} else {
|
|
69
|
+
console.log(`⚠️ Failed to register: ${error.error || response.statusText}`);
|
|
70
|
+
console.log(` You can manually add this version in Admin → CLI Versions\n`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.log(`⚠️ Could not reach server: ${error.message}`);
|
|
75
|
+
console.log(` You can manually add this version in Admin → CLI Versions\n`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
registerVersion();
|
package/src/commands/doctor.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { homedir } from 'os';
|
|
|
5
5
|
import { isHookInstalled } from './install-hook.js';
|
|
6
6
|
import { getApiKey } from '../config.js';
|
|
7
7
|
import { checkApiKeyValidity, checkForUpdates, getCliVersion } from '../lib/api.js';
|
|
8
|
+
import { CODEBAKERS_STATS } from '../lib/stats.js';
|
|
8
9
|
|
|
9
10
|
interface CheckResult {
|
|
10
11
|
ok: boolean;
|
|
@@ -134,18 +135,18 @@ function checkProject(): CheckResult[] {
|
|
|
134
135
|
const files = readdirSync(claudeDir).filter(f => f.endsWith('.md'));
|
|
135
136
|
const moduleCount = files.length;
|
|
136
137
|
|
|
137
|
-
if (moduleCount >=
|
|
138
|
+
if (moduleCount >= 50) {
|
|
138
139
|
results.push({ ok: true, message: `${moduleCount} modules present (full set)` });
|
|
139
140
|
} else if (moduleCount >= 10) {
|
|
140
141
|
results.push({
|
|
141
142
|
ok: true,
|
|
142
143
|
message: `${moduleCount} modules present (partial set)`,
|
|
143
|
-
details:
|
|
144
|
+
details: `Run: codebakers upgrade to get all ${CODEBAKERS_STATS.moduleCount} modules`
|
|
144
145
|
});
|
|
145
146
|
} else if (moduleCount > 0) {
|
|
146
147
|
results.push({
|
|
147
148
|
ok: false,
|
|
148
|
-
message: `Only ${moduleCount} modules found (expected
|
|
149
|
+
message: `Only ${moduleCount} modules found (expected ${CODEBAKERS_STATS.moduleCount})`,
|
|
149
150
|
details: 'Run: codebakers upgrade to get all modules'
|
|
150
151
|
});
|
|
151
152
|
} else {
|
package/src/commands/go.ts
CHANGED
|
@@ -39,6 +39,57 @@ interface GoOptions {
|
|
|
39
39
|
verbose?: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
interface ConfirmData {
|
|
43
|
+
version: string;
|
|
44
|
+
moduleCount: number;
|
|
45
|
+
cliVersion?: string;
|
|
46
|
+
command: string;
|
|
47
|
+
projectName?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface AuthInfo {
|
|
51
|
+
apiKey?: string;
|
|
52
|
+
trialId?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get CLI version from package.json
|
|
57
|
+
*/
|
|
58
|
+
function getCliVersion(): string {
|
|
59
|
+
try {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
61
|
+
const pkg = require('../../package.json');
|
|
62
|
+
return pkg.version || '0.0.0';
|
|
63
|
+
} catch {
|
|
64
|
+
return '0.0.0';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Confirm download to server (non-blocking, fire-and-forget)
|
|
70
|
+
*/
|
|
71
|
+
async function confirmDownload(apiUrl: string, auth: AuthInfo, data: ConfirmData): Promise<void> {
|
|
72
|
+
try {
|
|
73
|
+
const headers: Record<string, string> = {
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
};
|
|
76
|
+
if (auth.apiKey) {
|
|
77
|
+
headers['Authorization'] = `Bearer ${auth.apiKey}`;
|
|
78
|
+
}
|
|
79
|
+
if (auth.trialId) {
|
|
80
|
+
headers['X-Trial-ID'] = auth.trialId;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await fetch(`${apiUrl}/api/content/confirm`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers,
|
|
86
|
+
body: JSON.stringify(data),
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
// Silently ignore - this is just for analytics
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
42
93
|
function log(message: string, options?: GoOptions): void {
|
|
43
94
|
if (options?.verbose) {
|
|
44
95
|
console.log(chalk.gray(` [verbose] ${message}`));
|
|
@@ -317,7 +368,7 @@ async function installPatternsWithApiKey(apiKey: string, options: GoOptions = {}
|
|
|
317
368
|
log('Response OK, parsing JSON...', options);
|
|
318
369
|
const content: ContentResponse = await response.json();
|
|
319
370
|
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
320
|
-
await writePatternFiles(cwd, content, spinner, options);
|
|
371
|
+
await writePatternFiles(cwd, content, spinner, options, { apiKey });
|
|
321
372
|
|
|
322
373
|
} catch (error) {
|
|
323
374
|
log(`Error: ${error instanceof Error ? error.message : String(error)}`, options);
|
|
@@ -363,13 +414,13 @@ async function installPatterns(trialId: string, options: GoOptions = {}): Promis
|
|
|
363
414
|
|
|
364
415
|
const content: ContentResponse = await publicResponse.json();
|
|
365
416
|
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
366
|
-
await writePatternFiles(cwd, content, spinner, options);
|
|
417
|
+
await writePatternFiles(cwd, content, spinner, options, { trialId });
|
|
367
418
|
return;
|
|
368
419
|
}
|
|
369
420
|
|
|
370
421
|
const content: ContentResponse = await response.json();
|
|
371
422
|
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
372
|
-
await writePatternFiles(cwd, content, spinner, options);
|
|
423
|
+
await writePatternFiles(cwd, content, spinner, options, { trialId });
|
|
373
424
|
|
|
374
425
|
} catch (error) {
|
|
375
426
|
log(`Error: ${error instanceof Error ? error.message : String(error)}`, options);
|
|
@@ -378,7 +429,13 @@ async function installPatterns(trialId: string, options: GoOptions = {}): Promis
|
|
|
378
429
|
}
|
|
379
430
|
}
|
|
380
431
|
|
|
381
|
-
async function writePatternFiles(
|
|
432
|
+
async function writePatternFiles(
|
|
433
|
+
cwd: string,
|
|
434
|
+
content: ContentResponse,
|
|
435
|
+
spinner: ReturnType<typeof ora>,
|
|
436
|
+
options: GoOptions = {},
|
|
437
|
+
auth?: AuthInfo
|
|
438
|
+
): Promise<void> {
|
|
382
439
|
log(`Writing pattern files to ${cwd}...`, options);
|
|
383
440
|
// Check if patterns already exist
|
|
384
441
|
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
@@ -393,7 +450,8 @@ async function writePatternFiles(cwd: string, content: ContentResponse, spinner:
|
|
|
393
450
|
}
|
|
394
451
|
|
|
395
452
|
// Write pattern modules to .claude/
|
|
396
|
-
|
|
453
|
+
const moduleCount = Object.keys(content.modules || {}).length;
|
|
454
|
+
if (content.modules && moduleCount > 0) {
|
|
397
455
|
const modulesDir = join(cwd, '.claude');
|
|
398
456
|
if (!existsSync(modulesDir)) {
|
|
399
457
|
mkdirSync(modulesDir, { recursive: true });
|
|
@@ -415,5 +473,16 @@ async function writePatternFiles(cwd: string, content: ContentResponse, spinner:
|
|
|
415
473
|
}
|
|
416
474
|
|
|
417
475
|
spinner.succeed(`CodeBakers patterns installed (v${content.version})`);
|
|
418
|
-
console.log(chalk.gray(` ${
|
|
476
|
+
console.log(chalk.gray(` ${moduleCount} pattern modules ready\n`));
|
|
477
|
+
|
|
478
|
+
// Confirm download to server (non-blocking)
|
|
479
|
+
if (auth) {
|
|
480
|
+
const apiUrl = getApiUrl();
|
|
481
|
+
confirmDownload(apiUrl, auth, {
|
|
482
|
+
version: content.version,
|
|
483
|
+
moduleCount,
|
|
484
|
+
cliVersion: getCliVersion(),
|
|
485
|
+
command: 'go',
|
|
486
|
+
}).catch(() => {}); // Silently ignore
|
|
487
|
+
}
|
|
419
488
|
}
|
package/src/commands/install.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { getApiKey, getApiUrl } from '../config.js';
|
|
6
6
|
import { getCliVersion } from '../lib/api.js';
|
|
7
|
+
import { CODEBAKERS_STATS } from '../lib/stats.js';
|
|
7
8
|
|
|
8
9
|
interface ContentResponse {
|
|
9
10
|
version: string;
|
|
@@ -94,7 +95,7 @@ export async function install(): Promise<void> {
|
|
|
94
95
|
if (moduleCount > 0) {
|
|
95
96
|
console.log(chalk.gray(` - .claude/ (${moduleCount} pattern modules)`));
|
|
96
97
|
}
|
|
97
|
-
console.log(chalk.blue(
|
|
98
|
+
console.log(chalk.blue(`\n Start building! Your AI now knows ${CODEBAKERS_STATS.moduleCount} production modules.\n`));
|
|
98
99
|
} catch (error) {
|
|
99
100
|
spinner.fail('Installation failed');
|
|
100
101
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -11,6 +11,32 @@ interface ContentResponse {
|
|
|
11
11
|
modules: Record<string, string>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
interface ConfirmData {
|
|
15
|
+
version: string;
|
|
16
|
+
moduleCount: number;
|
|
17
|
+
cliVersion: string;
|
|
18
|
+
command: string;
|
|
19
|
+
projectName?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Confirm download to server (non-blocking, fire-and-forget)
|
|
24
|
+
*/
|
|
25
|
+
async function confirmDownload(apiUrl: string, apiKey: string, data: ConfirmData): Promise<void> {
|
|
26
|
+
try {
|
|
27
|
+
await fetch(`${apiUrl}/api/content/confirm`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(data),
|
|
34
|
+
});
|
|
35
|
+
} catch {
|
|
36
|
+
// Silently ignore - this is just for analytics
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
14
40
|
/**
|
|
15
41
|
* Upgrade CodeBakers patterns to the latest version
|
|
16
42
|
*/
|
|
@@ -101,6 +127,14 @@ export async function upgrade(): Promise<void> {
|
|
|
101
127
|
writeFileSync(join(claudeDir, '.version.json'), JSON.stringify(versionInfo, null, 2));
|
|
102
128
|
console.log(chalk.green(' ✓ Version info saved'));
|
|
103
129
|
|
|
130
|
+
// Confirm download to server (non-blocking)
|
|
131
|
+
confirmDownload(apiUrl, apiKey, {
|
|
132
|
+
version: content.version,
|
|
133
|
+
moduleCount,
|
|
134
|
+
cliVersion: getCliVersion(),
|
|
135
|
+
command: 'upgrade',
|
|
136
|
+
}).catch(() => {}); // Silently ignore confirmation failures
|
|
137
|
+
|
|
104
138
|
console.log(chalk.green(`\n ✅ Upgraded to patterns v${content.version}!\n`));
|
|
105
139
|
|
|
106
140
|
// Show what's new if available
|