codebakers 2.5.4 → 3.0.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.
Files changed (60) hide show
  1. package/README.md +54 -255
  2. package/dist/chunk-HOWR3YTF.js +146 -0
  3. package/dist/index.d.ts +0 -3
  4. package/dist/index.js +10488 -7993
  5. package/dist/terminal-6ZQVP6R7.js +10 -0
  6. package/package.json +26 -41
  7. package/AUDIT_REPORT.md +0 -138
  8. package/dist/advisors-RWRTSJRR.js +0 -7
  9. package/dist/chunk-ASIJIQYC.js +0 -320
  10. package/dist/chunk-D44U3IEA.js +0 -565
  11. package/dist/chunk-LANM5XQW.js +0 -326
  12. package/dist/prd-RYITSL6Q.js +0 -7
  13. package/install.bat +0 -9
  14. package/installers/CodeBakers-Install.bat +0 -207
  15. package/installers/CodeBakers-Install.command +0 -232
  16. package/installers/README.md +0 -157
  17. package/installers/mac/assets/README.txt +0 -31
  18. package/installers/mac/build-mac-installer.sh +0 -240
  19. package/installers/windows/CodeBakers.iss +0 -256
  20. package/installers/windows/assets/README.txt +0 -16
  21. package/installers/windows/scripts/post-install.bat +0 -15
  22. package/src/channels/discord.ts +0 -5
  23. package/src/channels/slack.ts +0 -5
  24. package/src/channels/sms.ts +0 -4
  25. package/src/channels/telegram.ts +0 -5
  26. package/src/channels/whatsapp.ts +0 -7
  27. package/src/commands/advisors.ts +0 -699
  28. package/src/commands/build.ts +0 -1025
  29. package/src/commands/check.ts +0 -365
  30. package/src/commands/code.ts +0 -806
  31. package/src/commands/connect.ts +0 -12
  32. package/src/commands/deploy.ts +0 -448
  33. package/src/commands/design.ts +0 -298
  34. package/src/commands/fix.ts +0 -20
  35. package/src/commands/gateway.ts +0 -604
  36. package/src/commands/generate.ts +0 -178
  37. package/src/commands/init.ts +0 -634
  38. package/src/commands/integrate.ts +0 -884
  39. package/src/commands/learn.ts +0 -36
  40. package/src/commands/migrate.ts +0 -419
  41. package/src/commands/prd-maker.ts +0 -588
  42. package/src/commands/prd.ts +0 -419
  43. package/src/commands/security.ts +0 -102
  44. package/src/commands/setup.ts +0 -600
  45. package/src/commands/status.ts +0 -56
  46. package/src/commands/website.ts +0 -741
  47. package/src/index.ts +0 -627
  48. package/src/patterns/loader.ts +0 -337
  49. package/src/services/github.ts +0 -61
  50. package/src/services/supabase.ts +0 -147
  51. package/src/services/vercel.ts +0 -61
  52. package/src/utils/claude-md.ts +0 -287
  53. package/src/utils/config.ts +0 -375
  54. package/src/utils/display.ts +0 -338
  55. package/src/utils/files.ts +0 -418
  56. package/src/utils/nlp.ts +0 -312
  57. package/src/utils/ui.ts +0 -441
  58. package/src/utils/updates.ts +0 -8
  59. package/src/utils/voice.ts +0 -323
  60. package/tsconfig.json +0 -26
