@codebakers/cli 3.0.0 → 3.1.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/commands/doctor.js +10 -3
- package/dist/commands/go.d.ts +5 -1
- package/dist/commands/go.js +189 -8
- package/dist/index.js +3 -2
- package/dist/mcp/server.js +1270 -0
- package/package.json +8 -3
- package/src/commands/doctor.ts +9 -3
- package/src/commands/go.ts +231 -9
- package/src/index.ts +3 -2
- package/src/mcp/server.ts +1464 -0
- package/tests/api.test.ts +216 -0
- package/tests/doctor.test.ts +168 -0
- package/tests/go.test.ts +142 -0
- package/vitest.config.ts +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codebakers/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "CodeBakers CLI - Production patterns for AI-assisted development",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"dev": "tsx src/index.ts",
|
|
14
14
|
"start": "node dist/index.js",
|
|
15
|
-
"mcp": "tsx src/mcp/server.ts"
|
|
15
|
+
"mcp": "tsx src/mcp/server.ts",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:coverage": "vitest run --coverage"
|
|
16
19
|
},
|
|
17
20
|
"keywords": [
|
|
18
21
|
"codebakers",
|
|
@@ -33,7 +36,9 @@
|
|
|
33
36
|
},
|
|
34
37
|
"devDependencies": {
|
|
35
38
|
"@types/node": "^20.10.0",
|
|
39
|
+
"@vitest/coverage-v8": "^2.1.9",
|
|
36
40
|
"tsx": "^4.7.0",
|
|
37
|
-
"typescript": "^5.3.0"
|
|
41
|
+
"typescript": "^5.3.0",
|
|
42
|
+
"vitest": "^2.1.9"
|
|
38
43
|
}
|
|
39
44
|
}
|
package/src/commands/doctor.ts
CHANGED
|
@@ -134,12 +134,18 @@ function checkProject(): CheckResult[] {
|
|
|
134
134
|
const files = readdirSync(claudeDir).filter(f => f.endsWith('.md'));
|
|
135
135
|
const moduleCount = files.length;
|
|
136
136
|
|
|
137
|
-
if (moduleCount >=
|
|
138
|
-
results.push({ ok: true, message: `${moduleCount} modules present` });
|
|
137
|
+
if (moduleCount >= 40) {
|
|
138
|
+
results.push({ ok: true, message: `${moduleCount} modules present (full set)` });
|
|
139
|
+
} else if (moduleCount >= 10) {
|
|
140
|
+
results.push({
|
|
141
|
+
ok: true,
|
|
142
|
+
message: `${moduleCount} modules present (partial set)`,
|
|
143
|
+
details: 'Run: codebakers upgrade to get all 47 modules'
|
|
144
|
+
});
|
|
139
145
|
} else if (moduleCount > 0) {
|
|
140
146
|
results.push({
|
|
141
147
|
ok: false,
|
|
142
|
-
message: `Only ${moduleCount} modules found (expected
|
|
148
|
+
message: `Only ${moduleCount} modules found (expected 47)`,
|
|
143
149
|
details: 'Run: codebakers upgrade to get all modules'
|
|
144
150
|
});
|
|
145
151
|
} else {
|
package/src/commands/go.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { execSync } from 'child_process';
|
|
3
|
+
import { execSync, spawn } from 'child_process';
|
|
4
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { createInterface } from 'readline';
|
|
4
7
|
import {
|
|
5
8
|
getTrialState,
|
|
6
9
|
setTrialState,
|
|
@@ -12,10 +15,44 @@ import {
|
|
|
12
15
|
} from '../config.js';
|
|
13
16
|
import { getDeviceFingerprint } from '../lib/fingerprint.js';
|
|
14
17
|
|
|
18
|
+
function prompt(question: string): Promise<string> {
|
|
19
|
+
const rl = createInterface({
|
|
20
|
+
input: process.stdin,
|
|
21
|
+
output: process.stdout,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
rl.question(question, (answer) => {
|
|
26
|
+
rl.close();
|
|
27
|
+
resolve(answer.trim().toLowerCase());
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ContentResponse {
|
|
33
|
+
version: string;
|
|
34
|
+
router: string;
|
|
35
|
+
modules: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GoOptions {
|
|
39
|
+
verbose?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function log(message: string, options?: GoOptions): void {
|
|
43
|
+
if (options?.verbose) {
|
|
44
|
+
console.log(chalk.gray(` [verbose] ${message}`));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
15
48
|
/**
|
|
16
49
|
* Zero-friction entry point - start using CodeBakers instantly
|
|
17
50
|
*/
|
|
18
|
-
export async function go(): Promise<void> {
|
|
51
|
+
export async function go(options: GoOptions = {}): Promise<void> {
|
|
52
|
+
log('Starting go command...', options);
|
|
53
|
+
log(`API URL: ${getApiUrl()}`, options);
|
|
54
|
+
log(`Working directory: ${process.cwd()}`, options);
|
|
55
|
+
|
|
19
56
|
console.log(chalk.blue(`
|
|
20
57
|
╔═══════════════════════════════════════════════════════════╗
|
|
21
58
|
║ ║
|
|
@@ -25,12 +62,18 @@ export async function go(): Promise<void> {
|
|
|
25
62
|
`));
|
|
26
63
|
|
|
27
64
|
// Check if user already has an API key (paid user)
|
|
65
|
+
log('Checking for existing API key...', options);
|
|
28
66
|
const apiKey = getApiKey();
|
|
29
67
|
if (apiKey) {
|
|
68
|
+
log(`Found API key: ${apiKey.substring(0, 8)}...`, options);
|
|
30
69
|
console.log(chalk.green(' ✓ You\'re already logged in with an API key!\n'));
|
|
31
|
-
|
|
70
|
+
|
|
71
|
+
// Still install patterns if not already installed
|
|
72
|
+
await installPatternsWithApiKey(apiKey, options);
|
|
73
|
+
await configureMCP(options);
|
|
32
74
|
return;
|
|
33
75
|
}
|
|
76
|
+
log('No API key found, checking trial state...', options);
|
|
34
77
|
|
|
35
78
|
// Check existing trial
|
|
36
79
|
const existingTrial = getTrialState();
|
|
@@ -44,7 +87,10 @@ export async function go(): Promise<void> {
|
|
|
44
87
|
console.log(chalk.cyan(' codebakers extend\n'));
|
|
45
88
|
}
|
|
46
89
|
|
|
47
|
-
|
|
90
|
+
// Install patterns if not already installed
|
|
91
|
+
await installPatterns(existingTrial.trialId, options);
|
|
92
|
+
|
|
93
|
+
await configureMCP(options);
|
|
48
94
|
return;
|
|
49
95
|
}
|
|
50
96
|
|
|
@@ -132,8 +178,11 @@ export async function go(): Promise<void> {
|
|
|
132
178
|
spinner.succeed(`Trial started (${data.daysRemaining} days free)`);
|
|
133
179
|
console.log('');
|
|
134
180
|
|
|
181
|
+
// Install patterns (CLAUDE.md and .claude/)
|
|
182
|
+
await installPatterns(data.trialId, options);
|
|
183
|
+
|
|
135
184
|
// Configure MCP
|
|
136
|
-
await configureMCP();
|
|
185
|
+
await configureMCP(options);
|
|
137
186
|
|
|
138
187
|
// Show success message
|
|
139
188
|
console.log(chalk.green(`
|
|
@@ -165,7 +214,8 @@ export async function go(): Promise<void> {
|
|
|
165
214
|
}
|
|
166
215
|
}
|
|
167
216
|
|
|
168
|
-
async function configureMCP(): Promise<void> {
|
|
217
|
+
async function configureMCP(options: GoOptions = {}): Promise<void> {
|
|
218
|
+
log('Configuring MCP integration...', options);
|
|
169
219
|
const spinner = ora('Configuring Claude Code integration...').start();
|
|
170
220
|
const isWindows = process.platform === 'win32';
|
|
171
221
|
|
|
@@ -189,9 +239,181 @@ async function configureMCP(): Promise<void> {
|
|
|
189
239
|
}
|
|
190
240
|
|
|
191
241
|
async function attemptAutoRestart(): Promise<void> {
|
|
242
|
+
const cwd = process.cwd();
|
|
243
|
+
|
|
192
244
|
console.log(chalk.yellow('\n ⚠️ RESTART REQUIRED\n'));
|
|
193
|
-
console.log(chalk.gray('
|
|
245
|
+
console.log(chalk.gray(' Claude Code needs to restart to load CodeBakers.\n'));
|
|
246
|
+
|
|
247
|
+
const answer = await prompt(chalk.cyan(' Restart Claude Code now? (Y/n): '));
|
|
248
|
+
|
|
249
|
+
if (answer === 'n' || answer === 'no') {
|
|
250
|
+
console.log(chalk.gray('\n No problem! Just restart Claude Code manually when ready.\n'));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Attempt to restart Claude Code
|
|
255
|
+
console.log(chalk.gray('\n Restarting Claude Code...\n'));
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const isWindows = process.platform === 'win32';
|
|
259
|
+
|
|
260
|
+
if (isWindows) {
|
|
261
|
+
// On Windows, spawn a new Claude process detached and exit
|
|
262
|
+
spawn('cmd', ['/c', 'start', 'claude'], {
|
|
263
|
+
cwd,
|
|
264
|
+
detached: true,
|
|
265
|
+
stdio: 'ignore',
|
|
266
|
+
shell: true,
|
|
267
|
+
}).unref();
|
|
268
|
+
} else {
|
|
269
|
+
// On Mac/Linux, spawn claude in new terminal
|
|
270
|
+
spawn('claude', [], {
|
|
271
|
+
cwd,
|
|
272
|
+
detached: true,
|
|
273
|
+
stdio: 'ignore',
|
|
274
|
+
shell: true,
|
|
275
|
+
}).unref();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
console.log(chalk.green(' ✓ Claude Code is restarting...\n'));
|
|
279
|
+
console.log(chalk.gray(' This terminal will close. Claude Code will open in a new window.\n'));
|
|
280
|
+
|
|
281
|
+
// Give the spawn a moment to start
|
|
282
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
283
|
+
|
|
284
|
+
// Exit this process
|
|
285
|
+
process.exit(0);
|
|
286
|
+
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.log(chalk.yellow(' Could not auto-restart. Please restart Claude Code manually.\n'));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Install pattern files for API key users (paid users)
|
|
294
|
+
*/
|
|
295
|
+
async function installPatternsWithApiKey(apiKey: string, options: GoOptions = {}): Promise<void> {
|
|
296
|
+
log('Installing patterns with API key...', options);
|
|
297
|
+
const spinner = ora('Installing CodeBakers patterns...').start();
|
|
298
|
+
const cwd = process.cwd();
|
|
299
|
+
const apiUrl = getApiUrl();
|
|
300
|
+
|
|
301
|
+
log(`Fetching from: ${apiUrl}/api/content`, options);
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
305
|
+
method: 'GET',
|
|
306
|
+
headers: {
|
|
307
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (!response.ok) {
|
|
312
|
+
log(`Response not OK: ${response.status} ${response.statusText}`, options);
|
|
313
|
+
spinner.warn('Could not download patterns');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
log('Response OK, parsing JSON...', options);
|
|
318
|
+
const content: ContentResponse = await response.json();
|
|
319
|
+
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
320
|
+
await writePatternFiles(cwd, content, spinner, options);
|
|
321
|
+
|
|
322
|
+
} catch (error) {
|
|
323
|
+
log(`Error: ${error instanceof Error ? error.message : String(error)}`, options);
|
|
324
|
+
spinner.warn('Could not install patterns');
|
|
325
|
+
console.log(chalk.gray(' Check your internet connection.\n'));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Install pattern files (CLAUDE.md and .claude/) for trial users
|
|
331
|
+
*/
|
|
332
|
+
async function installPatterns(trialId: string, options: GoOptions = {}): Promise<void> {
|
|
333
|
+
log(`Installing patterns with trial ID: ${trialId.substring(0, 8)}...`, options);
|
|
334
|
+
const spinner = ora('Installing CodeBakers patterns...').start();
|
|
335
|
+
const cwd = process.cwd();
|
|
336
|
+
const apiUrl = getApiUrl();
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// Fetch patterns using trial ID
|
|
340
|
+
log(`Fetching from: ${apiUrl}/api/content`, options);
|
|
341
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
342
|
+
method: 'GET',
|
|
343
|
+
headers: {
|
|
344
|
+
'X-Trial-ID': trialId,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
log(`Primary endpoint failed: ${response.status}, trying trial endpoint...`, options);
|
|
350
|
+
// Try without auth - some patterns may be available for trial
|
|
351
|
+
const publicResponse = await fetch(`${apiUrl}/api/content/trial`, {
|
|
352
|
+
method: 'GET',
|
|
353
|
+
headers: {
|
|
354
|
+
'X-Trial-ID': trialId,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (!publicResponse.ok) {
|
|
359
|
+
log(`Trial endpoint also failed: ${publicResponse.status}`, options);
|
|
360
|
+
spinner.warn('Could not download patterns (will use MCP tools)');
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const content: ContentResponse = await publicResponse.json();
|
|
365
|
+
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
366
|
+
await writePatternFiles(cwd, content, spinner, options);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const content: ContentResponse = await response.json();
|
|
371
|
+
log(`Received version: ${content.version}, modules: ${Object.keys(content.modules || {}).length}`, options);
|
|
372
|
+
await writePatternFiles(cwd, content, spinner, options);
|
|
373
|
+
|
|
374
|
+
} catch (error) {
|
|
375
|
+
log(`Error: ${error instanceof Error ? error.message : String(error)}`, options);
|
|
376
|
+
spinner.warn('Could not install patterns (will use MCP tools)');
|
|
377
|
+
console.log(chalk.gray(' Patterns will be available via MCP tools.\n'));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function writePatternFiles(cwd: string, content: ContentResponse, spinner: ReturnType<typeof ora>, options: GoOptions = {}): Promise<void> {
|
|
382
|
+
log(`Writing pattern files to ${cwd}...`, options);
|
|
383
|
+
// Check if patterns already exist
|
|
384
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
385
|
+
if (existsSync(claudeMdPath)) {
|
|
386
|
+
spinner.succeed('CodeBakers patterns already installed');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Write CLAUDE.md (router file)
|
|
391
|
+
if (content.router) {
|
|
392
|
+
writeFileSync(claudeMdPath, content.router);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Write pattern modules to .claude/
|
|
396
|
+
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
397
|
+
const modulesDir = join(cwd, '.claude');
|
|
398
|
+
if (!existsSync(modulesDir)) {
|
|
399
|
+
mkdirSync(modulesDir, { recursive: true });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
403
|
+
writeFileSync(join(modulesDir, name), data);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Update .gitignore to exclude encoded patterns
|
|
408
|
+
const gitignorePath = join(cwd, '.gitignore');
|
|
409
|
+
if (existsSync(gitignorePath)) {
|
|
410
|
+
const { readFileSync } = await import('fs');
|
|
411
|
+
const gitignore = readFileSync(gitignorePath, 'utf-8');
|
|
412
|
+
if (!gitignore.includes('.claude/')) {
|
|
413
|
+
writeFileSync(gitignorePath, gitignore + '\n# CodeBakers patterns\n.claude/\n');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
194
416
|
|
|
195
|
-
|
|
196
|
-
|
|
417
|
+
spinner.succeed(`CodeBakers patterns installed (v${content.version})`);
|
|
418
|
+
console.log(chalk.gray(` ${Object.keys(content.modules || {}).length} pattern modules ready\n`));
|
|
197
419
|
}
|
package/src/index.ts
CHANGED
|
@@ -72,14 +72,15 @@ const program = new Command();
|
|
|
72
72
|
program
|
|
73
73
|
.name('codebakers')
|
|
74
74
|
.description('CodeBakers CLI - Production patterns for AI-assisted development')
|
|
75
|
-
.version('
|
|
75
|
+
.version('3.1.0');
|
|
76
76
|
|
|
77
77
|
// Zero-friction trial entry (no signup required)
|
|
78
78
|
program
|
|
79
79
|
.command('go')
|
|
80
80
|
.alias('start')
|
|
81
81
|
.description('Start using CodeBakers instantly (no signup required)')
|
|
82
|
-
.
|
|
82
|
+
.option('-v, --verbose', 'Show detailed debug output for troubleshooting')
|
|
83
|
+
.action((options) => go({ verbose: options.verbose }));
|
|
83
84
|
|
|
84
85
|
program
|
|
85
86
|
.command('extend')
|