@codebakers/cli 1.4.1 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,6 +41,8 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
41
41
  const config_js_1 = require("../config.js");
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
+ const child_process_1 = require("child_process");
45
+ const templates = __importStar(require("../templates/nextjs-supabase.js"));
44
46
  // Pattern cache to avoid repeated API calls
45
47
  const patternCache = new Map();
46
48
  const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
@@ -357,6 +359,60 @@ class CodeBakersServer {
357
359
  required: ['pattern', 'section'],
358
360
  },
359
361
  },
362
+ {
363
+ name: 'scaffold_project',
364
+ description: 'Create a new project from scratch with Next.js + Supabase + Drizzle. Use this when user wants to build something new and no project exists yet. Creates all files, installs dependencies, and sets up CodeBakers patterns automatically.',
365
+ inputSchema: {
366
+ type: 'object',
367
+ properties: {
368
+ projectName: {
369
+ type: 'string',
370
+ description: 'Name of the project (lowercase, no spaces)',
371
+ },
372
+ description: {
373
+ type: 'string',
374
+ description: 'Brief description of what the project is for (used in PRD.md)',
375
+ },
376
+ },
377
+ required: ['projectName'],
378
+ },
379
+ },
380
+ {
381
+ name: 'init_project',
382
+ description: 'Add CodeBakers patterns to an existing project. Use this when user has an existing codebase and wants to add AI patterns to it.',
383
+ inputSchema: {
384
+ type: 'object',
385
+ properties: {
386
+ projectName: {
387
+ type: 'string',
388
+ description: 'Name of the project (optional, will be auto-detected from package.json)',
389
+ },
390
+ },
391
+ },
392
+ },
393
+ {
394
+ name: 'set_experience_level',
395
+ description: 'Set the user experience level. This affects how detailed explanations are when building features. Use "beginner" for new developers who need more explanations, "intermediate" for developers who know the basics, or "advanced" for experienced developers who want minimal explanations.',
396
+ inputSchema: {
397
+ type: 'object',
398
+ properties: {
399
+ level: {
400
+ type: 'string',
401
+ enum: ['beginner', 'intermediate', 'advanced'],
402
+ description: 'Experience level: beginner (detailed explanations), intermediate (balanced), advanced (minimal explanations)',
403
+ },
404
+ },
405
+ required: ['level'],
406
+ },
407
+ },
408
+ {
409
+ name: 'get_experience_level',
410
+ description: 'Get the current user experience level setting. Returns beginner, intermediate, or advanced. Use this at the start of building to know how much detail to include in explanations.',
411
+ inputSchema: {
412
+ type: 'object',
413
+ properties: {},
414
+ },
415
+ },
360
416
  ],
361
417
  }));
362
418
  // Handle tool calls
@@ -378,6 +434,14 @@ class CodeBakersServer {
378
434
  return this.handleSearchPatterns(args);
379
435
  case 'get_pattern_section':
380
436
  return this.handleGetPatternSection(args);
437
+ case 'scaffold_project':
438
+ return this.handleScaffoldProject(args);
439
+ case 'init_project':
440
+ return this.handleInitProject(args);
441
+ case 'set_experience_level':
442
+ return this.handleSetExperienceLevel(args);
443
+ case 'get_experience_level':
444
+ return this.handleGetExperienceLevel();
381
445
  default:
382
446
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
383
447
  }
@@ -733,6 +797,322 @@ Show the user what their simple request was expanded into, then proceed with the
733
797
  ],
734
798
  };
735
799
  }
800
+ async handleScaffoldProject(args) {
801
+ const { projectName, description } = args;
802
+ const cwd = process.cwd();
803
+ // Check if directory has files
804
+ const files = fs.readdirSync(cwd);
805
+ const hasFiles = files.filter(f => !f.startsWith('.')).length > 0;
806
+ if (hasFiles) {
807
+ // Check if it's already a CodeBakers project
808
+ if (fs.existsSync(path.join(cwd, 'CLAUDE.md'))) {
809
+ return {
810
+ content: [{
811
+ type: 'text',
812
+ text: `# Project Already Exists\n\nThis directory already has a CodeBakers project. Use the existing project or navigate to an empty directory.`,
813
+ }],
814
+ };
815
+ }
816
+ }
817
+ const results = [];
818
+ results.push(`# šŸš€ Creating Project: ${projectName}\n`);
819
+ try {
820
+ // Create directories
821
+ const dirs = [
822
+ 'src/app',
823
+ 'src/components',
824
+ 'src/lib/supabase',
825
+ 'src/db',
826
+ 'src/db/migrations',
827
+ 'src/services',
828
+ 'src/types',
829
+ 'public',
830
+ ];
831
+ for (const dir of dirs) {
832
+ const dirPath = path.join(cwd, dir);
833
+ if (!fs.existsSync(dirPath)) {
834
+ fs.mkdirSync(dirPath, { recursive: true });
835
+ }
836
+ }
837
+ results.push('āœ“ Created directory structure');
838
+ // Write package.json
839
+ const packageJson = { ...templates.PACKAGE_JSON, name: projectName };
840
+ fs.writeFileSync(path.join(cwd, 'package.json'), JSON.stringify(packageJson, null, 2));
841
+ results.push('āœ“ Created package.json');
842
+ // Write config files
843
+ fs.writeFileSync(path.join(cwd, '.env.example'), templates.ENV_EXAMPLE);
844
+ fs.writeFileSync(path.join(cwd, '.env.local'), templates.ENV_EXAMPLE);
845
+ fs.writeFileSync(path.join(cwd, 'drizzle.config.ts'), templates.DRIZZLE_CONFIG);
846
+ fs.writeFileSync(path.join(cwd, 'tailwind.config.ts'), templates.TAILWIND_CONFIG);
847
+ fs.writeFileSync(path.join(cwd, 'postcss.config.mjs'), templates.POSTCSS_CONFIG);
848
+ fs.writeFileSync(path.join(cwd, 'tsconfig.json'), JSON.stringify(templates.TSCONFIG, null, 2));
849
+ fs.writeFileSync(path.join(cwd, 'next.config.ts'), templates.NEXT_CONFIG);
850
+ fs.writeFileSync(path.join(cwd, '.gitignore'), templates.GITIGNORE);
851
+ results.push('āœ“ Created configuration files');
852
+ // Write source files
853
+ fs.writeFileSync(path.join(cwd, 'src/lib/supabase/server.ts'), templates.SUPABASE_SERVER);
854
+ fs.writeFileSync(path.join(cwd, 'src/lib/supabase/client.ts'), templates.SUPABASE_CLIENT);
855
+ fs.writeFileSync(path.join(cwd, 'src/lib/supabase/middleware.ts'), templates.SUPABASE_MIDDLEWARE);
856
+ fs.writeFileSync(path.join(cwd, 'middleware.ts'), templates.MIDDLEWARE);
857
+ fs.writeFileSync(path.join(cwd, 'src/db/schema.ts'), templates.DB_SCHEMA);
858
+ fs.writeFileSync(path.join(cwd, 'src/db/index.ts'), templates.DB_INDEX);
859
+ fs.writeFileSync(path.join(cwd, 'src/app/globals.css'), templates.GLOBALS_CSS);
860
+ fs.writeFileSync(path.join(cwd, 'src/app/layout.tsx'), templates.LAYOUT_TSX);
861
+ fs.writeFileSync(path.join(cwd, 'src/app/page.tsx'), templates.PAGE_TSX);
862
+ fs.writeFileSync(path.join(cwd, 'src/lib/utils.ts'), templates.UTILS_CN);
863
+ results.push('āœ“ Created source files');
864
+ // Install dependencies
865
+ try {
866
+ (0, child_process_1.execSync)('npm install', { cwd, stdio: 'pipe' });
867
+ results.push('āœ“ Installed npm dependencies');
868
+ }
869
+ catch {
870
+ results.push('āš ļø Could not install dependencies - run `npm install` manually');
871
+ }
872
+ // Now install CodeBakers patterns
873
+ results.push('\n## Installing CodeBakers Patterns...\n');
874
+ const response = await fetch(`${this.apiUrl}/api/content`, {
875
+ method: 'GET',
876
+ headers: { Authorization: `Bearer ${this.apiKey}` },
877
+ });
878
+ if (response.ok) {
879
+ const content = await response.json();
880
+ // Write CLAUDE.md
881
+ if (content.router) {
882
+ fs.writeFileSync(path.join(cwd, 'CLAUDE.md'), content.router);
883
+ results.push('āœ“ Created CLAUDE.md (AI router)');
884
+ }
885
+ // Write pattern modules
886
+ if (content.modules && Object.keys(content.modules).length > 0) {
887
+ const modulesDir = path.join(cwd, '.claude');
888
+ if (!fs.existsSync(modulesDir)) {
889
+ fs.mkdirSync(modulesDir, { recursive: true });
890
+ }
891
+ for (const [name, data] of Object.entries(content.modules)) {
892
+ fs.writeFileSync(path.join(modulesDir, name), data);
893
+ }
894
+ results.push(`āœ“ Installed ${Object.keys(content.modules).length} pattern modules`);
895
+ }
896
+ // Create PRD with description
897
+ const date = new Date().toISOString().split('T')[0];
898
+ const prdContent = `# Product Requirements Document
899
+ # Project: ${projectName}
900
+ # Created: ${date}
901
+
902
+ ## Overview
903
+ **One-liner:** ${description || '[Describe this project in one sentence]'}
904
+
905
+ **Problem:** [What problem does this solve?]
906
+
907
+ **Solution:** [How does this solve it?]
908
+
909
+ ## Core Features (MVP)
910
+ 1. [ ] **Feature 1:** [Description]
911
+ 2. [ ] **Feature 2:** [Description]
912
+ 3. [ ] **Feature 3:** [Description]
913
+
914
+ ## Technical Requirements
915
+ - Framework: Next.js 14 (App Router)
916
+ - Database: PostgreSQL + Drizzle ORM
917
+ - Auth: Supabase Auth
918
+ - UI: Tailwind CSS + shadcn/ui
919
+
920
+ ---
921
+ <!-- AI: Reference this PRD when building features -->
922
+ `;
923
+ fs.writeFileSync(path.join(cwd, 'PRD.md'), prdContent);
924
+ results.push('āœ“ Created PRD.md');
925
+ // Create other project files
926
+ fs.writeFileSync(path.join(cwd, 'PROJECT-STATE.md'), `# PROJECT STATE
927
+ # Last Updated: ${date}
928
+
929
+ ## Project Info
930
+ name: ${projectName}
931
+ phase: setup
932
+
933
+ ## In Progress
934
+ ## Completed
935
+ ## Next Up
936
+ `);
937
+ results.push('āœ“ Created PROJECT-STATE.md');
938
+ }
939
+ results.push('\n---\n');
940
+ results.push('## āœ… Project Created Successfully!\n');
941
+ results.push('### Next Steps:\n');
942
+ results.push('1. **Set up Supabase:** Go to https://supabase.com and create a free project');
943
+ results.push('2. **Add credentials:** Copy your Supabase URL and anon key to `.env.local`');
944
+ results.push('3. **Start building:** Just tell me what features you want!\n');
945
+ results.push('### Example:\n');
946
+ results.push('> "Add user authentication with email/password"');
947
+ results.push('> "Create a dashboard with stats cards"');
948
+ results.push('> "Build a todo list with CRUD operations"');
949
+ }
950
+ catch (error) {
951
+ const message = error instanceof Error ? error.message : 'Unknown error';
952
+ results.push(`\nāŒ Error: ${message}`);
953
+ }
954
+ return {
955
+ content: [{
956
+ type: 'text',
957
+ text: results.join('\n'),
958
+ }],
959
+ };
960
+ }
961
+ async handleInitProject(args) {
962
+ const cwd = process.cwd();
963
+ const results = [];
964
+ // Detect project name from package.json
965
+ let projectName = args.projectName || 'my-project';
966
+ try {
967
+ const pkgPath = path.join(cwd, 'package.json');
968
+ if (fs.existsSync(pkgPath)) {
969
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
970
+ projectName = pkg.name || projectName;
971
+ }
972
+ }
973
+ catch {
974
+ // Use default
975
+ }
976
+ results.push(`# šŸŽØ Adding CodeBakers to: ${projectName}\n`);
977
+ // Check if already initialized
978
+ if (fs.existsSync(path.join(cwd, 'CLAUDE.md'))) {
979
+ results.push('āš ļø CLAUDE.md already exists. Updating patterns...\n');
980
+ }
981
+ try {
982
+ const response = await fetch(`${this.apiUrl}/api/content`, {
983
+ method: 'GET',
984
+ headers: { Authorization: `Bearer ${this.apiKey}` },
985
+ });
986
+ if (!response.ok) {
987
+ throw new Error('Failed to fetch patterns from API');
988
+ }
989
+ const content = await response.json();
990
+ // Write CLAUDE.md
991
+ if (content.router) {
992
+ fs.writeFileSync(path.join(cwd, 'CLAUDE.md'), content.router);
993
+ results.push('āœ“ Created/Updated CLAUDE.md');
994
+ }
995
+ // Write pattern modules
996
+ if (content.modules && Object.keys(content.modules).length > 0) {
997
+ const modulesDir = path.join(cwd, '.claude');
998
+ if (!fs.existsSync(modulesDir)) {
999
+ fs.mkdirSync(modulesDir, { recursive: true });
1000
+ }
1001
+ for (const [name, data] of Object.entries(content.modules)) {
1002
+ fs.writeFileSync(path.join(modulesDir, name), data);
1003
+ }
1004
+ results.push(`āœ“ Installed ${Object.keys(content.modules).length} pattern modules (v${content.version})`);
1005
+ }
1006
+ // Create PRD if doesn't exist
1007
+ const date = new Date().toISOString().split('T')[0];
1008
+ const prdPath = path.join(cwd, 'PRD.md');
1009
+ if (!fs.existsSync(prdPath)) {
1010
+ fs.writeFileSync(prdPath, `# Product Requirements Document
1011
+ # Project: ${projectName}
1012
+ # Created: ${date}
1013
+
1014
+ ## Overview
1015
+ **One-liner:** [Describe this project]
1016
+
1017
+ ## Core Features (MVP)
1018
+ 1. [ ] **Feature 1:** [Description]
1019
+ 2. [ ] **Feature 2:** [Description]
1020
+ `);
1021
+ results.push('āœ“ Created PRD.md template');
1022
+ }
1023
+ // Create PROJECT-STATE if doesn't exist
1024
+ const statePath = path.join(cwd, 'PROJECT-STATE.md');
1025
+ if (!fs.existsSync(statePath)) {
1026
+ fs.writeFileSync(statePath, `# PROJECT STATE
1027
+ # Last Updated: ${date}
1028
+
1029
+ ## Project Info
1030
+ name: ${projectName}
1031
+ phase: development
1032
+
1033
+ ## In Progress
1034
+ ## Completed
1035
+ ## Next Up
1036
+ `);
1037
+ results.push('āœ“ Created PROJECT-STATE.md');
1038
+ }
1039
+ // Update .gitignore
1040
+ const gitignorePath = path.join(cwd, '.gitignore');
1041
+ if (fs.existsSync(gitignorePath)) {
1042
+ const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
1043
+ if (!gitignore.includes('.claude/')) {
1044
+ fs.writeFileSync(gitignorePath, gitignore + '\n# CodeBakers\n.claude/\n');
1045
+ results.push('āœ“ Updated .gitignore');
1046
+ }
1047
+ }
1048
+ results.push('\n---\n');
1049
+ results.push('## āœ… CodeBakers Patterns Installed!\n');
1050
+ results.push('The AI now has access to production patterns for:');
1051
+ results.push('- Authentication, Database, API design');
1052
+ results.push('- Frontend components, Forms, Validation');
1053
+ results.push('- Payments, Email, Real-time features');
1054
+ results.push('- And 30+ more specialized patterns\n');
1055
+ results.push('Just describe what you want to build!');
1056
+ }
1057
+ catch (error) {
1058
+ const message = error instanceof Error ? error.message : 'Unknown error';
1059
+ results.push(`\nāŒ Error: ${message}`);
1060
+ }
1061
+ return {
1062
+ content: [{
1063
+ type: 'text',
1064
+ text: results.join('\n'),
1065
+ }],
1066
+ };
1067
+ }
1068
+ handleSetExperienceLevel(args) {
1069
+ const { level } = args;
1070
+ // Validate level
1071
+ if (!['beginner', 'intermediate', 'advanced'].includes(level)) {
1072
+ return {
1073
+ content: [{
1074
+ type: 'text',
1075
+ text: `āŒ Invalid experience level: "${level}". Must be: beginner, intermediate, or advanced.`,
1076
+ }],
1077
+ };
1078
+ }
1079
+ (0, config_js_1.setExperienceLevel)(level);
1080
+ const descriptions = {
1081
+ beginner: 'šŸŽ“ **Beginner Mode**\n\nI will:\n- Explain concepts as I go\n- Break down complex steps\n- Provide more context about what each piece of code does\n- Suggest learning resources when relevant',
1082
+ intermediate: '⚔ **Intermediate Mode**\n\nI will:\n- Provide balanced explanations\n- Focus on the "why" behind decisions\n- Skip basic explanations you already know',
1083
+ advanced: 'šŸš€ **Advanced Mode**\n\nI will:\n- Skip explanations, just build\n- Focus on efficiency and best practices\n- Assume you know the fundamentals\n- Get straight to the code',
1084
+ };
1085
+ return {
1086
+ content: [{
1087
+ type: 'text',
1088
+ text: `āœ… Experience level set to: **${level}**\n\n${descriptions[level]}`,
1089
+ }],
1090
+ };
1091
+ }
1092
+ handleGetExperienceLevel() {
1093
+ const level = (0, config_js_1.getExperienceLevel)();
1094
+ const modeInfo = {
1095
+ beginner: {
1096
+ emoji: 'šŸŽ“',
1097
+ description: 'Detailed explanations, step-by-step guidance'
1098
+ },
1099
+ intermediate: {
1100
+ emoji: '⚔',
1101
+ description: 'Balanced explanations, focus on decisions'
1102
+ },
1103
+ advanced: {
1104
+ emoji: 'šŸš€',
1105
+ description: 'Minimal explanations, straight to code'
1106
+ },
1107
+ };
1108
+ const info = modeInfo[level];
1109
+ return {
1110
+ content: [{
1111
+ type: 'text',
1112
+ text: `# Current Experience Level\n\n${info.emoji} **${level.charAt(0).toUpperCase() + level.slice(1)}**\n${info.description}\n\n---\n\nTo change, use: \`set_experience_level\` with "beginner", "intermediate", or "advanced"`,
1113
+ }],
1114
+ };
1115
+ }
736
1116
  async run() {
737
1117
  const transport = new stdio_js_1.StdioServerTransport();
738
1118
  await this.server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -29,8 +29,7 @@
29
29
  "chalk": "^5.3.0",
30
30
  "commander": "^12.1.0",
31
31
  "conf": "^13.0.1",
32
- "ora": "^8.1.1",
33
- "openai": "^4.77.0"
32
+ "ora": "^8.1.1"
34
33
  },
35
34
  "devDependencies": {
36
35
  "@types/node": "^20.10.0",
@@ -106,7 +106,9 @@ export async function installHook(): Promise<void> {
106
106
  console.log(chalk.gray(' āœ“ Self-review reminders after code changes'));
107
107
  console.log(chalk.gray(' āœ“ Pattern-based development from .claude/ folder\n'));
108
108
 
109
- console.log(chalk.yellow(' āš ļø Restart Claude Code for changes to take effect.\n'));
109
+ console.log(chalk.yellow(' āš ļø Next step: Restart required\n'));
110
+ console.log(chalk.white(' Close this terminal and open a new one in your project folder.\n'));
111
+ console.log(chalk.gray(' Then just start chatting - CodeBakers is now active!\n'));
110
112
  } catch (error) {
111
113
  spinner.fail('Hook installation failed');
112
114
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -154,7 +156,8 @@ export async function uninstallHook(): Promise<void> {
154
156
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
155
157
 
156
158
  spinner.succeed('Hook removed successfully!');
157
- console.log(chalk.yellow('\n āš ļø Restart Claude Code for changes to take effect.\n'));
159
+ console.log(chalk.yellow('\n āš ļø Next step: Restart required\n'));
160
+ console.log(chalk.white(' Close this terminal and open a new one in your project folder.\n'));
158
161
  } catch (error) {
159
162
  spinner.fail('Hook removal failed');
160
163
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -158,8 +158,9 @@ function installGlobalConfig(): void {
158
158
  console.log(chalk.green(' MCP configuration installed!\n'));
159
159
  console.log(chalk.gray(' Config written to:'));
160
160
  console.log(chalk.cyan(` ${configPath}\n`));
161
- console.log(chalk.white(' Next step:\n'));
162
- console.log(chalk.gray(' Restart Claude Code to activate the MCP server.\n'));
161
+ console.log(chalk.yellow(' āš ļø Next step: Restart required\n'));
162
+ console.log(chalk.white(' Close this terminal and open a new one in your project folder.\n'));
163
+ console.log(chalk.gray(' Then the MCP server will be active!\n'));
163
164
  } catch (error) {
164
165
  const message = error instanceof Error ? error.message : 'Unknown error';
165
166
  console.log(chalk.red(`\n Error: ${message}\n`));
@@ -221,7 +222,8 @@ export async function mcpUninstall(): Promise<void> {
221
222
  delete config.mcpServers.codebakers;
222
223
  writeFileSync(configPath, JSON.stringify(config, null, 2));
223
224
  console.log(chalk.green(' Removed from global config.\n'));
224
- console.log(chalk.gray(' Restart Claude Code to apply changes.\n'));
225
+ console.log(chalk.yellow(' āš ļø Next step: Restart required\n'));
226
+ console.log(chalk.white(' Close this terminal and open a new one in your project folder.\n'));
225
227
  } else {
226
228
  console.log(chalk.gray(' CodeBakers not found in global config.\n'));
227
229
  console.log(chalk.gray(' To remove from Claude Code, run:\n'));