@codebakers/cli 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.0",
3
+ "version": "1.4.2",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {