@codebakers/cli 1.2.1 → 1.3.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/install-hook.js +42 -68
- package/dist/commands/scaffold.d.ts +4 -0
- package/dist/commands/scaffold.js +205 -0
- package/dist/index.js +6 -0
- package/dist/mcp/server.js +211 -0
- package/dist/templates/nextjs-supabase.d.ts +81 -0
- package/dist/templates/nextjs-supabase.js +356 -0
- package/package.json +1 -1
- package/src/commands/install-hook.ts +44 -68
- package/src/commands/scaffold.ts +196 -0
- package/src/index.ts +7 -0
- package/src/mcp/server.ts +244 -0
- package/src/templates/nextjs-supabase.ts +371 -0
|
@@ -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
|
@@ -4,71 +4,40 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
6
|
|
|
7
|
-
// Enhanced hook with
|
|
7
|
+
// Enhanced hook with visible feedback and concise instructions
|
|
8
8
|
const HOOK_TEMPLATE = {
|
|
9
9
|
hooks: {
|
|
10
10
|
UserPromptSubmit: [
|
|
11
11
|
{
|
|
12
12
|
type: "command",
|
|
13
|
-
command: `echo '
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- CLAUDE.md → Router & module instructions
|
|
22
|
-
- PRD.md → What we are building (requirements!)
|
|
23
|
-
- PROJECT-CONTEXT.md → Codebase knowledge
|
|
24
|
-
- PROJECT-STATE.md → What is in progress
|
|
25
|
-
- DECISIONS.md → Past architectural choices
|
|
26
|
-
|
|
27
|
-
▸ PHASE 2: PRE-FLIGHT CHECK (before writing code)
|
|
28
|
-
□ What existing code does this touch?
|
|
29
|
-
□ Is similar code in the codebase? (copy that pattern!)
|
|
30
|
-
□ Whats the data model?
|
|
31
|
-
□ What are the error cases?
|
|
32
|
-
□ Is someone else working on this? (check In Progress)
|
|
33
|
-
|
|
34
|
-
If PROJECT-CONTEXT.md is empty/stale, SCAN PROJECT FIRST:
|
|
35
|
-
- Read package.json
|
|
36
|
-
- Check file structure
|
|
37
|
-
- Find existing patterns
|
|
38
|
-
- Update PROJECT-CONTEXT.md
|
|
39
|
-
|
|
40
|
-
▸ PHASE 3: ACKNOWLEDGE & EXECUTE
|
|
41
|
-
Output: 📋 CodeBakers | [Type] | Modules: [list]
|
|
42
|
-
Then: Follow patterns from .claude/ folder EXACTLY
|
|
43
|
-
|
|
44
|
-
▸ PHASE 4: SELF-REVIEW (before saying done)
|
|
45
|
-
□ TypeScript compiles? (npx tsc --noEmit)
|
|
46
|
-
□ Imports resolve?
|
|
47
|
-
□ Error handling exists?
|
|
48
|
-
□ Matches existing patterns?
|
|
49
|
-
□ Tests written?
|
|
50
|
-
|
|
51
|
-
If ANY fails → FIX before responding
|
|
52
|
-
|
|
53
|
-
▸ PHASE 5: UPDATE STATE
|
|
54
|
-
- Update PROJECT-STATE.md (move to Completed)
|
|
55
|
-
- Add to DECISIONS.md if architectural choice made
|
|
56
|
-
|
|
57
|
-
════════════════════════════════════════════════════════════════
|
|
58
|
-
🔄 MULTI-AGENT MODE
|
|
59
|
-
════════════════════════════════════════════════════════════════
|
|
60
|
-
- Check PROJECT-STATE.md "In Progress" - dont duplicate work
|
|
61
|
-
- Add YOUR task to In Progress when starting
|
|
62
|
-
- If conflict → STOP and ask user
|
|
63
|
-
|
|
64
|
-
════════════════════════════════════════════════════════════════
|
|
65
|
-
💡 REMEMBER: Check existing code FIRST. Copy patterns. Validate.
|
|
66
|
-
════════════════════════════════════════════════════════════════'`
|
|
13
|
+
command: `echo '[CodeBakers] Loading project context...'`
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
PostToolUse: [
|
|
17
|
+
{
|
|
18
|
+
type: "command",
|
|
19
|
+
matcher: "Write|Edit",
|
|
20
|
+
command: `echo '[CodeBakers] Code written - remember to self-review before marking done'`
|
|
67
21
|
}
|
|
68
22
|
]
|
|
69
23
|
}
|
|
70
24
|
};
|
|
71
25
|
|
|
26
|
+
// Instructions that get injected into the system prompt
|
|
27
|
+
const CODEBAKERS_INSTRUCTIONS = `
|
|
28
|
+
<user-prompt-submit-hook>
|
|
29
|
+
[CodeBakers] Active - Follow these steps for EVERY request:
|
|
30
|
+
|
|
31
|
+
1. CONTEXT: Read CLAUDE.md, PROJECT-CONTEXT.md, PROJECT-STATE.md
|
|
32
|
+
2. PRE-FLIGHT: Check existing code patterns before writing new code
|
|
33
|
+
3. EXECUTE: Use patterns from .claude/ folder
|
|
34
|
+
4. SELF-REVIEW: Verify TypeScript compiles, imports resolve, error handling exists
|
|
35
|
+
5. UPDATE: Mark tasks complete in PROJECT-STATE.md
|
|
36
|
+
|
|
37
|
+
Output format: "[CodeBakers] Building [feature] using [patterns]"
|
|
38
|
+
</user-prompt-submit-hook>
|
|
39
|
+
`;
|
|
40
|
+
|
|
72
41
|
/**
|
|
73
42
|
* Install the CodeBakers hook into ~/.claude/settings.json
|
|
74
43
|
*/
|
|
@@ -117,23 +86,25 @@ export async function installHook(): Promise<void> {
|
|
|
117
86
|
}
|
|
118
87
|
}
|
|
119
88
|
|
|
120
|
-
// Merge
|
|
89
|
+
// Merge hooks into settings
|
|
121
90
|
settings.hooks = settings.hooks || {};
|
|
122
91
|
(settings.hooks as Record<string, unknown>).UserPromptSubmit = HOOK_TEMPLATE.hooks.UserPromptSubmit;
|
|
92
|
+
(settings.hooks as Record<string, unknown>).PostToolUse = HOOK_TEMPLATE.hooks.PostToolUse;
|
|
123
93
|
|
|
124
94
|
// Write back
|
|
125
95
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
126
96
|
|
|
127
97
|
spinner.succeed('Hook installed successfully!');
|
|
128
98
|
|
|
129
|
-
console.log(chalk.white('\n
|
|
130
|
-
console.log(chalk.
|
|
131
|
-
console.log(chalk.
|
|
132
|
-
|
|
133
|
-
console.log(chalk.
|
|
134
|
-
console.log(chalk.gray(' ✓
|
|
135
|
-
console.log(chalk.gray(' ✓
|
|
136
|
-
console.log(chalk.gray(' ✓
|
|
99
|
+
console.log(chalk.white('\n You\'ll see [CodeBakers] feedback in terminal:\n'));
|
|
100
|
+
console.log(chalk.cyan(' [CodeBakers] Loading project context...'));
|
|
101
|
+
console.log(chalk.cyan(' [CodeBakers] Code written - remember to self-review\n'));
|
|
102
|
+
|
|
103
|
+
console.log(chalk.white(' What happens automatically:\n'));
|
|
104
|
+
console.log(chalk.gray(' ✓ Loads project context before every response'));
|
|
105
|
+
console.log(chalk.gray(' ✓ Pre-flight checks before writing code'));
|
|
106
|
+
console.log(chalk.gray(' ✓ Self-review reminders after code changes'));
|
|
107
|
+
console.log(chalk.gray(' ✓ Pattern-based development from .claude/ folder\n'));
|
|
137
108
|
|
|
138
109
|
console.log(chalk.yellow(' ⚠️ Restart Claude Code for changes to take effect.\n'));
|
|
139
110
|
} catch (error) {
|
|
@@ -162,13 +133,18 @@ export async function uninstallHook(): Promise<void> {
|
|
|
162
133
|
|
|
163
134
|
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
164
135
|
|
|
165
|
-
if (!settings.hooks?.UserPromptSubmit) {
|
|
166
|
-
spinner.info('No
|
|
136
|
+
if (!settings.hooks?.UserPromptSubmit && !settings.hooks?.PostToolUse) {
|
|
137
|
+
spinner.info('No CodeBakers hooks found. Nothing to remove.');
|
|
167
138
|
return;
|
|
168
139
|
}
|
|
169
140
|
|
|
170
|
-
// Remove
|
|
171
|
-
|
|
141
|
+
// Remove both hooks
|
|
142
|
+
if (settings.hooks?.UserPromptSubmit) {
|
|
143
|
+
delete settings.hooks.UserPromptSubmit;
|
|
144
|
+
}
|
|
145
|
+
if (settings.hooks?.PostToolUse) {
|
|
146
|
+
delete settings.hooks.PostToolUse;
|
|
147
|
+
}
|
|
172
148
|
|
|
173
149
|
// Clean up empty hooks object
|
|
174
150
|
if (Object.keys(settings.hooks).length === 0) {
|