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,392 @@
1
+ /**
2
+ * Bundler error parser and fixer for non-TypeScript build errors.
3
+ * Handles CSS/PostCSS/Tailwind, webpack module resolution, and generic bundler errors.
4
+ * Used as a fallback when parseTypeScriptErrors() returns empty but the build failed.
5
+ */
6
+
7
+ import { promises as fs } from 'node:fs';
8
+ import path from 'node:path';
9
+ import { executePrompt } from '../adapters/claude.js';
10
+ import type { AutoFixResult, BuildError } from './auto-fix.js';
11
+
12
+ /** Bundler-specific error with type classification */
13
+ export interface BundlerError extends BuildError {
14
+ type: 'css' | 'module-not-found' | 'syntax' | 'generic';
15
+ }
16
+
17
+ /** Config files that often need modification to fix CSS/bundler errors */
18
+ const RELATED_CONFIG_FILES = [
19
+ 'tailwind.config.ts',
20
+ 'tailwind.config.js',
21
+ 'postcss.config.js',
22
+ 'postcss.config.mjs',
23
+ 'next.config.js',
24
+ 'next.config.mjs',
25
+ 'next.config.ts',
26
+ 'vite.config.ts',
27
+ 'vite.config.js',
28
+ 'tsconfig.json',
29
+ 'package.json',
30
+ ];
31
+
32
+ /**
33
+ * Parse non-TypeScript build errors from bundler output.
34
+ * Catches CSS/PostCSS/Tailwind errors, module-not-found, and generic syntax errors.
35
+ */
36
+ export function parseBundlerErrors(output: string): BundlerError[] {
37
+ const errors: BundlerError[] = [];
38
+ const clean = output.replace(/\x1b\[[0-9;]*m/g, '');
39
+ const seen = new Set<string>();
40
+
41
+ // Pattern 1: CSS/PostCSS/Tailwind class not found
42
+ // "Syntax error: /abs/path/file.css The `className` class does not exist..."
43
+ const cssClassPattern = /Syntax error:\s*(\S+\.css)\s+(The [`'][\w-]+['`] class does not exist[^]*?)(?=\n\n|\n>|\nat\s|$)/gm;
44
+ let match;
45
+ while ((match = cssClassPattern.exec(clean)) !== null) {
46
+ const key = `css:${match[1]}`;
47
+ if (!seen.has(key)) {
48
+ seen.add(key);
49
+ errors.push({
50
+ file: match[1],
51
+ message: match[2].trim().split('\n')[0],
52
+ type: 'css',
53
+ });
54
+ }
55
+ }
56
+
57
+ // Pattern 2: File reference with line:col (bundler-style, non-TS)
58
+ // "./src/app/globals.css:1:1" followed by "Syntax error:" on next lines
59
+ const fileLinePattern = /^\.\/([\w/.@-]+\.(?:css|scss|less|json|mjs|cjs)):(\d+):(\d+)\s*$/gm;
60
+ while ((match = fileLinePattern.exec(clean)) !== null) {
61
+ const filePath = `./${match[1]}`;
62
+ const line = parseInt(match[2], 10);
63
+ const col = parseInt(match[3], 10);
64
+ // Grab up to 500 chars after match for context
65
+ const afterMatch = clean.slice(match.index + match[0].length, match.index + match[0].length + 500);
66
+ const errorLine = afterMatch.split('\n').find(l => l.trim().length > 10);
67
+ const key = `ref:${filePath}:${line}:${col}`;
68
+ if (!seen.has(key) && errorLine) {
69
+ seen.add(key);
70
+ errors.push({
71
+ file: filePath,
72
+ line,
73
+ column: col,
74
+ message: errorLine.trim(),
75
+ type: 'syntax',
76
+ });
77
+ }
78
+ }
79
+
80
+ // Pattern 3: Module not found
81
+ // "Module not found: Can't resolve 'package' in '/path'"
82
+ const modulePattern = /Module not found:\s*(?:Error:\s*)?Can't resolve '([^']+)'\s+in\s+'([^']+)'/gm;
83
+ while ((match = modulePattern.exec(clean)) !== null) {
84
+ const key = `module:${match[1]}:${match[2]}`;
85
+ if (!seen.has(key)) {
86
+ seen.add(key);
87
+ errors.push({
88
+ file: match[2],
89
+ message: `Cannot resolve module '${match[1]}'`,
90
+ type: 'module-not-found',
91
+ });
92
+ }
93
+ }
94
+
95
+ // Pattern 4: Generic "Build failed because of webpack errors" — extract file references
96
+ if (errors.length === 0 && /Build failed because of webpack errors/i.test(clean)) {
97
+ // Try to find Import trace lines: "./src/path/file.ext"
98
+ const importTracePattern = /^\.\/([\w/.@-]+\.\w+)\s*$/gm;
99
+ while ((match = importTracePattern.exec(clean)) !== null) {
100
+ const filePath = `./${match[1]}`;
101
+ const key = `trace:${filePath}`;
102
+ if (!seen.has(key)) {
103
+ seen.add(key);
104
+ errors.push({
105
+ file: filePath,
106
+ message: 'Referenced in build error import trace',
107
+ type: 'generic',
108
+ });
109
+ }
110
+ }
111
+ }
112
+
113
+ return errors;
114
+ }
115
+
116
+ /**
117
+ * Resolve the app directory from an error file path.
118
+ * E.g., "apps/website/src/foo.css" -> "<projectDir>/apps/website"
119
+ */
120
+ function resolveAppDir(projectDir: string, errorFile: string): string {
121
+ const appsMatch = errorFile.match(/apps\/([^/]+)\//);
122
+ if (appsMatch) {
123
+ return path.join(projectDir, 'apps', appsMatch[1]);
124
+ }
125
+ return projectDir;
126
+ }
127
+
128
+ /**
129
+ * Find config files related to the error (tailwind.config, postcss.config, etc.)
130
+ * Searches both the app directory and project root.
131
+ */
132
+ export async function findRelatedConfigs(
133
+ projectDir: string,
134
+ errorFile: string,
135
+ ): Promise<Array<{ path: string; content: string }>> {
136
+ const configs: Array<{ path: string; content: string }> = [];
137
+ const appDir = resolveAppDir(projectDir, errorFile);
138
+
139
+ // Search in both app dir and project root (deduped)
140
+ const searchDirs = [...new Set([appDir, projectDir])];
141
+
142
+ for (const dir of searchDirs) {
143
+ for (const configFile of RELATED_CONFIG_FILES) {
144
+ const configPath = path.join(dir, configFile);
145
+ try {
146
+ const content = await fs.readFile(configPath, 'utf-8');
147
+ // Cap config file size to avoid token bloat
148
+ configs.push({ path: configPath, content: content.slice(0, 4000) });
149
+ } catch { /* not found */ }
150
+ }
151
+ }
152
+
153
+ return configs;
154
+ }
155
+
156
+ /**
157
+ * Resolve the absolute path of a bundler error file.
158
+ * Tries multiple locations: absolute, relative to app dir, relative to project root.
159
+ */
160
+ async function resolveErrorFile(
161
+ projectDir: string,
162
+ errorFile: string,
163
+ ): Promise<{ resolvedPath: string; content: string } | null> {
164
+ const appDir = resolveAppDir(projectDir, errorFile);
165
+ const candidates = [
166
+ path.isAbsolute(errorFile) ? errorFile : null,
167
+ path.join(appDir, errorFile.replace(/^\.\//, '')),
168
+ path.join(projectDir, errorFile.replace(/^\.\//, '')),
169
+ ].filter(Boolean) as string[];
170
+
171
+ for (const p of candidates) {
172
+ try {
173
+ const content = await fs.readFile(p, 'utf-8');
174
+ return { resolvedPath: p, content };
175
+ } catch { /* continue */ }
176
+ }
177
+
178
+ return null;
179
+ }
180
+
181
+ /**
182
+ * Generate a fix prompt for bundler/CSS errors.
183
+ * Includes the error file, related config files, and instructions for multi-file fixes.
184
+ */
185
+ function generateBundlerFixPrompt(
186
+ errorFile: string,
187
+ fileContent: string,
188
+ errors: BundlerError[],
189
+ configs: Array<{ path: string; content: string }>,
190
+ rawOutput: string,
191
+ ): string {
192
+ const errorList = errors.map(e => `- ${e.type}: ${e.message}`).join('\n');
193
+
194
+ const configSection = configs.length > 0
195
+ ? '\n\n## Related Configuration Files:\n' +
196
+ configs.map(c => `### ${c.path}\n\`\`\`\n${c.content}\n\`\`\``).join('\n\n')
197
+ : '';
198
+
199
+ // Include raw output snippet for full context (last 1500 chars)
200
+ const outputSnippet = rawOutput.slice(-1500);
201
+
202
+ return `Fix the following build error. This is a CSS/bundler/config error, NOT a TypeScript error.
203
+
204
+ ## Error File: ${errorFile}
205
+
206
+ ## Errors:
207
+ ${errorList}
208
+
209
+ ## Raw Build Output (last 1500 chars):
210
+ \`\`\`
211
+ ${outputSnippet}
212
+ \`\`\`
213
+
214
+ ## Error file content:
215
+ \`\`\`
216
+ ${fileContent}
217
+ \`\`\`${configSection}
218
+
219
+ ## Instructions:
220
+ 1. Analyze the error carefully — it may be a CSS, Tailwind, PostCSS, or webpack bundler error
221
+ 2. The fix might be in the error file OR in a configuration file (e.g., tailwind.config.ts)
222
+ 3. For Tailwind "class does not exist" errors: the fix is usually adding the class definition to tailwind.config.ts theme.extend.colors (e.g., background: 'hsl(var(--background))')
223
+ 4. For module-not-found: the fix is usually a missing dependency or wrong import path
224
+ 5. Return your response using this exact format for EACH file that needs changes:
225
+
226
+ FILE: <absolute path to file>
227
+ \`\`\`
228
+ <complete fixed file content>
229
+ \`\`\`
230
+
231
+ If multiple files need changes, repeat the FILE: pattern for each.
232
+ Return ONLY the files that need changes.`.trim();
233
+ }
234
+
235
+ /**
236
+ * Parse Claude's multi-file response format.
237
+ * Extracts "FILE: <path>\n```\n<content>\n```" blocks.
238
+ */
239
+ export function parseMultiFileResponse(
240
+ response: string,
241
+ ): Array<{ targetPath: string; content: string }> {
242
+ const results: Array<{ targetPath: string; content: string }> = [];
243
+ const filePattern = /FILE:\s*(.+)\n```(?:\w*)\n([\s\S]*?)\n```/g;
244
+ let match;
245
+ while ((match = filePattern.exec(response)) !== null) {
246
+ const content = match[2].trim();
247
+ if (content.length > 20) {
248
+ results.push({ targetPath: match[1].trim(), content });
249
+ }
250
+ }
251
+ return results;
252
+ }
253
+
254
+ /**
255
+ * Resolve a target path from Claude's response to an actual writable file.
256
+ */
257
+ async function resolveTargetPath(
258
+ targetPath: string,
259
+ projectDir: string,
260
+ appDir: string,
261
+ configs: Array<{ path: string; content: string }>,
262
+ ): Promise<string | null> {
263
+ const candidates = [
264
+ path.isAbsolute(targetPath) ? targetPath : null,
265
+ path.join(appDir, targetPath.replace(/^\.\//, '')),
266
+ path.join(projectDir, targetPath.replace(/^\.\//, '')),
267
+ // Match against known config paths by basename
268
+ ...configs.map(c => c.path).filter(p => p.endsWith(path.basename(targetPath))),
269
+ ].filter(Boolean) as string[];
270
+
271
+ for (const p of candidates) {
272
+ try {
273
+ await fs.access(p);
274
+ return p;
275
+ } catch { /* continue */ }
276
+ }
277
+ return null;
278
+ }
279
+
280
+ /**
281
+ * Fix bundler errors by sending them to Claude with full context.
282
+ * Handles CSS/Tailwind/PostCSS/webpack errors that parseTypeScriptErrors() can't parse.
283
+ */
284
+ export async function fixBundlerErrors(
285
+ projectDir: string,
286
+ buildOutput: string,
287
+ errors: BundlerError[],
288
+ _language: string,
289
+ onProgress?: (message: string) => void,
290
+ ): Promise<AutoFixResult> {
291
+ const fixes: Array<{ file: string; description: string }> = [];
292
+
293
+ // Group errors by file
294
+ const errorsByFile = new Map<string, BundlerError[]>();
295
+ for (const error of errors) {
296
+ const existing = errorsByFile.get(error.file) || [];
297
+ existing.push(error);
298
+ errorsByFile.set(error.file, existing);
299
+ }
300
+
301
+ for (const [errorFile, fileErrors] of errorsByFile) {
302
+ onProgress?.(`Fixing ${fileErrors.length} bundler error(s) in ${path.basename(errorFile)}...`);
303
+
304
+ // Read the error file
305
+ const resolved = await resolveErrorFile(projectDir, errorFile);
306
+ if (!resolved) {
307
+ onProgress?.(`Cannot find ${errorFile}, skipping`);
308
+ continue;
309
+ }
310
+
311
+ // Find related config files
312
+ const configs = await findRelatedConfigs(projectDir, errorFile);
313
+
314
+ // Generate fix prompt
315
+ const prompt = generateBundlerFixPrompt(
316
+ errorFile, resolved.content, fileErrors, configs, buildOutput,
317
+ );
318
+
319
+ // Ask Claude to fix
320
+ const result = await executePrompt(prompt, {
321
+ allowedTools: [],
322
+ permissionMode: 'default',
323
+ });
324
+
325
+ if (!result.success || !result.response) {
326
+ onProgress?.(`Failed to get fix for ${path.basename(errorFile)}: ${result.error}`);
327
+ continue;
328
+ }
329
+
330
+ // Parse multi-file response
331
+ const fileChanges = parseMultiFileResponse(result.response);
332
+ const appDir = resolveAppDir(projectDir, errorFile);
333
+
334
+ for (const change of fileChanges) {
335
+ const resolvedTarget = await resolveTargetPath(
336
+ change.targetPath, projectDir, appDir, configs,
337
+ );
338
+
339
+ if (!resolvedTarget) {
340
+ onProgress?.(`Cannot resolve target path: ${change.targetPath}`);
341
+ continue;
342
+ }
343
+
344
+ await fs.writeFile(resolvedTarget, change.content, 'utf-8');
345
+ fixes.push({
346
+ file: resolvedTarget,
347
+ description: `Fixed bundler error: ${fileErrors[0].message.slice(0, 80)}`,
348
+ });
349
+ onProgress?.(`Fixed ${path.basename(resolvedTarget)}`);
350
+ }
351
+
352
+ // Fallback: if no FILE: pattern found, try single code-block extraction
353
+ if (fileChanges.length === 0) {
354
+ const singleBlock = result.response.match(/```(?:\w*)\n([\s\S]*?)\n```/);
355
+ if (singleBlock && singleBlock[1].length > 20) {
356
+ const content = singleBlock[1];
357
+ // Determine if this is a config fix or a direct file fix
358
+ let targetFile = resolved.resolvedPath;
359
+ if (
360
+ (content.includes('export default') || content.includes('module.exports')) &&
361
+ !resolved.resolvedPath.endsWith('.css')
362
+ ) {
363
+ const configMatch = configs.find(c =>
364
+ (content.includes('tailwind') && c.path.includes('tailwind')) ||
365
+ (content.includes('postcss') && c.path.includes('postcss')) ||
366
+ (content.includes('next') && c.path.includes('next.config')),
367
+ );
368
+ if (configMatch) targetFile = configMatch.path;
369
+ }
370
+
371
+ await fs.writeFile(targetFile, content, 'utf-8');
372
+ fixes.push({
373
+ file: targetFile,
374
+ description: `Fixed bundler error: ${fileErrors[0].message.slice(0, 80)}`,
375
+ });
376
+ onProgress?.(`Fixed ${path.basename(targetFile)}`);
377
+ }
378
+ }
379
+ }
380
+
381
+ return {
382
+ success: fixes.length > 0,
383
+ fixedErrors: fixes.length,
384
+ remainingErrors: 0,
385
+ attempts: 1,
386
+ fixes,
387
+ missingFileCount: 0,
388
+ totalErrorFiles: errorsByFile.size,
389
+ isStructuralIssue: false,
390
+ missingFiles: [],
391
+ };
392
+ }
@@ -7,6 +7,7 @@ import { promises as fs } from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { executePrompt } from '../adapters/claude.js';
9
9
  import { isWorkspace, type OutputLanguage } from '../types/project.js';
10
+ import { parseBundlerErrors, fixBundlerErrors } from './auto-fix-bundler.js';
10
11
 
11
12
  /** Standard workspace subdirectories to search when a file isn't at the root */
12
13
  const WORKSPACE_SUBDIRS = ['apps/frontend', 'apps/backend', 'apps/website', 'packages/frontend', 'packages/backend'];
@@ -250,10 +251,17 @@ export async function autoFixTypeScriptErrors(
250
251
 
251
252
  if (errors.length === 0) {
252
253
  // First attempt with zero parsed errors and no prior fixes: the build failed
253
- // but we can't parse the error format. This is NOT success.
254
+ // but we can't parse the error format. Try bundler error parser as fallback.
254
255
  const noParsedOnFirstAttempt = attempts === 1 && fixes.length === 0;
255
256
  if (noParsedOnFirstAttempt) {
256
- onProgress?.('No parseable TypeScript errors in build output (may be bundler/non-TS errors)');
257
+ // Fallback: try parsing CSS/PostCSS/Tailwind/webpack bundler errors
258
+ const bundlerErrors = parseBundlerErrors(currentOutput);
259
+ if (bundlerErrors.length > 0) {
260
+ onProgress?.(`Found ${bundlerErrors.length} bundler/CSS error(s), attempting fix...`);
261
+ return fixBundlerErrors(projectDir, currentOutput, bundlerErrors, language, onProgress);
262
+ }
263
+
264
+ onProgress?.('No parseable errors in build output (not TS or bundler format)');
257
265
  return {
258
266
  success: false,
259
267
  fixedErrors: 0,
@@ -264,7 +272,7 @@ export async function autoFixTypeScriptErrors(
264
272
  totalErrorFiles: 0,
265
273
  isStructuralIssue: false,
266
274
  missingFiles: [],
267
- error: 'Build failed but no parseable TypeScript errors found in output',
275
+ error: 'Build failed but no parseable errors found in output',
268
276
  };
269
277
  }
270
278
 
@@ -79,6 +79,7 @@ async function requestReviewerConsensus(
79
79
  model: config.geminiModel,
80
80
  temperature: config.temperature,
81
81
  maxTokens: config.maxTokens,
82
+ reviewerPersona: config.reviewerPersona,
82
83
  });
83
84
  }
84
85
  if (reviewer === 'grok') {
@@ -86,6 +87,7 @@ async function requestReviewerConsensus(
86
87
  model: config.grokModel,
87
88
  temperature: config.temperature,
88
89
  maxTokens: config.maxTokens,
90
+ reviewerPersona: config.reviewerPersona,
89
91
  });
90
92
  }
91
93
  return requestOpenAIConsensus(plan, context, config);
@@ -782,6 +782,14 @@ function buildTaskContext(
782
782
  ): string {
783
783
  const lines: string[] = [];
784
784
 
785
+ // No-hardcode enforcement rule
786
+ lines.push('## CRITICAL RULES');
787
+ lines.push('- NEVER use hardcoded placeholder content, mock data, or invented copy.');
788
+ lines.push('- ALL text, features, pricing, colors, and data MUST come from the project specification and user documentation below.');
789
+ lines.push('- If information is not available in the spec, leave a TODO comment rather than inventing content.');
790
+ lines.push('- Do NOT hallucinate product names, features, pricing tiers, testimonials, or blog content.');
791
+ lines.push('');
792
+
785
793
  lines.push(`## Project: ${state.name}`);
786
794
  lines.push(`Language: ${state.language}`);
787
795
  lines.push('');
@@ -798,6 +806,19 @@ function buildTaskContext(
798
806
  lines.push('');
799
807
  }
800
808
 
809
+ // Include user documentation if available
810
+ if (state.userDocs) {
811
+ lines.push('## Project Documentation');
812
+ lines.push(state.userDocs.slice(0, 3000));
813
+ lines.push('');
814
+ }
815
+
816
+ // Include brand context if available
817
+ if (state.brandContext?.primaryColor) {
818
+ lines.push(`## Brand: Primary color ${state.brandContext.primaryColor}`);
819
+ lines.push('');
820
+ }
821
+
801
822
  // Include UI design context if available
802
823
  if (uiDesignContext) {
803
824
  lines.push(uiDesignContext);
@@ -41,6 +41,7 @@ export * from './ui-designer.js';
41
41
  export * from './ui-verification.js';
42
42
  export * from './project-verification.js';
43
43
  export * from './auto-fix.js';
44
+ export * from './auto-fix-bundler.js';
44
45
  export * from './project-structure.js';
45
46
  // Note: plan-parser.js exports are accessible but have naming conflicts with plan-mode.js
46
47
  // Import directly from './plan-parser.js' if you need the extended TaskAppTag type (includes 'WEB')
@@ -50,6 +51,9 @@ export * from './task-workflow.js';
50
51
  export * from './milestone-workflow.js';
51
52
  export * from './plan-storage.js';
52
53
  export * from './workspace-manager.js';
54
+ export * from './website-updater.js';
55
+ export * from './website-strategy.js';
56
+ export * from './overview.js';
53
57
 
54
58
  /**
55
59
  * Workflow options
@@ -116,6 +120,17 @@ export async function runWorkflow(
116
120
  };
117
121
  }
118
122
 
123
+ // Post-plan: Update website content with project context
124
+ if (spec.language === 'website' || spec.language === 'all' || spec.language === 'fullstack') {
125
+ try {
126
+ onProgress?.('website-update', 'Updating website with project context...');
127
+ const { updateWebsiteContent } = await import('./website-updater.js');
128
+ await updateWebsiteContent(projectDir, planResult.state, spec.language, (msg) => onProgress?.('website-update', msg));
129
+ } catch {
130
+ // Non-blocking: website content update failure should not stop workflow
131
+ }
132
+ }
133
+
119
134
  // Phase 2: Execution Mode
120
135
  onProgress?.('workflow', 'Starting Execution Mode...');
121
136
 
@@ -224,6 +239,17 @@ export async function resumeWorkflow(
224
239
  };
225
240
  }
226
241
 
242
+ // Post-plan: Update website content with project context
243
+ if (state.language === 'website' || state.language === 'all' || state.language === 'fullstack') {
244
+ try {
245
+ onProgress?.('website-update', 'Updating website with project context...');
246
+ const { updateWebsiteContent } = await import('./website-updater.js');
247
+ await updateWebsiteContent(projectDir, planResult.state, state.language, (msg) => onProgress?.('website-update', msg));
248
+ } catch {
249
+ // Non-blocking
250
+ }
251
+ }
252
+
227
253
  // Continue to execution
228
254
  onProgress?.('workflow', 'Starting Execution Mode...');
229
255
 
@@ -244,6 +270,17 @@ export async function resumeWorkflow(
244
270
  }
245
271
 
246
272
  case 'execution': {
273
+ // Update website content before resuming execution
274
+ if (state.language === 'website' || state.language === 'all' || state.language === 'fullstack') {
275
+ try {
276
+ onProgress?.('website-update', 'Updating website with project context before execution resume...');
277
+ const { updateWebsiteContent } = await import('./website-updater.js');
278
+ await updateWebsiteContent(projectDir, state, state.language, (msg) => onProgress?.('website-update', msg));
279
+ } catch {
280
+ // Non-blocking: website content update failure should not stop workflow
281
+ }
282
+ }
283
+
247
284
  onProgress?.('workflow', 'Resuming Execution Mode...');
248
285
 
249
286
  const executionResult = await resumeExecutionMode({