@codebakers/cli 3.5.0 → 3.6.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.d.ts +1 -1
- package/dist/commands/doctor.js +40 -82
- package/dist/commands/init.js +56 -41
- package/dist/commands/install-hook.js +8 -8
- package/dist/commands/install.js +54 -55
- package/dist/commands/scaffold.js +39 -22
- package/dist/commands/status.js +41 -13
- package/dist/mcp/server.js +425 -147
- package/package.json +1 -1
- package/src/commands/doctor.ts +39 -81
- package/src/commands/init.ts +55 -43
- package/src/commands/install-hook.ts +8 -8
- package/src/commands/install.ts +59 -68
- package/src/commands/scaffold.ts +39 -22
- package/src/commands/status.ts +45 -15
- package/src/mcp/server.ts +428 -161
package/dist/mcp/server.js
CHANGED
|
@@ -1062,13 +1062,13 @@ class CodeBakersServer {
|
|
|
1062
1062
|
},
|
|
1063
1063
|
{
|
|
1064
1064
|
name: 'update_patterns',
|
|
1065
|
-
description: '
|
|
1065
|
+
description: 'Update to CodeBakers v6.0 server-enforced patterns. Use when user says "upgrade codebakers", "update patterns", or "sync codebakers". In v6.0, patterns are server-side - this tool installs minimal bootstrap files (CLAUDE.md and .cursorrules) and removes old .claude/ folder if present.',
|
|
1066
1066
|
inputSchema: {
|
|
1067
1067
|
type: 'object',
|
|
1068
1068
|
properties: {
|
|
1069
1069
|
force: {
|
|
1070
1070
|
type: 'boolean',
|
|
1071
|
-
description: 'Force
|
|
1071
|
+
description: 'Force reinstall even if already on v6.0 (default: false)',
|
|
1072
1072
|
},
|
|
1073
1073
|
},
|
|
1074
1074
|
},
|
|
@@ -2372,35 +2372,66 @@ Or if user declines, call without fullDeploy:
|
|
|
2372
2372
|
catch {
|
|
2373
2373
|
// Use default
|
|
2374
2374
|
}
|
|
2375
|
-
results.push(`# 🎨 Adding CodeBakers to: ${projectName}\n`);
|
|
2376
|
-
//
|
|
2377
|
-
|
|
2378
|
-
|
|
2375
|
+
results.push(`# 🎨 Adding CodeBakers v6.0 to: ${projectName}\n`);
|
|
2376
|
+
// v6.0 bootstrap content - minimal files, patterns from server
|
|
2377
|
+
const V6_CLAUDE_MD = `# CodeBakers v6.0
|
|
2378
|
+
|
|
2379
|
+
**MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
|
|
2380
|
+
|
|
2381
|
+
\`\`\`
|
|
2382
|
+
Tool: discover_patterns
|
|
2383
|
+
Args: { task: "description of what you're building" }
|
|
2384
|
+
\`\`\`
|
|
2385
|
+
|
|
2386
|
+
The server will return:
|
|
2387
|
+
- All coding patterns and rules you must follow
|
|
2388
|
+
- Test requirements
|
|
2389
|
+
- Validation instructions
|
|
2390
|
+
|
|
2391
|
+
**You cannot write code without calling this tool first. The server tracks compliance.**
|
|
2392
|
+
|
|
2393
|
+
---
|
|
2394
|
+
*CodeBakers v6.0 - Server-Enforced*
|
|
2395
|
+
`;
|
|
2396
|
+
const V6_CURSORRULES = `# CodeBakers v6.0
|
|
2397
|
+
|
|
2398
|
+
MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
|
|
2399
|
+
|
|
2400
|
+
Tool: discover_patterns
|
|
2401
|
+
Args: { task: "description of what you're building" }
|
|
2402
|
+
|
|
2403
|
+
The server returns all patterns, rules, and test requirements.
|
|
2404
|
+
You cannot write code without calling this tool first.
|
|
2405
|
+
`;
|
|
2406
|
+
// Check if already v6
|
|
2407
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
2408
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
2409
|
+
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
2410
|
+
if (content.includes('v6.0') && content.includes('discover_patterns')) {
|
|
2411
|
+
results.push('✓ CodeBakers v6.0 already installed\n');
|
|
2412
|
+
results.push('Patterns are server-enforced. Just call `discover_patterns` before coding!');
|
|
2413
|
+
return {
|
|
2414
|
+
content: [{ type: 'text', text: results.join('\n') }],
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
results.push('⚠️ Upgrading to v6.0 (server-enforced patterns)...\n');
|
|
2379
2418
|
}
|
|
2380
2419
|
try {
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
results.push('✓ Created/Updated CLAUDE.md');
|
|
2393
|
-
}
|
|
2394
|
-
// Write pattern modules
|
|
2395
|
-
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
2396
|
-
const modulesDir = path.join(cwd, '.claude');
|
|
2397
|
-
if (!fs.existsSync(modulesDir)) {
|
|
2398
|
-
fs.mkdirSync(modulesDir, { recursive: true });
|
|
2420
|
+
// Write v6.0 bootstrap files
|
|
2421
|
+
fs.writeFileSync(claudeMdPath, V6_CLAUDE_MD);
|
|
2422
|
+
results.push('✓ Created CLAUDE.md (v6.0 bootstrap)');
|
|
2423
|
+
fs.writeFileSync(path.join(cwd, '.cursorrules'), V6_CURSORRULES);
|
|
2424
|
+
results.push('✓ Created .cursorrules (v6.0 bootstrap)');
|
|
2425
|
+
// Remove old .claude folder if it exists (v5 → v6 migration)
|
|
2426
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
2427
|
+
if (fs.existsSync(claudeDir)) {
|
|
2428
|
+
try {
|
|
2429
|
+
fs.rmSync(claudeDir, { recursive: true, force: true });
|
|
2430
|
+
results.push('✓ Removed .claude/ folder (patterns now server-side)');
|
|
2399
2431
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2432
|
+
catch {
|
|
2433
|
+
results.push('⚠️ Could not remove .claude/ folder - please delete manually');
|
|
2402
2434
|
}
|
|
2403
|
-
results.push(`✓ Installed ${Object.keys(content.modules).length} pattern modules (v${content.version})`);
|
|
2404
2435
|
}
|
|
2405
2436
|
// Create PRD if doesn't exist
|
|
2406
2437
|
const date = new Date().toISOString().split('T')[0];
|
|
@@ -2419,39 +2450,29 @@ Or if user declines, call without fullDeploy:
|
|
|
2419
2450
|
`);
|
|
2420
2451
|
results.push('✓ Created PRD.md template');
|
|
2421
2452
|
}
|
|
2422
|
-
//
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
## In Progress
|
|
2433
|
-
## Completed
|
|
2434
|
-
## Next Up
|
|
2435
|
-
`);
|
|
2436
|
-
results.push('✓ Created PROJECT-STATE.md');
|
|
2437
|
-
}
|
|
2438
|
-
// Update .gitignore
|
|
2439
|
-
const gitignorePath = path.join(cwd, '.gitignore');
|
|
2440
|
-
if (fs.existsSync(gitignorePath)) {
|
|
2441
|
-
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
2442
|
-
if (!gitignore.includes('.claude/')) {
|
|
2443
|
-
fs.writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.claude/\n');
|
|
2444
|
-
results.push('✓ Updated .gitignore');
|
|
2453
|
+
// Update .codebakers.json
|
|
2454
|
+
const stateFile = path.join(cwd, '.codebakers.json');
|
|
2455
|
+
let state = {};
|
|
2456
|
+
if (fs.existsSync(stateFile)) {
|
|
2457
|
+
try {
|
|
2458
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
2459
|
+
}
|
|
2460
|
+
catch {
|
|
2461
|
+
// Ignore errors
|
|
2445
2462
|
}
|
|
2446
2463
|
}
|
|
2464
|
+
state.version = '6.0';
|
|
2465
|
+
state.serverEnforced = true;
|
|
2466
|
+
state.updatedAt = new Date().toISOString();
|
|
2467
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
2447
2468
|
results.push('\n---\n');
|
|
2448
|
-
results.push('## ✅ CodeBakers
|
|
2449
|
-
results.push('
|
|
2450
|
-
results.push('
|
|
2451
|
-
results.push('
|
|
2452
|
-
results.push('
|
|
2453
|
-
results.push('
|
|
2454
|
-
results.push('
|
|
2469
|
+
results.push('## ✅ CodeBakers v6.0 Installed!\n');
|
|
2470
|
+
results.push('**How it works now:**');
|
|
2471
|
+
results.push('1. Call `discover_patterns` before writing code');
|
|
2472
|
+
results.push('2. Server returns all patterns and rules');
|
|
2473
|
+
results.push('3. Call `validate_complete` before marking done');
|
|
2474
|
+
results.push('4. Server verifies compliance\n');
|
|
2475
|
+
results.push('No local pattern files needed - everything is server-side!');
|
|
2455
2476
|
}
|
|
2456
2477
|
catch (error) {
|
|
2457
2478
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -3395,6 +3416,8 @@ Just describe what you want to build! I'll automatically:
|
|
|
3395
3416
|
let testsPass = false;
|
|
3396
3417
|
let typescriptPass = false;
|
|
3397
3418
|
const testsWritten = [];
|
|
3419
|
+
// v6.1: Code analysis for compliance scoring
|
|
3420
|
+
const codeAnalysis = {};
|
|
3398
3421
|
// Step 1: Get session token (from memory or state file)
|
|
3399
3422
|
let sessionToken = this.currentSessionToken;
|
|
3400
3423
|
if (!sessionToken) {
|
|
@@ -3443,6 +3466,74 @@ Just describe what you want to build! I'll automatically:
|
|
|
3443
3466
|
catch {
|
|
3444
3467
|
// Ignore errors
|
|
3445
3468
|
}
|
|
3469
|
+
// Step 2.5: v6.1 - Analyze code for compliance scoring
|
|
3470
|
+
try {
|
|
3471
|
+
let totalLines = 0;
|
|
3472
|
+
let hasErrorHandling = false;
|
|
3473
|
+
let hasLoadingStates = false;
|
|
3474
|
+
let hasConsoleLog = false;
|
|
3475
|
+
let hasAnyType = false;
|
|
3476
|
+
// Analyze provided files
|
|
3477
|
+
const filesToAnalyze = files.length > 0 ? files : [];
|
|
3478
|
+
// Also scan for recently modified .ts/.tsx files if no files provided
|
|
3479
|
+
if (filesToAnalyze.length === 0) {
|
|
3480
|
+
const srcDir = path.join(cwd, 'src');
|
|
3481
|
+
if (fs.existsSync(srcDir)) {
|
|
3482
|
+
const recentFiles = fs.readdirSync(srcDir, { recursive: true })
|
|
3483
|
+
.filter((f) => {
|
|
3484
|
+
const name = String(f);
|
|
3485
|
+
return (name.endsWith('.ts') || name.endsWith('.tsx')) &&
|
|
3486
|
+
!name.includes('.test.') && !name.includes('.spec.');
|
|
3487
|
+
})
|
|
3488
|
+
.slice(0, 20) // Limit to 20 files for performance
|
|
3489
|
+
.map(f => path.join('src', String(f)));
|
|
3490
|
+
filesToAnalyze.push(...recentFiles);
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
for (const file of filesToAnalyze) {
|
|
3494
|
+
try {
|
|
3495
|
+
const filePath = path.isAbsolute(file) ? file : path.join(cwd, file);
|
|
3496
|
+
if (!fs.existsSync(filePath))
|
|
3497
|
+
continue;
|
|
3498
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
3499
|
+
const lines = content.split('\n').length;
|
|
3500
|
+
totalLines += lines;
|
|
3501
|
+
// Check for error handling patterns
|
|
3502
|
+
if (content.includes('try {') || content.includes('catch (') ||
|
|
3503
|
+
content.includes('.catch(') || content.includes('onError') ||
|
|
3504
|
+
content.includes('error:') || content.includes('handleError')) {
|
|
3505
|
+
hasErrorHandling = true;
|
|
3506
|
+
}
|
|
3507
|
+
// Check for loading states
|
|
3508
|
+
if (content.includes('isLoading') || content.includes('loading:') ||
|
|
3509
|
+
content.includes('isPending') || content.includes('Skeleton') ||
|
|
3510
|
+
content.includes('Spinner') || content.includes('Loading')) {
|
|
3511
|
+
hasLoadingStates = true;
|
|
3512
|
+
}
|
|
3513
|
+
// Check for console.log (bad in production)
|
|
3514
|
+
if (content.includes('console.log') || content.includes('console.warn') ||
|
|
3515
|
+
content.includes('console.error')) {
|
|
3516
|
+
hasConsoleLog = true;
|
|
3517
|
+
}
|
|
3518
|
+
// Check for any type (should be avoided)
|
|
3519
|
+
if (content.includes(': any') || content.includes(':any') ||
|
|
3520
|
+
content.includes('as any') || content.includes('<any>')) {
|
|
3521
|
+
hasAnyType = true;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
catch {
|
|
3525
|
+
// Skip files that can't be read
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
codeAnalysis.linesOfCode = totalLines;
|
|
3529
|
+
codeAnalysis.hasErrorHandling = hasErrorHandling;
|
|
3530
|
+
codeAnalysis.hasLoadingStates = hasLoadingStates;
|
|
3531
|
+
codeAnalysis.hasConsoleLog = hasConsoleLog;
|
|
3532
|
+
codeAnalysis.hasAnyType = hasAnyType;
|
|
3533
|
+
}
|
|
3534
|
+
catch {
|
|
3535
|
+
// Ignore code analysis errors
|
|
3536
|
+
}
|
|
3446
3537
|
// Step 3: Run tests locally
|
|
3447
3538
|
if (testsExist) {
|
|
3448
3539
|
try {
|
|
@@ -3501,6 +3592,7 @@ Just describe what you want to build! I'll automatically:
|
|
|
3501
3592
|
testsRun: testsExist,
|
|
3502
3593
|
testsPassed: testsPass,
|
|
3503
3594
|
typescriptPassed: typescriptPass,
|
|
3595
|
+
codeAnalysis, // v6.1: Send code analysis for compliance scoring
|
|
3504
3596
|
}),
|
|
3505
3597
|
});
|
|
3506
3598
|
const result = await response.json();
|
|
@@ -3537,6 +3629,52 @@ Just describe what you want to build! I'll automatically:
|
|
|
3537
3629
|
let responseText = `# ✅ Feature Validation: ${feature}\n\n`;
|
|
3538
3630
|
responseText += `## Server Validation Result\n\n`;
|
|
3539
3631
|
responseText += `**Status:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n\n`;
|
|
3632
|
+
// v6.1: Show compliance score
|
|
3633
|
+
if (result.compliance) {
|
|
3634
|
+
const score = result.compliance.score || 0;
|
|
3635
|
+
const scoreEmoji = score >= 90 ? '🏆' : score >= 70 ? '👍' : score >= 50 ? '⚠️' : '❌';
|
|
3636
|
+
responseText += `## ${scoreEmoji} Compliance Score: ${score}/100\n\n`;
|
|
3637
|
+
if (result.compliance.patternScores) {
|
|
3638
|
+
responseText += `### Pattern Scores:\n`;
|
|
3639
|
+
responseText += `| Pattern | Score |\n|---------|-------|\n`;
|
|
3640
|
+
for (const [pattern, patternScore] of Object.entries(result.compliance.patternScores)) {
|
|
3641
|
+
const emoji = patternScore >= 80 ? '✅' : patternScore >= 50 ? '⚠️' : '❌';
|
|
3642
|
+
responseText += `| ${pattern} | ${emoji} ${patternScore}/100 |\n`;
|
|
3643
|
+
}
|
|
3644
|
+
responseText += `\n`;
|
|
3645
|
+
}
|
|
3646
|
+
if (result.compliance.deductions && result.compliance.deductions.length > 0) {
|
|
3647
|
+
responseText += `### Deductions:\n`;
|
|
3648
|
+
for (const deduction of result.compliance.deductions) {
|
|
3649
|
+
responseText += `- ❌ **${deduction.rule}**: ${deduction.issue} (-${deduction.points} pts)\n`;
|
|
3650
|
+
}
|
|
3651
|
+
responseText += `\n`;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
// v6.1: Show test quality metrics
|
|
3655
|
+
if (result.testQuality) {
|
|
3656
|
+
const tq = result.testQuality;
|
|
3657
|
+
responseText += `## 🧪 Test Quality Score: ${tq.overallScore}/100\n\n`;
|
|
3658
|
+
responseText += `| Metric | Status |\n|--------|--------|\n`;
|
|
3659
|
+
responseText += `| Coverage | ${tq.coverage || 0}% |\n`;
|
|
3660
|
+
responseText += `| Happy Path Tests | ${tq.hasHappyPath ? '✅' : '❌'} |\n`;
|
|
3661
|
+
responseText += `| Error Case Tests | ${tq.hasErrorCases ? '✅' : '❌'} |\n`;
|
|
3662
|
+
responseText += `| Boundary Cases | ${tq.hasBoundaryCases ? '✅' : '❌'} |\n\n`;
|
|
3663
|
+
if (tq.missingTests && tq.missingTests.length > 0) {
|
|
3664
|
+
responseText += `### Missing Tests:\n`;
|
|
3665
|
+
for (const missing of tq.missingTests) {
|
|
3666
|
+
responseText += `- ⚠️ ${missing}\n`;
|
|
3667
|
+
}
|
|
3668
|
+
responseText += `\n`;
|
|
3669
|
+
}
|
|
3670
|
+
if (tq.recommendations && tq.recommendations.length > 0) {
|
|
3671
|
+
responseText += `### Recommendations:\n`;
|
|
3672
|
+
for (const rec of tq.recommendations) {
|
|
3673
|
+
responseText += `- 💡 ${rec}\n`;
|
|
3674
|
+
}
|
|
3675
|
+
responseText += `\n`;
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3540
3678
|
if (result.issues && result.issues.length > 0) {
|
|
3541
3679
|
responseText += `### Issues:\n\n`;
|
|
3542
3680
|
for (const issue of result.issues) {
|
|
@@ -3551,7 +3689,10 @@ Just describe what you want to build! I'll automatically:
|
|
|
3551
3689
|
responseText += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
|
|
3552
3690
|
if (result.passed) {
|
|
3553
3691
|
responseText += `## ✅ Feature is COMPLETE\n\n`;
|
|
3554
|
-
|
|
3692
|
+
const completionMsg = result.compliance && result.compliance.score >= 90
|
|
3693
|
+
? 'Excellent work! High compliance score achieved.'
|
|
3694
|
+
: 'Server has recorded this completion. You may now mark this feature as done.';
|
|
3695
|
+
responseText += `${completionMsg}\n`;
|
|
3555
3696
|
}
|
|
3556
3697
|
else {
|
|
3557
3698
|
responseText += `## ❌ Feature is NOT COMPLETE\n\n`;
|
|
@@ -3619,11 +3760,92 @@ Just describe what you want to build! I'll automatically:
|
|
|
3619
3760
|
// Generate project hash for context
|
|
3620
3761
|
let projectHash;
|
|
3621
3762
|
let projectName;
|
|
3763
|
+
let detectedStack = {};
|
|
3622
3764
|
try {
|
|
3623
3765
|
const pkgPath = path.join(cwd, 'package.json');
|
|
3624
3766
|
if (fs.existsSync(pkgPath)) {
|
|
3625
3767
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
3626
3768
|
projectName = pkg.name || path.basename(cwd);
|
|
3769
|
+
// v6.1: Extract detected stack from dependencies for conflict detection
|
|
3770
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3771
|
+
const depNames = Object.keys(allDeps);
|
|
3772
|
+
// Detect framework
|
|
3773
|
+
if (depNames.includes('next'))
|
|
3774
|
+
detectedStack.framework = 'nextjs';
|
|
3775
|
+
else if (depNames.includes('remix'))
|
|
3776
|
+
detectedStack.framework = 'remix';
|
|
3777
|
+
else if (depNames.includes('gatsby'))
|
|
3778
|
+
detectedStack.framework = 'gatsby';
|
|
3779
|
+
else if (depNames.includes('react'))
|
|
3780
|
+
detectedStack.framework = 'react';
|
|
3781
|
+
else if (depNames.includes('vue'))
|
|
3782
|
+
detectedStack.framework = 'vue';
|
|
3783
|
+
// Detect ORM/database
|
|
3784
|
+
const orms = [];
|
|
3785
|
+
if (depNames.includes('drizzle-orm'))
|
|
3786
|
+
orms.push('drizzle');
|
|
3787
|
+
if (depNames.includes('prisma') || depNames.includes('@prisma/client'))
|
|
3788
|
+
orms.push('prisma');
|
|
3789
|
+
if (depNames.includes('typeorm'))
|
|
3790
|
+
orms.push('typeorm');
|
|
3791
|
+
if (depNames.includes('mongoose'))
|
|
3792
|
+
orms.push('mongoose');
|
|
3793
|
+
if (depNames.includes('sequelize'))
|
|
3794
|
+
orms.push('sequelize');
|
|
3795
|
+
if (orms.length > 0)
|
|
3796
|
+
detectedStack.orm = orms.length === 1 ? orms[0] : orms;
|
|
3797
|
+
// Detect state management
|
|
3798
|
+
const stateLibs = [];
|
|
3799
|
+
if (depNames.includes('@reduxjs/toolkit') || depNames.includes('redux'))
|
|
3800
|
+
stateLibs.push('redux');
|
|
3801
|
+
if (depNames.includes('zustand'))
|
|
3802
|
+
stateLibs.push('zustand');
|
|
3803
|
+
if (depNames.includes('jotai'))
|
|
3804
|
+
stateLibs.push('jotai');
|
|
3805
|
+
if (depNames.includes('recoil'))
|
|
3806
|
+
stateLibs.push('recoil');
|
|
3807
|
+
if (depNames.includes('mobx'))
|
|
3808
|
+
stateLibs.push('mobx');
|
|
3809
|
+
if (stateLibs.length > 0)
|
|
3810
|
+
detectedStack.stateManagement = stateLibs.length === 1 ? stateLibs[0] : stateLibs;
|
|
3811
|
+
// Detect styling
|
|
3812
|
+
const styleLibs = [];
|
|
3813
|
+
if (depNames.includes('tailwindcss'))
|
|
3814
|
+
styleLibs.push('tailwind');
|
|
3815
|
+
if (depNames.includes('@emotion/react') || depNames.includes('@emotion/styled'))
|
|
3816
|
+
styleLibs.push('emotion');
|
|
3817
|
+
if (depNames.includes('styled-components'))
|
|
3818
|
+
styleLibs.push('styled-components');
|
|
3819
|
+
if (depNames.includes('@mui/material'))
|
|
3820
|
+
styleLibs.push('mui');
|
|
3821
|
+
if (depNames.includes('@chakra-ui/react'))
|
|
3822
|
+
styleLibs.push('chakra');
|
|
3823
|
+
if (styleLibs.length > 0)
|
|
3824
|
+
detectedStack.styling = styleLibs.length === 1 ? styleLibs[0] : styleLibs;
|
|
3825
|
+
// Detect form libraries
|
|
3826
|
+
const formLibs = [];
|
|
3827
|
+
if (depNames.includes('react-hook-form'))
|
|
3828
|
+
formLibs.push('react-hook-form');
|
|
3829
|
+
if (depNames.includes('formik'))
|
|
3830
|
+
formLibs.push('formik');
|
|
3831
|
+
if (depNames.includes('react-final-form'))
|
|
3832
|
+
formLibs.push('react-final-form');
|
|
3833
|
+
if (formLibs.length > 0)
|
|
3834
|
+
detectedStack.forms = formLibs.length === 1 ? formLibs[0] : formLibs;
|
|
3835
|
+
// Detect auth
|
|
3836
|
+
if (depNames.includes('@supabase/supabase-js'))
|
|
3837
|
+
detectedStack.auth = 'supabase';
|
|
3838
|
+
else if (depNames.includes('next-auth') || depNames.includes('@auth/core'))
|
|
3839
|
+
detectedStack.auth = 'next-auth';
|
|
3840
|
+
else if (depNames.includes('@clerk/nextjs'))
|
|
3841
|
+
detectedStack.auth = 'clerk';
|
|
3842
|
+
else if (depNames.includes('firebase'))
|
|
3843
|
+
detectedStack.auth = 'firebase';
|
|
3844
|
+
// Detect payments
|
|
3845
|
+
if (depNames.includes('stripe'))
|
|
3846
|
+
detectedStack.payments = 'stripe';
|
|
3847
|
+
else if (depNames.includes('@paypal/react-paypal-js'))
|
|
3848
|
+
detectedStack.payments = 'paypal';
|
|
3627
3849
|
}
|
|
3628
3850
|
else {
|
|
3629
3851
|
projectName = path.basename(cwd);
|
|
@@ -3648,6 +3870,7 @@ Just describe what you want to build! I'll automatically:
|
|
|
3648
3870
|
keywords,
|
|
3649
3871
|
projectHash,
|
|
3650
3872
|
projectName,
|
|
3873
|
+
detectedStack, // v6.1: Send stack for conflict detection
|
|
3651
3874
|
}),
|
|
3652
3875
|
});
|
|
3653
3876
|
if (!response.ok) {
|
|
@@ -3680,6 +3903,63 @@ Just describe what you want to build! I'll automatically:
|
|
|
3680
3903
|
let responseText = `# 🔍 Pattern Discovery: ${task}\n\n`;
|
|
3681
3904
|
responseText += `## ⛔ SERVER-ENFORCED SESSION ACTIVE\n\n`;
|
|
3682
3905
|
responseText += `**Session Token:** \`${result.sessionToken}\`\n\n`;
|
|
3906
|
+
// v6.1: Show detected conflicts (high priority warning)
|
|
3907
|
+
if (result.detectedConflicts && result.detectedConflicts.length > 0) {
|
|
3908
|
+
responseText += `---\n\n`;
|
|
3909
|
+
responseText += `## ⚠️ ARCHITECTURE CONFLICTS DETECTED\n\n`;
|
|
3910
|
+
responseText += `The following conflicts were found in your project:\n\n`;
|
|
3911
|
+
for (const conflict of result.detectedConflicts) {
|
|
3912
|
+
responseText += `### ${conflict.type}\n`;
|
|
3913
|
+
responseText += `**Conflicting:** ${conflict.items.join(' + ')}\n`;
|
|
3914
|
+
responseText += `**Recommendation:** ${conflict.recommendation}\n`;
|
|
3915
|
+
responseText += `**Reason:** ${conflict.reason}\n\n`;
|
|
3916
|
+
}
|
|
3917
|
+
responseText += `**Please resolve these conflicts before proceeding.**\n\n`;
|
|
3918
|
+
}
|
|
3919
|
+
// v6.1: Show team profile settings if configured
|
|
3920
|
+
if (result.teamProfile) {
|
|
3921
|
+
responseText += `---\n\n`;
|
|
3922
|
+
responseText += `## 🏢 TEAM PROFILE\n\n`;
|
|
3923
|
+
responseText += `| Setting | Value |\n|---------|-------|\n`;
|
|
3924
|
+
responseText += `| Industry | ${result.teamProfile.industryProfile || 'general'} |\n`;
|
|
3925
|
+
responseText += `| Strictness | ${result.teamProfile.strictnessLevel || 'standard'} |\n`;
|
|
3926
|
+
if (result.teamProfile.requireHipaa)
|
|
3927
|
+
responseText += `| HIPAA | ✅ Required |\n`;
|
|
3928
|
+
if (result.teamProfile.requirePci)
|
|
3929
|
+
responseText += `| PCI-DSS | ✅ Required |\n`;
|
|
3930
|
+
if (result.teamProfile.requireSoc2)
|
|
3931
|
+
responseText += `| SOC2 | ✅ Required |\n`;
|
|
3932
|
+
if (result.teamProfile.requireGdpr)
|
|
3933
|
+
responseText += `| GDPR | ✅ Required |\n`;
|
|
3934
|
+
responseText += `\n`;
|
|
3935
|
+
}
|
|
3936
|
+
// v6.1: Show project memory if available
|
|
3937
|
+
if (result.projectMemory) {
|
|
3938
|
+
responseText += `---\n\n`;
|
|
3939
|
+
responseText += `## 🧠 PROJECT MEMORY\n\n`;
|
|
3940
|
+
responseText += `Server has remembered your project's architectural decisions:\n\n`;
|
|
3941
|
+
const memory = result.projectMemory;
|
|
3942
|
+
if (memory.stackDecisions) {
|
|
3943
|
+
const stack = typeof memory.stackDecisions === 'string'
|
|
3944
|
+
? JSON.parse(memory.stackDecisions)
|
|
3945
|
+
: memory.stackDecisions;
|
|
3946
|
+
if (Object.keys(stack).length > 0) {
|
|
3947
|
+
responseText += `### Stack Decisions\n`;
|
|
3948
|
+
responseText += `| Category | Choice |\n|----------|--------|\n`;
|
|
3949
|
+
for (const [key, value] of Object.entries(stack)) {
|
|
3950
|
+
responseText += `| ${key} | ${value} |\n`;
|
|
3951
|
+
}
|
|
3952
|
+
responseText += `\n`;
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
if (memory.namingConventions) {
|
|
3956
|
+
responseText += `### Naming Conventions\n\`\`\`\n${memory.namingConventions}\n\`\`\`\n\n`;
|
|
3957
|
+
}
|
|
3958
|
+
if (memory.projectRules) {
|
|
3959
|
+
responseText += `### Project Rules\n${memory.projectRules}\n\n`;
|
|
3960
|
+
}
|
|
3961
|
+
responseText += `**Follow these established patterns for consistency.**\n\n`;
|
|
3962
|
+
}
|
|
3683
3963
|
responseText += `---\n\n`;
|
|
3684
3964
|
// Section 1: Patterns from server
|
|
3685
3965
|
if (result.patterns && result.patterns.length > 0) {
|
|
@@ -5517,7 +5797,7 @@ ${handlers.join('\n')}
|
|
|
5517
5797
|
`;
|
|
5518
5798
|
}
|
|
5519
5799
|
/**
|
|
5520
|
-
*
|
|
5800
|
+
* Update to CodeBakers v6.0 - server-enforced patterns
|
|
5521
5801
|
* This is the MCP equivalent of the `codebakers upgrade` CLI command
|
|
5522
5802
|
*/
|
|
5523
5803
|
async handleUpdatePatterns(args) {
|
|
@@ -5525,54 +5805,63 @@ ${handlers.join('\n')}
|
|
|
5525
5805
|
const cwd = process.cwd();
|
|
5526
5806
|
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
5527
5807
|
const claudeDir = path.join(cwd, '.claude');
|
|
5528
|
-
const
|
|
5529
|
-
let response = `# 🔄 CodeBakers
|
|
5808
|
+
const codebakersJson = path.join(cwd, '.codebakers.json');
|
|
5809
|
+
let response = `# 🔄 CodeBakers v6.0 Update\n\n`;
|
|
5810
|
+
// v6.0 bootstrap content
|
|
5811
|
+
const V6_CLAUDE_MD = `# CodeBakers v6.0
|
|
5812
|
+
|
|
5813
|
+
**MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
|
|
5814
|
+
|
|
5815
|
+
\`\`\`
|
|
5816
|
+
Tool: discover_patterns
|
|
5817
|
+
Args: { task: "description of what you're building" }
|
|
5818
|
+
\`\`\`
|
|
5819
|
+
|
|
5820
|
+
The server will return:
|
|
5821
|
+
- All coding patterns and rules you must follow
|
|
5822
|
+
- Test requirements
|
|
5823
|
+
- Validation instructions
|
|
5824
|
+
|
|
5825
|
+
**You cannot write code without calling this tool first. The server tracks compliance.**
|
|
5826
|
+
|
|
5827
|
+
---
|
|
5828
|
+
*CodeBakers v6.0 - Server-Enforced*
|
|
5829
|
+
`;
|
|
5830
|
+
const V6_CURSORRULES = `# CodeBakers v6.0
|
|
5831
|
+
|
|
5832
|
+
MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
|
|
5833
|
+
|
|
5834
|
+
Tool: discover_patterns
|
|
5835
|
+
Args: { task: "description of what you're building" }
|
|
5836
|
+
|
|
5837
|
+
The server returns all patterns, rules, and test requirements.
|
|
5838
|
+
You cannot write code without calling this tool first.
|
|
5839
|
+
`;
|
|
5530
5840
|
try {
|
|
5531
5841
|
// Check current version
|
|
5532
5842
|
let currentVersion = null;
|
|
5533
|
-
let
|
|
5534
|
-
if (fs.existsSync(
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
currentVersion = versionInfo.version;
|
|
5538
|
-
currentModuleCount = versionInfo.moduleCount || 0;
|
|
5539
|
-
}
|
|
5540
|
-
catch {
|
|
5541
|
-
// Ignore parse errors
|
|
5542
|
-
}
|
|
5843
|
+
let isV6 = false;
|
|
5844
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
5845
|
+
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
5846
|
+
isV6 = content.includes('v6.0') && content.includes('discover_patterns');
|
|
5543
5847
|
}
|
|
5544
|
-
|
|
5545
|
-
if (fs.existsSync(claudeDir)) {
|
|
5848
|
+
if (fs.existsSync(codebakersJson)) {
|
|
5546
5849
|
try {
|
|
5547
|
-
const
|
|
5548
|
-
|
|
5850
|
+
const state = JSON.parse(fs.readFileSync(codebakersJson, 'utf-8'));
|
|
5851
|
+
currentVersion = state.version || null;
|
|
5549
5852
|
}
|
|
5550
5853
|
catch {
|
|
5551
|
-
// Ignore
|
|
5854
|
+
// Ignore parse errors
|
|
5552
5855
|
}
|
|
5553
5856
|
}
|
|
5554
5857
|
response += `## Current Status\n`;
|
|
5555
5858
|
response += `- Version: ${currentVersion || 'Unknown'}\n`;
|
|
5556
|
-
response += `-
|
|
5557
|
-
//
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
throw new Error('Failed to check version from server');
|
|
5563
|
-
}
|
|
5564
|
-
const latestInfo = await versionResponse.json();
|
|
5565
|
-
const latestVersion = latestInfo.version;
|
|
5566
|
-
const latestModuleCount = latestInfo.moduleCount || 0;
|
|
5567
|
-
response += `## Server Status\n`;
|
|
5568
|
-
response += `- Latest Version: ${latestVersion}\n`;
|
|
5569
|
-
response += `- Available Modules: ${latestModuleCount}\n\n`;
|
|
5570
|
-
// Check if update needed
|
|
5571
|
-
const needsUpdate = force || !currentVersion || currentVersion !== latestVersion || currentModuleCount < latestModuleCount;
|
|
5572
|
-
if (!needsUpdate) {
|
|
5573
|
-
response += `✅ **Already up to date!**\n\n`;
|
|
5574
|
-
response += `Your patterns are current (v${latestVersion} with ${latestModuleCount} modules).\n`;
|
|
5575
|
-
response += `Use \`force: true\` to re-download anyway.\n`;
|
|
5859
|
+
response += `- v6.0 (Server-Enforced): ${isV6 ? 'Yes ✓' : 'No'}\n\n`;
|
|
5860
|
+
// Check if already on v6
|
|
5861
|
+
if (isV6 && !force) {
|
|
5862
|
+
response += `✅ **Already on v6.0!**\n\n`;
|
|
5863
|
+
response += `Your patterns are server-enforced. Just use \`discover_patterns\` before coding.\n`;
|
|
5864
|
+
response += `Use \`force: true\` to reinstall bootstrap files.\n`;
|
|
5576
5865
|
response += this.getUpdateNotice();
|
|
5577
5866
|
return {
|
|
5578
5867
|
content: [{
|
|
@@ -5581,65 +5870,54 @@ ${handlers.join('\n')}
|
|
|
5581
5870
|
}],
|
|
5582
5871
|
};
|
|
5583
5872
|
}
|
|
5584
|
-
response += `##
|
|
5585
|
-
//
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
}
|
|
5600
|
-
// Update CLAUDE.md router
|
|
5601
|
-
if (content.router) {
|
|
5602
|
-
fs.writeFileSync(claudeMdPath, content.router);
|
|
5603
|
-
response += `✓ Updated CLAUDE.md (router)\n`;
|
|
5873
|
+
response += `## Upgrading to v6.0...\n\n`;
|
|
5874
|
+
// Write v6.0 bootstrap files
|
|
5875
|
+
fs.writeFileSync(claudeMdPath, V6_CLAUDE_MD);
|
|
5876
|
+
response += `✓ Updated CLAUDE.md (v6.0 bootstrap)\n`;
|
|
5877
|
+
fs.writeFileSync(path.join(cwd, '.cursorrules'), V6_CURSORRULES);
|
|
5878
|
+
response += `✓ Updated .cursorrules (v6.0 bootstrap)\n`;
|
|
5879
|
+
// Remove old .claude folder (v5 → v6 migration)
|
|
5880
|
+
if (fs.existsSync(claudeDir)) {
|
|
5881
|
+
try {
|
|
5882
|
+
fs.rmSync(claudeDir, { recursive: true, force: true });
|
|
5883
|
+
response += `✓ Removed .claude/ folder (patterns now server-side)\n`;
|
|
5884
|
+
}
|
|
5885
|
+
catch {
|
|
5886
|
+
response += `⚠️ Could not remove .claude/ folder - please delete manually\n`;
|
|
5887
|
+
}
|
|
5604
5888
|
}
|
|
5605
|
-
// Update
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5889
|
+
// Update .codebakers.json
|
|
5890
|
+
let state = {};
|
|
5891
|
+
if (fs.existsSync(codebakersJson)) {
|
|
5892
|
+
try {
|
|
5893
|
+
state = JSON.parse(fs.readFileSync(codebakersJson, 'utf-8'));
|
|
5894
|
+
}
|
|
5895
|
+
catch {
|
|
5896
|
+
// Ignore errors
|
|
5609
5897
|
}
|
|
5610
|
-
response += `✓ Updated ${moduleCount} modules in .claude/\n`;
|
|
5611
5898
|
}
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
response +=
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
response +=
|
|
5625
|
-
response +=
|
|
5626
|
-
response +=
|
|
5627
|
-
|
|
5628
|
-
response += `🆕 **${moduleCount - currentModuleCount} new modules added!**\n\n`;
|
|
5629
|
-
}
|
|
5630
|
-
response += `Your patterns are now up to date. The new patterns will be used in your next response.\n`;
|
|
5899
|
+
state.version = '6.0';
|
|
5900
|
+
state.serverEnforced = true;
|
|
5901
|
+
state.updatedAt = new Date().toISOString();
|
|
5902
|
+
fs.writeFileSync(codebakersJson, JSON.stringify(state, null, 2));
|
|
5903
|
+
response += `✓ Updated .codebakers.json\n`;
|
|
5904
|
+
// Confirm to server (non-blocking analytics)
|
|
5905
|
+
this.confirmDownload('6.0', 0).catch(() => { });
|
|
5906
|
+
response += `\n## ✅ Upgrade Complete!\n\n`;
|
|
5907
|
+
response += `**What changed in v6.0:**\n`;
|
|
5908
|
+
response += `- No local pattern files (.claude/ folder removed)\n`;
|
|
5909
|
+
response += `- All patterns fetched from server in real-time\n`;
|
|
5910
|
+
response += `- Server tracks compliance via discover_patterns/validate_complete\n\n`;
|
|
5911
|
+
response += `**How to use:**\n`;
|
|
5912
|
+
response += `1. Call \`discover_patterns\` before writing any code\n`;
|
|
5913
|
+
response += `2. Follow the patterns returned by server\n`;
|
|
5914
|
+
response += `3. Call \`validate_complete\` before marking done\n`;
|
|
5631
5915
|
}
|
|
5632
5916
|
catch (error) {
|
|
5633
5917
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
5634
5918
|
response += `\n## ❌ Update Failed\n\n`;
|
|
5635
5919
|
response += `Error: ${message}\n\n`;
|
|
5636
|
-
|
|
5637
|
-
response += `Your API key may be invalid or expired.\n`;
|
|
5638
|
-
response += `Run \`codebakers setup\` in terminal to reconfigure.\n`;
|
|
5639
|
-
}
|
|
5640
|
-
else {
|
|
5641
|
-
response += `Please try again or run \`codebakers upgrade\` in terminal.\n`;
|
|
5642
|
-
}
|
|
5920
|
+
response += `Please try again or run \`codebakers upgrade\` in terminal.\n`;
|
|
5643
5921
|
}
|
|
5644
5922
|
// Add CLI update notice if available
|
|
5645
5923
|
response += this.getUpdateNotice();
|