popeye-cli 1.2.0 → 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.
Files changed (134) hide show
  1. package/.env.example +4 -1
  2. package/CONTRIBUTING.md +10 -0
  3. package/README.md +111 -2
  4. package/dist/adapters/claude.d.ts +26 -2
  5. package/dist/adapters/claude.d.ts.map +1 -1
  6. package/dist/adapters/claude.js +257 -10
  7. package/dist/adapters/claude.js.map +1 -1
  8. package/dist/adapters/grok.d.ts +2 -1
  9. package/dist/adapters/grok.d.ts.map +1 -1
  10. package/dist/adapters/grok.js.map +1 -1
  11. package/dist/adapters/index.d.ts +8 -0
  12. package/dist/adapters/index.d.ts.map +1 -0
  13. package/dist/adapters/index.js +12 -0
  14. package/dist/adapters/index.js.map +1 -0
  15. package/dist/adapters/openai.d.ts +2 -2
  16. package/dist/adapters/openai.d.ts.map +1 -1
  17. package/dist/adapters/openai.js.map +1 -1
  18. package/dist/cli/commands/create.d.ts.map +1 -1
  19. package/dist/cli/commands/create.js +25 -5
  20. package/dist/cli/commands/create.js.map +1 -1
  21. package/dist/cli/interactive.d.ts.map +1 -1
  22. package/dist/cli/interactive.js +79 -6
  23. package/dist/cli/interactive.js.map +1 -1
  24. package/dist/generators/all.d.ts +40 -0
  25. package/dist/generators/all.d.ts.map +1 -0
  26. package/dist/generators/all.js +826 -0
  27. package/dist/generators/all.js.map +1 -0
  28. package/dist/generators/fullstack.d.ts +9 -0
  29. package/dist/generators/fullstack.d.ts.map +1 -1
  30. package/dist/generators/fullstack.js.map +1 -1
  31. package/dist/generators/index.d.ts +3 -1
  32. package/dist/generators/index.d.ts.map +1 -1
  33. package/dist/generators/index.js +33 -0
  34. package/dist/generators/index.js.map +1 -1
  35. package/dist/generators/templates/index.d.ts +2 -0
  36. package/dist/generators/templates/index.d.ts.map +1 -1
  37. package/dist/generators/templates/index.js +2 -0
  38. package/dist/generators/templates/index.js.map +1 -1
  39. package/dist/generators/templates/website.d.ts +85 -0
  40. package/dist/generators/templates/website.d.ts.map +1 -0
  41. package/dist/generators/templates/website.js +877 -0
  42. package/dist/generators/templates/website.js.map +1 -0
  43. package/dist/generators/website.d.ts +56 -0
  44. package/dist/generators/website.d.ts.map +1 -0
  45. package/dist/generators/website.js +269 -0
  46. package/dist/generators/website.js.map +1 -0
  47. package/dist/types/consensus.d.ts +8 -3
  48. package/dist/types/consensus.d.ts.map +1 -1
  49. package/dist/types/index.d.ts +2 -2
  50. package/dist/types/index.d.ts.map +1 -1
  51. package/dist/types/index.js +2 -2
  52. package/dist/types/index.js.map +1 -1
  53. package/dist/types/project.d.ts +115 -1
  54. package/dist/types/project.d.ts.map +1 -1
  55. package/dist/types/project.js +41 -1
  56. package/dist/types/project.js.map +1 -1
  57. package/dist/types/workflow.d.ts +8 -0
  58. package/dist/types/workflow.d.ts.map +1 -1
  59. package/dist/types/workflow.js +2 -2
  60. package/dist/types/workflow.js.map +1 -1
  61. package/dist/workflow/consensus.d.ts +2 -1
  62. package/dist/workflow/consensus.d.ts.map +1 -1
  63. package/dist/workflow/consensus.js.map +1 -1
  64. package/dist/workflow/execution-mode.d.ts +2 -0
  65. package/dist/workflow/execution-mode.d.ts.map +1 -1
  66. package/dist/workflow/execution-mode.js +20 -0
  67. package/dist/workflow/execution-mode.js.map +1 -1
  68. package/dist/workflow/index.d.ts +8 -0
  69. package/dist/workflow/index.d.ts.map +1 -1
  70. package/dist/workflow/index.js +19 -0
  71. package/dist/workflow/index.js.map +1 -1
  72. package/dist/workflow/milestone-workflow.d.ts +2 -0
  73. package/dist/workflow/milestone-workflow.d.ts.map +1 -1
  74. package/dist/workflow/milestone-workflow.js +17 -0
  75. package/dist/workflow/milestone-workflow.js.map +1 -1
  76. package/dist/workflow/plan-mode.d.ts +3 -3
  77. package/dist/workflow/plan-mode.d.ts.map +1 -1
  78. package/dist/workflow/plan-mode.js.map +1 -1
  79. package/dist/workflow/plan-parser.d.ts +97 -0
  80. package/dist/workflow/plan-parser.d.ts.map +1 -0
  81. package/dist/workflow/plan-parser.js +235 -0
  82. package/dist/workflow/plan-parser.js.map +1 -0
  83. package/dist/workflow/plan-storage.d.ts +40 -12
  84. package/dist/workflow/plan-storage.d.ts.map +1 -1
  85. package/dist/workflow/plan-storage.js +47 -20
  86. package/dist/workflow/plan-storage.js.map +1 -1
  87. package/dist/workflow/seo-tests.d.ts +43 -0
  88. package/dist/workflow/seo-tests.d.ts.map +1 -0
  89. package/dist/workflow/seo-tests.js +192 -0
  90. package/dist/workflow/seo-tests.js.map +1 -0
  91. package/dist/workflow/separation-guard.d.ts +35 -0
  92. package/dist/workflow/separation-guard.d.ts.map +1 -0
  93. package/dist/workflow/separation-guard.js +154 -0
  94. package/dist/workflow/separation-guard.js.map +1 -0
  95. package/dist/workflow/task-workflow.d.ts +2 -0
  96. package/dist/workflow/task-workflow.d.ts.map +1 -1
  97. package/dist/workflow/task-workflow.js +19 -0
  98. package/dist/workflow/task-workflow.js.map +1 -1
  99. package/dist/workflow/test-runner.d.ts.map +1 -1
  100. package/dist/workflow/test-runner.js +128 -0
  101. package/dist/workflow/test-runner.js.map +1 -1
  102. package/dist/workflow/workspace-manager.d.ts +31 -20
  103. package/dist/workflow/workspace-manager.d.ts.map +1 -1
  104. package/dist/workflow/workspace-manager.js +38 -9
  105. package/dist/workflow/workspace-manager.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/adapters/claude.ts +289 -14
  108. package/src/adapters/grok.ts +2 -1
  109. package/src/adapters/index.ts +15 -0
  110. package/src/adapters/openai.ts +2 -2
  111. package/src/cli/commands/create.ts +25 -5
  112. package/src/cli/interactive.ts +76 -6
  113. package/src/generators/all.ts +897 -0
  114. package/src/generators/fullstack.ts +10 -0
  115. package/src/generators/index.ts +54 -0
  116. package/src/generators/templates/index.ts +2 -0
  117. package/src/generators/templates/website.ts +906 -0
  118. package/src/generators/website.ts +350 -0
  119. package/src/types/consensus.ts +9 -3
  120. package/src/types/index.ts +33 -0
  121. package/src/types/project.ts +139 -2
  122. package/src/types/workflow.ts +2 -2
  123. package/src/workflow/consensus.ts +3 -2
  124. package/src/workflow/execution-mode.ts +32 -0
  125. package/src/workflow/index.ts +20 -0
  126. package/src/workflow/milestone-workflow.ts +22 -0
  127. package/src/workflow/plan-mode.ts +3 -3
  128. package/src/workflow/plan-parser.ts +317 -0
  129. package/src/workflow/plan-storage.ts +69 -30
  130. package/src/workflow/seo-tests.ts +246 -0
  131. package/src/workflow/separation-guard.ts +200 -0
  132. package/src/workflow/task-workflow.ts +25 -0
  133. package/src/workflow/test-runner.ts +149 -0
  134. package/src/workflow/workspace-manager.ts +68 -31
@@ -0,0 +1,877 @@
1
+ /**
2
+ * Website templates for Next.js marketing sites
3
+ * Generates SEO-ready Next.js App Router projects
4
+ */
5
+ /**
6
+ * Generate Next.js package.json
7
+ */
8
+ export function generateWebsitePackageJson(projectName) {
9
+ return `{
10
+ "name": "${projectName}-website",
11
+ "version": "1.0.0",
12
+ "private": true,
13
+ "scripts": {
14
+ "dev": "next dev -p 3001",
15
+ "build": "next build",
16
+ "start": "next start -p 3001",
17
+ "lint": "next lint",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "dependencies": {
23
+ "next": "^14.1.0",
24
+ "react": "^18.2.0",
25
+ "react-dom": "^18.2.0",
26
+ "lucide-react": "^0.312.0",
27
+ "clsx": "^2.1.0",
28
+ "tailwind-merge": "^2.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.11.0",
32
+ "@types/react": "^18.2.0",
33
+ "@types/react-dom": "^18.2.0",
34
+ "autoprefixer": "^10.4.17",
35
+ "postcss": "^8.4.33",
36
+ "tailwindcss": "^3.4.1",
37
+ "typescript": "^5.3.3",
38
+ "@testing-library/react": "^14.1.2",
39
+ "@vitejs/plugin-react": "^4.2.1",
40
+ "vitest": "^1.2.0",
41
+ "jsdom": "^24.0.0"
42
+ }
43
+ }
44
+ `;
45
+ }
46
+ /**
47
+ * Generate Next.js config
48
+ */
49
+ export function generateNextConfig() {
50
+ return `/** @type {import('next').NextConfig} */
51
+ const nextConfig = {
52
+ // Enable React Strict Mode for better development
53
+ reactStrictMode: true,
54
+
55
+ // Image optimization
56
+ images: {
57
+ domains: [],
58
+ formats: ['image/avif', 'image/webp'],
59
+ },
60
+
61
+ // Disable x-powered-by header
62
+ poweredByHeader: false,
63
+
64
+ // Trailing slash config
65
+ trailingSlash: false,
66
+
67
+ // Headers for security
68
+ async headers() {
69
+ return [
70
+ {
71
+ source: '/:path*',
72
+ headers: [
73
+ {
74
+ key: 'X-DNS-Prefetch-Control',
75
+ value: 'on',
76
+ },
77
+ {
78
+ key: 'X-Content-Type-Options',
79
+ value: 'nosniff',
80
+ },
81
+ ],
82
+ },
83
+ ];
84
+ },
85
+ };
86
+
87
+ module.exports = nextConfig;
88
+ `;
89
+ }
90
+ /**
91
+ * Generate website tsconfig.json
92
+ */
93
+ export function generateWebsiteTsconfig() {
94
+ return `{
95
+ "compilerOptions": {
96
+ "target": "ES2017",
97
+ "lib": ["dom", "dom.iterable", "esnext"],
98
+ "allowJs": true,
99
+ "skipLibCheck": true,
100
+ "strict": true,
101
+ "noEmit": true,
102
+ "esModuleInterop": true,
103
+ "module": "esnext",
104
+ "moduleResolution": "bundler",
105
+ "resolveJsonModule": true,
106
+ "isolatedModules": true,
107
+ "jsx": "preserve",
108
+ "incremental": true,
109
+ "plugins": [
110
+ {
111
+ "name": "next"
112
+ }
113
+ ],
114
+ "paths": {
115
+ "@/*": ["./src/*"]
116
+ }
117
+ },
118
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
119
+ "exclude": ["node_modules"]
120
+ }
121
+ `;
122
+ }
123
+ /**
124
+ * Generate Tailwind config for website
125
+ */
126
+ export function generateWebsiteTailwindConfig() {
127
+ return `import type { Config } from 'tailwindcss';
128
+
129
+ const config: Config = {
130
+ content: [
131
+ './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
132
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}',
133
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
134
+ ],
135
+ theme: {
136
+ extend: {
137
+ colors: {
138
+ primary: {
139
+ 50: '#f0f9ff',
140
+ 100: '#e0f2fe',
141
+ 200: '#bae6fd',
142
+ 300: '#7dd3fc',
143
+ 400: '#38bdf8',
144
+ 500: '#0ea5e9',
145
+ 600: '#0284c7',
146
+ 700: '#0369a1',
147
+ 800: '#075985',
148
+ 900: '#0c4a6e',
149
+ },
150
+ },
151
+ fontFamily: {
152
+ sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
153
+ },
154
+ },
155
+ },
156
+ plugins: [],
157
+ };
158
+
159
+ export default config;
160
+ `;
161
+ }
162
+ /**
163
+ * Generate PostCSS config for website
164
+ */
165
+ export function generateWebsitePostcssConfig() {
166
+ return `module.exports = {
167
+ plugins: {
168
+ tailwindcss: {},
169
+ autoprefixer: {},
170
+ },
171
+ };
172
+ `;
173
+ }
174
+ /**
175
+ * Generate root layout.tsx with metadata
176
+ */
177
+ export function generateWebsiteLayout(projectName) {
178
+ const title = projectName
179
+ .split('-')
180
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
181
+ .join(' ');
182
+ return `import type { Metadata } from 'next';
183
+ import { Inter } from 'next/font/google';
184
+ import './globals.css';
185
+
186
+ const inter = Inter({
187
+ subsets: ['latin'],
188
+ variable: '--font-inter',
189
+ });
190
+
191
+ export const metadata: Metadata = {
192
+ title: {
193
+ default: '${title}',
194
+ template: '%s | ${title}',
195
+ },
196
+ description: '${title} - Your modern web application',
197
+ keywords: ['${projectName}', 'web app', 'nextjs'],
198
+ authors: [{ name: '${title} Team' }],
199
+ creator: '${title}',
200
+ openGraph: {
201
+ type: 'website',
202
+ locale: 'en_US',
203
+ url: 'https://${projectName}.com',
204
+ siteName: '${title}',
205
+ title: '${title}',
206
+ description: '${title} - Your modern web application',
207
+ },
208
+ twitter: {
209
+ card: 'summary_large_image',
210
+ title: '${title}',
211
+ description: '${title} - Your modern web application',
212
+ },
213
+ robots: {
214
+ index: true,
215
+ follow: true,
216
+ },
217
+ };
218
+
219
+ export default function RootLayout({
220
+ children,
221
+ }: {
222
+ children: React.ReactNode;
223
+ }) {
224
+ return (
225
+ <html lang="en" className={inter.variable}>
226
+ <body className="min-h-screen bg-white antialiased">
227
+ {children}
228
+ </body>
229
+ </html>
230
+ );
231
+ }
232
+ `;
233
+ }
234
+ /**
235
+ * Generate globals.css
236
+ */
237
+ export function generateWebsiteGlobalsCss() {
238
+ return `@tailwind base;
239
+ @tailwind components;
240
+ @tailwind utilities;
241
+
242
+ @layer base {
243
+ :root {
244
+ --background: 0 0% 100%;
245
+ --foreground: 222.2 84% 4.9%;
246
+ --primary: 199 89% 48%;
247
+ --primary-foreground: 210 40% 98%;
248
+ }
249
+
250
+ body {
251
+ @apply bg-background text-foreground;
252
+ }
253
+ }
254
+
255
+ @layer components {
256
+ .container {
257
+ @apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8;
258
+ }
259
+ }
260
+ `;
261
+ }
262
+ /**
263
+ * Generate landing page.tsx
264
+ */
265
+ export function generateWebsiteLandingPage(projectName) {
266
+ const title = projectName
267
+ .split('-')
268
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
269
+ .join(' ');
270
+ return `import type { Metadata } from 'next';
271
+ import Link from 'next/link';
272
+
273
+ export const metadata: Metadata = {
274
+ title: 'Welcome',
275
+ description: 'Welcome to ${title} - Your modern web application',
276
+ };
277
+
278
+ export default function HomePage() {
279
+ return (
280
+ <main className="flex min-h-screen flex-col">
281
+ {/* Hero Section */}
282
+ <section className="relative overflow-hidden bg-gradient-to-b from-primary-50 to-white py-20 sm:py-32">
283
+ <div className="container">
284
+ <div className="mx-auto max-w-2xl text-center">
285
+ <h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">
286
+ ${title}
287
+ </h1>
288
+ <p className="mt-6 text-lg leading-8 text-gray-600">
289
+ Build something amazing with our modern, scalable platform.
290
+ Get started today and see the difference.
291
+ </p>
292
+ <div className="mt-10 flex items-center justify-center gap-x-6">
293
+ <Link
294
+ href="/pricing"
295
+ className="rounded-md bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
296
+ >
297
+ Get started
298
+ </Link>
299
+ <Link
300
+ href="/docs"
301
+ className="text-sm font-semibold leading-6 text-gray-900 hover:text-primary-600"
302
+ >
303
+ Learn more <span aria-hidden="true">-&gt;</span>
304
+ </Link>
305
+ </div>
306
+ </div>
307
+ </div>
308
+ </section>
309
+
310
+ {/* Features Section */}
311
+ <section className="py-20 sm:py-32">
312
+ <div className="container">
313
+ <div className="mx-auto max-w-2xl text-center">
314
+ <h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
315
+ Everything you need
316
+ </h2>
317
+ <p className="mt-4 text-lg text-gray-600">
318
+ All the features you need to build amazing products.
319
+ </p>
320
+ </div>
321
+ <div className="mx-auto mt-16 max-w-5xl">
322
+ <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
323
+ {[
324
+ {
325
+ title: 'Fast',
326
+ description: 'Optimized for speed and performance.',
327
+ },
328
+ {
329
+ title: 'Secure',
330
+ description: 'Built with security best practices.',
331
+ },
332
+ {
333
+ title: 'Scalable',
334
+ description: 'Grows with your business needs.',
335
+ },
336
+ ].map((feature) => (
337
+ <div
338
+ key={feature.title}
339
+ className="rounded-2xl border border-gray-200 p-8"
340
+ >
341
+ <h3 className="text-lg font-semibold text-gray-900">
342
+ {feature.title}
343
+ </h3>
344
+ <p className="mt-2 text-gray-600">{feature.description}</p>
345
+ </div>
346
+ ))}
347
+ </div>
348
+ </div>
349
+ </div>
350
+ </section>
351
+
352
+ {/* Footer */}
353
+ <footer className="border-t border-gray-200 py-12">
354
+ <div className="container">
355
+ <p className="text-center text-sm text-gray-500">
356
+ &copy; {new Date().getFullYear()} ${title}. All rights reserved.
357
+ </p>
358
+ </div>
359
+ </footer>
360
+ </main>
361
+ );
362
+ }
363
+ `;
364
+ }
365
+ /**
366
+ * Generate pricing page
367
+ */
368
+ export function generateWebsitePricingPage(projectName) {
369
+ const title = projectName
370
+ .split('-')
371
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
372
+ .join(' ');
373
+ return `import type { Metadata } from 'next';
374
+
375
+ export const metadata: Metadata = {
376
+ title: 'Pricing',
377
+ description: 'Choose the perfect plan for your needs - ${title}',
378
+ };
379
+
380
+ const tiers = [
381
+ {
382
+ name: 'Free',
383
+ price: '$0',
384
+ description: 'Perfect for getting started',
385
+ features: ['Up to 3 projects', 'Basic support', 'Community access'],
386
+ cta: 'Get started',
387
+ featured: false,
388
+ },
389
+ {
390
+ name: 'Pro',
391
+ price: '$29',
392
+ description: 'For growing teams',
393
+ features: [
394
+ 'Unlimited projects',
395
+ 'Priority support',
396
+ 'Advanced analytics',
397
+ 'Custom integrations',
398
+ ],
399
+ cta: 'Start free trial',
400
+ featured: true,
401
+ },
402
+ {
403
+ name: 'Enterprise',
404
+ price: 'Custom',
405
+ description: 'For large organizations',
406
+ features: [
407
+ 'Everything in Pro',
408
+ 'Dedicated support',
409
+ 'SLA guarantee',
410
+ 'Custom contracts',
411
+ ],
412
+ cta: 'Contact sales',
413
+ featured: false,
414
+ },
415
+ ];
416
+
417
+ export default function PricingPage() {
418
+ return (
419
+ <main className="py-20 sm:py-32">
420
+ <div className="container">
421
+ <div className="mx-auto max-w-2xl text-center">
422
+ <h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
423
+ Simple, transparent pricing
424
+ </h1>
425
+ <p className="mt-6 text-lg text-gray-600">
426
+ Choose the plan that works best for you.
427
+ </p>
428
+ </div>
429
+
430
+ <div className="mx-auto mt-16 grid max-w-lg grid-cols-1 gap-8 lg:max-w-5xl lg:grid-cols-3">
431
+ {tiers.map((tier) => (
432
+ <div
433
+ key={tier.name}
434
+ className={\`rounded-2xl p-8 \${
435
+ tier.featured
436
+ ? 'bg-primary-600 text-white ring-2 ring-primary-600'
437
+ : 'border border-gray-200 bg-white'
438
+ }\`}
439
+ >
440
+ <h2
441
+ className={\`text-lg font-semibold \${
442
+ tier.featured ? 'text-white' : 'text-gray-900'
443
+ }\`}
444
+ >
445
+ {tier.name}
446
+ </h2>
447
+ <p
448
+ className={\`mt-2 text-sm \${
449
+ tier.featured ? 'text-primary-100' : 'text-gray-600'
450
+ }\`}
451
+ >
452
+ {tier.description}
453
+ </p>
454
+ <p className="mt-6">
455
+ <span
456
+ className={\`text-4xl font-bold \${
457
+ tier.featured ? 'text-white' : 'text-gray-900'
458
+ }\`}
459
+ >
460
+ {tier.price}
461
+ </span>
462
+ {tier.price !== 'Custom' && (
463
+ <span
464
+ className={\`text-sm \${
465
+ tier.featured ? 'text-primary-100' : 'text-gray-600'
466
+ }\`}
467
+ >
468
+ /month
469
+ </span>
470
+ )}
471
+ </p>
472
+ <ul className="mt-8 space-y-4">
473
+ {tier.features.map((feature) => (
474
+ <li
475
+ key={feature}
476
+ className={\`flex text-sm \${
477
+ tier.featured ? 'text-primary-100' : 'text-gray-600'
478
+ }\`}
479
+ >
480
+ <svg
481
+ className={\`h-5 w-5 flex-shrink-0 \${
482
+ tier.featured ? 'text-white' : 'text-primary-600'
483
+ }\`}
484
+ viewBox="0 0 20 20"
485
+ fill="currentColor"
486
+ >
487
+ <path
488
+ fillRule="evenodd"
489
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
490
+ clipRule="evenodd"
491
+ />
492
+ </svg>
493
+ <span className="ml-3">{feature}</span>
494
+ </li>
495
+ ))}
496
+ </ul>
497
+ <button
498
+ className={\`mt-8 w-full rounded-md px-4 py-2 text-sm font-semibold \${
499
+ tier.featured
500
+ ? 'bg-white text-primary-600 hover:bg-primary-50'
501
+ : 'bg-primary-600 text-white hover:bg-primary-500'
502
+ }\`}
503
+ >
504
+ {tier.cta}
505
+ </button>
506
+ </div>
507
+ ))}
508
+ </div>
509
+ </div>
510
+ </main>
511
+ );
512
+ }
513
+ `;
514
+ }
515
+ /**
516
+ * Generate sitemap.ts
517
+ */
518
+ export function generateWebsiteSitemap(projectName) {
519
+ return `import { MetadataRoute } from 'next';
520
+
521
+ export default function sitemap(): MetadataRoute.Sitemap {
522
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
523
+
524
+ return [
525
+ {
526
+ url: baseUrl,
527
+ lastModified: new Date(),
528
+ changeFrequency: 'weekly',
529
+ priority: 1,
530
+ },
531
+ {
532
+ url: \`\${baseUrl}/pricing\`,
533
+ lastModified: new Date(),
534
+ changeFrequency: 'monthly',
535
+ priority: 0.8,
536
+ },
537
+ {
538
+ url: \`\${baseUrl}/docs\`,
539
+ lastModified: new Date(),
540
+ changeFrequency: 'weekly',
541
+ priority: 0.8,
542
+ },
543
+ {
544
+ url: \`\${baseUrl}/blog\`,
545
+ lastModified: new Date(),
546
+ changeFrequency: 'daily',
547
+ priority: 0.7,
548
+ },
549
+ ];
550
+ }
551
+ `;
552
+ }
553
+ /**
554
+ * Generate robots.ts
555
+ */
556
+ export function generateWebsiteRobots(projectName) {
557
+ return `import { MetadataRoute } from 'next';
558
+
559
+ export default function robots(): MetadataRoute.Robots {
560
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
561
+
562
+ return {
563
+ rules: [
564
+ {
565
+ userAgent: '*',
566
+ allow: '/',
567
+ disallow: ['/api/', '/admin/'],
568
+ },
569
+ ],
570
+ sitemap: \`\${baseUrl}/sitemap.xml\`,
571
+ };
572
+ }
573
+ `;
574
+ }
575
+ /**
576
+ * Generate website Dockerfile
577
+ */
578
+ export function generateWebsiteDockerfile() {
579
+ return `# Build stage
580
+ FROM node:20-alpine AS builder
581
+
582
+ WORKDIR /app
583
+
584
+ # Copy package files
585
+ COPY package*.json ./
586
+
587
+ # Install dependencies
588
+ RUN npm ci
589
+
590
+ # Copy source
591
+ COPY . .
592
+
593
+ # Build
594
+ RUN npm run build
595
+
596
+ # Production stage
597
+ FROM node:20-alpine AS runner
598
+
599
+ WORKDIR /app
600
+
601
+ ENV NODE_ENV=production
602
+ ENV NEXT_TELEMETRY_DISABLED=1
603
+
604
+ # Create non-root user
605
+ RUN addgroup --system --gid 1001 nodejs
606
+ RUN adduser --system --uid 1001 nextjs
607
+
608
+ # Copy built assets
609
+ COPY --from=builder /app/public ./public
610
+ COPY --from=builder /app/.next/standalone ./
611
+ COPY --from=builder /app/.next/static ./.next/static
612
+
613
+ USER nextjs
614
+
615
+ EXPOSE 3000
616
+
617
+ ENV PORT=3000
618
+ ENV HOSTNAME="0.0.0.0"
619
+
620
+ CMD ["node", "server.js"]
621
+ `;
622
+ }
623
+ /**
624
+ * Generate website README
625
+ */
626
+ export function generateWebsiteReadme(projectName) {
627
+ const title = projectName
628
+ .split('-')
629
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
630
+ .join(' ');
631
+ return `# ${title} Website
632
+
633
+ Next.js marketing website with SEO optimization.
634
+
635
+ ## Getting Started
636
+
637
+ \`\`\`bash
638
+ # Install dependencies
639
+ npm install
640
+
641
+ # Run development server (port 3001)
642
+ npm run dev
643
+
644
+ # Build for production
645
+ npm run build
646
+
647
+ # Run production server
648
+ npm start
649
+ \`\`\`
650
+
651
+ ## SEO Features
652
+
653
+ - Server-side rendering (SSR)
654
+ - Auto-generated sitemap
655
+ - robots.txt configuration
656
+ - OpenGraph and Twitter meta tags
657
+ - Structured data support
658
+
659
+ ## Project Structure
660
+
661
+ \`\`\`
662
+ src/
663
+ app/
664
+ layout.tsx # Root layout with metadata
665
+ page.tsx # Landing page
666
+ pricing/ # Pricing page
667
+ docs/ # Documentation
668
+ blog/ # Blog
669
+ sitemap.ts # Auto-generated sitemap
670
+ robots.ts # robots.txt config
671
+ components/ # UI components
672
+ lib/ # Utilities
673
+ content/
674
+ blog/ # MDX blog posts
675
+ docs/ # MDX documentation
676
+ \`\`\`
677
+
678
+ ## Development
679
+
680
+ - Port: 3001 (to avoid conflicts with frontend on 5173)
681
+ - API URL: Configure via NEXT_PUBLIC_APP_URL
682
+ `;
683
+ }
684
+ /**
685
+ * Generate website spec JSON
686
+ */
687
+ export function generateWebsiteSpec(projectName) {
688
+ const title = projectName
689
+ .split('-')
690
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
691
+ .join(' ');
692
+ return JSON.stringify({
693
+ version: '1.0',
694
+ brand: {
695
+ name: title,
696
+ tagline: 'Build something amazing',
697
+ colors: {
698
+ primary: '#0ea5e9',
699
+ secondary: '#64748b',
700
+ accent: '#f59e0b',
701
+ background: '#ffffff',
702
+ foreground: '#0f172a',
703
+ },
704
+ typography: {
705
+ headingFont: 'Inter',
706
+ bodyFont: 'Inter',
707
+ },
708
+ },
709
+ seo: {
710
+ title: title,
711
+ description: `${title} - Your modern web application`,
712
+ keywords: [projectName, 'web app', 'nextjs', 'saas'],
713
+ locale: 'en_US',
714
+ },
715
+ pages: [
716
+ { name: 'Home', path: '/', type: 'landing' },
717
+ { name: 'Pricing', path: '/pricing', type: 'pricing' },
718
+ { name: 'Documentation', path: '/docs', type: 'docs' },
719
+ { name: 'Blog', path: '/blog', type: 'blog' },
720
+ ],
721
+ cta: {
722
+ primary: { text: 'Get Started', href: '/pricing' },
723
+ secondary: { text: 'Learn More', href: '/docs' },
724
+ },
725
+ features: {
726
+ analytics: true,
727
+ newsletter: false,
728
+ mdxBlog: true,
729
+ docsSearch: false,
730
+ },
731
+ }, null, 2);
732
+ }
733
+ /**
734
+ * Generate vitest config for website
735
+ */
736
+ export function generateWebsiteVitestConfig() {
737
+ return `import { defineConfig } from 'vitest/config';
738
+ import react from '@vitejs/plugin-react';
739
+ import path from 'path';
740
+
741
+ export default defineConfig({
742
+ plugins: [react()],
743
+ test: {
744
+ environment: 'jsdom',
745
+ include: ['**/*.test.{ts,tsx}'],
746
+ globals: true,
747
+ setupFiles: ['./tests/setup.ts'],
748
+ },
749
+ resolve: {
750
+ alias: {
751
+ '@': path.resolve(__dirname, './src'),
752
+ },
753
+ },
754
+ });
755
+ `;
756
+ }
757
+ /**
758
+ * Generate vitest setup for website
759
+ */
760
+ export function generateWebsiteVitestSetup() {
761
+ return `import '@testing-library/jest-dom';
762
+
763
+ // Mock next/navigation
764
+ vi.mock('next/navigation', () => ({
765
+ useRouter: () => ({
766
+ push: vi.fn(),
767
+ replace: vi.fn(),
768
+ prefetch: vi.fn(),
769
+ }),
770
+ useSearchParams: () => new URLSearchParams(),
771
+ usePathname: () => '/',
772
+ }));
773
+
774
+ // Mock next/image
775
+ vi.mock('next/image', () => ({
776
+ default: (props: Record<string, unknown>) => {
777
+ // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
778
+ return <img {...props} />;
779
+ },
780
+ }));
781
+ `;
782
+ }
783
+ /**
784
+ * Generate sample test for website
785
+ */
786
+ export function generateWebsiteTest(projectName) {
787
+ const title = projectName
788
+ .split('-')
789
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
790
+ .join(' ');
791
+ return `import { describe, it, expect } from 'vitest';
792
+ import { render, screen } from '@testing-library/react';
793
+ import HomePage from '@/app/page';
794
+
795
+ describe('HomePage', () => {
796
+ it('renders the title', () => {
797
+ render(<HomePage />);
798
+ expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('${title}');
799
+ });
800
+
801
+ it('renders the call-to-action buttons', () => {
802
+ render(<HomePage />);
803
+ expect(screen.getByRole('link', { name: /get started/i })).toBeInTheDocument();
804
+ expect(screen.getByRole('link', { name: /learn more/i })).toBeInTheDocument();
805
+ });
806
+
807
+ it('renders feature cards', () => {
808
+ render(<HomePage />);
809
+ expect(screen.getByText('Fast')).toBeInTheDocument();
810
+ expect(screen.getByText('Secure')).toBeInTheDocument();
811
+ expect(screen.getByText('Scalable')).toBeInTheDocument();
812
+ });
813
+ });
814
+ `;
815
+ }
816
+ /**
817
+ * Generate docs page placeholder
818
+ */
819
+ export function generateWebsiteDocsPage() {
820
+ return `import type { Metadata } from 'next';
821
+
822
+ export const metadata: Metadata = {
823
+ title: 'Documentation',
824
+ description: 'Learn how to use our platform with comprehensive documentation.',
825
+ };
826
+
827
+ export default function DocsPage() {
828
+ return (
829
+ <main className="py-20">
830
+ <div className="container">
831
+ <h1 className="text-4xl font-bold text-gray-900">Documentation</h1>
832
+ <p className="mt-4 text-lg text-gray-600">
833
+ Documentation coming soon...
834
+ </p>
835
+ </div>
836
+ </main>
837
+ );
838
+ }
839
+ `;
840
+ }
841
+ /**
842
+ * Generate blog listing page placeholder
843
+ */
844
+ export function generateWebsiteBlogPage() {
845
+ return `import type { Metadata } from 'next';
846
+
847
+ export const metadata: Metadata = {
848
+ title: 'Blog',
849
+ description: 'Latest news, updates, and insights from our team.',
850
+ };
851
+
852
+ export default function BlogPage() {
853
+ return (
854
+ <main className="py-20">
855
+ <div className="container">
856
+ <h1 className="text-4xl font-bold text-gray-900">Blog</h1>
857
+ <p className="mt-4 text-lg text-gray-600">
858
+ Blog posts coming soon...
859
+ </p>
860
+ </div>
861
+ </main>
862
+ );
863
+ }
864
+ `;
865
+ }
866
+ /**
867
+ * Generate Next.js environment declaration
868
+ */
869
+ export function generateWebsiteNextEnv() {
870
+ return `/// <reference types="next" />
871
+ /// <reference types="next/image-types/global" />
872
+
873
+ // NOTE: This file should not be edited
874
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
875
+ `;
876
+ }
877
+ //# sourceMappingURL=website.js.map