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,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
+ }
@@ -195,9 +195,9 @@ export interface EscalationDetails {
195
195
  }
196
196
 
197
197
  /**
198
- * App target for fullstack reviews
198
+ * App target for fullstack/all reviews
199
199
  */
200
- export type ReviewAppTarget = 'frontend' | 'backend' | 'unified';
200
+ export type ReviewAppTarget = 'frontend' | 'backend' | 'website' | 'unified';
201
201
 
202
202
  /**
203
203
  * Tagged concern/recommendation with app context
@@ -208,11 +208,12 @@ export interface TaggedItem {
208
208
  }
209
209
 
210
210
  /**
211
- * Per-app consensus scores for fullstack projects
211
+ * Per-app consensus scores for fullstack/all projects
212
212
  */
213
213
  export interface AppConsensusScores {
214
214
  frontend?: number;
215
215
  backend?: number;
216
+ website?: number;
216
217
  unified: number; // Combined/overall score
217
218
  }
218
219
 
@@ -253,6 +254,7 @@ export interface FullstackConsensusIteration extends ConsensusIteration {
253
254
  appApproved?: {
254
255
  frontend?: boolean;
255
256
  backend?: boolean;
257
+ website?: boolean;
256
258
  unified: boolean;
257
259
  };
258
260
  }
@@ -295,6 +297,10 @@ export interface ConsensusTrackingRecord {
295
297
  backendApproved?: boolean;
296
298
  backendIterations?: number;
297
299
 
300
+ websiteScore?: number;
301
+ websiteApproved?: boolean;
302
+ websiteIterations?: number;
303
+
298
304
  /** All corrections/revisions made */
299
305
  corrections: CorrectionRecord[];
300
306
 
@@ -9,11 +9,28 @@ export {
9
9
  OpenAIModelSchema,
10
10
  ProjectSpecSchema,
11
11
  OPENAI_MODELS,
12
+ languageToApps,
13
+ hasApp,
12
14
  type OutputLanguage,
13
15
  type OpenAIModel,
14
16
  type ProjectSpec,
15
17
  type GeneratedProject,
16
18
  type GenerationOptions,
19
+ type AppType,
20
+ type WorkspaceApp,
21
+ type WorkspaceAppCommands,
22
+ type WorkspaceAppDocker,
23
+ type WorkspaceShared,
24
+ type WorkspaceCommands,
25
+ type WorkspaceDocker,
26
+ type WorkspaceConfig,
27
+ type WebsiteSpec,
28
+ type WebsiteBrandColors,
29
+ type WebsiteTypography,
30
+ type WebsiteSeo,
31
+ type WebsitePage,
32
+ type WebsiteCta,
33
+ type WebsiteFeatures,
17
34
  } from './project.js';
18
35
 
19
36
  // Workflow types
@@ -37,12 +54,28 @@ export {
37
54
  export {
38
55
  ConsensusConfigSchema,
39
56
  DEFAULT_CONSENSUS_CONFIG,
57
+ DEFAULT_GROK_MODEL,
58
+ AIProviderSchema,
59
+ GeminiModelSchema,
60
+ GrokModelSchema,
61
+ type AIProvider,
62
+ type GeminiModel,
63
+ type GrokModel,
40
64
  type ConsensusResult,
41
65
  type ConsensusIteration,
42
66
  type ConsensusConfig,
43
67
  type PlanDocument,
44
68
  type ConsensusRequest,
45
69
  type EscalationDetails,
70
+ type ArbitrationResult,
71
+ type ReviewAppTarget,
72
+ type TaggedItem,
73
+ type AppConsensusScores,
74
+ type AppFeedback,
75
+ type FullstackConsensusResult,
76
+ type FullstackConsensusIteration,
77
+ type CorrectionRecord,
78
+ type ConsensusTrackingRecord,
46
79
  } from './consensus.js';
47
80
 
48
81
  // CLI types
@@ -8,9 +8,56 @@ 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
+
14
61
  /**
15
62
  * Commands configuration for a workspace app
16
63
  */
@@ -56,6 +103,10 @@ export interface WorkspaceShared {
56
103
  contracts?: string;
57
104
  /** Generator command for FE client from OpenAPI */
58
105
  contractsGenerator?: string;
106
+ /** Shared UI components package path */
107
+ ui?: string;
108
+ /** Design tokens package path */
109
+ designTokens?: string;
59
110
  }
60
111
 
61
112
  /**
@@ -78,13 +129,14 @@ export interface WorkspaceDocker {
78
129
  }
79
130
 
80
131
  /**
81
- * Workspace configuration for fullstack projects
132
+ * Workspace configuration for fullstack/website/all projects
82
133
  */
83
134
  export interface WorkspaceConfig {
84
135
  version: '1.0';
85
136
  apps: {
86
137
  frontend?: WorkspaceApp;
87
138
  backend?: WorkspaceApp;
139
+ website?: WorkspaceApp;
88
140
  };
89
141
  shared?: WorkspaceShared;
90
142
  /** Repo-level commands that orchestrate across apps */
@@ -92,6 +144,91 @@ export interface WorkspaceConfig {
92
144
  docker: WorkspaceDocker;
93
145
  }
94
146
 
147
+ /**
148
+ * Brand colors for website design
149
+ */
150
+ export interface WebsiteBrandColors {
151
+ primary: string;
152
+ secondary: string;
153
+ accent: string;
154
+ background: string;
155
+ foreground: string;
156
+ }
157
+
158
+ /**
159
+ * Typography configuration for website
160
+ */
161
+ export interface WebsiteTypography {
162
+ headingFont: string;
163
+ bodyFont: string;
164
+ }
165
+
166
+ /**
167
+ * SEO configuration for website
168
+ */
169
+ export interface WebsiteSeo {
170
+ title: string;
171
+ description: string;
172
+ keywords: string[];
173
+ ogImage?: string;
174
+ twitterHandle?: string;
175
+ locale: string;
176
+ }
177
+
178
+ /**
179
+ * Page configuration for website
180
+ */
181
+ export interface WebsitePage {
182
+ name: string;
183
+ path: string;
184
+ type: 'landing' | 'pricing' | 'docs' | 'blog' | 'changelog' | 'legal';
185
+ seo?: {
186
+ title?: string;
187
+ description?: string;
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Call-to-action configuration
193
+ */
194
+ export interface WebsiteCta {
195
+ primary: {
196
+ text: string;
197
+ href: string;
198
+ };
199
+ secondary?: {
200
+ text: string;
201
+ href: string;
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Feature flags for website
207
+ */
208
+ export interface WebsiteFeatures {
209
+ analytics?: boolean;
210
+ newsletter?: boolean;
211
+ mdxBlog?: boolean;
212
+ docsSearch?: boolean;
213
+ }
214
+
215
+ /**
216
+ * Website specification for Next.js marketing sites
217
+ */
218
+ export interface WebsiteSpec {
219
+ version: '1.0';
220
+ brand: {
221
+ name: string;
222
+ tagline?: string;
223
+ colors: WebsiteBrandColors;
224
+ typography: WebsiteTypography;
225
+ };
226
+ seo: WebsiteSeo;
227
+ pages: WebsitePage[];
228
+ cta: WebsiteCta;
229
+ features?: WebsiteFeatures;
230
+ }
231
+
95
232
  /**
96
233
  * Supported OpenAI models for consensus reviews
97
234
  */
@@ -16,13 +16,13 @@ export type WorkflowPhase = z.infer<typeof WorkflowPhaseSchema>;
16
16
  /**
17
17
  * Project status
18
18
  */
19
- export const ProjectStatusSchema = z.enum(['pending', 'in-progress', 'complete', 'failed']);
19
+ export const ProjectStatusSchema = z.enum(['pending', 'in-progress', 'complete', 'failed', 'paused']);
20
20
  export type ProjectStatus = z.infer<typeof ProjectStatusSchema>;
21
21
 
22
22
  /**
23
23
  * Status of a task or milestone
24
24
  */
25
- export const TaskStatusSchema = z.enum(['pending', 'in-progress', 'complete', 'failed']);
25
+ export const TaskStatusSchema = z.enum(['pending', 'in-progress', 'complete', 'failed', 'paused']);
26
26
  export type TaskStatus = z.infer<typeof TaskStatusSchema>;
27
27
 
28
28
  /**
@@ -14,6 +14,7 @@ import type {
14
14
  AppConsensusScores,
15
15
  CorrectionRecord,
16
16
  } from '../types/consensus.js';
17
+ import type { OutputLanguage } from '../types/project.js';
17
18
  import { DEFAULT_CONSENSUS_CONFIG } from '../types/consensus.js';
18
19
  import { requestConsensus as requestOpenAIConsensus } from '../adapters/openai.js';
19
20
  import { requestConsensus as requestGeminiConsensus, requestArbitration as requestGeminiArbitration } from '../adapters/gemini.js';
@@ -36,7 +37,7 @@ export interface ConsensusOptions {
36
37
  /** Whether this is a fullstack project (enables per-app tracking) */
37
38
  isFullstack?: boolean;
38
39
  /** Project language for revision prompts */
39
- language?: 'python' | 'typescript' | 'fullstack';
40
+ language?: OutputLanguage;
40
41
  onIteration?: (iteration: number, result: ConsensusResult) => void;
41
42
  onRevision?: (iteration: number, revisedPlan: string) => void;
42
43
  onConcerns?: (concerns: string[], recommendations: string[]) => void;
@@ -947,7 +948,7 @@ export async function runOptimizedConsensusProcess(
947
948
  } = options;
948
949
 
949
950
  // Derive language from isFullstack for revision prompts
950
- const language: 'python' | 'typescript' | 'fullstack' = isFullstack ? 'fullstack' : 'python';
951
+ const language: OutputLanguage = isFullstack ? 'fullstack' : 'python';
951
952
 
952
953
  const {
953
954
  threshold = DEFAULT_CONSENSUS_CONFIG.threshold,