popeye-cli 1.4.7 → 1.6.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 (214) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +264 -63
  3. package/dist/adapters/gemini.d.ts +1 -0
  4. package/dist/adapters/gemini.d.ts.map +1 -1
  5. package/dist/adapters/gemini.js +9 -4
  6. package/dist/adapters/gemini.js.map +1 -1
  7. package/dist/adapters/grok.d.ts +1 -0
  8. package/dist/adapters/grok.d.ts.map +1 -1
  9. package/dist/adapters/grok.js +9 -4
  10. package/dist/adapters/grok.js.map +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js +35 -9
  14. package/dist/adapters/openai.js.map +1 -1
  15. package/dist/cli/commands/create.d.ts.map +1 -1
  16. package/dist/cli/commands/create.js +54 -4
  17. package/dist/cli/commands/create.js.map +1 -1
  18. package/dist/cli/interactive.d.ts +29 -0
  19. package/dist/cli/interactive.d.ts.map +1 -1
  20. package/dist/cli/interactive.js +132 -7
  21. package/dist/cli/interactive.js.map +1 -1
  22. package/dist/generators/all.d.ts +8 -2
  23. package/dist/generators/all.d.ts.map +1 -1
  24. package/dist/generators/all.js +37 -316
  25. package/dist/generators/all.js.map +1 -1
  26. package/dist/generators/doc-parser.d.ts +64 -0
  27. package/dist/generators/doc-parser.d.ts.map +1 -0
  28. package/dist/generators/doc-parser.js +407 -0
  29. package/dist/generators/doc-parser.js.map +1 -0
  30. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  31. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  32. package/dist/generators/frontend-design-analyzer.js +208 -0
  33. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  34. package/dist/generators/shared-packages.d.ts +45 -0
  35. package/dist/generators/shared-packages.d.ts.map +1 -0
  36. package/dist/generators/shared-packages.js +456 -0
  37. package/dist/generators/shared-packages.js.map +1 -0
  38. package/dist/generators/templates/index.d.ts +8 -0
  39. package/dist/generators/templates/index.d.ts.map +1 -1
  40. package/dist/generators/templates/index.js +8 -0
  41. package/dist/generators/templates/index.js.map +1 -1
  42. package/dist/generators/templates/website-components.d.ts +33 -0
  43. package/dist/generators/templates/website-components.d.ts.map +1 -0
  44. package/dist/generators/templates/website-components.js +303 -0
  45. package/dist/generators/templates/website-components.js.map +1 -0
  46. package/dist/generators/templates/website-config.d.ts +55 -0
  47. package/dist/generators/templates/website-config.d.ts.map +1 -0
  48. package/dist/generators/templates/website-config.js +425 -0
  49. package/dist/generators/templates/website-config.js.map +1 -0
  50. package/dist/generators/templates/website-conversion.d.ts +27 -0
  51. package/dist/generators/templates/website-conversion.d.ts.map +1 -0
  52. package/dist/generators/templates/website-conversion.js +326 -0
  53. package/dist/generators/templates/website-conversion.js.map +1 -0
  54. package/dist/generators/templates/website-landing.d.ts +24 -0
  55. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  56. package/dist/generators/templates/website-landing.js +276 -0
  57. package/dist/generators/templates/website-landing.js.map +1 -0
  58. package/dist/generators/templates/website-layout.d.ts +42 -0
  59. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  60. package/dist/generators/templates/website-layout.js +408 -0
  61. package/dist/generators/templates/website-layout.js.map +1 -0
  62. package/dist/generators/templates/website-pricing.d.ts +11 -0
  63. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  64. package/dist/generators/templates/website-pricing.js +313 -0
  65. package/dist/generators/templates/website-pricing.js.map +1 -0
  66. package/dist/generators/templates/website-sections.d.ts +102 -0
  67. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  68. package/dist/generators/templates/website-sections.js +444 -0
  69. package/dist/generators/templates/website-sections.js.map +1 -0
  70. package/dist/generators/templates/website-seo.d.ts +76 -0
  71. package/dist/generators/templates/website-seo.d.ts.map +1 -0
  72. package/dist/generators/templates/website-seo.js +326 -0
  73. package/dist/generators/templates/website-seo.js.map +1 -0
  74. package/dist/generators/templates/website.d.ts +10 -83
  75. package/dist/generators/templates/website.d.ts.map +1 -1
  76. package/dist/generators/templates/website.js +12 -875
  77. package/dist/generators/templates/website.js.map +1 -1
  78. package/dist/generators/website-content-scanner.d.ts +37 -0
  79. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  80. package/dist/generators/website-content-scanner.js +165 -0
  81. package/dist/generators/website-content-scanner.js.map +1 -0
  82. package/dist/generators/website-context.d.ts +119 -0
  83. package/dist/generators/website-context.d.ts.map +1 -0
  84. package/dist/generators/website-context.js +350 -0
  85. package/dist/generators/website-context.js.map +1 -0
  86. package/dist/generators/website-debug.d.ts +68 -0
  87. package/dist/generators/website-debug.d.ts.map +1 -0
  88. package/dist/generators/website-debug.js +93 -0
  89. package/dist/generators/website-debug.js.map +1 -0
  90. package/dist/generators/website.d.ts +5 -0
  91. package/dist/generators/website.d.ts.map +1 -1
  92. package/dist/generators/website.js +136 -11
  93. package/dist/generators/website.js.map +1 -1
  94. package/dist/generators/workspace-root.d.ts +27 -0
  95. package/dist/generators/workspace-root.d.ts.map +1 -0
  96. package/dist/generators/workspace-root.js +100 -0
  97. package/dist/generators/workspace-root.js.map +1 -0
  98. package/dist/state/index.d.ts +35 -0
  99. package/dist/state/index.d.ts.map +1 -1
  100. package/dist/state/index.js +40 -0
  101. package/dist/state/index.js.map +1 -1
  102. package/dist/types/consensus.d.ts +3 -0
  103. package/dist/types/consensus.d.ts.map +1 -1
  104. package/dist/types/consensus.js +1 -0
  105. package/dist/types/consensus.js.map +1 -1
  106. package/dist/types/website-strategy.d.ts +263 -0
  107. package/dist/types/website-strategy.d.ts.map +1 -0
  108. package/dist/types/website-strategy.js +105 -0
  109. package/dist/types/website-strategy.js.map +1 -0
  110. package/dist/types/workflow.d.ts +21 -0
  111. package/dist/types/workflow.d.ts.map +1 -1
  112. package/dist/types/workflow.js +8 -0
  113. package/dist/types/workflow.js.map +1 -1
  114. package/dist/upgrade/handlers.d.ts +15 -0
  115. package/dist/upgrade/handlers.d.ts.map +1 -1
  116. package/dist/upgrade/handlers.js +52 -0
  117. package/dist/upgrade/handlers.js.map +1 -1
  118. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  119. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  120. package/dist/workflow/auto-fix-bundler.js +320 -0
  121. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  122. package/dist/workflow/auto-fix.d.ts.map +1 -1
  123. package/dist/workflow/auto-fix.js +10 -3
  124. package/dist/workflow/auto-fix.js.map +1 -1
  125. package/dist/workflow/consensus.d.ts.map +1 -1
  126. package/dist/workflow/consensus.js +2 -0
  127. package/dist/workflow/consensus.js.map +1 -1
  128. package/dist/workflow/execution-mode.d.ts.map +1 -1
  129. package/dist/workflow/execution-mode.js +18 -0
  130. package/dist/workflow/execution-mode.js.map +1 -1
  131. package/dist/workflow/index.d.ts +4 -0
  132. package/dist/workflow/index.d.ts.map +1 -1
  133. package/dist/workflow/index.js +37 -0
  134. package/dist/workflow/index.js.map +1 -1
  135. package/dist/workflow/overview.d.ts +89 -0
  136. package/dist/workflow/overview.d.ts.map +1 -0
  137. package/dist/workflow/overview.js +358 -0
  138. package/dist/workflow/overview.js.map +1 -0
  139. package/dist/workflow/plan-mode.d.ts +6 -4
  140. package/dist/workflow/plan-mode.d.ts.map +1 -1
  141. package/dist/workflow/plan-mode.js +148 -6
  142. package/dist/workflow/plan-mode.js.map +1 -1
  143. package/dist/workflow/website-strategy.d.ts +79 -0
  144. package/dist/workflow/website-strategy.d.ts.map +1 -0
  145. package/dist/workflow/website-strategy.js +310 -0
  146. package/dist/workflow/website-strategy.js.map +1 -0
  147. package/dist/workflow/website-updater.d.ts +17 -0
  148. package/dist/workflow/website-updater.d.ts.map +1 -0
  149. package/dist/workflow/website-updater.js +116 -0
  150. package/dist/workflow/website-updater.js.map +1 -0
  151. package/dist/workflow/workflow-logger.d.ts +1 -1
  152. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  153. package/dist/workflow/workflow-logger.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/adapters/gemini.ts +10 -4
  156. package/src/adapters/grok.ts +10 -4
  157. package/src/adapters/openai.ts +38 -6
  158. package/src/cli/commands/create.ts +58 -4
  159. package/src/cli/interactive.ts +143 -7
  160. package/src/generators/all.ts +49 -332
  161. package/src/generators/doc-parser.ts +449 -0
  162. package/src/generators/frontend-design-analyzer.ts +261 -0
  163. package/src/generators/shared-packages.ts +500 -0
  164. package/src/generators/templates/index.ts +8 -0
  165. package/src/generators/templates/website-components.ts +330 -0
  166. package/src/generators/templates/website-config.ts +444 -0
  167. package/src/generators/templates/website-conversion.ts +341 -0
  168. package/src/generators/templates/website-landing.ts +331 -0
  169. package/src/generators/templates/website-layout.ts +443 -0
  170. package/src/generators/templates/website-pricing.ts +330 -0
  171. package/src/generators/templates/website-sections.ts +541 -0
  172. package/src/generators/templates/website-seo.ts +370 -0
  173. package/src/generators/templates/website.ts +38 -905
  174. package/src/generators/website-content-scanner.ts +208 -0
  175. package/src/generators/website-context.ts +493 -0
  176. package/src/generators/website-debug.ts +130 -0
  177. package/src/generators/website.ts +178 -20
  178. package/src/generators/workspace-root.ts +113 -0
  179. package/src/state/index.ts +56 -0
  180. package/src/types/consensus.ts +3 -0
  181. package/src/types/website-strategy.ts +243 -0
  182. package/src/types/workflow.ts +21 -0
  183. package/src/upgrade/handlers.ts +65 -0
  184. package/src/workflow/auto-fix-bundler.ts +392 -0
  185. package/src/workflow/auto-fix.ts +11 -3
  186. package/src/workflow/consensus.ts +2 -0
  187. package/src/workflow/execution-mode.ts +21 -0
  188. package/src/workflow/index.ts +37 -0
  189. package/src/workflow/overview.ts +475 -0
  190. package/src/workflow/plan-mode.ts +193 -8
  191. package/src/workflow/website-strategy.ts +379 -0
  192. package/src/workflow/website-updater.ts +142 -0
  193. package/src/workflow/workflow-logger.ts +1 -0
  194. package/tests/adapters/persona-switching.test.ts +63 -0
  195. package/tests/cli/project-naming.test.ts +136 -0
  196. package/tests/generators/doc-parser.test.ts +121 -0
  197. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  198. package/tests/generators/quality-gate.test.ts +183 -0
  199. package/tests/generators/shared-packages.test.ts +83 -0
  200. package/tests/generators/website-components.test.ts +159 -0
  201. package/tests/generators/website-config.test.ts +84 -0
  202. package/tests/generators/website-content-scanner.test.ts +181 -0
  203. package/tests/generators/website-context.test.ts +331 -0
  204. package/tests/generators/website-debug.test.ts +77 -0
  205. package/tests/generators/website-landing.test.ts +188 -0
  206. package/tests/generators/website-pricing.test.ts +98 -0
  207. package/tests/generators/website-sections.test.ts +245 -0
  208. package/tests/generators/website-seo-quality.test.ts +246 -0
  209. package/tests/generators/workspace-root.test.ts +105 -0
  210. package/tests/upgrade/handlers.test.ts +162 -0
  211. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  212. package/tests/workflow/overview.test.ts +392 -0
  213. package/tests/workflow/plan-mode.test.ts +111 -1
  214. package/tests/workflow/website-strategy.test.ts +246 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Frontend design language analyzer
