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.
- package/CHANGELOG.md +54 -0
- package/README.md +264 -63
- package/dist/adapters/gemini.d.ts +1 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +9 -4
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +1 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +9 -4
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +35 -9
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +54 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts +29 -0
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +132 -7
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts +8 -2
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +37 -316
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +64 -0
- package/dist/generators/doc-parser.d.ts.map +1 -0
- package/dist/generators/doc-parser.js +407 -0
- package/dist/generators/doc-parser.js.map +1 -0
- package/dist/generators/frontend-design-analyzer.d.ts +30 -0
- package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
- package/dist/generators/frontend-design-analyzer.js +208 -0
- package/dist/generators/frontend-design-analyzer.js.map +1 -0
- package/dist/generators/shared-packages.d.ts +45 -0
- package/dist/generators/shared-packages.d.ts.map +1 -0
- package/dist/generators/shared-packages.js +456 -0
- package/dist/generators/shared-packages.js.map +1 -0
- package/dist/generators/templates/index.d.ts +8 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +8 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/generators/templates/website-components.d.ts +33 -0
- package/dist/generators/templates/website-components.d.ts.map +1 -0
- package/dist/generators/templates/website-components.js +303 -0
- package/dist/generators/templates/website-components.js.map +1 -0
- package/dist/generators/templates/website-config.d.ts +55 -0
- package/dist/generators/templates/website-config.d.ts.map +1 -0
- package/dist/generators/templates/website-config.js +425 -0
- package/dist/generators/templates/website-config.js.map +1 -0
- package/dist/generators/templates/website-conversion.d.ts +27 -0
- package/dist/generators/templates/website-conversion.d.ts.map +1 -0
- package/dist/generators/templates/website-conversion.js +326 -0
- package/dist/generators/templates/website-conversion.js.map +1 -0
- package/dist/generators/templates/website-landing.d.ts +24 -0
- package/dist/generators/templates/website-landing.d.ts.map +1 -0
- package/dist/generators/templates/website-landing.js +276 -0
- package/dist/generators/templates/website-landing.js.map +1 -0
- package/dist/generators/templates/website-layout.d.ts +42 -0
- package/dist/generators/templates/website-layout.d.ts.map +1 -0
- package/dist/generators/templates/website-layout.js +408 -0
- package/dist/generators/templates/website-layout.js.map +1 -0
- package/dist/generators/templates/website-pricing.d.ts +11 -0
- package/dist/generators/templates/website-pricing.d.ts.map +1 -0
- package/dist/generators/templates/website-pricing.js +313 -0
- package/dist/generators/templates/website-pricing.js.map +1 -0
- package/dist/generators/templates/website-sections.d.ts +102 -0
- package/dist/generators/templates/website-sections.d.ts.map +1 -0
- package/dist/generators/templates/website-sections.js +444 -0
- package/dist/generators/templates/website-sections.js.map +1 -0
- package/dist/generators/templates/website-seo.d.ts +76 -0
- package/dist/generators/templates/website-seo.d.ts.map +1 -0
- package/dist/generators/templates/website-seo.js +326 -0
- package/dist/generators/templates/website-seo.js.map +1 -0
- package/dist/generators/templates/website.d.ts +10 -83
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +12 -875
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-scanner.d.ts +37 -0
- package/dist/generators/website-content-scanner.d.ts.map +1 -0
- package/dist/generators/website-content-scanner.js +165 -0
- package/dist/generators/website-content-scanner.js.map +1 -0
- package/dist/generators/website-context.d.ts +119 -0
- package/dist/generators/website-context.d.ts.map +1 -0
- package/dist/generators/website-context.js +350 -0
- package/dist/generators/website-context.js.map +1 -0
- package/dist/generators/website-debug.d.ts +68 -0
- package/dist/generators/website-debug.d.ts.map +1 -0
- package/dist/generators/website-debug.js +93 -0
- package/dist/generators/website-debug.js.map +1 -0
- package/dist/generators/website.d.ts +5 -0
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +136 -11
- package/dist/generators/website.js.map +1 -1
- package/dist/generators/workspace-root.d.ts +27 -0
- package/dist/generators/workspace-root.d.ts.map +1 -0
- package/dist/generators/workspace-root.js +100 -0
- package/dist/generators/workspace-root.js.map +1 -0
- package/dist/state/index.d.ts +35 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +40 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +263 -0
- package/dist/types/website-strategy.d.ts.map +1 -0
- package/dist/types/website-strategy.js +105 -0
- package/dist/types/website-strategy.js.map +1 -0
- package/dist/types/workflow.d.ts +21 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +8 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts +15 -0
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +52 -0
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/auto-fix-bundler.d.ts +37 -0
- package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
- package/dist/workflow/auto-fix-bundler.js +320 -0
- package/dist/workflow/auto-fix-bundler.js.map +1 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -1
- package/dist/workflow/auto-fix.js +10 -3
- package/dist/workflow/auto-fix.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +2 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +18 -0
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +4 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +37 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/overview.d.ts +89 -0
- package/dist/workflow/overview.d.ts.map +1 -0
- package/dist/workflow/overview.js +358 -0
- package/dist/workflow/overview.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +6 -4
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +148 -6
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts +79 -0
- package/dist/workflow/website-strategy.d.ts.map +1 -0
- package/dist/workflow/website-strategy.js +310 -0
- package/dist/workflow/website-strategy.js.map +1 -0
- package/dist/workflow/website-updater.d.ts +17 -0
- package/dist/workflow/website-updater.d.ts.map +1 -0
- package/dist/workflow/website-updater.js +116 -0
- package/dist/workflow/website-updater.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +10 -4
- package/src/adapters/grok.ts +10 -4
- package/src/adapters/openai.ts +38 -6
- package/src/cli/commands/create.ts +58 -4
- package/src/cli/interactive.ts +143 -7
- package/src/generators/all.ts +49 -332
- package/src/generators/doc-parser.ts +449 -0
- package/src/generators/frontend-design-analyzer.ts +261 -0
- package/src/generators/shared-packages.ts +500 -0
- package/src/generators/templates/index.ts +8 -0
- package/src/generators/templates/website-components.ts +330 -0
- package/src/generators/templates/website-config.ts +444 -0
- package/src/generators/templates/website-conversion.ts +341 -0
- package/src/generators/templates/website-landing.ts +331 -0
- package/src/generators/templates/website-layout.ts +443 -0
- package/src/generators/templates/website-pricing.ts +330 -0
- package/src/generators/templates/website-sections.ts +541 -0
- package/src/generators/templates/website-seo.ts +370 -0
- package/src/generators/templates/website.ts +38 -905
- package/src/generators/website-content-scanner.ts +208 -0
- package/src/generators/website-context.ts +493 -0
- package/src/generators/website-debug.ts +130 -0
- package/src/generators/website.ts +178 -20
- package/src/generators/workspace-root.ts +113 -0
- package/src/state/index.ts +56 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/website-strategy.ts +243 -0
- package/src/types/workflow.ts +21 -0
- package/src/upgrade/handlers.ts +65 -0
- package/src/workflow/auto-fix-bundler.ts +392 -0
- package/src/workflow/auto-fix.ts +11 -3
- package/src/workflow/consensus.ts +2 -0
- package/src/workflow/execution-mode.ts +21 -0
- package/src/workflow/index.ts +37 -0
- package/src/workflow/overview.ts +475 -0
- package/src/workflow/plan-mode.ts +193 -8
- package/src/workflow/website-strategy.ts +379 -0
- package/src/workflow/website-updater.ts +142 -0
- package/src/workflow/workflow-logger.ts +1 -0
- package/tests/adapters/persona-switching.test.ts +63 -0
- package/tests/cli/project-naming.test.ts +136 -0
- package/tests/generators/doc-parser.test.ts +121 -0
- package/tests/generators/frontend-design-analyzer.test.ts +90 -0
- package/tests/generators/quality-gate.test.ts +183 -0
- package/tests/generators/shared-packages.test.ts +83 -0
- package/tests/generators/website-components.test.ts +159 -0
- package/tests/generators/website-config.test.ts +84 -0
- package/tests/generators/website-content-scanner.test.ts +181 -0
- package/tests/generators/website-context.test.ts +331 -0
- package/tests/generators/website-debug.test.ts +77 -0
- package/tests/generators/website-landing.test.ts +188 -0
- package/tests/generators/website-pricing.test.ts +98 -0
- package/tests/generators/website-sections.test.ts +245 -0
- package/tests/generators/website-seo-quality.test.ts +246 -0
- package/tests/generators/workspace-root.test.ts +105 -0
- package/tests/upgrade/handlers.test.ts +162 -0
- package/tests/workflow/auto-fix-bundler.test.ts +242 -0
- package/tests/workflow/overview.test.ts +392 -0
- package/tests/workflow/plan-mode.test.ts +111 -1
- 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
|
+
}
|
package/src/workflow/auto-fix.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
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);
|
package/src/workflow/index.ts
CHANGED
|
@@ -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({
|