@codebakers/cli 1.2.1 → 1.3.1

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.
@@ -325,6 +325,38 @@ class CodeBakersServer {
325
325
  required: ['patterns'],
326
326
  },
327
327
  },
328
+ {
329
+ name: 'search_patterns',
330
+ description: 'Search CodeBakers patterns by keyword or topic. Returns relevant code snippets without reading entire files. Use this when you need specific guidance like "supabase auth setup", "optimistic updates", "soft delete", "form validation".',
331
+ inputSchema: {
332
+ type: 'object',
333
+ properties: {
334
+ query: {
335
+ type: 'string',
336
+ description: 'Search query (e.g., "supabase auth", "stripe checkout", "zod validation", "loading states")',
337
+ },
338
+ },
339
+ required: ['query'],
340
+ },
341
+ },
342
+ {
343
+ name: 'get_pattern_section',
344
+ description: 'Get a specific section from a pattern file instead of the whole file. Much faster than get_pattern for targeted lookups.',
345
+ inputSchema: {
346
+ type: 'object',
347
+ properties: {
348
+ pattern: {
349
+ type: 'string',
350
+ description: 'Pattern name (e.g., "02-auth", "03-api")',
351
+ },
352
+ section: {
353
+ type: 'string',
354
+ description: 'Section name or keyword to find within the pattern (e.g., "OAuth", "rate limiting", "error handling")',
355
+ },
356
+ },
357
+ required: ['pattern', 'section'],
358
+ },
359
+ },
328
360
  ],
329
361
  }));
330
362
  // Handle tool calls
@@ -342,6 +374,10 @@ class CodeBakersServer {
342
374
  return this.handleListPatterns();
343
375
  case 'get_patterns':
344
376
  return this.handleGetPatterns(args);
377
+ case 'search_patterns':
378
+ return this.handleSearchPatterns(args);
379
+ case 'get_pattern_section':
380
+ return this.handleGetPatternSection(args);
345
381
  default:
346
382
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
347
383
  }
@@ -522,6 +558,181 @@ Show the user what their simple request was expanded into, then proceed with the
522
558
  }
523
559
  return response.json();
524
560
  }