3
+ * Extracts design tokens (colors, fonts, component library) from an existing
4
+ * frontend app to use as fallback for website generation
5
+ */
6
+
7
+ import { promises as fs } from 'node:fs';
8
+ import path from 'node:path';
9
+
10
+ /**
11
+ * Analyzed design context from a frontend application
12
+ */
13
+ export interface FrontendDesignContext {
14
+ colors?: Record<string, string>;
15
+ primaryColor?: string;
16
+ fonts?: { heading?: string; body?: string; mono?: string };
17
+ borderRadius?: string;
18
+ componentLibrary?: 'shadcn' | 'radix' | 'mui' | 'chakra' | 'unknown';
19
+ darkMode?: boolean;
20
+ source: 'tailwind-config' | 'css-variables' | 'defaults';
21
+ }
22
+
23
+ /**
24
+ * Analyze the frontend app design language
25
+ * Extracts colors, fonts, and component library from the project
26
+ *
27
+ * @param projectDir - Root project directory
28
+ * @returns Design context or null if no frontend app found
29
+ */
30
+ export async function analyzeFrontendDesign(
31
+ projectDir: string
32
+ ): Promise<FrontendDesignContext | null> {
33
+ const frontendDir = path.join(projectDir, 'apps', 'frontend');
34
+
35
+ // Check if frontend app exists
36
+ try {
37
+ await fs.access(frontendDir);
38
+ } catch {
39
+ return null;
40
+ }
41
+
42
+ const context: FrontendDesignContext = {
43
+ source: 'defaults',
44
+ };
45
+
46
+ // Detect component library from package.json
47
+ context.componentLibrary = await detectComponentLibrary(frontendDir);
48
+
49
+ // Try CSS custom properties first (shadcn/ui convention)
50
+ const cssResult = await extractCssVariables(frontendDir);
51
+ if (cssResult) {
52
+ if (cssResult.primaryColor) context.primaryColor = cssResult.primaryColor;
53
+ if (cssResult.colors) context.colors = cssResult.colors;
54
+ if (cssResult.borderRadius) context.borderRadius = cssResult.borderRadius;
55
+ context.darkMode = cssResult.darkMode;
56
+ context.source = 'css-variables';
57
+ }
58
+
59
+ // Try tailwind config (regex-based since we can't import user's config)
60
+ const tailwindResult = await extractTailwindConfig(frontendDir);
61
+ if (tailwindResult) {
62
+ // Only override if CSS vars didn't provide the value
63
+ if (!context.primaryColor && tailwindResult.primaryColor) {
64
+ context.primaryColor = tailwindResult.primaryColor;
65
+ }
66
+ if (!context.colors && tailwindResult.colors) {
67
+ context.colors = tailwindResult.colors;
68
+ }
69
+ if (tailwindResult.fonts) context.fonts = tailwindResult.fonts;
70
+ if (tailwindResult.darkMode !== undefined && !cssResult) {
71
+ context.darkMode = tailwindResult.darkMode;
72
+ }
73
+ if (context.source === 'defaults') context.source = 'tailwind-config';
74
+ }
75
+
76
+ // Return null if nothing meaningful was found
77
+ if (!context.primaryColor && !context.colors && !context.componentLibrary) {
78
+ return null;
79
+ }
80
+
81
+ return context;
82
+ }
83
+
84
+ /**
85
+ * Detect component library from package.json dependencies
86
+ */
87
+ async function detectComponentLibrary(
88
+ frontendDir: string
89
+ ): Promise<FrontendDesignContext['componentLibrary']> {
90
+ try {
91
+ const pkgContent = await fs.readFile(
92
+ path.join(frontendDir, 'package.json'),
93
+ 'utf-8'
94
+ );
95
+ const pkg = JSON.parse(pkgContent);
96
+ const allDeps = {
97
+ ...pkg.dependencies,
98
+ ...pkg.devDependencies,
99
+ };
100
+
101
+ if (allDeps['@shadcn/ui'] || allDeps['shadcn-ui']) return 'shadcn';
102
+ if (allDeps['@radix-ui/react-dialog'] || allDeps['@radix-ui/themes']) return 'radix';
103
+ if (allDeps['@mui/material']) return 'mui';
104
+ if (allDeps['@chakra-ui/react']) return 'chakra';
105
+ } catch {
106
+ // Package.json not readable
107
+ }
108
+
109
+ return undefined;
110
+ }
111
+
112
+ /**
113
+ * Extract design tokens from CSS custom properties (index.css or globals.css)
114
+ */
115
+ async function extractCssVariables(
116
+ frontendDir: string
117
+ ): Promise<{
118
+ primaryColor?: string;
119
+ colors?: Record<string, string>;
120
+ borderRadius?: string;
121
+ darkMode?: boolean;
122
+ } | null> {
123
+ const cssFiles = [
124
+ path.join(frontendDir, 'src', 'index.css'),
125
+ path.join(frontendDir, 'src', 'globals.css'),
126
+ path.join(frontendDir, 'src', 'app', 'globals.css'),
127
+ ];
128
+
129
+ let cssContent = '';
130
+ for (const cssFile of cssFiles) {
131
+ try {
132
+ cssContent = await fs.readFile(cssFile, 'utf-8');
133
+ break;
134
+ } catch {
135
+ continue;
136
+ }
137
+ }
138
+
139
+ if (!cssContent) return null;
140
+
141
+ const result: {
142
+ primaryColor?: string;
143
+ colors?: Record<string, string>;
144
+ borderRadius?: string;
145
+ darkMode?: boolean;
146
+ } = {};
147
+
148
+ // Detect shadcn/ui convention: HSL values like --primary: 222.2 47.4% 11.2%
149
+ const primaryHsl = cssContent.match(/--primary:\s*([\d.]+)\s+([\d.]+)%\s+([\d.]+)%/);
150
+ if (primaryHsl) {
151
+ const h = parseFloat(primaryHsl[1]);
152
+ const s = parseFloat(primaryHsl[2]) / 100;
153
+ const l = parseFloat(primaryHsl[3]) / 100;
154
+ result.primaryColor = hslToHex(h / 360, s, l);
155
+ }
156
+
157
+ // Detect hex-based CSS variables
158
+ if (!result.primaryColor) {
159
+ const primaryHex = cssContent.match(/--primary(?:-color)?:\s*(#[0-9a-fA-F]{6})/);
160
+ if (primaryHex) result.primaryColor = primaryHex[1];
161
+ }
162
+
163
+ // Extract border radius
164
+ const radiusMatch = cssContent.match(/--radius:\s*([\d.]+rem)/);
165
+ if (radiusMatch) result.borderRadius = radiusMatch[1];
166
+
167
+ // Detect dark mode blocks
168
+ result.darkMode = /\.dark\s*\{/.test(cssContent) || /@media\s*\(prefers-color-scheme:\s*dark\)/.test(cssContent);
169
+
170
+ return result;
171
+ }
172
+
173
+ /**
174
+ * Extract design tokens from tailwind config (regex-based)
175
+ */
176
+ async function extractTailwindConfig(
177
+ frontendDir: string
178
+ ): Promise<{
179
+ primaryColor?: string;
180
+ colors?: Record<string, string>;
181
+ fonts?: { heading?: string; body?: string; mono?: string };
182
+ darkMode?: boolean;
183
+ } | null> {
184
+ const configFiles = [
185
+ path.join(frontendDir, 'tailwind.config.ts'),
186
+ path.join(frontendDir, 'tailwind.config.js'),
187
+ path.join(frontendDir, 'tailwind.config.mjs'),
188
+ ];
189
+
190
+ let configContent = '';
191
+ for (const configFile of configFiles) {
192
+ try {
193
+ configContent = await fs.readFile(configFile, 'utf-8');
194
+ break;
195
+ } catch {
196
+ continue;
197
+ }
198
+ }
199
+
200
+ if (!configContent) return null;
201
+
202
+ const result: {
203
+ primaryColor?: string;
204
+ colors?: Record<string, string>;
205
+ fonts?: { heading?: string; body?: string; mono?: string };
206
+ darkMode?: boolean;
207
+ } = {};
208
+
209
+ // Extract primary color: primary: { 500: '#...' } or primary: '#...'
210
+ const primary500 = configContent.match(/primary[^}]*?500:\s*['"]?(#[0-9a-fA-F]{6})/);
211
+ if (primary500) {
212
+ result.primaryColor = primary500[1];
213
+ } else {
214
+ const primaryDirect = configContent.match(/primary:\s*['"]?(#[0-9a-fA-F]{6})/);
215
+ if (primaryDirect) result.primaryColor = primaryDirect[1];
216
+ }
217
+
218
+ // Extract font family
219
+ const sansFont = configContent.match(/sans:\s*\[['"]([^'"]+)['"]/);
220
+ if (sansFont) {
221
+ result.fonts = { body: sansFont[1] };
222
+ }
223
+
224
+ // Detect dark mode configuration
225
+ result.darkMode = /darkMode:\s*['"]class['"]/.test(configContent);
226
+
227
+ return result;
228
+ }
229
+
230
+ // --- Color conversion helper ---
231
+
232
+ function hslToHex(h: number, s: number, l: number): string {
233
+ const hue2rgb = (p: number, q: number, t: number): number => {
234
+ let tn = t;
235
+ if (tn < 0) tn += 1;
236
+ if (tn > 1) tn -= 1;
237
+ if (tn < 1 / 6) return p + (q - p) * 6 * tn;
238
+ if (tn < 1 / 2) return q;
239
+ if (tn < 2 / 3) return p + (q - p) * (2 / 3 - tn) * 6;
240
+ return p;
241
+ };
242
+
243
+ let r: number, g: number, b: number;
244
+
245
+ if (s === 0) {
246
+ r = g = b = l;
247
+ } else {
248
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
249
+ const p = 2 * l - q;
250
+ r = hue2rgb(p, q, h + 1 / 3);
251
+ g = hue2rgb(p, q, h);
252
+ b = hue2rgb(p, q, h - 1 / 3);
253
+ }
254
+
255
+ const toHex = (n: number): string => {
256
+ const hex = Math.round(n * 255).toString(16);
257
+ return hex.length === 1 ? '0' + hex : hex;
258
+ };
259
+
260
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
261
+ }