popeye-cli 1.2.1 → 1.4.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 (166) hide show
  1. package/.env.example +4 -1
  2. package/CONTRIBUTING.md +10 -0
  3. package/README.md +224 -17
  4. package/dist/adapters/claude.d.ts +3 -2
  5. package/dist/adapters/claude.d.ts.map +1 -1
  6. package/dist/adapters/claude.js +214 -0
  7. package/dist/adapters/claude.js.map +1 -1
  8. package/dist/adapters/gemini.d.ts +2 -2
  9. package/dist/adapters/gemini.d.ts.map +1 -1
  10. package/dist/adapters/grok.d.ts +2 -1
  11. package/dist/adapters/grok.d.ts.map +1 -1
  12. package/dist/adapters/grok.js.map +1 -1
  13. package/dist/adapters/index.d.ts +8 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +12 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/openai.d.ts +2 -2
  18. package/dist/adapters/openai.d.ts.map +1 -1
  19. package/dist/adapters/openai.js.map +1 -1
  20. package/dist/cli/commands/create.d.ts.map +1 -1
  21. package/dist/cli/commands/create.js +25 -5
  22. package/dist/cli/commands/create.js.map +1 -1
  23. package/dist/cli/index.d.ts +1 -0
  24. package/dist/cli/index.d.ts.map +1 -1
  25. package/dist/cli/index.js +5 -2
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/cli/interactive.d.ts.map +1 -1
  28. package/dist/cli/interactive.js +354 -28
  29. package/dist/cli/interactive.js.map +1 -1
  30. package/dist/config/index.d.ts +2 -0
  31. package/dist/config/index.d.ts.map +1 -1
  32. package/dist/config/schema.d.ts +4 -0
  33. package/dist/config/schema.d.ts.map +1 -1
  34. package/dist/config/schema.js +2 -1
  35. package/dist/config/schema.js.map +1 -1
  36. package/dist/generators/all.d.ts +70 -0
  37. package/dist/generators/all.d.ts.map +1 -0
  38. package/dist/generators/all.js +826 -0
  39. package/dist/generators/all.js.map +1 -0
  40. package/dist/generators/fullstack.d.ts +9 -0
  41. package/dist/generators/fullstack.d.ts.map +1 -1
  42. package/dist/generators/fullstack.js.map +1 -1
  43. package/dist/generators/index.d.ts +3 -1
  44. package/dist/generators/index.d.ts.map +1 -1
  45. package/dist/generators/index.js +33 -0
  46. package/dist/generators/index.js.map +1 -1
  47. package/dist/generators/templates/index.d.ts +2 -0
  48. package/dist/generators/templates/index.d.ts.map +1 -1
  49. package/dist/generators/templates/index.js +2 -0
  50. package/dist/generators/templates/index.js.map +1 -1
  51. package/dist/generators/templates/website.d.ts +85 -0
  52. package/dist/generators/templates/website.d.ts.map +1 -0
  53. package/dist/generators/templates/website.js +877 -0
  54. package/dist/generators/templates/website.js.map +1 -0
  55. package/dist/generators/website.d.ts +56 -0
  56. package/dist/generators/website.d.ts.map +1 -0
  57. package/dist/generators/website.js +269 -0
  58. package/dist/generators/website.js.map +1 -0
  59. package/dist/types/consensus.d.ts +18 -23
  60. package/dist/types/consensus.d.ts.map +1 -1
  61. package/dist/types/consensus.js +8 -3
  62. package/dist/types/consensus.js.map +1 -1
  63. package/dist/types/index.d.ts +2 -2
  64. package/dist/types/index.d.ts.map +1 -1
  65. package/dist/types/index.js +2 -2
  66. package/dist/types/index.js.map +1 -1
  67. package/dist/types/project.d.ts +130 -17
  68. package/dist/types/project.d.ts.map +1 -1
  69. package/dist/types/project.js +55 -8
  70. package/dist/types/project.js.map +1 -1
  71. package/dist/types/workflow.d.ts +2 -0
  72. package/dist/types/workflow.d.ts.map +1 -1
  73. package/dist/types/workflow.js +2 -1
  74. package/dist/types/workflow.js.map +1 -1
  75. package/dist/upgrade/context.d.ts +37 -0
  76. package/dist/upgrade/context.d.ts.map +1 -0
  77. package/dist/upgrade/context.js +284 -0
  78. package/dist/upgrade/context.js.map +1 -0
  79. package/dist/upgrade/handlers.d.ts +103 -0
  80. package/dist/upgrade/handlers.d.ts.map +1 -0
  81. package/dist/upgrade/handlers.js +384 -0
  82. package/dist/upgrade/handlers.js.map +1 -0
  83. package/dist/upgrade/index.d.ts +26 -0
  84. package/dist/upgrade/index.d.ts.map +1 -0
  85. package/dist/upgrade/index.js +194 -0
  86. package/dist/upgrade/index.js.map +1 -0
  87. package/dist/upgrade/transitions.d.ts +34 -0
  88. package/dist/upgrade/transitions.d.ts.map +1 -0
  89. package/dist/upgrade/transitions.js +56 -0
  90. package/dist/upgrade/transitions.js.map +1 -0
  91. package/dist/workflow/consensus.d.ts +2 -1
  92. package/dist/workflow/consensus.d.ts.map +1 -1
  93. package/dist/workflow/consensus.js.map +1 -1
  94. package/dist/workflow/index.d.ts +6 -0
  95. package/dist/workflow/index.d.ts.map +1 -1
  96. package/dist/workflow/index.js +8 -0
  97. package/dist/workflow/index.js.map +1 -1
  98. package/dist/workflow/plan-mode.d.ts +3 -3
  99. package/dist/workflow/plan-mode.d.ts.map +1 -1
  100. package/dist/workflow/plan-mode.js +41 -5
  101. package/dist/workflow/plan-mode.js.map +1 -1
  102. package/dist/workflow/plan-parser.d.ts +97 -0
  103. package/dist/workflow/plan-parser.d.ts.map +1 -0
  104. package/dist/workflow/plan-parser.js +235 -0
  105. package/dist/workflow/plan-parser.js.map +1 -0
  106. package/dist/workflow/plan-storage.d.ts +40 -12
  107. package/dist/workflow/plan-storage.d.ts.map +1 -1
  108. package/dist/workflow/plan-storage.js +47 -20
  109. package/dist/workflow/plan-storage.js.map +1 -1
  110. package/dist/workflow/seo-tests.d.ts +43 -0
  111. package/dist/workflow/seo-tests.d.ts.map +1 -0
  112. package/dist/workflow/seo-tests.js +192 -0
  113. package/dist/workflow/seo-tests.js.map +1 -0
  114. package/dist/workflow/separation-guard.d.ts +35 -0
  115. package/dist/workflow/separation-guard.d.ts.map +1 -0
  116. package/dist/workflow/separation-guard.js +154 -0
  117. package/dist/workflow/separation-guard.js.map +1 -0
  118. package/dist/workflow/task-workflow.d.ts.map +1 -1
  119. package/dist/workflow/task-workflow.js +3 -2
  120. package/dist/workflow/task-workflow.js.map +1 -1
  121. package/dist/workflow/test-runner.d.ts.map +1 -1
  122. package/dist/workflow/test-runner.js +128 -0
  123. package/dist/workflow/test-runner.js.map +1 -1
  124. package/dist/workflow/workspace-manager.d.ts +31 -20
  125. package/dist/workflow/workspace-manager.d.ts.map +1 -1
  126. package/dist/workflow/workspace-manager.js +38 -9
  127. package/dist/workflow/workspace-manager.js.map +1 -1
  128. package/package.json +1 -1
  129. package/src/adapters/claude.ts +221 -4
  130. package/src/adapters/gemini.ts +2 -2
  131. package/src/adapters/grok.ts +2 -1
  132. package/src/adapters/index.ts +15 -0
  133. package/src/adapters/openai.ts +2 -2
  134. package/src/cli/commands/create.ts +25 -5
  135. package/src/cli/index.ts +5 -2
  136. package/src/cli/interactive.ts +400 -29
  137. package/src/config/schema.ts +2 -1
  138. package/src/generators/all.ts +897 -0
  139. package/src/generators/fullstack.ts +10 -0
  140. package/src/generators/index.ts +54 -0
  141. package/src/generators/templates/index.ts +2 -0
  142. package/src/generators/templates/website.ts +906 -0
  143. package/src/generators/website.ts +350 -0
  144. package/src/types/consensus.ts +20 -8
  145. package/src/types/index.ts +35 -0
  146. package/src/types/project.ts +157 -11
  147. package/src/types/workflow.ts +2 -1
  148. package/src/upgrade/context.ts +332 -0
  149. package/src/upgrade/handlers.ts +477 -0
  150. package/src/upgrade/index.ts +244 -0
  151. package/src/upgrade/transitions.ts +80 -0
  152. package/src/workflow/consensus.ts +3 -2
  153. package/src/workflow/index.ts +8 -0
  154. package/src/workflow/plan-mode.ts +44 -10
  155. package/src/workflow/plan-parser.ts +317 -0
  156. package/src/workflow/plan-storage.ts +69 -30
  157. package/src/workflow/seo-tests.ts +246 -0
  158. package/src/workflow/separation-guard.ts +200 -0
  159. package/src/workflow/task-workflow.ts +3 -2
  160. package/src/workflow/test-runner.ts +149 -0
  161. package/src/workflow/workspace-manager.ts +68 -31
  162. package/tests/cli/model-command.test.ts +93 -0
  163. package/tests/types/project.test.ts +90 -15
  164. package/tests/types/workflow-schema.test.ts +59 -0
  165. package/tests/upgrade/context.test.ts +211 -0
  166. package/tests/upgrade/transitions.test.ts +85 -0
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Website project generator
3
+ * Creates SEO-ready Next.js marketing website
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { ProjectSpec } from '../types/project.js';
9
+ import {
10
+ generateWebsitePackageJson,
11
+ generateNextConfig,
12
+ generateWebsiteTsconfig,
13
+ generateWebsiteTailwindConfig,
14
+ generateWebsitePostcssConfig,
15
+ generateWebsiteLayout,
16
+ generateWebsiteGlobalsCss,
17
+ generateWebsiteLandingPage,
18
+ generateWebsitePricingPage,
19
+ generateWebsiteSitemap,
20
+ generateWebsiteRobots,
21
+ generateWebsiteDockerfile,
22
+ generateWebsiteReadme,
23
+ generateWebsiteSpec,
24
+ generateWebsiteVitestConfig,
25
+ generateWebsiteVitestSetup,
26
+ generateWebsiteTest,
27
+ generateWebsiteDocsPage,
28
+ generateWebsiteBlogPage,
29
+ generateWebsiteNextEnv,
30
+ } from './templates/website.js';
31
+
32
+ /**
33
+ * Project generation result
34
+ */
35
+ export interface GenerationResult {
36
+ success: boolean;
37
+ projectDir: string;
38
+ filesCreated: string[];
39
+ error?: string;
40
+ }
41
+
42
+ /**
43
+ * Website generator options for workspace/monorepo support
44
+ */
45
+ export interface WebsiteGeneratorOptions {
46
+ /** Base directory for project (defaults to outputDir/projectName) */
47
+ baseDir?: string;
48
+ /** Override auto-derived package name */
49
+ packageName?: string;
50
+ /** Adjust paths for monorepo structure */
51
+ workspaceMode?: boolean;
52
+ /** Skip Docker files (fullstack uses root docker-compose) */
53
+ skipDocker?: boolean;
54
+ /** Skip README (fullstack has root README) */
55
+ skipReadme?: boolean;
56
+ }
57
+
58
+ /**
59
+ * Create a directory if it doesn't exist
60
+ */
61
+ async function ensureDir(dirPath: string): Promise<void> {
62
+ await fs.mkdir(dirPath, { recursive: true });
63
+ }
64
+
65
+ /**
66
+ * Write a file with content
67
+ */
68
+ async function writeFile(filePath: string, content: string): Promise<void> {
69
+ await fs.writeFile(filePath, content, 'utf-8');
70
+ }
71
+
72
+ /**
73
+ * Generate a complete Next.js website project
74
+ *
75
+ * @param spec - Project specification
76
+ * @param outputDir - Output directory
77
+ * @param options - Generator options for workspace/monorepo support
78
+ * @returns Generation result
79
+ */
80
+ export async function generateWebsiteProject(
81
+ spec: ProjectSpec,
82
+ outputDir: string,
83
+ options: WebsiteGeneratorOptions = {}
84
+ ): Promise<GenerationResult> {
85
+ const {
86
+ baseDir,
87
+ // packageName reserved for future use
88
+ workspaceMode = false,
89
+ skipDocker = false,
90
+ skipReadme = false,
91
+ } = options;
92
+
93
+ const projectName = spec.name || 'my-project';
94
+
95
+ // In workspace mode with baseDir, use it directly; otherwise create subdirectory
96
+ const projectDir = baseDir || path.join(outputDir, projectName);
97
+ const filesCreated: string[] = [];
98
+
99
+ try {
100
+ // Create project directory structure
101
+ await ensureDir(projectDir);
102
+ await ensureDir(path.join(projectDir, 'src', 'app'));
103
+ await ensureDir(path.join(projectDir, 'src', 'app', 'pricing'));
104
+ await ensureDir(path.join(projectDir, 'src', 'app', 'docs'));
105
+ await ensureDir(path.join(projectDir, 'src', 'app', 'blog'));
106
+ await ensureDir(path.join(projectDir, 'src', 'components'));
107
+ await ensureDir(path.join(projectDir, 'src', 'lib'));
108
+ await ensureDir(path.join(projectDir, 'content', 'blog'));
109
+ await ensureDir(path.join(projectDir, 'content', 'docs'));
110
+ await ensureDir(path.join(projectDir, 'public'));
111
+ await ensureDir(path.join(projectDir, 'tests'));
112
+
113
+ // Only create .popeye dir in standalone mode
114
+ if (!workspaceMode) {
115
+ await ensureDir(path.join(projectDir, '.popeye'));
116
+ }
117
+
118
+ // Generate and write files
119
+ const files: Array<{ path: string; content: string }> = [
120
+ // Config files
121
+ {
122
+ path: path.join(projectDir, 'package.json'),
123
+ content: generateWebsitePackageJson(projectName),
124
+ },
125
+ {
126
+ path: path.join(projectDir, 'next.config.mjs'),
127
+ content: generateNextConfig(),
128
+ },
129
+ {
130
+ path: path.join(projectDir, 'tsconfig.json'),
131
+ content: generateWebsiteTsconfig(),
132
+ },
133
+ {
134
+ path: path.join(projectDir, 'tailwind.config.ts'),
135
+ content: generateWebsiteTailwindConfig(),
136
+ },
137
+ {
138
+ path: path.join(projectDir, 'postcss.config.js'),
139
+ content: generateWebsitePostcssConfig(),
140
+ },
141
+ {
142
+ path: path.join(projectDir, 'vitest.config.ts'),
143
+ content: generateWebsiteVitestConfig(),
144
+ },
145
+ {
146
+ path: path.join(projectDir, 'next-env.d.ts'),
147
+ content: generateWebsiteNextEnv(),
148
+ },
149
+
150
+ // App Router files
151
+ {
152
+ path: path.join(projectDir, 'src', 'app', 'layout.tsx'),
153
+ content: generateWebsiteLayout(projectName),
154
+ },
155
+ {
156
+ path: path.join(projectDir, 'src', 'app', 'globals.css'),
157
+ content: generateWebsiteGlobalsCss(),
158
+ },
159
+ {
160
+ path: path.join(projectDir, 'src', 'app', 'page.tsx'),
161
+ content: generateWebsiteLandingPage(projectName),
162
+ },
163
+ {
164
+ path: path.join(projectDir, 'src', 'app', 'pricing', 'page.tsx'),
165
+ content: generateWebsitePricingPage(projectName),
166
+ },
167
+ {
168
+ path: path.join(projectDir, 'src', 'app', 'docs', 'page.tsx'),
169
+ content: generateWebsiteDocsPage(),
170
+ },
171
+ {
172
+ path: path.join(projectDir, 'src', 'app', 'blog', 'page.tsx'),
173
+ content: generateWebsiteBlogPage(),
174
+ },
175
+
176
+ // SEO files
177
+ {
178
+ path: path.join(projectDir, 'src', 'app', 'sitemap.ts'),
179
+ content: generateWebsiteSitemap(projectName),
180
+ },
181
+ {
182
+ path: path.join(projectDir, 'src', 'app', 'robots.ts'),
183
+ content: generateWebsiteRobots(projectName),
184
+ },
185
+
186
+ // Test files
187
+ {
188
+ path: path.join(projectDir, 'tests', 'setup.ts'),
189
+ content: generateWebsiteVitestSetup(),
190
+ },
191
+ {
192
+ path: path.join(projectDir, 'tests', 'page.test.tsx'),
193
+ content: generateWebsiteTest(projectName),
194
+ },
195
+
196
+ // Placeholder files
197
+ {
198
+ path: path.join(projectDir, 'public', '.gitkeep'),
199
+ content: '',
200
+ },
201
+ {
202
+ path: path.join(projectDir, 'content', 'blog', '.gitkeep'),
203
+ content: '',
204
+ },
205
+ {
206
+ path: path.join(projectDir, 'content', 'docs', '.gitkeep'),
207
+ content: '',
208
+ },
209
+ {
210
+ path: path.join(projectDir, 'src', 'components', '.gitkeep'),
211
+ content: '',
212
+ },
213
+ {
214
+ path: path.join(projectDir, 'src', 'lib', '.gitkeep'),
215
+ content: '',
216
+ },
217
+
218
+ // Environment
219
+ {
220
+ path: path.join(projectDir, '.env.example'),
221
+ content: 'NEXT_PUBLIC_SITE_URL=http://localhost:3001\nNEXT_PUBLIC_APP_URL=http://localhost:3000\n',
222
+ },
223
+ {
224
+ path: path.join(projectDir, '.gitignore'),
225
+ content:
226
+ 'node_modules/\n.next/\nout/\n.env\n.env.local\n.env.*.local\ncoverage/\n*.log\n.DS_Store\n',
227
+ },
228
+ ];
229
+
230
+ // Add website spec in standalone mode
231
+ if (!workspaceMode) {
232
+ files.push({
233
+ path: path.join(projectDir, '.popeye', 'website-spec.json'),
234
+ content: generateWebsiteSpec(projectName),
235
+ });
236
+ }
237
+
238
+ // Add README if not skipped
239
+ if (!skipReadme) {
240
+ files.push({
241
+ path: path.join(projectDir, 'README.md'),
242
+ content: generateWebsiteReadme(projectName),
243
+ });
244
+ }
245
+
246
+ // Add Docker files if not skipped
247
+ if (!skipDocker) {
248
+ files.push({
249
+ path: path.join(projectDir, 'Dockerfile'),
250
+ content: generateWebsiteDockerfile(),
251
+ });
252
+ }
253
+
254
+ // Write all files
255
+ for (const file of files) {
256
+ await writeFile(file.path, file.content);
257
+ filesCreated.push(file.path);
258
+ }
259
+
260
+ return {
261
+ success: true,
262
+ projectDir,
263
+ filesCreated,
264
+ };
265
+ } catch (error) {
266
+ return {
267
+ success: false,
268
+ projectDir,
269
+ filesCreated,
270
+ error: error instanceof Error ? error.message : 'Unknown error',
271
+ };
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Get the list of files that would be generated for a website project
277
+ *
278
+ * @param _projectName - Project name
279
+ * @returns List of relative file paths
280
+ */
281
+ export function getWebsiteProjectFiles(_projectName: string): string[] {
282
+ return [
283
+ // Config
284
+ 'package.json',
285
+ 'next.config.mjs',
286
+ 'tsconfig.json',
287
+ 'tailwind.config.ts',
288
+ 'postcss.config.js',
289
+ 'vitest.config.ts',
290
+ 'next-env.d.ts',
291
+ '.gitignore',
292
+ '.env.example',
293
+ 'README.md',
294
+ 'Dockerfile',
295
+ // App Router
296
+ 'src/app/layout.tsx',
297
+ 'src/app/globals.css',
298
+ 'src/app/page.tsx',
299
+ 'src/app/pricing/page.tsx',
300
+ 'src/app/docs/page.tsx',
301
+ 'src/app/blog/page.tsx',
302
+ 'src/app/sitemap.ts',
303
+ 'src/app/robots.ts',
304
+ // Tests
305
+ 'tests/setup.ts',
306
+ 'tests/page.test.tsx',
307
+ // Content
308
+ 'content/blog/.gitkeep',
309
+ 'content/docs/.gitkeep',
310
+ // Spec
311
+ '.popeye/website-spec.json',
312
+ ];
313
+ }
314
+
315
+ /**
316
+ * Validate a website project structure
317
+ *
318
+ * @param projectDir - Project directory
319
+ * @returns Validation result
320
+ */
321
+ export async function validateWebsiteProject(projectDir: string): Promise<{
322
+ valid: boolean;
323
+ missingFiles: string[];
324
+ }> {
325
+ const missingFiles: string[] = [];
326
+
327
+ const requiredFiles = [
328
+ 'package.json',
329
+ 'next.config.mjs',
330
+ 'tsconfig.json',
331
+ 'src/app/layout.tsx',
332
+ 'src/app/page.tsx',
333
+ 'src/app/sitemap.ts',
334
+ 'src/app/robots.ts',
335
+ ];
336
+
337
+ for (const file of requiredFiles) {
338
+ const filePath = path.join(projectDir, file);
339
+ try {
340
+ await fs.access(filePath);
341
+ } catch {
342
+ missingFiles.push(file);
343
+ }
344
+ }
345
+
346
+ return {
347
+ valid: missingFiles.length === 0,
348
+ missingFiles,
349
+ };
350
+ }
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { z } from 'zod';
7
7
  import type { OpenAIModel } from './project.js';
8
+ import { OpenAIModelSchema } from './project.js';
8
9
 
9
10
  /**
10
11
  * Supported AI providers for reviews and arbitration
@@ -12,9 +13,9 @@ import type { OpenAIModel } from './project.js';
12
13
  export type AIProvider = 'openai' | 'gemini' | 'grok';
13
14
 
14
15
  /**
15
- * Supported Gemini models
16
+ * Gemini model type - flexible string to support new models
16
17
  */
17
- export type GeminiModel = 'gemini-2.0-flash' | 'gemini-1.5-pro' | 'gemini-1.5-flash';
18
+ export type GeminiModel = string;
18
19
 
19
20
  /**
20
21
  * Grok model type (flexible string - API evolves fast)
@@ -115,9 +116,14 @@ export const DEFAULT_CONSENSUS_CONFIG: Omit<ConsensusConfig, 'openaiKey' | 'gemi
115
116
  export const AIProviderSchema = z.enum(['openai', 'gemini', 'grok']);
116
117
 
117
118
  /**
118
- * Zod schema for Gemini model
119
+ * Known Gemini models (used for suggestions and display, not strict validation)
119
120
  */
120
- export const GeminiModelSchema = z.enum(['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash']);
121
+ export const KNOWN_GEMINI_MODELS = ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'] as const;
122
+
123
+ /**
124
+ * Zod schema for Gemini model - accepts any non-empty string to support new models
125
+ */
126
+ export const GeminiModelSchema = z.string().min(1, 'Model name must not be empty');
121
127
 
122
128
  /**
123
129
  * Zod schema for Grok model (flexible string)
@@ -133,7 +139,7 @@ export const ConsensusConfigSchema = z.object({
133
139
  openaiKey: z.string().optional(),
134
140
  geminiKey: z.string().optional(),
135
141
  grokKey: z.string().optional(),
136
- openaiModel: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini']),
142
+ openaiModel: OpenAIModelSchema,
137
143
  geminiModel: GeminiModelSchema.default('gemini-2.0-flash'),
138
144
  grokModel: GrokModelSchema.default(DEFAULT_GROK_MODEL),
139
145
  reviewer: AIProviderSchema.default('openai'),
@@ -195,9 +201,9 @@ export interface EscalationDetails {
195
201
  }
196
202
 
197
203
  /**
198
- * App target for fullstack reviews
204
+ * App target for fullstack/all reviews
199
205
  */
200
- export type ReviewAppTarget = 'frontend' | 'backend' | 'unified';
206
+ export type ReviewAppTarget = 'frontend' | 'backend' | 'website' | 'unified';
201
207
 
202
208
  /**
203
209
  * Tagged concern/recommendation with app context
@@ -208,11 +214,12 @@ export interface TaggedItem {
208
214
  }
209
215
 
210
216
  /**
211
- * Per-app consensus scores for fullstack projects
217
+ * Per-app consensus scores for fullstack/all projects
212
218
  */
213
219
  export interface AppConsensusScores {
214
220
  frontend?: number;
215
221
  backend?: number;
222
+ website?: number;
216
223
  unified: number; // Combined/overall score
217
224
  }
218
225
 
@@ -253,6 +260,7 @@ export interface FullstackConsensusIteration extends ConsensusIteration {
253
260
  appApproved?: {
254
261
  frontend?: boolean;
255
262
  backend?: boolean;
263
+ website?: boolean;
256
264
  unified: boolean;
257
265
  };
258
266
  }
@@ -295,6 +303,10 @@ export interface ConsensusTrackingRecord {
295
303
  backendApproved?: boolean;
296
304
  backendIterations?: number;
297
305
 
306
+ websiteScore?: number;
307
+ websiteApproved?: boolean;
308
+ websiteIterations?: number;
309
+
298
310
  /** All corrections/revisions made */
299
311
  corrections: CorrectionRecord[];
300
312
 
@@ -7,13 +7,31 @@
7
7
  export {
8
8
  OutputLanguageSchema,
9
9
  OpenAIModelSchema,
10
+ KNOWN_OPENAI_MODELS,
10
11
  ProjectSpecSchema,
11
12
  OPENAI_MODELS,
13
+ languageToApps,
14
+ hasApp,
12
15
  type OutputLanguage,
13
16
  type OpenAIModel,
14
17
  type ProjectSpec,
15
18
  type GeneratedProject,
16
19
  type GenerationOptions,
20
+ type AppType,
21
+ type WorkspaceApp,
22
+ type WorkspaceAppCommands,
23
+ type WorkspaceAppDocker,
24
+ type WorkspaceShared,
25
+ type WorkspaceCommands,
26
+ type WorkspaceDocker,
27
+ type WorkspaceConfig,
28
+ type WebsiteSpec,
29
+ type WebsiteBrandColors,
30
+ type WebsiteTypography,
31
+ type WebsiteSeo,
32
+ type WebsitePage,
33
+ type WebsiteCta,
34
+ type WebsiteFeatures,
17
35
  } from './project.js';
18
36
 
19
37
  // Workflow types
@@ -37,12 +55,29 @@ export {
37
55
  export {
38
56
  ConsensusConfigSchema,
39
57
  DEFAULT_CONSENSUS_CONFIG,
58
+ DEFAULT_GROK_MODEL,
59
+ AIProviderSchema,
60
+ KNOWN_GEMINI_MODELS,
61
+ GeminiModelSchema,
62
+ GrokModelSchema,
63
+ type AIProvider,
64
+ type GeminiModel,
65
+ type GrokModel,
40
66
  type ConsensusResult,
41
67
  type ConsensusIteration,
42
68
  type ConsensusConfig,
43
69
  type PlanDocument,
44
70
  type ConsensusRequest,
45
71
  type EscalationDetails,
72
+ type ArbitrationResult,
73
+ type ReviewAppTarget,
74
+ type TaggedItem,
75
+ type AppConsensusScores,
76
+ type AppFeedback,
77
+ type FullstackConsensusResult,
78
+ type FullstackConsensusIteration,
79
+ type CorrectionRecord,
80
+ type ConsensusTrackingRecord,
46
81
  } from './consensus.js';
47
82
 
48
83
  // CLI types
@@ -8,9 +8,66 @@ import { z } from 'zod';
8
8
  /**
9
9
  * Supported output languages for generated projects
10
10
  */
11
- export const OutputLanguageSchema = z.enum(['python', 'typescript', 'fullstack']);
11
+ /**
12
+ * Supported output languages for generated projects
13
+ * - python: Backend only (FastAPI)
14
+ * - typescript: Frontend only (React/Vite)
15
+ * - fullstack: FE + BE
16
+ * - website: Website only (Next.js SSG/SSR)
17
+ * - all: FE + BE + Website (everything)
18
+ */
19
+ export const OutputLanguageSchema = z.enum([
20
+ 'python',
21
+ 'typescript',
22
+ 'fullstack',
23
+ 'website',
24
+ 'all',
25
+ ]);
12
26
  export type OutputLanguage = z.infer<typeof OutputLanguageSchema>;
13
27
 
28
+ /**
29
+ * App types that can be generated
30
+ */
31
+ export type AppType = 'frontend' | 'backend' | 'website';
32
+
33
+ /**
34
+ * Maps a language to the apps it will generate
35
+ *
36
+ * @param language - The output language
37
+ * @returns Array of app types to generate
38
+ */
39
+ export function languageToApps(language: OutputLanguage): AppType[] {
40
+ const mapping: Record<OutputLanguage, AppType[]> = {
41
+ python: ['backend'],
42
+ typescript: ['frontend'],
43
+ fullstack: ['frontend', 'backend'],
44
+ website: ['website'],
45
+ all: ['frontend', 'backend', 'website'],
46
+ };
47
+ return mapping[language];
48
+ }
49
+
50
+ /**
51
+ * Checks if a language generates a specific app type
52
+ *
53
+ * @param language - The output language
54
+ * @param app - The app type to check
55
+ * @returns True if the language generates the app
56
+ */
57
+ export function hasApp(language: OutputLanguage, app: AppType): boolean {
58
+ return languageToApps(language).includes(app);
59
+ }
60
+
61
+ /**
62
+ * Checks if a language is a workspace type (multi-app monorepo)
63
+ *
64
+ * @param language - The output language
65
+ * @returns True if the language uses workspace/monorepo structure
66
+ */
67
+ export function isWorkspace(language: OutputLanguage): boolean {
68
+ return language === 'fullstack' || language === 'all';
69
+ }
70
+
14
71
  /**
15
72
  * Commands configuration for a workspace app
16
73
  */
@@ -56,6 +113,10 @@ export interface WorkspaceShared {
56
113
  contracts?: string;
57
114
  /** Generator command for FE client from OpenAPI */
58
115
  contractsGenerator?: string;
116
+ /** Shared UI components package path */
117
+ ui?: string;
118
+ /** Design tokens package path */
119
+ designTokens?: string;
59
120
  }
60
121
 
61
122
  /**
@@ -78,13 +139,14 @@ export interface WorkspaceDocker {
78
139
  }
79
140
 
80
141
  /**
81
- * Workspace configuration for fullstack projects
142
+ * Workspace configuration for fullstack/website/all projects
82
143
  */
83
144
  export interface WorkspaceConfig {
84
145
  version: '1.0';
85
146
  apps: {
86
147
  frontend?: WorkspaceApp;
87
148
  backend?: WorkspaceApp;
149
+ website?: WorkspaceApp;
88
150
  };
89
151
  shared?: WorkspaceShared;
90
152
  /** Repo-level commands that orchestrate across apps */
@@ -93,16 +155,100 @@ export interface WorkspaceConfig {
93
155
  }
94
156
 
95
157
  /**
96
- * Supported OpenAI models for consensus reviews
158
+ * Brand colors for website design
97
159
  */
98
- export const OpenAIModelSchema = z.enum([
99
- 'gpt-4o',
100
- 'gpt-4o-mini',
101
- 'gpt-4-turbo',
102
- 'o1-preview',
103
- 'o1-mini',
104
- ]);
105
- export type OpenAIModel = z.infer<typeof OpenAIModelSchema>;
160
+ export interface WebsiteBrandColors {
161
+ primary: string;
162
+ secondary: string;
163
+ accent: string;
164
+ background: string;
165
+ foreground: string;
166
+ }
167
+
168
+ /**
169
+ * Typography configuration for website
170
+ */
171
+ export interface WebsiteTypography {
172
+ headingFont: string;
173
+ bodyFont: string;
174
+ }
175
+
176
+ /**
177
+ * SEO configuration for website
178
+ */
179
+ export interface WebsiteSeo {
180
+ title: string;
181
+ description: string;
182
+ keywords: string[];
183
+ ogImage?: string;
184
+ twitterHandle?: string;
185
+ locale: string;
186
+ }
187
+
188
+ /**
189
+ * Page configuration for website
190
+ */
191
+ export interface WebsitePage {
192
+ name: string;
193
+ path: string;
194
+ type: 'landing' | 'pricing' | 'docs' | 'blog' | 'changelog' | 'legal';
195
+ seo?: {
196
+ title?: string;
197
+ description?: string;
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Call-to-action configuration
203
+ */
204
+ export interface WebsiteCta {
205
+ primary: {
206
+ text: string;
207
+ href: string;
208
+ };
209
+ secondary?: {
210
+ text: string;
211
+ href: string;
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Feature flags for website
217
+ */
218
+ export interface WebsiteFeatures {
219
+ analytics?: boolean;
220
+ newsletter?: boolean;
221
+ mdxBlog?: boolean;
222
+ docsSearch?: boolean;
223
+ }
224
+
225
+ /**
226
+ * Website specification for Next.js marketing sites
227
+ */
228
+ export interface WebsiteSpec {
229
+ version: '1.0';
230
+ brand: {
231
+ name: string;
232
+ tagline?: string;
233
+ colors: WebsiteBrandColors;
234
+ typography: WebsiteTypography;
235
+ };
236
+ seo: WebsiteSeo;
237
+ pages: WebsitePage[];
238
+ cta: WebsiteCta;
239
+ features?: WebsiteFeatures;
240
+ }
241
+
242
+ /**
243
+ * Known OpenAI models (used for suggestions and display, not strict validation)
244
+ */
245
+ export const KNOWN_OPENAI_MODELS = ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'] as const;
246
+
247
+ /**
248
+ * OpenAI model schema - accepts any non-empty string to support new models
249
+ */
250
+ export const OpenAIModelSchema = z.string().min(1, 'Model name must not be empty');
251
+ export type OpenAIModel = string;
106
252
 
107
253
  /**
108
254
  * Project specification provided by user