561
+ async handleSearchPatterns(args) {
562
+ const { query } = args;
563
+ // Call API endpoint for semantic search
564
+ const response = await fetch(`${this.apiUrl}/api/patterns/search`, {
565
+ method: 'POST',
566
+ headers: {
567
+ 'Content-Type': 'application/json',
568
+ Authorization: `Bearer ${this.apiKey}`,
569
+ },
570
+ body: JSON.stringify({ query }),
571
+ });
572
+ if (!response.ok) {
573
+ // Fallback: If search endpoint doesn't exist, do client-side search
574
+ return this.fallbackSearch(query);
575
+ }
576
+ const data = await response.json();
577
+ const results = data.results
578
+ .map((r) => `### ${r.pattern} - ${r.section}\n\n\`\`\`typescript\n${r.content}\n\`\`\`\n\nRelevance: ${Math.round(r.relevance * 100)}%`)
579
+ .join('\n\n---\n\n');
580
+ return {
581
+ content: [
582
+ {
583
+ type: 'text',
584
+ text: `# Search Results for "${query}"\n\n${results || 'No results found. Try a different query.'}`,
585
+ },
586
+ ],
587
+ };
588
+ }
589
+ async fallbackSearch(query) {
590
+ // Keyword-based fallback if API search not available
591
+ const keywordPatternMap = {
592
+ 'auth': ['02-auth'],
593
+ 'login': ['02-auth'],
594
+ 'oauth': ['02-auth'],
595
+ 'supabase': ['02-auth', '01-database'],
596
+ 'database': ['01-database'],
597
+ 'drizzle': ['01-database'],
598
+ 'schema': ['01-database'],
599
+ 'api': ['03-api'],
600
+ 'route': ['03-api'],
601
+ 'validation': ['03-api', '04-frontend'],
602
+ 'zod': ['03-api', '04-frontend'],
603
+ 'frontend': ['04-frontend'],
604
+ 'form': ['04-frontend'],
605
+ 'react': ['04-frontend'],
606
+ 'component': ['04-frontend'],
607
+ 'stripe': ['05-payments'],
608
+ 'payment': ['05-payments'],
609
+ 'checkout': ['05-payments'],
610
+ 'subscription': ['05-payments'],
611
+ 'email': ['06-integrations'],
612
+ 'webhook': ['06-integrations'],
613
+ 'cache': ['07-performance'],
614
+ 'test': ['08-testing'],
615
+ 'playwright': ['08-testing'],
616
+ 'design': ['09-design'],
617
+ 'ui': ['09-design'],
618
+ 'accessibility': ['09-design'],
619
+ 'websocket': ['11-realtime'],
620
+ 'realtime': ['11-realtime'],
621
+ 'notification': ['11-realtime'],
622
+ 'saas': ['12-saas'],
623
+ 'tenant': ['12-saas'],
624
+ 'mobile': ['13-mobile'],
625
+ 'expo': ['13-mobile'],
626
+ 'ai': ['14-ai'],
627
+ 'openai': ['14-ai'],
628
+ 'embedding': ['14-ai'],
629
+ 'analytics': ['26-analytics'],
630
+ 'search': ['27-search'],
631
+ 'animation': ['30-motion'],
632
+ 'framer': ['30-motion'],
633
+ };
634
+ const lowerQuery = query.toLowerCase();
635
+ const matchedPatterns = new Set();
636
+ for (const [keyword, patterns] of Object.entries(keywordPatternMap)) {
637
+ if (lowerQuery.includes(keyword)) {
638
+ patterns.forEach(p => matchedPatterns.add(p));
639
+ }
640
+ }
641
+ if (matchedPatterns.size === 0) {
642
+ return {
643
+ content: [
644
+ {
645
+ type: 'text',
646
+ text: `# No patterns found for "${query}"\n\nTry:\n- "auth" for authentication patterns\n- "api" for API route patterns\n- "form" for frontend form patterns\n- "stripe" for payment patterns\n\nOr use \`list_patterns\` to see all available patterns.`,
647
+ },
648
+ ],
649
+ };
650
+ }
651
+ const patterns = Array.from(matchedPatterns).slice(0, 3);
652
+ const result = await this.fetchPatterns(patterns);
653
+ const content = Object.entries(result.patterns || {})
654
+ .map(([name, text]) => `## ${name}\n\n${text}`)
655
+ .join('\n\n---\n\n');
656
+ return {
657
+ content: [
658
+ {
659
+ type: 'text',
660
+ text: `# Patterns matching "${query}"\n\nFound in: ${patterns.join(', ')}\n\n${content}`,
661
+ },
662
+ ],
663
+ };
664
+ }
665
+ async handleGetPatternSection(args) {
666
+ const { pattern, section } = args;
667
+ // Fetch the full pattern first
668
+ const result = await this.fetchPatterns([pattern]);
669
+ if (!result.patterns || !result.patterns[pattern]) {
670
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, `Pattern "${pattern}" not found. Use list_patterns to see available patterns.`);
671
+ }
672
+ const fullContent = result.patterns[pattern];
673
+ // Find the section (case-insensitive search for headers or content)
674
+ const sectionLower = section.toLowerCase();
675
+ const lines = fullContent.split('\n');
676
+ const sections = [];
677
+ let currentSection = '';
678
+ let currentContent = [];
679
+ let capturing = false;
680
+ let relevanceScore = 0;
681
+ for (let i = 0; i < lines.length; i++) {
682
+ const line = lines[i];
683
+ // Check if this is a header
684
+ if (line.match(/^#{1,3}\s/)) {
685
+ // Save previous section if we were capturing
686
+ if (capturing && currentContent.length > 0) {
687
+ sections.push(`### ${currentSection}\n\n${currentContent.join('\n')}`);
688
+ }
689
+ currentSection = line.replace(/^#+\s*/, '');
690
+ currentContent = [];
691
+ // Check if this section matches our query
692
+ if (currentSection.toLowerCase().includes(sectionLower)) {
693
+ capturing = true;
694
+ relevanceScore++;
695
+ }
696
+ else {
697
+ capturing = false;
698
+ }
699
+ }
700
+ else if (capturing) {
701
+ currentContent.push(line);
702
+ }
703
+ // Also check content for keyword matches
704
+ if (!capturing && line.toLowerCase().includes(sectionLower)) {
705
+ // Found keyword in content, capture surrounding context
706
+ const start = Math.max(0, i - 5);
707
+ const end = Math.min(lines.length, i + 20);
708
+ const context = lines.slice(start, end).join('\n');
709
+ sections.push(`### Found at line ${i + 1}\n\n${context}`);
710
+ relevanceScore++;
711
+ }
712
+ }
713
+ // Capture last section if we were still capturing
714
+ if (capturing && currentContent.length > 0) {
715
+ sections.push(`### ${currentSection}\n\n${currentContent.join('\n')}`);
716
+ }
717
+ if (sections.length === 0) {
718
+ return {
719
+ content: [
720
+ {
721
+ type: 'text',
722
+ text: `# Section "${section}" not found in ${pattern}\n\nThe pattern exists but doesn't contain a section matching "${section}".\n\nTry:\n- A broader search term\n- \`get_pattern ${pattern}\` to see the full content\n- \`search_patterns ${section}\` to search across all patterns`,
723
+ },
724
+ ],
725
+ };
726
+ }
727
+ return {
728
+ content: [
729
+ {
730
+ type: 'text',
731
+ text: `# ${pattern} - "${section}"\n\n${sections.slice(0, 5).join('\n\n---\n\n')}`,
732
+ },
733
+ ],
734
+ };
735
+ }
525
736
  async run() {
526
737
  const transport = new stdio_js_1.StdioServerTransport();
527
738
  await this.server.connect(transport);
@@ -0,0 +1,81 @@
1
+ export declare const PACKAGE_JSON: {
2
+ name: string;
3
+ version: string;
4
+ private: boolean;
5
+ scripts: {
6
+ dev: string;
7
+ build: string;
8
+ start: string;
9
+ lint: string;
10
+ "db:generate": string;
11
+ "db:push": string;
12
+ "db:studio": string;
13
+ };
14
+ dependencies: {
15
+ next: string;
16
+ react: string;
17
+ "react-dom": string;
18
+ "@supabase/supabase-js": string;
19
+ "@supabase/ssr": string;
20
+ "drizzle-orm": string;
21
+ postgres: string;
22
+ zod: string;
23
+ "react-hook-form": string;
24
+ "@hookform/resolvers": string;
25
+ "lucide-react": string;
26
+ clsx: string;
27
+ "tailwind-merge": string;
28
+ };
29
+ devDependencies: {
30
+ "@types/node": string;
31
+ "@types/react": string;
32
+ "@types/react-dom": string;
33
+ typescript: string;
34
+ tailwindcss: string;
35
+ postcss: string;
36
+ autoprefixer: string;
37
+ "drizzle-kit": string;
38
+ eslint: string;
39
+ "eslint-config-next": string;
40
+ };
41
+ };
42
+ export declare const ENV_EXAMPLE = "# Supabase\nNEXT_PUBLIC_SUPABASE_URL=your-supabase-url\nNEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key\nSUPABASE_SERVICE_ROLE_KEY=your-service-role-key\n\n# Database (from Supabase connection string)\nDATABASE_URL=postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres\n";
43
+ export declare const DRIZZLE_CONFIG = "import { defineConfig } from \"drizzle-kit\";\n\nexport default defineConfig({\n schema: \"./src/db/schema.ts\",\n out: \"./src/db/migrations\",\n dialect: \"postgresql\",\n dbCredentials: {\n url: process.env.DATABASE_URL!,\n },\n});\n";
44
+ export declare const DB_SCHEMA = "import { pgTable, text, timestamp, uuid, boolean } from \"drizzle-orm/pg-core\";\n\n// Users table (synced with Supabase Auth)\nexport const users = pgTable(\"users\", {\n id: uuid(\"id\").primaryKey().notNull(),\n email: text(\"email\").notNull().unique(),\n name: text(\"name\"),\n avatarUrl: text(\"avatar_url\"),\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n// Example: Add your tables here following this pattern\n// export const posts = pgTable(\"posts\", {\n// id: uuid(\"id\").primaryKey().defaultRandom(),\n// title: text(\"title\").notNull(),\n// content: text(\"content\"),\n// authorId: uuid(\"author_id\").references(() => users.id),\n// published: boolean(\"published\").default(false),\n// createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n// });\n";
45
+ export declare const DB_INDEX = "import { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres from \"postgres\";\nimport * as schema from \"./schema\";\n\nconst connectionString = process.env.DATABASE_URL!;\n\nconst client = postgres(connectionString, { prepare: false });\nexport const db = drizzle(client, { schema });\n\nexport type DB = typeof db;\n";
46
+ export declare const SUPABASE_SERVER = "import { createServerClient } from \"@supabase/ssr\";\nimport { cookies } from \"next/headers\";\n\nexport async function createClient() {\n const cookieStore = await cookies();\n\n return createServerClient(\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\n process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,\n {\n cookies: {\n getAll() {\n return cookieStore.getAll();\n },\n setAll(cookiesToSet) {\n try {\n cookiesToSet.forEach(({ name, value, options }) =>\n cookieStore.set(name, value, options)\n );\n } catch {\n // The `setAll` method was called from a Server Component.\n // This can be ignored if you have middleware refreshing user sessions.\n }\n },\n },\n }\n );\n}\n";
47
+ export declare const SUPABASE_CLIENT = "import { createBrowserClient } from \"@supabase/ssr\";\n\nexport function createClient() {\n return createBrowserClient(\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\n process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!\n );\n}\n";
48
+ export declare const SUPABASE_MIDDLEWARE = "import { createServerClient } from \"@supabase/ssr\";\nimport { NextResponse, type NextRequest } from \"next/server\";\n\nexport async function updateSession(request: NextRequest) {\n let supabaseResponse = NextResponse.next({\n request,\n });\n\n const supabase = createServerClient(\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\n process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,\n {\n cookies: {\n getAll() {\n return request.cookies.getAll();\n },\n setAll(cookiesToSet) {\n cookiesToSet.forEach(({ name, value }) =>\n request.cookies.set(name, value)\n );\n supabaseResponse = NextResponse.next({\n request,\n });\n cookiesToSet.forEach(({ name, value, options }) =>\n supabaseResponse.cookies.set(name, value, options)\n );\n },\n },\n }\n );\n\n // This will refresh session if expired\n await supabase.auth.getUser();\n\n return supabaseResponse;\n}\n";
49
+ export declare const MIDDLEWARE = "import { type NextRequest } from \"next/server\";\nimport { updateSession } from \"@/lib/supabase/middleware\";\n\nexport async function middleware(request: NextRequest) {\n return await updateSession(request);\n}\n\nexport const config = {\n matcher: [\n /*\n * Match all request paths except for:\n * - _next/static (static files)\n * - _next/image (image optimization)\n * - favicon.ico\n * - public files\n */\n \"/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)\",\n ],\n};\n";
50
+ export declare const TAILWIND_CONFIG = "import type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n content: [\n \"./src/pages/**/*.{js,ts,jsx,tsx,mdx}\",\n \"./src/components/**/*.{js,ts,jsx,tsx,mdx}\",\n \"./src/app/**/*.{js,ts,jsx,tsx,mdx}\",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};\nexport default config;\n";
51
+ export declare const POSTCSS_CONFIG = "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};\n";
52
+ export declare const TSCONFIG: {
53
+ compilerOptions: {
54
+ lib: string[];
55
+ allowJs: boolean;
56
+ skipLibCheck: boolean;
57
+ strict: boolean;
58
+ noEmit: boolean;
59
+ esModuleInterop: boolean;
60
+ module: string;
61
+ moduleResolution: string;
62
+ resolveJsonModule: boolean;
63
+ isolatedModules: boolean;
64
+ jsx: string;
65
+ incremental: boolean;
66
+ plugins: {
67
+ name: string;
68
+ }[];
69
+ paths: {
70
+ "@/*": string[];
71
+ };
72
+ };
73
+ include: string[];
74
+ exclude: string[];
75
+ };
76
+ export declare const NEXT_CONFIG = "import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n /* config options here */\n};\n\nexport default nextConfig;\n";
77
+ export declare const GLOBALS_CSS = "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n:root {\n --background: #ffffff;\n --foreground: #171717;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --background: #0a0a0a;\n --foreground: #ededed;\n }\n}\n\nbody {\n color: var(--foreground);\n background: var(--background);\n font-family: Arial, Helvetica, sans-serif;\n}\n";
78
+ export declare const LAYOUT_TSX = "import type { Metadata } from \"next\";\nimport \"./globals.css\";\n\nexport const metadata: Metadata = {\n title: \"My App\",\n description: \"Built with CodeBakers patterns\",\n};\n\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: React.ReactNode;\n}>) {\n return (\n <html lang=\"en\">\n <body>{children}</body>\n </html>\n );\n}\n";
79
+ export declare const PAGE_TSX = "export default function Home() {\n return (\n <main className=\"flex min-h-screen flex-col items-center justify-center p-24\">\n <h1 className=\"text-4xl font-bold mb-4\">Welcome to Your App</h1>\n <p className=\"text-gray-600 mb-8\">Built with CodeBakers patterns</p>\n <div className=\"flex gap-4\">\n <a\n href=\"/login\"\n className=\"px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition\"\n >\n Get Started\n </a>\n <a\n href=\"/docs\"\n className=\"px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition\"\n >\n Documentation\n </a>\n </div>\n </main>\n );\n}\n";
80
+ export declare const UTILS_CN = "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n";
81
+ export declare const GITIGNORE = "# Dependencies\nnode_modules\n.pnpm-debug.log*\n\n# Next.js\n.next/\nout/\n\n# Production\nbuild\ndist\n\n# Misc\n.DS_Store\n*.pem\n\n# Debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Local env files\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# Vercel\n.vercel\n\n# TypeScript\n*.tsbuildinfo\nnext-env.d.ts\n\n# IDE\n.idea\n.vscode/*\n!.vscode/settings.json\n";
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ // Next.js + Supabase + Drizzle project template
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.GITIGNORE = exports.UTILS_CN = exports.PAGE_TSX = exports.LAYOUT_TSX = exports.GLOBALS_CSS = exports.NEXT_CONFIG = exports.TSCONFIG = exports.POSTCSS_CONFIG = exports.TAILWIND_CONFIG = exports.MIDDLEWARE = exports.SUPABASE_MIDDLEWARE = exports.SUPABASE_CLIENT = exports.SUPABASE_SERVER = exports.DB_INDEX = exports.DB_SCHEMA = exports.DRIZZLE_CONFIG = exports.ENV_EXAMPLE = exports.PACKAGE_JSON = void 0;
5
+ exports.PACKAGE_JSON = {
6
+ name: "my-project",
7
+ version: "0.1.0",
8
+ private: true,
9
+ scripts: {
10
+ "dev": "next dev",
11
+ "build": "next build",
12
+ "start": "next start",
13
+ "lint": "next lint",
14
+ "db:generate": "drizzle-kit generate",
15
+ "db:push": "drizzle-kit push",
16
+ "db:studio": "drizzle-kit studio"
17
+ },
18
+ dependencies: {
19
+ "next": "15.1.0",
20
+ "react": "^19.0.0",
21
+ "react-dom": "^19.0.0",
22
+ "@supabase/supabase-js": "^2.47.10",
23
+ "@supabase/ssr": "^0.5.2",
24
+ "drizzle-orm": "^0.38.2",
25
+ "postgres": "^3.4.5",
26
+ "zod": "^3.24.1",
27
+ "react-hook-form": "^7.54.1",
28
+ "@hookform/resolvers": "^3.9.1",
29
+ "lucide-react": "^0.468.0",
30
+ "clsx": "^2.1.1",
31
+ "tailwind-merge": "^2.6.0"
32
+ },
33
+ devDependencies: {
34
+ "@types/node": "^22.10.2",
35
+ "@types/react": "^19.0.1",
36
+ "@types/react-dom": "^19.0.1",
37
+ "typescript": "^5.7.2",
38
+ "tailwindcss": "^3.4.17",
39
+ "postcss": "^8.4.49",
40
+ "autoprefixer": "^10.4.20",
41
+ "drizzle-kit": "^0.30.1",
42
+ "eslint": "^9.17.0",
43
+ "eslint-config-next": "15.1.0"
44
+ }
45
+ };
46
+ exports.ENV_EXAMPLE = `# Supabase
47
+ NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
48
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
49
+ SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
50
+
51
+ # Database (from Supabase connection string)
52
+ DATABASE_URL=postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres
53
+ `;
54
+ exports.DRIZZLE_CONFIG = `import { defineConfig } from "drizzle-kit";
55
+
56
+ export default defineConfig({
57
+ schema: "./src/db/schema.ts",
58
+ out: "./src/db/migrations",
59
+ dialect: "postgresql",
60
+ dbCredentials: {
61
+ url: process.env.DATABASE_URL!,
62
+ },
63
+ });
64
+ `;
65
+ exports.DB_SCHEMA = `import { pgTable, text, timestamp, uuid, boolean } from "drizzle-orm/pg-core";
66
+
67
+ // Users table (synced with Supabase Auth)
68
+ export const users = pgTable("users", {
69
+ id: uuid("id").primaryKey().notNull(),
70
+ email: text("email").notNull().unique(),
71
+ name: text("name"),
72
+ avatarUrl: text("avatar_url"),
73
+ createdAt: timestamp("created_at").defaultNow().notNull(),
74
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
75
+ });
76
+
77
+ // Example: Add your tables here following this pattern
78
+ // export const posts = pgTable("posts", {
79
+ // id: uuid("id").primaryKey().defaultRandom(),
80
+ // title: text("title").notNull(),
81
+ // content: text("content"),
82
+ // authorId: uuid("author_id").references(() => users.id),
83
+ // published: boolean("published").default(false),
84
+ // createdAt: timestamp("created_at").defaultNow().notNull(),
85
+ // });
86
+ `;
87
+ exports.DB_INDEX = `import { drizzle } from "drizzle-orm/postgres-js";
88
+ import postgres from "postgres";
89
+ import * as schema from "./schema";
90
+
91
+ const connectionString = process.env.DATABASE_URL!;
92
+
93
+ const client = postgres(connectionString, { prepare: false });
94
+ export const db = drizzle(client, { schema });
95
+
96
+ export type DB = typeof db;
97
+ `;
98
+ exports.SUPABASE_SERVER = `import { createServerClient } from "@supabase/ssr";
99
+ import { cookies } from "next/headers";
100
+
101
+ export async function createClient() {
102
+ const cookieStore = await cookies();
103
+
104
+ return createServerClient(
105
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
106
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
107
+ {
108
+ cookies: {
109
+ getAll() {
110
+ return cookieStore.getAll();
111
+ },
112
+ setAll(cookiesToSet) {
113
+ try {
114
+ cookiesToSet.forEach(({ name, value, options }) =>
115
+ cookieStore.set(name, value, options)
116
+ );
117
+ } catch {
118
+ // The \`setAll\` method was called from a Server Component.
119
+ // This can be ignored if you have middleware refreshing user sessions.
120
+ }
121
+ },
122
+ },
123
+ }
124
+ );
125
+ }
126
+ `;
127
+ exports.SUPABASE_CLIENT = `import { createBrowserClient } from "@supabase/ssr";
128
+
129
+ export function createClient() {
130
+ return createBrowserClient(
131
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
132
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
133
+ );
134
+ }
135
+ `;
136
+ exports.SUPABASE_MIDDLEWARE = `import { createServerClient } from "@supabase/ssr";
137
+ import { NextResponse, type NextRequest } from "next/server";
138
+
139
+ export async function updateSession(request: NextRequest) {
140
+ let supabaseResponse = NextResponse.next({
141
+ request,
142
+ });
143
+
144
+ const supabase = createServerClient(
145
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
146
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
147
+ {
148
+ cookies: {
149
+ getAll() {
150
+ return request.cookies.getAll();
151
+ },
152
+ setAll(cookiesToSet) {
153
+ cookiesToSet.forEach(({ name, value }) =>
154
+ request.cookies.set(name, value)
155
+ );
156
+ supabaseResponse = NextResponse.next({
157
+ request,
158
+ });
159
+ cookiesToSet.forEach(({ name, value, options }) =>
160
+ supabaseResponse.cookies.set(name, value, options)
161
+ );
162
+ },
163
+ },
164
+ }
165
+ );
166
+
167
+ // This will refresh session if expired
168
+ await supabase.auth.getUser();
169
+
170
+ return supabaseResponse;
171
+ }
172
+ `;
173
+ exports.MIDDLEWARE = `import { type NextRequest } from "next/server";
174
+ import { updateSession } from "@/lib/supabase/middleware";
175
+
176
+ export async function middleware(request: NextRequest) {
177
+ return await updateSession(request);
178
+ }
179
+
180
+ export const config = {
181
+ matcher: [
182
+ /*
183
+ * Match all request paths except for:
184
+ * - _next/static (static files)
185
+ * - _next/image (image optimization)
186
+ * - favicon.ico
187
+ * - public files
188
+ */
189
+ "/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
190
+ ],
191
+ };
192
+ `;
193
+ exports.TAILWIND_CONFIG = `import type { Config } from "tailwindcss";
194
+
195
+ const config: Config = {
196
+ content: [
197
+ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
198
+ "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
199
+ "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
200
+ ],
201
+ theme: {
202
+ extend: {},
203
+ },
204
+ plugins: [],
205
+ };
206
+ export default config;
207
+ `;
208
+ exports.POSTCSS_CONFIG = `module.exports = {
209
+ plugins: {
210
+ tailwindcss: {},
211
+ autoprefixer: {},
212
+ },
213
+ };
214
+ `;
215
+ exports.TSCONFIG = {
216
+ compilerOptions: {
217
+ lib: ["dom", "dom.iterable", "esnext"],
218
+ allowJs: true,
219
+ skipLibCheck: true,
220
+ strict: true,
221
+ noEmit: true,
222
+ esModuleInterop: true,
223
+ module: "esnext",
224
+ moduleResolution: "bundler",
225
+ resolveJsonModule: true,
226
+ isolatedModules: true,
227
+ jsx: "preserve",
228
+ incremental: true,
229
+ plugins: [{ name: "next" }],
230
+ paths: {
231
+ "@/*": ["./src/*"]
232
+ }
233
+ },
234
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
235
+ exclude: ["node_modules"]
236
+ };
237
+ exports.NEXT_CONFIG = `import type { NextConfig } from "next";
238
+
239
+ const nextConfig: NextConfig = {
240
+ /* config options here */
241
+ };
242
+
243
+ export default nextConfig;
244
+ `;
245
+ exports.GLOBALS_CSS = `@tailwind base;
246
+ @tailwind components;
247
+ @tailwind utilities;
248
+
249
+ :root {
250
+ --background: #ffffff;
251
+ --foreground: #171717;
252
+ }
253
+
254
+ @media (prefers-color-scheme: dark) {
255
+ :root {
256
+ --background: #0a0a0a;
257
+ --foreground: #ededed;
258
+ }
259
+ }
260
+
261
+ body {
262
+ color: var(--foreground);
263
+ background: var(--background);
264
+ font-family: Arial, Helvetica, sans-serif;
265
+ }
266
+ `;
267
+ exports.LAYOUT_TSX = `import type { Metadata } from "next";
268
+ import "./globals.css";
269
+
270
+ export const metadata: Metadata = {
271
+ title: "My App",
272
+ description: "Built with CodeBakers patterns",
273
+ };
274
+
275
+ export default function RootLayout({
276
+ children,
277
+ }: Readonly<{
278
+ children: React.ReactNode;
279
+ }>) {
280
+ return (
281
+ <html lang="en">
282
+ <body>{children}</body>
283
+ </html>
284
+ );
285
+ }
286
+ `;
287
+ exports.PAGE_TSX = `export default function Home() {
288
+ return (
289
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
290
+ <h1 className="text-4xl font-bold mb-4">Welcome to Your App</h1>
291
+ <p className="text-gray-600 mb-8">Built with CodeBakers patterns</p>
292
+ <div className="flex gap-4">
293
+ <a
294
+ href="/login"
295
+ className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
296
+ >
297
+ Get Started
298
+ </a>
299
+ <a
300
+ href="/docs"
301
+ className="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition"
302
+ >
303
+ Documentation
304
+ </a>
305
+ </div>
306
+ </main>
307
+ );
308
+ }
309
+ `;
310
+ exports.UTILS_CN = `import { clsx, type ClassValue } from "clsx";
311
+ import { twMerge } from "tailwind-merge";
312
+
313
+ export function cn(...inputs: ClassValue[]) {
314
+ return twMerge(clsx(inputs));
315
+ }
316
+ `;
317
+ exports.GITIGNORE = `# Dependencies
318
+ node_modules
319
+ .pnpm-debug.log*
320
+
321
+ # Next.js
322
+ .next/
323
+ out/
324
+
325
+ # Production
326
+ build
327
+ dist
328
+
329
+ # Misc
330
+ .DS_Store
331
+ *.pem
332
+
333
+ # Debug
334
+ npm-debug.log*
335
+ yarn-debug.log*
336
+ yarn-error.log*
337
+
338
+ # Local env files
339
+ .env
340
+ .env.local
341
+ .env.development.local
342
+ .env.test.local
343
+ .env.production.local
344
+
345
+ # Vercel
346
+ .vercel
347
+
348
+ # TypeScript
349
+ *.tsbuildinfo
350
+ next-env.d.ts
351
+
352
+ # IDE
353
+ .idea
354
+ .vscode/*
355
+ !.vscode/settings.json
356
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {