@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/src/mcp/server.ts
CHANGED
|
@@ -1158,13 +1158,13 @@ class CodeBakersServer {
|
|
|
1158
1158
|
{
|
|
1159
1159
|
name: 'update_patterns',
|
|
1160
1160
|
description:
|
|
1161
|
-
'
|
|
1161
|
+
'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.',
|
|
1162
1162
|
inputSchema: {
|
|
1163
1163
|
type: 'object' as const,
|
|
1164
1164
|
properties: {
|
|
1165
1165
|
force: {
|
|
1166
1166
|
type: 'boolean',
|
|
1167
|
-
description: 'Force
|
|
1167
|
+
description: 'Force reinstall even if already on v6.0 (default: false)',
|
|
1168
1168
|
},
|
|
1169
1169
|
},
|
|
1170
1170
|
},
|
|
@@ -2705,41 +2705,71 @@ Or if user declines, call without fullDeploy:
|
|
|
2705
2705
|
// Use default
|
|
2706
2706
|
}
|
|
2707
2707
|
|
|
2708
|
-
results.push(`# 🎨 Adding CodeBakers to: ${projectName}\n`);
|
|
2708
|
+
results.push(`# 🎨 Adding CodeBakers v6.0 to: ${projectName}\n`);
|
|
2709
2709
|
|
|
2710
|
-
//
|
|
2711
|
-
|
|
2712
|
-
results.push('⚠️ CLAUDE.md already exists. Updating patterns...\n');
|
|
2713
|
-
}
|
|
2710
|
+
// v6.0 bootstrap content - minimal files, patterns from server
|
|
2711
|
+
const V6_CLAUDE_MD = `# CodeBakers v6.0
|
|
2714
2712
|
|
|
2715
|
-
|
|
2716
|
-
const response = await fetch(`${this.apiUrl}/api/content`, {
|
|
2717
|
-
method: 'GET',
|
|
2718
|
-
headers: this.getAuthHeaders(),
|
|
2719
|
-
});
|
|
2713
|
+
**MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
|
|
2720
2714
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2715
|
+
\`\`\`
|
|
2716
|
+
Tool: discover_patterns
|
|
2717
|
+
Args: { task: "description of what you're building" }
|
|
2718
|
+
\`\`\`
|
|
2719
|
+
|
|
2720
|
+
The server will return:
|
|
2721
|
+
- All coding patterns and rules you must follow
|
|
2722
|
+
- Test requirements
|
|
2723
|
+
- Validation instructions
|
|
2724
2724
|
|
|
2725
|
-
|
|
2725
|
+
**You cannot write code without calling this tool first. The server tracks compliance.**
|
|
2726
2726
|
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2727
|
+
---
|
|
2728
|
+
*CodeBakers v6.0 - Server-Enforced*
|
|
2729
|
+
`;
|
|
2730
|
+
|
|
2731
|
+
const V6_CURSORRULES = `# CodeBakers v6.0
|
|
2732
|
+
|
|
2733
|
+
MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
|
|
2734
|
+
|
|
2735
|
+
Tool: discover_patterns
|
|
2736
|
+
Args: { task: "description of what you're building" }
|
|
2737
|
+
|
|
2738
|
+
The server returns all patterns, rules, and test requirements.
|
|
2739
|
+
You cannot write code without calling this tool first.
|
|
2740
|
+
`;
|
|
2741
|
+
|
|
2742
|
+
// Check if already v6
|
|
2743
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
2744
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
2745
|
+
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
2746
|
+
if (content.includes('v6.0') && content.includes('discover_patterns')) {
|
|
2747
|
+
results.push('✓ CodeBakers v6.0 already installed\n');
|
|
2748
|
+
results.push('Patterns are server-enforced. Just call `discover_patterns` before coding!');
|
|
2749
|
+
return {
|
|
2750
|
+
content: [{ type: 'text' as const, text: results.join('\n') }],
|
|
2751
|
+
};
|
|
2731
2752
|
}
|
|
2753
|
+
results.push('⚠️ Upgrading to v6.0 (server-enforced patterns)...\n');
|
|
2754
|
+
}
|
|
2732
2755
|
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2756
|
+
try {
|
|
2757
|
+
// Write v6.0 bootstrap files
|
|
2758
|
+
fs.writeFileSync(claudeMdPath, V6_CLAUDE_MD);
|
|
2759
|
+
results.push('✓ Created CLAUDE.md (v6.0 bootstrap)');
|
|
2760
|
+
|
|
2761
|
+
fs.writeFileSync(path.join(cwd, '.cursorrules'), V6_CURSORRULES);
|
|
2762
|
+
results.push('✓ Created .cursorrules (v6.0 bootstrap)');
|
|
2763
|
+
|
|
2764
|
+
// Remove old .claude folder if it exists (v5 → v6 migration)
|
|
2765
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
2766
|
+
if (fs.existsSync(claudeDir)) {
|
|
2767
|
+
try {
|
|
2768
|
+
fs.rmSync(claudeDir, { recursive: true, force: true });
|
|
2769
|
+
results.push('✓ Removed .claude/ folder (patterns now server-side)');
|
|
2770
|
+
} catch {
|
|
2771
|
+
results.push('⚠️ Could not remove .claude/ folder - please delete manually');
|
|
2741
2772
|
}
|
|
2742
|
-
results.push(`✓ Installed ${Object.keys(content.modules).length} pattern modules (v${content.version})`);
|
|
2743
2773
|
}
|
|
2744
2774
|
|
|
2745
2775
|
// Create PRD if doesn't exist
|
|
@@ -2760,41 +2790,29 @@ Or if user declines, call without fullDeploy:
|
|
|
2760
2790
|
results.push('✓ Created PRD.md template');
|
|
2761
2791
|
}
|
|
2762
2792
|
|
|
2763
|
-
//
|
|
2764
|
-
const
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
phase: development
|
|
2772
|
-
|
|
2773
|
-
## In Progress
|
|
2774
|
-
## Completed
|
|
2775
|
-
## Next Up
|
|
2776
|
-
`);
|
|
2777
|
-
results.push('✓ Created PROJECT-STATE.md');
|
|
2778
|
-
}
|
|
2779
|
-
|
|
2780
|
-
// Update .gitignore
|
|
2781
|
-
const gitignorePath = path.join(cwd, '.gitignore');
|
|
2782
|
-
if (fs.existsSync(gitignorePath)) {
|
|
2783
|
-
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
2784
|
-
if (!gitignore.includes('.claude/')) {
|
|
2785
|
-
fs.writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.claude/\n');
|
|
2786
|
-
results.push('✓ Updated .gitignore');
|
|
2793
|
+
// Update .codebakers.json
|
|
2794
|
+
const stateFile = path.join(cwd, '.codebakers.json');
|
|
2795
|
+
let state: Record<string, unknown> = {};
|
|
2796
|
+
if (fs.existsSync(stateFile)) {
|
|
2797
|
+
try {
|
|
2798
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
2799
|
+
} catch {
|
|
2800
|
+
// Ignore errors
|
|
2787
2801
|
}
|
|
2788
2802
|
}
|
|
2803
|
+
state.version = '6.0';
|
|
2804
|
+
state.serverEnforced = true;
|
|
2805
|
+
state.updatedAt = new Date().toISOString();
|
|
2806
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
2789
2807
|
|
|
2790
2808
|
results.push('\n---\n');
|
|
2791
|
-
results.push('## ✅ CodeBakers
|
|
2792
|
-
results.push('
|
|
2793
|
-
results.push('
|
|
2794
|
-
results.push('
|
|
2795
|
-
results.push('
|
|
2796
|
-
results.push('
|
|
2797
|
-
results.push('
|
|
2809
|
+
results.push('## ✅ CodeBakers v6.0 Installed!\n');
|
|
2810
|
+
results.push('**How it works now:**');
|
|
2811
|
+
results.push('1. Call `discover_patterns` before writing code');
|
|
2812
|
+
results.push('2. Server returns all patterns and rules');
|
|
2813
|
+
results.push('3. Call `validate_complete` before marking done');
|
|
2814
|
+
results.push('4. Server verifies compliance\n');
|
|
2815
|
+
results.push('No local pattern files needed - everything is server-side!');
|
|
2798
2816
|
|
|
2799
2817
|
} catch (error) {
|
|
2800
2818
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -3835,6 +3853,16 @@ Just describe what you want to build! I'll automatically:
|
|
|
3835
3853
|
let typescriptPass = false;
|
|
3836
3854
|
const testsWritten: string[] = [];
|
|
3837
3855
|
|
|
3856
|
+
// v6.1: Code analysis for compliance scoring
|
|
3857
|
+
const codeAnalysis: {
|
|
3858
|
+
hasErrorHandling?: boolean;
|
|
3859
|
+
hasLoadingStates?: boolean;
|
|
3860
|
+
hasTypeAnnotations?: boolean;
|
|
3861
|
+
hasConsoleLog?: boolean;
|
|
3862
|
+
hasAnyType?: boolean;
|
|
3863
|
+
linesOfCode?: number;
|
|
3864
|
+
} = {};
|
|
3865
|
+
|
|
3838
3866
|
// Step 1: Get session token (from memory or state file)
|
|
3839
3867
|
let sessionToken = this.currentSessionToken;
|
|
3840
3868
|
if (!sessionToken) {
|
|
@@ -3885,6 +3913,81 @@ Just describe what you want to build! I'll automatically:
|
|
|
3885
3913
|
// Ignore errors
|
|
3886
3914
|
}
|
|
3887
3915
|
|
|
3916
|
+
// Step 2.5: v6.1 - Analyze code for compliance scoring
|
|
3917
|
+
try {
|
|
3918
|
+
let totalLines = 0;
|
|
3919
|
+
let hasErrorHandling = false;
|
|
3920
|
+
let hasLoadingStates = false;
|
|
3921
|
+
let hasConsoleLog = false;
|
|
3922
|
+
let hasAnyType = false;
|
|
3923
|
+
|
|
3924
|
+
// Analyze provided files
|
|
3925
|
+
const filesToAnalyze = files.length > 0 ? files : [];
|
|
3926
|
+
|
|
3927
|
+
// Also scan for recently modified .ts/.tsx files if no files provided
|
|
3928
|
+
if (filesToAnalyze.length === 0) {
|
|
3929
|
+
const srcDir = path.join(cwd, 'src');
|
|
3930
|
+
if (fs.existsSync(srcDir)) {
|
|
3931
|
+
const recentFiles = fs.readdirSync(srcDir, { recursive: true })
|
|
3932
|
+
.filter((f: string | Buffer) => {
|
|
3933
|
+
const name = String(f);
|
|
3934
|
+
return (name.endsWith('.ts') || name.endsWith('.tsx')) &&
|
|
3935
|
+
!name.includes('.test.') && !name.includes('.spec.');
|
|
3936
|
+
})
|
|
3937
|
+
.slice(0, 20) // Limit to 20 files for performance
|
|
3938
|
+
.map(f => path.join('src', String(f)));
|
|
3939
|
+
filesToAnalyze.push(...recentFiles);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
for (const file of filesToAnalyze) {
|
|
3944
|
+
try {
|
|
3945
|
+
const filePath = path.isAbsolute(file) ? file : path.join(cwd, file);
|
|
3946
|
+
if (!fs.existsSync(filePath)) continue;
|
|
3947
|
+
|
|
3948
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
3949
|
+
const lines = content.split('\n').length;
|
|
3950
|
+
totalLines += lines;
|
|
3951
|
+
|
|
3952
|
+
// Check for error handling patterns
|
|
3953
|
+
if (content.includes('try {') || content.includes('catch (') ||
|
|
3954
|
+
content.includes('.catch(') || content.includes('onError') ||
|
|
3955
|
+
content.includes('error:') || content.includes('handleError')) {
|
|
3956
|
+
hasErrorHandling = true;
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3959
|
+
// Check for loading states
|
|
3960
|
+
if (content.includes('isLoading') || content.includes('loading:') ||
|
|
3961
|
+
content.includes('isPending') || content.includes('Skeleton') ||
|
|
3962
|
+
content.includes('Spinner') || content.includes('Loading')) {
|
|
3963
|
+
hasLoadingStates = true;
|
|
3964
|
+
}
|
|
3965
|
+
|
|
3966
|
+
// Check for console.log (bad in production)
|
|
3967
|
+
if (content.includes('console.log') || content.includes('console.warn') ||
|
|
3968
|
+
content.includes('console.error')) {
|
|
3969
|
+
hasConsoleLog = true;
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3972
|
+
// Check for any type (should be avoided)
|
|
3973
|
+
if (content.includes(': any') || content.includes(':any') ||
|
|
3974
|
+
content.includes('as any') || content.includes('<any>')) {
|
|
3975
|
+
hasAnyType = true;
|
|
3976
|
+
}
|
|
3977
|
+
} catch {
|
|
3978
|
+
// Skip files that can't be read
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
codeAnalysis.linesOfCode = totalLines;
|
|
3983
|
+
codeAnalysis.hasErrorHandling = hasErrorHandling;
|
|
3984
|
+
codeAnalysis.hasLoadingStates = hasLoadingStates;
|
|
3985
|
+
codeAnalysis.hasConsoleLog = hasConsoleLog;
|
|
3986
|
+
codeAnalysis.hasAnyType = hasAnyType;
|
|
3987
|
+
} catch {
|
|
3988
|
+
// Ignore code analysis errors
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3888
3991
|
// Step 3: Run tests locally
|
|
3889
3992
|
if (testsExist) {
|
|
3890
3993
|
try {
|
|
@@ -3941,6 +4044,7 @@ Just describe what you want to build! I'll automatically:
|
|
|
3941
4044
|
testsRun: testsExist,
|
|
3942
4045
|
testsPassed: testsPass,
|
|
3943
4046
|
typescriptPassed: typescriptPass,
|
|
4047
|
+
codeAnalysis, // v6.1: Send code analysis for compliance scoring
|
|
3944
4048
|
}),
|
|
3945
4049
|
});
|
|
3946
4050
|
|
|
@@ -3983,6 +4087,58 @@ Just describe what you want to build! I'll automatically:
|
|
|
3983
4087
|
responseText += `## Server Validation Result\n\n`;
|
|
3984
4088
|
responseText += `**Status:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n\n`;
|
|
3985
4089
|
|
|
4090
|
+
// v6.1: Show compliance score
|
|
4091
|
+
if (result.compliance) {
|
|
4092
|
+
const score = result.compliance.score || 0;
|
|
4093
|
+
const scoreEmoji = score >= 90 ? '🏆' : score >= 70 ? '👍' : score >= 50 ? '⚠️' : '❌';
|
|
4094
|
+
responseText += `## ${scoreEmoji} Compliance Score: ${score}/100\n\n`;
|
|
4095
|
+
|
|
4096
|
+
if (result.compliance.patternScores) {
|
|
4097
|
+
responseText += `### Pattern Scores:\n`;
|
|
4098
|
+
responseText += `| Pattern | Score |\n|---------|-------|\n`;
|
|
4099
|
+
for (const [pattern, patternScore] of Object.entries(result.compliance.patternScores)) {
|
|
4100
|
+
const emoji = (patternScore as number) >= 80 ? '✅' : (patternScore as number) >= 50 ? '⚠️' : '❌';
|
|
4101
|
+
responseText += `| ${pattern} | ${emoji} ${patternScore}/100 |\n`;
|
|
4102
|
+
}
|
|
4103
|
+
responseText += `\n`;
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
if (result.compliance.deductions && result.compliance.deductions.length > 0) {
|
|
4107
|
+
responseText += `### Deductions:\n`;
|
|
4108
|
+
for (const deduction of result.compliance.deductions) {
|
|
4109
|
+
responseText += `- ❌ **${deduction.rule}**: ${deduction.issue} (-${deduction.points} pts)\n`;
|
|
4110
|
+
}
|
|
4111
|
+
responseText += `\n`;
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
// v6.1: Show test quality metrics
|
|
4116
|
+
if (result.testQuality) {
|
|
4117
|
+
const tq = result.testQuality;
|
|
4118
|
+
responseText += `## 🧪 Test Quality Score: ${tq.overallScore}/100\n\n`;
|
|
4119
|
+
responseText += `| Metric | Status |\n|--------|--------|\n`;
|
|
4120
|
+
responseText += `| Coverage | ${tq.coverage || 0}% |\n`;
|
|
4121
|
+
responseText += `| Happy Path Tests | ${tq.hasHappyPath ? '✅' : '❌'} |\n`;
|
|
4122
|
+
responseText += `| Error Case Tests | ${tq.hasErrorCases ? '✅' : '❌'} |\n`;
|
|
4123
|
+
responseText += `| Boundary Cases | ${tq.hasBoundaryCases ? '✅' : '❌'} |\n\n`;
|
|
4124
|
+
|
|
4125
|
+
if (tq.missingTests && tq.missingTests.length > 0) {
|
|
4126
|
+
responseText += `### Missing Tests:\n`;
|
|
4127
|
+
for (const missing of tq.missingTests) {
|
|
4128
|
+
responseText += `- ⚠️ ${missing}\n`;
|
|
4129
|
+
}
|
|
4130
|
+
responseText += `\n`;
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
if (tq.recommendations && tq.recommendations.length > 0) {
|
|
4134
|
+
responseText += `### Recommendations:\n`;
|
|
4135
|
+
for (const rec of tq.recommendations) {
|
|
4136
|
+
responseText += `- 💡 ${rec}\n`;
|
|
4137
|
+
}
|
|
4138
|
+
responseText += `\n`;
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
|
|
3986
4142
|
if (result.issues && result.issues.length > 0) {
|
|
3987
4143
|
responseText += `### Issues:\n\n`;
|
|
3988
4144
|
for (const issue of result.issues) {
|
|
@@ -3999,7 +4155,10 @@ Just describe what you want to build! I'll automatically:
|
|
|
3999
4155
|
|
|
4000
4156
|
if (result.passed) {
|
|
4001
4157
|
responseText += `## ✅ Feature is COMPLETE\n\n`;
|
|
4002
|
-
|
|
4158
|
+
const completionMsg = result.compliance && result.compliance.score >= 90
|
|
4159
|
+
? 'Excellent work! High compliance score achieved.'
|
|
4160
|
+
: 'Server has recorded this completion. You may now mark this feature as done.';
|
|
4161
|
+
responseText += `${completionMsg}\n`;
|
|
4003
4162
|
} else {
|
|
4004
4163
|
responseText += `## ❌ Feature is NOT COMPLETE\n\n`;
|
|
4005
4164
|
responseText += `**${result.nextSteps || 'Fix the issues above and try again.'}**\n`;
|
|
@@ -4070,11 +4229,68 @@ Just describe what you want to build! I'll automatically:
|
|
|
4070
4229
|
// Generate project hash for context
|
|
4071
4230
|
let projectHash: string | undefined;
|
|
4072
4231
|
let projectName: string | undefined;
|
|
4232
|
+
let detectedStack: Record<string, string | string[]> = {};
|
|
4233
|
+
|
|
4073
4234
|
try {
|
|
4074
4235
|
const pkgPath = path.join(cwd, 'package.json');
|
|
4075
4236
|
if (fs.existsSync(pkgPath)) {
|
|
4076
4237
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
4077
4238
|
projectName = pkg.name || path.basename(cwd);
|
|
4239
|
+
|
|
4240
|
+
// v6.1: Extract detected stack from dependencies for conflict detection
|
|
4241
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4242
|
+
const depNames = Object.keys(allDeps);
|
|
4243
|
+
|
|
4244
|
+
// Detect framework
|
|
4245
|
+
if (depNames.includes('next')) detectedStack.framework = 'nextjs';
|
|
4246
|
+
else if (depNames.includes('remix')) detectedStack.framework = 'remix';
|
|
4247
|
+
else if (depNames.includes('gatsby')) detectedStack.framework = 'gatsby';
|
|
4248
|
+
else if (depNames.includes('react')) detectedStack.framework = 'react';
|
|
4249
|
+
else if (depNames.includes('vue')) detectedStack.framework = 'vue';
|
|
4250
|
+
|
|
4251
|
+
// Detect ORM/database
|
|
4252
|
+
const orms: string[] = [];
|
|
4253
|
+
if (depNames.includes('drizzle-orm')) orms.push('drizzle');
|
|
4254
|
+
if (depNames.includes('prisma') || depNames.includes('@prisma/client')) orms.push('prisma');
|
|
4255
|
+
if (depNames.includes('typeorm')) orms.push('typeorm');
|
|
4256
|
+
if (depNames.includes('mongoose')) orms.push('mongoose');
|
|
4257
|
+
if (depNames.includes('sequelize')) orms.push('sequelize');
|
|
4258
|
+
if (orms.length > 0) detectedStack.orm = orms.length === 1 ? orms[0] : orms;
|
|
4259
|
+
|
|
4260
|
+
// Detect state management
|
|
4261
|
+
const stateLibs: string[] = [];
|
|
4262
|
+
if (depNames.includes('@reduxjs/toolkit') || depNames.includes('redux')) stateLibs.push('redux');
|
|
4263
|
+
if (depNames.includes('zustand')) stateLibs.push('zustand');
|
|
4264
|
+
if (depNames.includes('jotai')) stateLibs.push('jotai');
|
|
4265
|
+
if (depNames.includes('recoil')) stateLibs.push('recoil');
|
|
4266
|
+
if (depNames.includes('mobx')) stateLibs.push('mobx');
|
|
4267
|
+
if (stateLibs.length > 0) detectedStack.stateManagement = stateLibs.length === 1 ? stateLibs[0] : stateLibs;
|
|
4268
|
+
|
|
4269
|
+
// Detect styling
|
|
4270
|
+
const styleLibs: string[] = [];
|
|
4271
|
+
if (depNames.includes('tailwindcss')) styleLibs.push('tailwind');
|
|
4272
|
+
if (depNames.includes('@emotion/react') || depNames.includes('@emotion/styled')) styleLibs.push('emotion');
|
|
4273
|
+
if (depNames.includes('styled-components')) styleLibs.push('styled-components');
|
|
4274
|
+
if (depNames.includes('@mui/material')) styleLibs.push('mui');
|
|
4275
|
+
if (depNames.includes('@chakra-ui/react')) styleLibs.push('chakra');
|
|
4276
|
+
if (styleLibs.length > 0) detectedStack.styling = styleLibs.length === 1 ? styleLibs[0] : styleLibs;
|
|
4277
|
+
|
|
4278
|
+
// Detect form libraries
|
|
4279
|
+
const formLibs: string[] = [];
|
|
4280
|
+
if (depNames.includes('react-hook-form')) formLibs.push('react-hook-form');
|
|
4281
|
+
if (depNames.includes('formik')) formLibs.push('formik');
|
|
4282
|
+
if (depNames.includes('react-final-form')) formLibs.push('react-final-form');
|
|
4283
|
+
if (formLibs.length > 0) detectedStack.forms = formLibs.length === 1 ? formLibs[0] : formLibs;
|
|
4284
|
+
|
|
4285
|
+
// Detect auth
|
|
4286
|
+
if (depNames.includes('@supabase/supabase-js')) detectedStack.auth = 'supabase';
|
|
4287
|
+
else if (depNames.includes('next-auth') || depNames.includes('@auth/core')) detectedStack.auth = 'next-auth';
|
|
4288
|
+
else if (depNames.includes('@clerk/nextjs')) detectedStack.auth = 'clerk';
|
|
4289
|
+
else if (depNames.includes('firebase')) detectedStack.auth = 'firebase';
|
|
4290
|
+
|
|
4291
|
+
// Detect payments
|
|
4292
|
+
if (depNames.includes('stripe')) detectedStack.payments = 'stripe';
|
|
4293
|
+
else if (depNames.includes('@paypal/react-paypal-js')) detectedStack.payments = 'paypal';
|
|
4078
4294
|
} else {
|
|
4079
4295
|
projectName = path.basename(cwd);
|
|
4080
4296
|
}
|
|
@@ -4098,6 +4314,7 @@ Just describe what you want to build! I'll automatically:
|
|
|
4098
4314
|
keywords,
|
|
4099
4315
|
projectHash,
|
|
4100
4316
|
projectName,
|
|
4317
|
+
detectedStack, // v6.1: Send stack for conflict detection
|
|
4101
4318
|
}),
|
|
4102
4319
|
});
|
|
4103
4320
|
|
|
@@ -4134,6 +4351,67 @@ Just describe what you want to build! I'll automatically:
|
|
|
4134
4351
|
let responseText = `# 🔍 Pattern Discovery: ${task}\n\n`;
|
|
4135
4352
|
responseText += `## ⛔ SERVER-ENFORCED SESSION ACTIVE\n\n`;
|
|
4136
4353
|
responseText += `**Session Token:** \`${result.sessionToken}\`\n\n`;
|
|
4354
|
+
|
|
4355
|
+
// v6.1: Show detected conflicts (high priority warning)
|
|
4356
|
+
if (result.detectedConflicts && result.detectedConflicts.length > 0) {
|
|
4357
|
+
responseText += `---\n\n`;
|
|
4358
|
+
responseText += `## ⚠️ ARCHITECTURE CONFLICTS DETECTED\n\n`;
|
|
4359
|
+
responseText += `The following conflicts were found in your project:\n\n`;
|
|
4360
|
+
for (const conflict of result.detectedConflicts) {
|
|
4361
|
+
responseText += `### ${conflict.type}\n`;
|
|
4362
|
+
responseText += `**Conflicting:** ${conflict.items.join(' + ')}\n`;
|
|
4363
|
+
responseText += `**Recommendation:** ${conflict.recommendation}\n`;
|
|
4364
|
+
responseText += `**Reason:** ${conflict.reason}\n\n`;
|
|
4365
|
+
}
|
|
4366
|
+
responseText += `**Please resolve these conflicts before proceeding.**\n\n`;
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4369
|
+
// v6.1: Show team profile settings if configured
|
|
4370
|
+
if (result.teamProfile) {
|
|
4371
|
+
responseText += `---\n\n`;
|
|
4372
|
+
responseText += `## 🏢 TEAM PROFILE\n\n`;
|
|
4373
|
+
responseText += `| Setting | Value |\n|---------|-------|\n`;
|
|
4374
|
+
responseText += `| Industry | ${result.teamProfile.industryProfile || 'general'} |\n`;
|
|
4375
|
+
responseText += `| Strictness | ${result.teamProfile.strictnessLevel || 'standard'} |\n`;
|
|
4376
|
+
if (result.teamProfile.requireHipaa) responseText += `| HIPAA | ✅ Required |\n`;
|
|
4377
|
+
if (result.teamProfile.requirePci) responseText += `| PCI-DSS | ✅ Required |\n`;
|
|
4378
|
+
if (result.teamProfile.requireSoc2) responseText += `| SOC2 | ✅ Required |\n`;
|
|
4379
|
+
if (result.teamProfile.requireGdpr) responseText += `| GDPR | ✅ Required |\n`;
|
|
4380
|
+
responseText += `\n`;
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4383
|
+
// v6.1: Show project memory if available
|
|
4384
|
+
if (result.projectMemory) {
|
|
4385
|
+
responseText += `---\n\n`;
|
|
4386
|
+
responseText += `## 🧠 PROJECT MEMORY\n\n`;
|
|
4387
|
+
responseText += `Server has remembered your project's architectural decisions:\n\n`;
|
|
4388
|
+
|
|
4389
|
+
const memory = result.projectMemory;
|
|
4390
|
+
if (memory.stackDecisions) {
|
|
4391
|
+
const stack = typeof memory.stackDecisions === 'string'
|
|
4392
|
+
? JSON.parse(memory.stackDecisions)
|
|
4393
|
+
: memory.stackDecisions;
|
|
4394
|
+
if (Object.keys(stack).length > 0) {
|
|
4395
|
+
responseText += `### Stack Decisions\n`;
|
|
4396
|
+
responseText += `| Category | Choice |\n|----------|--------|\n`;
|
|
4397
|
+
for (const [key, value] of Object.entries(stack)) {
|
|
4398
|
+
responseText += `| ${key} | ${value} |\n`;
|
|
4399
|
+
}
|
|
4400
|
+
responseText += `\n`;
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
if (memory.namingConventions) {
|
|
4405
|
+
responseText += `### Naming Conventions\n\`\`\`\n${memory.namingConventions}\n\`\`\`\n\n`;
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
if (memory.projectRules) {
|
|
4409
|
+
responseText += `### Project Rules\n${memory.projectRules}\n\n`;
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
responseText += `**Follow these established patterns for consistency.**\n\n`;
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4137
4415
|
responseText += `---\n\n`;
|
|
4138
4416
|
|
|
4139
4417
|
// Section 1: Patterns from server
|
|
@@ -6242,7 +6520,7 @@ ${handlers.join('\n')}
|
|
|
6242
6520
|
}
|
|
6243
6521
|
|
|
6244
6522
|
/**
|
|
6245
|
-
*
|
|
6523
|
+
* Update to CodeBakers v6.0 - server-enforced patterns
|
|
6246
6524
|
* This is the MCP equivalent of the `codebakers upgrade` CLI command
|
|
6247
6525
|
*/
|
|
6248
6526
|
private async handleUpdatePatterns(args: { force?: boolean }) {
|
|
@@ -6250,63 +6528,70 @@ ${handlers.join('\n')}
|
|
|
6250
6528
|
const cwd = process.cwd();
|
|
6251
6529
|
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
6252
6530
|
const claudeDir = path.join(cwd, '.claude');
|
|
6253
|
-
const
|
|
6531
|
+
const codebakersJson = path.join(cwd, '.codebakers.json');
|
|
6254
6532
|
|
|
6255
|
-
let response = `# 🔄 CodeBakers
|
|
6533
|
+
let response = `# 🔄 CodeBakers v6.0 Update\n\n`;
|
|
6534
|
+
|
|
6535
|
+
// v6.0 bootstrap content
|
|
6536
|
+
const V6_CLAUDE_MD = `# CodeBakers v6.0
|
|
6537
|
+
|
|
6538
|
+
**MANDATORY: Before writing ANY code, call the \`discover_patterns\` MCP tool.**
|
|
6539
|
+
|
|
6540
|
+
\`\`\`
|
|
6541
|
+
Tool: discover_patterns
|
|
6542
|
+
Args: { task: "description of what you're building" }
|
|
6543
|
+
\`\`\`
|
|
6544
|
+
|
|
6545
|
+
The server will return:
|
|
6546
|
+
- All coding patterns and rules you must follow
|
|
6547
|
+
- Test requirements
|
|
6548
|
+
- Validation instructions
|
|
6549
|
+
|
|
6550
|
+
**You cannot write code without calling this tool first. The server tracks compliance.**
|
|
6551
|
+
|
|
6552
|
+
---
|
|
6553
|
+
*CodeBakers v6.0 - Server-Enforced*
|
|
6554
|
+
`;
|
|
6555
|
+
|
|
6556
|
+
const V6_CURSORRULES = `# CodeBakers v6.0
|
|
6557
|
+
|
|
6558
|
+
MANDATORY: Before writing ANY code, call the discover_patterns MCP tool.
|
|
6559
|
+
|
|
6560
|
+
Tool: discover_patterns
|
|
6561
|
+
Args: { task: "description of what you're building" }
|
|
6562
|
+
|
|
6563
|
+
The server returns all patterns, rules, and test requirements.
|
|
6564
|
+
You cannot write code without calling this tool first.
|
|
6565
|
+
`;
|
|
6256
6566
|
|
|
6257
6567
|
try {
|
|
6258
6568
|
// Check current version
|
|
6259
6569
|
let currentVersion: string | null = null;
|
|
6260
|
-
let
|
|
6570
|
+
let isV6 = false;
|
|
6261
6571
|
|
|
6262
|
-
if (fs.existsSync(
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
currentVersion = versionInfo.version;
|
|
6266
|
-
currentModuleCount = versionInfo.moduleCount || 0;
|
|
6267
|
-
} catch {
|
|
6268
|
-
// Ignore parse errors
|
|
6269
|
-
}
|
|
6572
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
6573
|
+
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
6574
|
+
isV6 = content.includes('v6.0') && content.includes('discover_patterns');
|
|
6270
6575
|
}
|
|
6271
6576
|
|
|
6272
|
-
|
|
6273
|
-
if (fs.existsSync(claudeDir)) {
|
|
6577
|
+
if (fs.existsSync(codebakersJson)) {
|
|
6274
6578
|
try {
|
|
6275
|
-
const
|
|
6276
|
-
|
|
6579
|
+
const state = JSON.parse(fs.readFileSync(codebakersJson, 'utf-8'));
|
|
6580
|
+
currentVersion = state.version || null;
|
|
6277
6581
|
} catch {
|
|
6278
|
-
// Ignore
|
|
6582
|
+
// Ignore parse errors
|
|
6279
6583
|
}
|
|
6280
6584
|
}
|
|
6281
6585
|
|
|
6282
6586
|
response += `## Current Status\n`;
|
|
6283
6587
|
response += `- Version: ${currentVersion || 'Unknown'}\n`;
|
|
6284
|
-
response += `-
|
|
6285
|
-
|
|
6286
|
-
// Fetch latest version info first
|
|
6287
|
-
const versionResponse = await fetch(`${this.apiUrl}/api/content/version`, {
|
|
6288
|
-
headers: this.getAuthHeaders(),
|
|
6289
|
-
});
|
|
6588
|
+
response += `- v6.0 (Server-Enforced): ${isV6 ? 'Yes ✓' : 'No'}\n\n`;
|
|
6290
6589
|
|
|
6291
|
-
if
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
const latestVersion = latestInfo.version;
|
|
6297
|
-
const latestModuleCount = latestInfo.moduleCount || 0;
|
|
6298
|
-
|
|
6299
|
-
response += `## Server Status\n`;
|
|
6300
|
-
response += `- Latest Version: ${latestVersion}\n`;
|
|
6301
|
-
response += `- Available Modules: ${latestModuleCount}\n\n`;
|
|
6302
|
-
|
|
6303
|
-
// Check if update needed
|
|
6304
|
-
const needsUpdate = force || !currentVersion || currentVersion !== latestVersion || currentModuleCount < latestModuleCount;
|
|
6305
|
-
|
|
6306
|
-
if (!needsUpdate) {
|
|
6307
|
-
response += `✅ **Already up to date!**\n\n`;
|
|
6308
|
-
response += `Your patterns are current (v${latestVersion} with ${latestModuleCount} modules).\n`;
|
|
6309
|
-
response += `Use \`force: true\` to re-download anyway.\n`;
|
|
6590
|
+
// Check if already on v6
|
|
6591
|
+
if (isV6 && !force) {
|
|
6592
|
+
response += `✅ **Already on v6.0!**\n\n`;
|
|
6593
|
+
response += `Your patterns are server-enforced. Just use \`discover_patterns\` before coding.\n`;
|
|
6594
|
+
response += `Use \`force: true\` to reinstall bootstrap files.\n`;
|
|
6310
6595
|
response += this.getUpdateNotice();
|
|
6311
6596
|
|
|
6312
6597
|
return {
|
|
@@ -6317,76 +6602,58 @@ ${handlers.join('\n')}
|
|
|
6317
6602
|
};
|
|
6318
6603
|
}
|
|
6319
6604
|
|
|
6320
|
-
response += `##
|
|
6321
|
-
|
|
6322
|
-
// Fetch full content
|
|
6323
|
-
const contentResponse = await fetch(`${this.apiUrl}/api/content`, {
|
|
6324
|
-
headers: this.getAuthHeaders(),
|
|
6325
|
-
});
|
|
6326
|
-
|
|
6327
|
-
if (!contentResponse.ok) {
|
|
6328
|
-
const error = await contentResponse.json().catch(() => ({}));
|
|
6329
|
-
throw new Error(error.error || error.message || 'Failed to fetch patterns');
|
|
6330
|
-
}
|
|
6331
|
-
|
|
6332
|
-
const content = await contentResponse.json();
|
|
6333
|
-
const moduleCount = content.modules ? Object.keys(content.modules).length : 0;
|
|
6605
|
+
response += `## Upgrading to v6.0...\n\n`;
|
|
6334
6606
|
|
|
6335
|
-
//
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
response += `✓ Created .claude/ directory\n`;
|
|
6339
|
-
}
|
|
6607
|
+
// Write v6.0 bootstrap files
|
|
6608
|
+
fs.writeFileSync(claudeMdPath, V6_CLAUDE_MD);
|
|
6609
|
+
response += `✓ Updated CLAUDE.md (v6.0 bootstrap)\n`;
|
|
6340
6610
|
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
fs.writeFileSync(claudeMdPath, content.router);
|
|
6344
|
-
response += `✓ Updated CLAUDE.md (router)\n`;
|
|
6345
|
-
}
|
|
6611
|
+
fs.writeFileSync(path.join(cwd, '.cursorrules'), V6_CURSORRULES);
|
|
6612
|
+
response += `✓ Updated .cursorrules (v6.0 bootstrap)\n`;
|
|
6346
6613
|
|
|
6347
|
-
//
|
|
6348
|
-
if (
|
|
6349
|
-
|
|
6350
|
-
fs.
|
|
6614
|
+
// Remove old .claude folder (v5 → v6 migration)
|
|
6615
|
+
if (fs.existsSync(claudeDir)) {
|
|
6616
|
+
try {
|
|
6617
|
+
fs.rmSync(claudeDir, { recursive: true, force: true });
|
|
6618
|
+
response += `✓ Removed .claude/ folder (patterns now server-side)\n`;
|
|
6619
|
+
} catch {
|
|
6620
|
+
response += `⚠️ Could not remove .claude/ folder - please delete manually\n`;
|
|
6351
6621
|
}
|
|
6352
|
-
response += `✓ Updated ${moduleCount} modules in .claude/\n`;
|
|
6353
6622
|
}
|
|
6354
6623
|
|
|
6355
|
-
//
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
fs.writeFileSync(versionPath, JSON.stringify(newVersionInfo, null, 2));
|
|
6364
|
-
response += `✓ Saved version info\n`;
|
|
6365
|
-
|
|
6366
|
-
// Confirm download to server (non-blocking analytics)
|
|
6367
|
-
this.confirmDownload(content.version || latestVersion, moduleCount).catch(() => {});
|
|
6368
|
-
|
|
6369
|
-
response += `\n## ✅ Update Complete!\n\n`;
|
|
6370
|
-
response += `- **From:** v${currentVersion || 'none'} (${currentModuleCount} modules)\n`;
|
|
6371
|
-
response += `- **To:** v${content.version || latestVersion} (${moduleCount} modules)\n\n`;
|
|
6372
|
-
|
|
6373
|
-
if (moduleCount > currentModuleCount) {
|
|
6374
|
-
response += `🆕 **${moduleCount - currentModuleCount} new modules added!**\n\n`;
|
|
6624
|
+
// Update .codebakers.json
|
|
6625
|
+
let state: Record<string, unknown> = {};
|
|
6626
|
+
if (fs.existsSync(codebakersJson)) {
|
|
6627
|
+
try {
|
|
6628
|
+
state = JSON.parse(fs.readFileSync(codebakersJson, 'utf-8'));
|
|
6629
|
+
} catch {
|
|
6630
|
+
// Ignore errors
|
|
6631
|
+
}
|
|
6375
6632
|
}
|
|
6376
|
-
|
|
6377
|
-
|
|
6633
|
+
state.version = '6.0';
|
|
6634
|
+
state.serverEnforced = true;
|
|
6635
|
+
state.updatedAt = new Date().toISOString();
|
|
6636
|
+
fs.writeFileSync(codebakersJson, JSON.stringify(state, null, 2));
|
|
6637
|
+
response += `✓ Updated .codebakers.json\n`;
|
|
6638
|
+
|
|
6639
|
+
// Confirm to server (non-blocking analytics)
|
|
6640
|
+
this.confirmDownload('6.0', 0).catch(() => {});
|
|
6641
|
+
|
|
6642
|
+
response += `\n## ✅ Upgrade Complete!\n\n`;
|
|
6643
|
+
response += `**What changed in v6.0:**\n`;
|
|
6644
|
+
response += `- No local pattern files (.claude/ folder removed)\n`;
|
|
6645
|
+
response += `- All patterns fetched from server in real-time\n`;
|
|
6646
|
+
response += `- Server tracks compliance via discover_patterns/validate_complete\n\n`;
|
|
6647
|
+
response += `**How to use:**\n`;
|
|
6648
|
+
response += `1. Call \`discover_patterns\` before writing any code\n`;
|
|
6649
|
+
response += `2. Follow the patterns returned by server\n`;
|
|
6650
|
+
response += `3. Call \`validate_complete\` before marking done\n`;
|
|
6378
6651
|
|
|
6379
6652
|
} catch (error) {
|
|
6380
6653
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
6381
6654
|
response += `\n## ❌ Update Failed\n\n`;
|
|
6382
6655
|
response += `Error: ${message}\n\n`;
|
|
6383
|
-
|
|
6384
|
-
if (message.includes('401') || message.includes('Invalid') || message.includes('expired')) {
|
|
6385
|
-
response += `Your API key may be invalid or expired.\n`;
|
|
6386
|
-
response += `Run \`codebakers setup\` in terminal to reconfigure.\n`;
|
|
6387
|
-
} else {
|
|
6388
|
-
response += `Please try again or run \`codebakers upgrade\` in terminal.\n`;
|
|
6389
|
-
}
|
|
6656
|
+
response += `Please try again or run \`codebakers upgrade\` in terminal.\n`;
|
|
6390
6657
|
}
|
|
6391
6658
|
|
|
6392
6659
|
// Add CLI update notice if available
|