@@ -1,634 +0,0 @@
1
- import * as p from '@clack/prompts';
2
- import chalk from 'chalk';
3
- import fs from 'fs-extra';
4
- import * as path from 'path';
5
- import { execa } from 'execa';
6
- import { Config } from '../utils/config.js';
7
- import { GitHubService } from '../services/github.js';
8
- import { VercelService } from '../services/vercel.js';
9
- import { SupabaseService } from '../services/supabase.js';
10
- import { generateClaudeMd } from '../utils/claude-md.js';
11
-
12
- interface InitOptions {
13
- name?: string;
14
- template?: string;
15
- git?: boolean;
16
- vercel?: boolean;
17
- supabase?: boolean;
18
- }
19
-
20
- export async function initCommand(options: InitOptions = {}): Promise<void> {
21
- const config = new Config();
22
-
23
- // Check if configured
24
- if (!config.isConfigured()) {
25
- p.log.error('Please run `codebakers setup` first.');
26
- return;
27
- }
28
-
29
- p.intro(chalk.bgCyan.black(' Create New Project '));
30
-
31
- // Project name
32
- let projectName = options.name;
33
- if (!projectName) {
34
- const name = await p.text({
35
- message: 'Project name:',
36
- placeholder: 'my-app',
37
- validate: (value) => {
38
- if (!value) return 'Project name is required';
39
- if (!/^[a-z0-9-]+$/.test(value)) return 'Use lowercase letters, numbers, and hyphens only';
40
- return undefined;
41
- },
42
- });
43
- if (p.isCancel(name)) return;
44
- projectName = name as string;
45
- }
46
-
47
- // Project type
48
- const projectType = await p.select({
49
- message: 'What are you building?',
50
- options: [
51
- { value: 'web', label: '🌐 Web app', hint: 'Next.js → Vercel' },
52
- { value: 'mobile', label: '📱 Mobile app', hint: 'Expo → App stores' },
53
- { value: 'desktop', label: '🖥️ Desktop app', hint: 'Tauri → Windows/Mac/Linux' },
54
- { value: 'fullstack', label: '📦 Full stack', hint: 'Web + Mobile + Desktop' },
55
- ],
56
- });
57
- if (p.isCancel(projectType)) return;
58
-
59
- // Framework selection (for web)
60
- let framework = 'nextjs';
61
- if (projectType === 'web' || projectType === 'fullstack') {
62
- const selected = await p.select({
63
- message: 'Framework:',
64
- options: [
65
- { value: 'nextjs', label: 'Next.js 14+', hint: config.getPreference('defaultFramework') === 'nextjs' ? '(your usual choice)' : '' },
66
- { value: 'remix', label: 'Remix' },
67
- { value: 'vite', label: 'Vite + React' },
68
- { value: 'astro', label: 'Astro' },
69
- ],
70
- });
71
- if (p.isCancel(selected)) return;
72
- framework = selected as string;
73
- config.learnPreference('defaultFramework', framework);
74
- }
75
-
76
- // UI Library
77
- const uiLibrary = await p.select({
78
- message: 'UI Library:',
79
- options: [
80
- { value: 'shadcn', label: 'shadcn/ui', hint: config.getPreference('defaultUI') === 'shadcn' ? '(your usual choice)' : 'Recommended' },
81
- { value: 'chakra', label: 'Chakra UI' },
82
- { value: 'mantine', label: 'Mantine' },
83
- { value: 'mui', label: 'Material UI' },
84
- { value: 'headless', label: 'Headless UI' },
85
- { value: 'tailwind', label: 'Tailwind only' },
86
- ],
87
- });
88
- if (p.isCancel(uiLibrary)) return;
89
- config.learnPreference('defaultUI', uiLibrary);
90
-
91
- // Packages
92
- const packages = await p.multiselect({
93
- message: 'Additional packages:',
94
- options: [
95
- { value: 'typescript', label: 'TypeScript', hint: 'Always recommended' },
96
- { value: 'tailwind', label: 'Tailwind CSS' },
97
- { value: 'zod', label: 'Zod', hint: 'Validation' },
98
- { value: 'tanstack-query', label: 'TanStack Query', hint: 'Data fetching' },
99
- { value: 'zustand', label: 'Zustand', hint: 'State management' },
100
- { value: 'react-hook-form', label: 'React Hook Form' },
101
- { value: 'lucide', label: 'Lucide Icons' },
102
- { value: 'trpc', label: 'tRPC' },
103
- ],
104
- initialValues: ['typescript', 'tailwind', 'zod', 'zustand', 'lucide'],
105
- });
106
- if (p.isCancel(packages)) return;
107
-
108
- // Services to provision
109
- const services = await p.multiselect({
110
- message: 'Services to provision:',
111
- options: [
112
- { value: 'github', label: 'GitHub', hint: 'Repository' },
113
- { value: 'vercel', label: 'Vercel', hint: 'Hosting' },
114
- { value: 'supabase', label: 'Supabase', hint: 'Database + Auth' },
115
- { value: 'stripe', label: 'Stripe', hint: 'Payments' },
116
- { value: 'resend', label: 'Resend', hint: 'Email' },
117
- { value: 'vapi', label: 'VAPI', hint: 'Voice AI' },
118
- ],
119
- initialValues: ['github', 'vercel', 'supabase'],
120
- });
121
- if (p.isCancel(services)) return;
122
-
123
- // Domain
124
- let domain: string | undefined;
125
- const domainChoice = await p.select({
126
- message: 'Domain:',
127
- options: [
128
- { value: 'subdomain', label: 'Subdomain', hint: `${projectName}.yourdomain.com` },
129
- { value: 'vercel', label: 'Vercel subdomain', hint: `${projectName}.vercel.app` },
130
- { value: 'purchase', label: 'Purchase new domain' },
131
- { value: 'skip', label: 'Skip for now' },
132
- ],
133
- });
134
- if (p.isCancel(domainChoice)) return;
135
-
136
- if (domainChoice === 'subdomain') {
137
- const sub = await p.text({
138
- message: 'Subdomain:',
139
- placeholder: projectName,
140
- initialValue: projectName,
141
- });
142
- if (!p.isCancel(sub)) {
143
- domain = `${sub}.yourdomain.com`;
144
- }
145
- }
146
-
147
- // Confirm
148
- const projectConfig = {
149
- name: projectName,
150
- type: projectType,
151
- framework,
152
- ui: uiLibrary,
153
- packages,
154
- services,
155
- domain,
156
- };
157
-
158
- p.note(
159
- `Name: ${projectName}
160
- Type: ${projectType}
161
- Framework: ${framework}
162
- UI: ${uiLibrary}
163
- Packages: ${(packages as string[]).join(', ')}
164
- Services: ${(services as string[]).join(', ')}
165
- Domain: ${domain || 'Vercel default'}`,
166
- 'Configuration'
167
- );
168
-
169
- const confirmed = await p.confirm({
170
- message: 'Create project?',
171
- initialValue: true,
172
- });
173
-
174
- if (!confirmed || p.isCancel(confirmed)) {
175
- p.cancel('Project creation cancelled.');
176
- return;
177
- }
178
-
179
- // Create project
180
- const spinner = p.spinner();
181
- const projectPath = path.join(process.cwd(), projectName);
182
-
183
- try {
184
- // Step 1: Create local project
185
- spinner.start('Creating local project...');
186
- await createLocalProject(projectPath, projectConfig);
187
- spinner.stop('Local project created');
188
-
189
- // Step 2: Install dependencies
190
- spinner.start('Installing dependencies...');
191
-
192
- // Try npm first (most common), fall back to pnpm or yarn
193
- let installSuccess = false;
194
- try {
195
- await execa('npm', ['install'], { cwd: projectPath, stdio: 'inherit' });
196
- installSuccess = true;
197
- } catch {
198
- try {
199
- await execa('pnpm', ['install'], { cwd: projectPath, stdio: 'inherit' });
200
- installSuccess = true;
201
- } catch {
202
- try {
203
- await execa('yarn', ['install'], { cwd: projectPath, stdio: 'inherit' });
204
- installSuccess = true;
205
- } catch {
206
- // All failed
207
- }
208
- }
209
- }
210
-
211
- if (installSuccess) {
212
- spinner.stop('Dependencies installed');
213
- } else {
214
- spinner.stop('Dependencies installation failed');
215
- console.log(chalk.yellow(' ⚠️ Run "npm install" manually in the project folder'));
216
- }
217
-
218
- // Step 3: GitHub
219
- if ((services as string[]).includes('github')) {
220
- spinner.start('Creating GitHub repository...');
221
- try {
222
- const github = new GitHubService(config);
223
- const repo = await github.createRepo(projectName, { private: true });
224
- spinner.stop(`GitHub repo created: ${repo.html_url}`);
225
-
226
- // Init git and push
227
- try {
228
- await execa('git', ['init'], { cwd: projectPath });
229
- await execa('git', ['add', '.'], { cwd: projectPath });
230
- await execa('git', ['commit', '-m', 'Initial commit by CodeBakers'], { cwd: projectPath });
231
- await execa('git', ['remote', 'add', 'origin', repo.clone_url], { cwd: projectPath });
232
- await execa('git', ['push', '-u', 'origin', 'main'], { cwd: projectPath });
233
- } catch (gitError) {
234
- console.log(chalk.yellow(' ⚠️ Git push failed. You can push manually later.'));
235
- }
236
- } catch (error) {
237
- spinner.stop('GitHub setup failed');
238
- console.log(chalk.yellow(' ⚠️ Could not create GitHub repo. Check your GitHub token.'));
239
- }
240
- }
241
-
242
- // Step 4: Supabase
243
- if ((services as string[]).includes('supabase')) {
244
- spinner.start('Creating Supabase project...');
245
- try {
246
- const supabase = new SupabaseService(config);
247
- const project = await supabase.createProject(projectName);
248
- spinner.stop(`Supabase project created: ${project.name}`);
249
-
250
- // Save Supabase config
251
- await fs.writeJson(
252
- path.join(projectPath, '.codebakers', 'supabase.json'),
253
- { projectId: project.id, projectUrl: project.api_url },
254
- { spaces: 2 }
255
- );
256
- } catch (error) {
257
- spinner.stop('Supabase setup failed');
258
- console.log(chalk.yellow(' ⚠️ Could not create Supabase project. Check your Supabase token.'));
259
- }
260
- }
261
-
262
- // Step 5: Vercel
263
- if ((services as string[]).includes('vercel')) {
264
- spinner.start('Creating Vercel project...');
265
- try {
266
- const vercel = new VercelService(config);
267
- const project = await vercel.createProject(projectName);
268
- spinner.stop(`Vercel project created`);
269
-
270
- // Configure domain if specified
271
- if (domain) {
272
- spinner.start(`Configuring domain: ${domain}...`);
273
- try {
274
- await vercel.addDomain(projectName, domain);
275
- spinner.stop('Domain configured');
276
- } catch {
277
- spinner.stop('Domain configuration failed');
278
- console.log(chalk.yellow(' ⚠️ Could not configure domain. Add it manually in Vercel.'));
279
- }
280
- }
281
-
282
- // Deploy
283
- spinner.start('Deploying to Vercel...');
284
- try {
285
- const deployment = await vercel.deploy(projectPath);
286
- spinner.stop(`Deployed: ${deployment.url}`);
287
- } catch {
288
- spinner.stop('Deployment failed');
289
- console.log(chalk.yellow(' ⚠️ Could not deploy. Run "vercel" manually later.'));
290
- }
291
- } catch (error) {
292
- spinner.stop('Vercel setup failed');
293
- console.log(chalk.yellow(' ⚠️ Could not create Vercel project. Check your Vercel token.'));
294
- }
295
- }
296
-
297
- // Step 6: Generate CLAUDE.md
298
- spinner.start('Generating CLAUDE.md...');
299
- const claudeMd = generateClaudeMd(projectConfig);
300
- await fs.writeFile(path.join(projectPath, 'CLAUDE.md'), claudeMd);
301
- spinner.stop('CLAUDE.md generated');
302
-
303
- // Step 7: Setup git hooks
304
- spinner.start('Setting up CodeBakers enforcement...');
305
- await setupGitHooks(projectPath);
306
- spinner.stop('CodeBakers enforcement configured');
307
-
308
- // Save project to config
309
- config.addProject({
310
- name: projectName,
311
- path: projectPath,
312
- createdAt: new Date().toISOString(),
313
- });
314
-
315
- // Success!
316
- p.outro(chalk.green(`
317
- ✓ Project created!
318
-
319
- ${chalk.bold('Your project is ready:')}
320
- ${chalk.cyan(`cd ${projectName}`)}
321
- ${chalk.cyan('codebakers code')}
322
-
323
- ${chalk.dim('Shortcuts:')}
324
- ${chalk.cyan('codebakers')} — Interactive menu
325
- ${chalk.cyan('codebakers code')} — AI coding agent
326
- ${chalk.cyan('codebakers check')} — Pattern enforcement
327
- ${chalk.cyan('codebakers deploy')} — Deploy to production
328
- `));
329
-
330
- } catch (error) {
331
- spinner.stop('Error occurred');
332
- p.log.error(`Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`);
333
-
334
- // Offer to clean up
335
- const cleanup = await p.confirm({
336
- message: 'Clean up partially created project?',
337
- });
338
-
339
- if (cleanup && !p.isCancel(cleanup)) {
340
- await fs.remove(projectPath);
341
- p.log.info('Cleaned up.');
342
- }
343
- }
344
- }
345
-
346
- async function createLocalProject(
347
- projectPath: string,
348
- config: Record<string, unknown>
349
- ): Promise<void> {
350
- const framework = config.framework as string;
351
- const ui = config.ui as string;
352
- const packages = config.packages as string[];
353
-
354
- // Create directory structure
355
- await fs.ensureDir(projectPath);
356
- await fs.ensureDir(path.join(projectPath, '.codebakers'));
357
- await fs.ensureDir(path.join(projectPath, 'src', 'app'));
358
- await fs.ensureDir(path.join(projectPath, 'src', 'components', 'ui'));
359
- await fs.ensureDir(path.join(projectPath, 'src', 'components', 'features'));
360
- await fs.ensureDir(path.join(projectPath, 'src', 'lib'));
361
- await fs.ensureDir(path.join(projectPath, 'src', 'hooks'));
362
- await fs.ensureDir(path.join(projectPath, 'src', 'types'));
363
- await fs.ensureDir(path.join(projectPath, 'src', 'stores'));
364
- await fs.ensureDir(path.join(projectPath, 'src', 'services'));
365
-
366
- // Create package.json
367
- const packageJson: {
368
- name: string;
369
- version: string;
370
- private: boolean;
371
- scripts: Record<string, string>;
372
- dependencies: Record<string, string>;
373
- devDependencies: Record<string, string>;
374
- } = {
375
- name: path.basename(projectPath),
376
- version: '0.1.0',
377
- private: true,
378
- scripts: {
379
- dev: 'next dev',
380
- build: 'next build',
381
- start: 'next start',
382
- lint: 'next lint',
383
- typecheck: 'tsc --noEmit',
384
- 'codebakers:check': 'codebakers check',
385
- },
386
- dependencies: {
387
- next: '^14.2.0',
388
- react: '^18.3.0',
389
- 'react-dom': '^18.3.0',
390
- },
391
- devDependencies: {
392
- typescript: '^5.5.0',
393
- '@types/node': '^22.0.0',
394
- '@types/react': '^18.3.0',
395
- '@types/react-dom': '^18.3.0',
396
- },
397
- };
398
-
399
- // Add packages based on selection
400
- if (packages.includes('tailwind')) {
401
- packageJson.devDependencies['tailwindcss'] = '^3.4.0';
402
- packageJson.devDependencies['postcss'] = '^8.4.0';
403
- packageJson.devDependencies['autoprefixer'] = '^10.4.0';
404
- }
405
- if (packages.includes('zod')) {
406
- packageJson.dependencies['zod'] = '^3.23.0';
407
- }
408
- if (packages.includes('zustand')) {
409
- packageJson.dependencies['zustand'] = '^4.5.0';
410
- }
411
- if (packages.includes('tanstack-query')) {
412
- packageJson.dependencies['@tanstack/react-query'] = '^5.50.0';
413
- }
414
- if (packages.includes('react-hook-form')) {
415
- packageJson.dependencies['react-hook-form'] = '^7.52.0';
416
- packageJson.dependencies['@hookform/resolvers'] = '^3.9.0';
417
- }
418
- if (packages.includes('lucide')) {
419
- packageJson.dependencies['lucide-react'] = '^0.400.0';
420
- }
421
- if (ui === 'shadcn') {
422
- packageJson.dependencies['class-variance-authority'] = '^0.7.0';
423
- packageJson.dependencies['clsx'] = '^2.1.0';
424
- packageJson.dependencies['tailwind-merge'] = '^2.4.0';
425
- packageJson.dependencies['sonner'] = '^1.5.0';
426
- }
427
-
428
- // Add Supabase
429
- packageJson.dependencies['@supabase/supabase-js'] = '^2.45.0';
430
- packageJson.dependencies['@supabase/ssr'] = '^0.4.0';
431
-
432
- await fs.writeJson(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
433
-
434
- // Create other config files
435
- await createConfigFiles(projectPath, config);
436
-
437
- // Create CodeBakers project config
438
- await fs.writeJson(
439
- path.join(projectPath, '.codebakers', 'config.json'),
440
- {
441
- version: '1.0.0',
442
- framework,
443
- ui,
444
- packages,
445
- patterns: ['00-core', '01-database', '02-auth', '03-api', '04-frontend'],
446
- createdAt: new Date().toISOString(),
447
- },
448
- { spaces: 2 }
449
- );
450
- }
451
-
452
- async function createConfigFiles(
453
- projectPath: string,
454
- config: Record<string, unknown>
455
- ): Promise<void> {
456
- // TypeScript config
457
- const tsConfig = {
458
- compilerOptions: {
459
- target: 'ES2022',
460
- lib: ['dom', 'dom.iterable', 'ES2022'],
461
- allowJs: true,
462
- skipLibCheck: true,
463
- strict: true,
464
- noEmit: true,
465
- esModuleInterop: true,
466
- module: 'esnext',
467
- moduleResolution: 'bundler',
468
- resolveJsonModule: true,
469
- isolatedModules: true,
470
- jsx: 'preserve',
471
- incremental: true,
472
- plugins: [{ name: 'next' }],
473
- paths: { '@/*': ['./src/*'] },
474
- },
475
- include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
476
- exclude: ['node_modules'],
477
- };
478
- await fs.writeJson(path.join(projectPath, 'tsconfig.json'), tsConfig, { spaces: 2 });
479
-
480
- // Next.js config
481
- const nextConfig = `/** @type {import('next').NextConfig} */
482
- const nextConfig = {
483
- experimental: {
484
- typedRoutes: true,
485
- },
486
- };
487
-
488
- export default nextConfig;
489
- `;
490
- await fs.writeFile(path.join(projectPath, 'next.config.mjs'), nextConfig);
491
-
492
- // Tailwind config
493
- const tailwindConfig = `import type { Config } from 'tailwindcss';
494
-
495
- const config: Config = {
496
- darkMode: ['class'],
497
- content: [
498
- './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
499
- './src/components/**/*.{js,ts,jsx,tsx,mdx}',
500
- './src/app/**/*.{js,ts,jsx,tsx,mdx}',
501
- ],
502
- theme: {
503
- extend: {
504
- colors: {
505
- border: 'hsl(var(--border))',
506
- input: 'hsl(var(--input))',
507
- ring: 'hsl(var(--ring))',
508
- background: 'hsl(var(--background))',
509
- foreground: 'hsl(var(--foreground))',
510
- primary: {
511
- DEFAULT: 'hsl(var(--primary))',
512
- foreground: 'hsl(var(--primary-foreground))',
513
- },
514
- secondary: {
515
- DEFAULT: 'hsl(var(--secondary))',
516
- foreground: 'hsl(var(--secondary-foreground))',
517
- },
518
- destructive: {
519
- DEFAULT: 'hsl(var(--destructive))',
520
- foreground: 'hsl(var(--destructive-foreground))',
521
- },
522
- muted: {
523
- DEFAULT: 'hsl(var(--muted))',
524
- foreground: 'hsl(var(--muted-foreground))',
525
- },
526
- accent: {
527
- DEFAULT: 'hsl(var(--accent))',
528
- foreground: 'hsl(var(--accent-foreground))',
529
- },
530
- },
531
- borderRadius: {
532
- lg: 'var(--radius)',
533
- md: 'calc(var(--radius) - 2px)',
534
- sm: 'calc(var(--radius) - 4px)',
535
- },
536
- },
537
- },
538
- plugins: [require('tailwindcss-animate')],
539
- };
540
-
541
- export default config;
542
- `;
543
- await fs.writeFile(path.join(projectPath, 'tailwind.config.ts'), tailwindConfig);
544
-
545
- // .env.example
546
- const envExample = `# Supabase
547
- NEXT_PUBLIC_SUPABASE_URL=
548
- NEXT_PUBLIC_SUPABASE_ANON_KEY=
549
- SUPABASE_SERVICE_ROLE_KEY=
550
-
551
- # Stripe (optional)
552
- STRIPE_SECRET_KEY=
553
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
554
- STRIPE_WEBHOOK_SECRET=
555
-
556
- # App
557
- NEXT_PUBLIC_APP_URL=http://localhost:3000
558
- `;
559
- await fs.writeFile(path.join(projectPath, '.env.example'), envExample);
560
- await fs.writeFile(path.join(projectPath, '.env.local'), envExample);
561
-
562
- // .gitignore
563
- const gitignore = `# Dependencies
564
- node_modules/
565
- .pnp
566
- .pnp.js
567
-
568
- # Build
569
- .next/
570
- out/
571
- build/
572
- dist/
573
-
574
- # Environment
575
- .env
576
- .env.local
577
- .env.*.local
578
-
579
- # IDE
580
- .vscode/
581
- .idea/
582
- *.swp
583
- *.swo
584
-
585
- # OS
586
- .DS_Store
587
- Thumbs.db
588
-
589
- # Logs
590
- npm-debug.log*
591
- yarn-debug.log*
592
- yarn-error.log*
593
-
594
- # Testing
595
- coverage/
596
- .nyc_output/
597
-
598
- # Misc
599
- *.pem
600
- `;
601
- await fs.writeFile(path.join(projectPath, '.gitignore'), gitignore);
602
- }
603
-
604
- async function setupGitHooks(projectPath: string): Promise<void> {
605
- // Create pre-commit hook
606
- const hooksDir = path.join(projectPath, '.git', 'hooks');
607
-
608
- // Check if git is initialized
609
- if (!await fs.pathExists(path.join(projectPath, '.git'))) {
610
- await execa('git', ['init'], { cwd: projectPath });
611
- }
612
-
613
- await fs.ensureDir(hooksDir);
614
-
615
- const preCommitHook = `#!/bin/sh
616
- # CodeBakers pre-commit hook
617
-
618
- echo "🔍 Running CodeBakers check..."
619
-
620
- # Run pattern check
621
- codebakers check
622
-
623
- # If check fails, abort commit
624
- if [ $? -ne 0 ]; then
625
- echo "❌ CodeBakers check failed. Fix issues or use --no-verify to bypass."
626
- exit 1
627
- fi
628
-
629
- echo "✓ CodeBakers check passed"
630
- `;
631
-
632
- await fs.writeFile(path.join(hooksDir, 'pre-commit'), preCommitHook);
633
- await fs.chmod(path.join(hooksDir, 'pre-commit'), '755');
634
- }