@auto-engineer/react-component-implementer 1.12.1
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 +12 -0
- package/LICENSE +10 -0
- package/dist/src/commands/implement-react-component.d.ts +26 -0
- package/dist/src/commands/implement-react-component.d.ts.map +1 -0
- package/dist/src/commands/implement-react-component.js +295 -0
- package/dist/src/commands/implement-react-component.js.map +1 -0
- package/dist/src/component-generator.d.ts +37 -0
- package/dist/src/component-generator.d.ts.map +1 -0
- package/dist/src/component-generator.js +228 -0
- package/dist/src/component-generator.js.map +1 -0
- package/dist/src/component-writer.d.ts +3 -0
- package/dist/src/component-writer.d.ts.map +1 -0
- package/dist/src/component-writer.js +23 -0
- package/dist/src/component-writer.js.map +1 -0
- package/dist/src/design-reference.d.ts +5 -0
- package/dist/src/design-reference.d.ts.map +1 -0
- package/dist/src/design-reference.js +24 -0
- package/dist/src/design-reference.js.map +1 -0
- package/dist/src/file-tree.d.ts +8 -0
- package/dist/src/file-tree.d.ts.map +1 -0
- package/dist/src/file-tree.js +13 -0
- package/dist/src/file-tree.js.map +1 -0
- package/dist/src/functional-validator.d.ts +9 -0
- package/dist/src/functional-validator.d.ts.map +1 -0
- package/dist/src/functional-validator.js +31 -0
- package/dist/src/functional-validator.js.map +1 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp-client.d.ts +10 -0
- package/dist/src/mcp-client.d.ts.map +1 -0
- package/dist/src/mcp-client.js +43 -0
- package/dist/src/mcp-client.js.map +1 -0
- package/dist/src/type-checker.d.ts +6 -0
- package/dist/src/type-checker.d.ts.map +1 -0
- package/dist/src/type-checker.js +36 -0
- package/dist/src/type-checker.js.map +1 -0
- package/dist/src/types.d.ts +21 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/visual-evaluator.d.ts +20 -0
- package/dist/src/visual-evaluator.d.ts.map +1 -0
- package/dist/src/visual-evaluator.js +70 -0
- package/dist/src/visual-evaluator.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +39 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @auto-engineer/react-component-implementer
|
|
2
|
+
|
|
3
|
+
## 1.12.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`6557224`](https://github.com/BeOnAuto/auto-engineer/commit/6557224ec6f51c704855e5058931e81ab1de1544) Thanks [@osamanar](https://github.com/osamanar)! - - Updated project dependency lock file to ensure consistent package installations
|
|
8
|
+
|
|
9
|
+
- [`cd5f56b`](https://github.com/BeOnAuto/auto-engineer/commit/cd5f56b01951cd27392c51a384706a8c2a7401c5) Thanks [@osamanar](https://github.com/osamanar)! - - Removed unused packages to keep the project lean and reduce maintenance overhead
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`6557224`](https://github.com/BeOnAuto/auto-engineer/commit/6557224ec6f51c704855e5058931e81ab1de1544), [`cd5f56b`](https://github.com/BeOnAuto/auto-engineer/commit/cd5f56b01951cd27392c51a384706a8c2a7401c5)]:
|
|
12
|
+
- @auto-engineer/message-bus@1.12.1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
Copyright 2024 Sam Hatoum
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are licensed under the Elastic License 2.0 (the "License"). You may not use this file except in compliance with the License.
|
|
6
|
+
|
|
7
|
+
You may obtain a copy of the License at:
|
|
8
|
+
https://www.elastic.co/licensing/elastic-license
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Command, type Event } from '@auto-engineer/message-bus';
|
|
2
|
+
import type { Job } from '../types.js';
|
|
3
|
+
export type ImplementReactComponentCommand = Command<'ImplementReactComponent', {
|
|
4
|
+
targetDir: string;
|
|
5
|
+
job: Job;
|
|
6
|
+
}>;
|
|
7
|
+
export type ReactComponentImplementedEvent = Event<'ReactComponentImplemented', {
|
|
8
|
+
name: string;
|
|
9
|
+
componentPath: string;
|
|
10
|
+
storyPath: string;
|
|
11
|
+
iterations: number;
|
|
12
|
+
}>;
|
|
13
|
+
export type ReactComponentImplementationFailedEvent = Event<'ReactComponentImplementationFailed', {
|
|
14
|
+
error: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}>;
|
|
17
|
+
export type ImplementReactComponentEvents = ReactComponentImplementedEvent | ReactComponentImplementationFailedEvent;
|
|
18
|
+
export declare const commandHandler: import("@auto-engineer/message-bus").UnifiedCommandHandler<Readonly<{
|
|
19
|
+
type: string;
|
|
20
|
+
data: Readonly<Record<string, unknown>>;
|
|
21
|
+
timestamp?: Date;
|
|
22
|
+
requestId?: string;
|
|
23
|
+
correlationId?: string;
|
|
24
|
+
}>>;
|
|
25
|
+
export default commandHandler;
|
|
26
|
+
//# sourceMappingURL=implement-react-component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement-react-component.d.ts","sourceRoot":"","sources":["../../../src/commands/implement-react-component.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAwB,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAc5F,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,aAAa,CAAC;AAoItD,MAAM,MAAM,8BAA8B,GAAG,OAAO,CAClD,yBAAyB,EACzB;IACE,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,GAAG,CAAC;CACV,CACF,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAChD,2BAA2B,EAC3B;IACE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CACF,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG,KAAK,CACzD,oCAAoC,EACpC;IACE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CACF,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,8BAA8B,GAAG,uCAAuC,CAAC;AA4GrH,eAAO,MAAM,cAAc;;;;;;GAiIzB,CAAC;AAEH,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
4
|
+
import { defineCommandHandler } from '@auto-engineer/message-bus';
|
|
5
|
+
import createDebug from 'debug';
|
|
6
|
+
import { chromium } from 'playwright';
|
|
7
|
+
import { componentName, createComponentGenerator } from '../component-generator.js';
|
|
8
|
+
import { writeComponent, writeStory } from '../component-writer.js';
|
|
9
|
+
import { scanFileTree } from '../file-tree.js';
|
|
10
|
+
import { validateFunctional } from '../functional-validator.js';
|
|
11
|
+
import { connectMcpClient } from '../mcp-client.js';
|
|
12
|
+
import { checkTypes } from '../type-checker.js';
|
|
13
|
+
import { evaluateVisual } from '../visual-evaluator.js';
|
|
14
|
+
const debug = createDebug('auto:react-component-implementer:command');
|
|
15
|
+
const PORT = 6006;
|
|
16
|
+
const MAX_ITERATIONS = 3;
|
|
17
|
+
async function runValidationLoop(params) {
|
|
18
|
+
const { task, targetDir, outputDir, generator, context, mcpClient, browser, model, designReference, initialCode } = params;
|
|
19
|
+
const name = componentName(task.componentId);
|
|
20
|
+
let { code, history } = initialCode;
|
|
21
|
+
let componentPath = await writeComponent(name, code.componentCode, outputDir);
|
|
22
|
+
let storyPath = await writeStory(name, code.storyCode, outputDir);
|
|
23
|
+
let lastFeedback;
|
|
24
|
+
for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
25
|
+
console.log(` Iteration ${iteration}/${MAX_ITERATIONS} for ${name}`);
|
|
26
|
+
const typeResult = checkTypes([componentPath, storyPath], targetDir);
|
|
27
|
+
if (!typeResult.passed) {
|
|
28
|
+
console.log(' Type errors found, skipping functional/visual validation');
|
|
29
|
+
lastFeedback = collectFeedback(typeResult, [], []);
|
|
30
|
+
console.log(` Errors: ${summarizeFeedback(lastFeedback)}`);
|
|
31
|
+
if (iteration < MAX_ITERATIONS) {
|
|
32
|
+
console.log(` Refining ${name}...`);
|
|
33
|
+
const refined = await generator.refine(lastFeedback, context, history);
|
|
34
|
+
code = refined.code;
|
|
35
|
+
history = refined.history;
|
|
36
|
+
componentPath = await writeComponent(name, code.componentCode, outputDir);
|
|
37
|
+
storyPath = await writeStory(name, code.storyCode, outputDir);
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const storyExports = extractStoryExports(code.storyCode);
|
|
42
|
+
if (storyExports.length === 0) {
|
|
43
|
+
console.log(` Warning: no named exports found in story for ${name}, skipping validation`);
|
|
44
|
+
return { success: true, componentPath, storyPath, iterations: iteration };
|
|
45
|
+
}
|
|
46
|
+
const storyUrls = await waitForStoryUrls(mcpClient, storyExports, storyPath);
|
|
47
|
+
const storyResults = [];
|
|
48
|
+
for (const { exportName, url } of storyUrls) {
|
|
49
|
+
console.log(` Validating story ${exportName}...`);
|
|
50
|
+
const functionalResult = await validateFunctional(url, browser);
|
|
51
|
+
// Retry visual evaluation up to 3 times; if it passes early, stop.
|
|
52
|
+
// After 3 attempts, consider it a success regardless.
|
|
53
|
+
const MAX_VISUAL_ATTEMPTS = 3;
|
|
54
|
+
let visualResult = { score: 0, passed: false, feedback: '' };
|
|
55
|
+
for (let v = 1; v <= MAX_VISUAL_ATTEMPTS; v++) {
|
|
56
|
+
console.log(` Visual check ${v}/${MAX_VISUAL_ATTEMPTS} for ${exportName}...`);
|
|
57
|
+
visualResult = await evaluateVisual(url, { name, description: task.description }, browser, model, designReference);
|
|
58
|
+
if (visualResult.passed)
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
// After all attempts, visual always passes
|
|
62
|
+
visualResult.passed = true;
|
|
63
|
+
storyResults.push({ functionalResult, visualResult });
|
|
64
|
+
}
|
|
65
|
+
const functionalResults = storyResults.map((r) => r.functionalResult);
|
|
66
|
+
const visualResults = storyResults.map((r) => r.visualResult);
|
|
67
|
+
if (isPassing(typeResult, functionalResults, visualResults)) {
|
|
68
|
+
console.log(` ${name} passed all validations on iteration ${iteration}`);
|
|
69
|
+
return { success: true, componentPath, storyPath, iterations: iteration };
|
|
70
|
+
}
|
|
71
|
+
lastFeedback = collectFeedback(typeResult, functionalResults, visualResults);
|
|
72
|
+
console.log(` Errors: ${summarizeFeedback(lastFeedback)}`);
|
|
73
|
+
if (iteration < MAX_ITERATIONS) {
|
|
74
|
+
console.log(` Refining ${name}...`);
|
|
75
|
+
const refined = await generator.refine(lastFeedback, context, history);
|
|
76
|
+
code = refined.code;
|
|
77
|
+
history = refined.history;
|
|
78
|
+
componentPath = await writeComponent(name, code.componentCode, outputDir);
|
|
79
|
+
storyPath = await writeStory(name, code.storyCode, outputDir);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
console.log(` ${name} failed after ${MAX_ITERATIONS} iterations`);
|
|
83
|
+
return { success: false, componentPath, storyPath, iterations: MAX_ITERATIONS, lastFeedback };
|
|
84
|
+
}
|
|
85
|
+
async function checkStorybookRunning() {
|
|
86
|
+
try {
|
|
87
|
+
const response = await fetch(`http://localhost:${PORT}`);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error('Storybook responded with non-OK status');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw new Error(`Storybook is not running on port ${PORT}. Start it with \`pnpm storybook\` in the client directory before running this command.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function delay(ms) {
|
|
97
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
98
|
+
}
|
|
99
|
+
function collectFeedback(typeResult, functionalResults, visualResults) {
|
|
100
|
+
return {
|
|
101
|
+
typeErrors: typeResult.errors,
|
|
102
|
+
interactionErrors: functionalResults.flatMap((r) => r.interactionErrors),
|
|
103
|
+
consoleErrors: functionalResults.flatMap((r) => r.consoleErrors),
|
|
104
|
+
consoleWarnings: functionalResults.flatMap((r) => r.consoleWarnings),
|
|
105
|
+
visualFeedback: visualResults
|
|
106
|
+
.filter((r) => !r.passed)
|
|
107
|
+
.map((r) => r.feedback)
|
|
108
|
+
.join('\n') || undefined,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function summarizeFeedback(feedback) {
|
|
112
|
+
const parts = [];
|
|
113
|
+
const typeErrors = feedback.typeErrors ?? [];
|
|
114
|
+
const interactionErrors = feedback.interactionErrors ?? [];
|
|
115
|
+
const consoleErrors = feedback.consoleErrors ?? [];
|
|
116
|
+
const consoleWarnings = feedback.consoleWarnings ?? [];
|
|
117
|
+
if (typeErrors.length > 0) {
|
|
118
|
+
parts.push(`${typeErrors.length} type error(s): ${typeErrors[0]}`);
|
|
119
|
+
}
|
|
120
|
+
if (interactionErrors.length > 0) {
|
|
121
|
+
parts.push(`${interactionErrors.length} interaction error(s): ${interactionErrors[0]}`);
|
|
122
|
+
}
|
|
123
|
+
if (consoleErrors.length > 0) {
|
|
124
|
+
parts.push(`${consoleErrors.length} console error(s): ${consoleErrors[0]}`);
|
|
125
|
+
}
|
|
126
|
+
if (consoleWarnings.length > 0) {
|
|
127
|
+
parts.push(`${consoleWarnings.length} console warning(s): ${consoleWarnings[0]}`);
|
|
128
|
+
}
|
|
129
|
+
if (feedback.visualFeedback) {
|
|
130
|
+
parts.push(`visual: ${feedback.visualFeedback.split('\n')[0]}`);
|
|
131
|
+
}
|
|
132
|
+
return parts.join('; ');
|
|
133
|
+
}
|
|
134
|
+
function isPassing(typeResult, functionalResults, visualResults) {
|
|
135
|
+
return typeResult.passed && functionalResults.every((r) => r.passed) && visualResults.every((r) => r.passed);
|
|
136
|
+
}
|
|
137
|
+
function extractStoryExports(storyCode) {
|
|
138
|
+
const results = [];
|
|
139
|
+
const regex = /export\s+const\s+(\w+)/g;
|
|
140
|
+
let match = regex.exec(storyCode);
|
|
141
|
+
while (match) {
|
|
142
|
+
results.push(match[1]);
|
|
143
|
+
match = regex.exec(storyCode);
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
function toIframeUrl(url) {
|
|
148
|
+
const parsed = new URL(url);
|
|
149
|
+
const pathParam = parsed.searchParams.get('path');
|
|
150
|
+
if (pathParam && !parsed.pathname.includes('iframe.html')) {
|
|
151
|
+
const storyId = pathParam.replace(/^\/story\//, '');
|
|
152
|
+
parsed.pathname = '/iframe.html';
|
|
153
|
+
parsed.searchParams.delete('path');
|
|
154
|
+
parsed.searchParams.set('id', storyId);
|
|
155
|
+
return parsed.toString();
|
|
156
|
+
}
|
|
157
|
+
return url;
|
|
158
|
+
}
|
|
159
|
+
async function waitForStoryUrls(mcpClient, exportNames, storyPath, maxAttempts = 10, interval = 3000) {
|
|
160
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
161
|
+
const result = await mcpClient.getStoryUrl(exportNames[0], storyPath);
|
|
162
|
+
if (result.startsWith('http'))
|
|
163
|
+
break;
|
|
164
|
+
console.log(result);
|
|
165
|
+
console.log(` Story not indexed yet, retrying (${attempt + 1}/${maxAttempts})...`);
|
|
166
|
+
if (attempt === maxAttempts - 1) {
|
|
167
|
+
throw new Error(`Story not found after ${maxAttempts} attempts: ${storyPath}`);
|
|
168
|
+
}
|
|
169
|
+
await delay(interval);
|
|
170
|
+
}
|
|
171
|
+
const results = await Promise.all(exportNames.map(async (exportName) => {
|
|
172
|
+
const url = await mcpClient.getStoryUrl(exportName, storyPath);
|
|
173
|
+
return { exportName, url: toIframeUrl(url) };
|
|
174
|
+
}));
|
|
175
|
+
return results;
|
|
176
|
+
}
|
|
177
|
+
export const commandHandler = defineCommandHandler({
|
|
178
|
+
name: 'ImplementReactComponent',
|
|
179
|
+
displayName: 'Implement React Component',
|
|
180
|
+
alias: 'implement:react-component',
|
|
181
|
+
description: 'Generate a React/shadcn component with Storybook story, validated through type-checking, functional testing, and visual evaluation',
|
|
182
|
+
category: 'implement',
|
|
183
|
+
icon: 'component',
|
|
184
|
+
fields: {
|
|
185
|
+
targetDir: {
|
|
186
|
+
description: 'The client project root directory (components will be written to src/components/ui/)',
|
|
187
|
+
required: true,
|
|
188
|
+
},
|
|
189
|
+
job: {
|
|
190
|
+
description: 'Job object with payload containing component details (componentId, description, type, prompt)',
|
|
191
|
+
required: true,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
examples: [
|
|
195
|
+
'$ auto implement:react-component --target-dir=./client --job=\'{"id":"job_1","dependsOn":[],"target":"ImplementReactComponent","payload":{"componentId":"button","description":"A button","type":"component","prompt":"Create a Button"}}\'',
|
|
196
|
+
],
|
|
197
|
+
events: [
|
|
198
|
+
{ name: 'ReactComponentImplemented', displayName: 'React Component Implemented' },
|
|
199
|
+
{ name: 'ReactComponentImplementationFailed', displayName: 'React Component Implementation Failed' },
|
|
200
|
+
],
|
|
201
|
+
handle: async (command) => {
|
|
202
|
+
const { targetDir, job } = command.data;
|
|
203
|
+
const task = job.payload;
|
|
204
|
+
const name = componentName(task.componentId);
|
|
205
|
+
debug('Implementing React component: %s in %s', name, targetDir);
|
|
206
|
+
let browser;
|
|
207
|
+
let mcpClient;
|
|
208
|
+
try {
|
|
209
|
+
debug('Checking Storybook on port %d...', PORT);
|
|
210
|
+
await checkStorybookRunning();
|
|
211
|
+
debug('Connecting MCP client...');
|
|
212
|
+
mcpClient = await connectMcpClient({ baseUrl: `http://localhost:${PORT}` });
|
|
213
|
+
debug('Fetching generation context...');
|
|
214
|
+
const outputDir = path.resolve(targetDir, 'src', 'components', 'ui');
|
|
215
|
+
const [uiBuildingInstructions, existingComponents, fileTree] = await Promise.all([
|
|
216
|
+
mcpClient.getUiBuildingInstructions(),
|
|
217
|
+
mcpClient.listComponents(),
|
|
218
|
+
scanFileTree(outputDir, { readdir }),
|
|
219
|
+
]);
|
|
220
|
+
const context = { uiBuildingInstructions, existingComponents, fileTree };
|
|
221
|
+
const modelName = process.env.DEFAULT_AI_MODEL ?? 'claude-sonnet-4-20250514';
|
|
222
|
+
debug('Using AI model: %s', modelName);
|
|
223
|
+
const model = createAnthropic()(modelName);
|
|
224
|
+
debug('Launching browser...');
|
|
225
|
+
browser = await chromium.launch();
|
|
226
|
+
debug('Capturing design references...');
|
|
227
|
+
// const designReference = await captureDesignReferences(browser, PORT, existingComponents, uiBuildingInstructions);
|
|
228
|
+
const designReference = {
|
|
229
|
+
screenshots: [],
|
|
230
|
+
instructions: '',
|
|
231
|
+
};
|
|
232
|
+
const generator = createComponentGenerator(model);
|
|
233
|
+
debug('Generating component: %s', name);
|
|
234
|
+
const initialCode = await generator.generate(task, context);
|
|
235
|
+
const resolvedTargetDir = path.resolve(targetDir);
|
|
236
|
+
const result = await runValidationLoop({
|
|
237
|
+
task,
|
|
238
|
+
targetDir: resolvedTargetDir,
|
|
239
|
+
outputDir,
|
|
240
|
+
generator,
|
|
241
|
+
context,
|
|
242
|
+
mcpClient,
|
|
243
|
+
browser,
|
|
244
|
+
model,
|
|
245
|
+
designReference,
|
|
246
|
+
initialCode,
|
|
247
|
+
});
|
|
248
|
+
if (result.success) {
|
|
249
|
+
return {
|
|
250
|
+
type: 'ReactComponentImplemented',
|
|
251
|
+
data: {
|
|
252
|
+
name,
|
|
253
|
+
componentPath: result.componentPath,
|
|
254
|
+
storyPath: result.storyPath,
|
|
255
|
+
iterations: result.iterations,
|
|
256
|
+
},
|
|
257
|
+
timestamp: new Date(),
|
|
258
|
+
requestId: command.requestId,
|
|
259
|
+
correlationId: command.correlationId,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
type: 'ReactComponentImplementationFailed',
|
|
264
|
+
data: {
|
|
265
|
+
error: `Failed after ${MAX_ITERATIONS} iterations. Last feedback: ${result.lastFeedback ? summarizeFeedback(result.lastFeedback) : 'none'}`,
|
|
266
|
+
name,
|
|
267
|
+
},
|
|
268
|
+
timestamp: new Date(),
|
|
269
|
+
requestId: command.requestId,
|
|
270
|
+
correlationId: command.correlationId,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
275
|
+
debug('Implementation failed: %s', errorMessage);
|
|
276
|
+
return {
|
|
277
|
+
type: 'ReactComponentImplementationFailed',
|
|
278
|
+
data: { error: errorMessage, name },
|
|
279
|
+
timestamp: new Date(),
|
|
280
|
+
requestId: command.requestId,
|
|
281
|
+
correlationId: command.correlationId,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
if (browser) {
|
|
286
|
+
await browser.close();
|
|
287
|
+
}
|
|
288
|
+
if (mcpClient) {
|
|
289
|
+
await mcpClient.close();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
export default commandHandler;
|
|
295
|
+
//# sourceMappingURL=implement-react-component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement-react-component.js","sourceRoot":"","sources":["../../../src/commands/implement-react-component.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
|
+
import type { ComponentTask } from './types.js';
|
|
3
|
+
export declare function componentName(componentId: string): string;
|
|
4
|
+
export interface GeneratedCode {
|
|
5
|
+
componentCode: string;
|
|
6
|
+
storyCode: string;
|
|
7
|
+
}
|
|
8
|
+
export interface GenerationContext {
|
|
9
|
+
uiBuildingInstructions: string;
|
|
10
|
+
existingComponents: string;
|
|
11
|
+
fileTree: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ValidationFeedback {
|
|
14
|
+
typeErrors?: string[];
|
|
15
|
+
interactionErrors?: string[];
|
|
16
|
+
consoleErrors?: string[];
|
|
17
|
+
consoleWarnings?: string[];
|
|
18
|
+
visualFeedback?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface Message {
|
|
21
|
+
role: 'user' | 'assistant';
|
|
22
|
+
content: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RefinementHistory {
|
|
25
|
+
messages: Message[];
|
|
26
|
+
}
|
|
27
|
+
export declare function createComponentGenerator(model: LanguageModel): {
|
|
28
|
+
generate(task: ComponentTask, context: GenerationContext): Promise<{
|
|
29
|
+
code: GeneratedCode;
|
|
30
|
+
history: RefinementHistory;
|
|
31
|
+
}>;
|
|
32
|
+
refine(feedback: ValidationFeedback, context: GenerationContext, history: RefinementHistory): Promise<{
|
|
33
|
+
code: GeneratedCode;
|
|
34
|
+
history: RefinementHistory;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=component-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-generator.d.ts","sourceRoot":"","sources":["../../src/component-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAgMD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa;mBAGjD,aAAa,WACV,iBAAiB,GACzB,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,iBAAiB,CAAA;KAAE,CAAC;qBA4BnD,kBAAkB,WACnB,iBAAiB,WACjB,iBAAiB,GACzB,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,iBAAiB,CAAA;KAAE,CAAC;EA6BlE"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { generateText } from 'ai';
|
|
2
|
+
export function componentName(componentId) {
|
|
3
|
+
return componentId
|
|
4
|
+
.split('_')
|
|
5
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
6
|
+
.join('');
|
|
7
|
+
}
|
|
8
|
+
function buildSystemPrompt(context) {
|
|
9
|
+
const lines = [
|
|
10
|
+
'You are a React component generator for a shadcn/ui design system.',
|
|
11
|
+
'Tech stack: React, react-router-dom, shadcn/ui, Tailwind CSS.',
|
|
12
|
+
'Do not use Next.js or any Next.js-specific APIs (no next/link, next/image, next/router, useRouter from next/navigation, server components, etc.). Use react-router-dom for navigation (Link, useNavigate, useParams).',
|
|
13
|
+
'Follow these conventions:',
|
|
14
|
+
'- Use Tailwind CSS for styling',
|
|
15
|
+
'- Use the data-slot pattern for component identification (e.g. data-slot="button")',
|
|
16
|
+
'- Export components as named exports',
|
|
17
|
+
'- Stories must use play functions for interaction testing',
|
|
18
|
+
'- Use React.forwardRef where appropriate',
|
|
19
|
+
'- Story titles must be just the component name (e.g. title: "Button"), never with folder prefixes',
|
|
20
|
+
'- Import existing components from "@/components/ui/{kebab-name}" (e.g. import { Button } from "@/components/ui/button")',
|
|
21
|
+
'- The parenthesized name in the existing components list is the file name for imports',
|
|
22
|
+
'- In story files, import the component being tested using a relative path (e.g. import { BookCard } from "./book-card")',
|
|
23
|
+
'- When a component would benefit from imagery (e.g. cards, lists, heroes, profiles, products), use Unsplash image URLs (e.g. "https://images.unsplash.com/photo-{id}?w=300&h=200&fit=crop") to make the component visually rich and realistic. Pick real Unsplash photo IDs that match the content (e.g. a food photo for a restaurant card, a book cover for a bookstore). Use a different photo per item for visual variety.',
|
|
24
|
+
'',
|
|
25
|
+
'UI Building Instructions:',
|
|
26
|
+
context.uiBuildingInstructions,
|
|
27
|
+
'',
|
|
28
|
+
'Existing Components:',
|
|
29
|
+
context.existingComponents,
|
|
30
|
+
'',
|
|
31
|
+
'File Tree:',
|
|
32
|
+
context.fileTree,
|
|
33
|
+
'Use these exact file paths when constructing import statements. Do not guess or invent file paths.',
|
|
34
|
+
'',
|
|
35
|
+
'GraphQL & Data Fetching (for components with data requirements):',
|
|
36
|
+
'- When a component has GraphQL requests, use @tanstack/react-query with graphql-request',
|
|
37
|
+
'- Import { useQuery, useMutation } from "@tanstack/react-query"',
|
|
38
|
+
'- Import { request, gql } from "graphql-request"',
|
|
39
|
+
'- Define the GraphQL document using gql`...` with the exact operation string provided',
|
|
40
|
+
'- Use a queryFn that calls request(endpoint, document, variables)',
|
|
41
|
+
'- Accept the GraphQL endpoint via props or use a default "/graphql"',
|
|
42
|
+
'- Handle loading (isPending) and error (isError) states in the component',
|
|
43
|
+
'',
|
|
44
|
+
'Storybook MSW Mocking (for components with GraphQL requests):',
|
|
45
|
+
'- Mock GraphQL operations in stories using MSW (Mock Service Worker) via msw-storybook-addon',
|
|
46
|
+
'- Import { graphql, HttpResponse } from "msw"',
|
|
47
|
+
'- Define handlers using graphql.query("OperationName", ...) or graphql.mutation("OperationName", ...)',
|
|
48
|
+
'- Attach handlers to each story via parameters.msw.handlers',
|
|
49
|
+
'- Provide realistic, visually rich mock data that matches the GraphQL response shape',
|
|
50
|
+
'- Include Unsplash image URLs in mock data whenever the component displays images (e.g. cover art, thumbnails, avatars, product photos). Use real Unsplash photo IDs that are contextually relevant, and a different photo per item for visual variety.',
|
|
51
|
+
'- Example:',
|
|
52
|
+
' export const Default: StoryObj = {',
|
|
53
|
+
' parameters: {',
|
|
54
|
+
' msw: {',
|
|
55
|
+
' handlers: [',
|
|
56
|
+
' graphql.query("SearchBooks", () => HttpResponse.json({',
|
|
57
|
+
' data: { searchBooks: { results: [',
|
|
58
|
+
' { bookId: "1", title: "The Great Gatsby", author: "F. Scott Fitzgerald", price: 12.99, coverUrl: "https://images.unsplash.com/photo-1544947950-fa07a98d237f?w=200&h=300&fit=crop" },',
|
|
59
|
+
' { bookId: "2", title: "To Kill a Mockingbird", author: "Harper Lee", price: 14.99, coverUrl: "https://images.unsplash.com/photo-1512820790803-83ca734da794?w=200&h=300&fit=crop" },',
|
|
60
|
+
' ] } }',
|
|
61
|
+
' }))',
|
|
62
|
+
' ]',
|
|
63
|
+
' }',
|
|
64
|
+
' }',
|
|
65
|
+
' }',
|
|
66
|
+
'- Each story variant should have its own handlers (e.g. loading, error, empty state)',
|
|
67
|
+
'- For error stories, return errors: graphql.query("Op", () => HttpResponse.json({ errors: [{ message: "Failed" }] }))',
|
|
68
|
+
'',
|
|
69
|
+
'Visual Hierarchy & Typography:',
|
|
70
|
+
'- Headlines: text-4xl or text-5xl font-bold tracking-tight',
|
|
71
|
+
'- Subheadings: text-2xl font-semibold',
|
|
72
|
+
'- Body text: text-base leading-relaxed',
|
|
73
|
+
'- Captions/metadata: text-sm text-muted-foreground',
|
|
74
|
+
'- Use consistent font weights: bold for emphasis, semibold for subheadings, normal for body',
|
|
75
|
+
'',
|
|
76
|
+
'Spacing Rhythm:',
|
|
77
|
+
'- Section gaps: space-y-16 or py-16 between major page sections',
|
|
78
|
+
'- Content block gaps: space-y-6 between related content groups',
|
|
79
|
+
'- Element spacing: space-y-3 or gap-3 between individual elements',
|
|
80
|
+
'- Use consistent padding: p-6 for cards, p-4 for compact elements, p-8 for spacious sections',
|
|
81
|
+
'',
|
|
82
|
+
'Interactive States:',
|
|
83
|
+
'- Hover: hover:shadow-lg, hover:bg-accent, hover:scale-[1.02]',
|
|
84
|
+
'- Focus: focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
85
|
+
'- Active: active:scale-95',
|
|
86
|
+
'- Transitions: transition-all duration-200 for smooth state changes',
|
|
87
|
+
'- Disabled: opacity-50 cursor-not-allowed',
|
|
88
|
+
'',
|
|
89
|
+
'Layout Architecture Patterns:',
|
|
90
|
+
'- Full-height layout: <div className="h-screen flex flex-col"><header className="flex-shrink-0">...</header><main className="flex-1 overflow-auto">...</main></div>',
|
|
91
|
+
'- Sidebar + main: <div className="h-full flex"><aside className="w-64 flex-shrink-0 border-r">...</aside><main className="flex-1 overflow-auto">...</main></div>',
|
|
92
|
+
'- Scrollable content area: Use flex-1 overflow-auto on the scrollable container, flex-shrink-0 on fixed elements',
|
|
93
|
+
'- Centered content: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
|
|
94
|
+
'- Grid layouts: grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6',
|
|
95
|
+
'',
|
|
96
|
+
'Error Prevention Rules:',
|
|
97
|
+
'- Zustand selectors: NEVER use object-literal selectors like useStore(s => ({ a: s.a, b: s.b })). Select primitives individually: const a = useStore(s => s.a)',
|
|
98
|
+
'- Render loops: NEVER call setState during render. Use useEffect for side effects with stable dependency arrays',
|
|
99
|
+
'- Import validation: Verify named vs default exports. Use correct import paths matching the file tree above',
|
|
100
|
+
'- Dependency validation: Only import packages that are available. Do not import from packages not listed in the file tree or existing components',
|
|
101
|
+
'',
|
|
102
|
+
'BAD vs GOOD Examples:',
|
|
103
|
+
'- BAD: const { a, b } = useStore(s => ({ a: s.a, b: s.b })) — creates new object every render, causes infinite re-renders',
|
|
104
|
+
'- GOOD: const a = useStore(s => s.a); const b = useStore(s => s.b); — stable primitive selectors',
|
|
105
|
+
'- BAD: import { Button } from "shadcn/ui" — wrong import path',
|
|
106
|
+
'- GOOD: import { Button } from "@/components/ui/button" — correct shadcn/ui import path',
|
|
107
|
+
'- BAD: import Link from "react-router-dom" — wrong, Link is a named export',
|
|
108
|
+
'- GOOD: import { Link } from "react-router-dom" — correct named import',
|
|
109
|
+
'',
|
|
110
|
+
'Respond with exactly two tsx code blocks.',
|
|
111
|
+
'The first code block is the component implementation.',
|
|
112
|
+
'The second code block is the Storybook story.',
|
|
113
|
+
];
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
function buildUserPrompt(task) {
|
|
117
|
+
const name = componentName(task.componentId);
|
|
118
|
+
const lines = [
|
|
119
|
+
`Component: ${name}`,
|
|
120
|
+
`Description: ${task.description}`,
|
|
121
|
+
`Level: ${task.type}`,
|
|
122
|
+
'',
|
|
123
|
+
`Implementation: ${task.implementation}`,
|
|
124
|
+
'',
|
|
125
|
+
'Acceptance Criteria:',
|
|
126
|
+
...task.acceptanceCriteria.map((c) => ` - ${c}`),
|
|
127
|
+
];
|
|
128
|
+
if (task.prompt) {
|
|
129
|
+
lines.push('', 'Additional Instructions:', task.prompt);
|
|
130
|
+
}
|
|
131
|
+
if (task.files.create.length > 0) {
|
|
132
|
+
lines.push('', 'Files to create:', ...task.files.create.map((f) => ` - ${f}`));
|
|
133
|
+
}
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
function parseCodeBlocks(text) {
|
|
137
|
+
const codeBlockRegex = /```tsx\n([\s\S]*?)```/g;
|
|
138
|
+
const matches = [];
|
|
139
|
+
let match = codeBlockRegex.exec(text);
|
|
140
|
+
while (match) {
|
|
141
|
+
matches.push(match[1].trim());
|
|
142
|
+
match = codeBlockRegex.exec(text);
|
|
143
|
+
}
|
|
144
|
+
if (matches.length < 2) {
|
|
145
|
+
throw new Error(`Expected 2 tsx code blocks but found ${matches.length}`);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
componentCode: matches[0],
|
|
149
|
+
storyCode: matches[1],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function buildFeedbackPrompt(feedback) {
|
|
153
|
+
const lines = [
|
|
154
|
+
'The component and story code you generated above had validation issues.',
|
|
155
|
+
'',
|
|
156
|
+
'Validation errors:',
|
|
157
|
+
];
|
|
158
|
+
if (feedback.typeErrors && feedback.typeErrors.length > 0) {
|
|
159
|
+
lines.push('', 'TypeScript errors:', ...feedback.typeErrors.map((e) => ` - ${e}`));
|
|
160
|
+
}
|
|
161
|
+
if (feedback.interactionErrors && feedback.interactionErrors.length > 0) {
|
|
162
|
+
lines.push('', 'Interaction errors:', ...feedback.interactionErrors.map((e) => ` - ${e}`));
|
|
163
|
+
}
|
|
164
|
+
if (feedback.consoleErrors && feedback.consoleErrors.length > 0) {
|
|
165
|
+
lines.push('', 'Console errors:', ...feedback.consoleErrors.map((e) => ` - ${e}`));
|
|
166
|
+
}
|
|
167
|
+
if (feedback.consoleWarnings && feedback.consoleWarnings.length > 0) {
|
|
168
|
+
lines.push('', 'Console warnings:', ...feedback.consoleWarnings.map((e) => ` - ${e}`));
|
|
169
|
+
}
|
|
170
|
+
if (feedback.visualFeedback) {
|
|
171
|
+
lines.push('', 'Visual feedback:', ` ${feedback.visualFeedback}`);
|
|
172
|
+
}
|
|
173
|
+
lines.push('', 'Fix all the issues above. Respond with exactly two tsx code blocks.', 'The first code block is the fixed component implementation.', 'The second code block is the fixed Storybook story.');
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
export function createComponentGenerator(model) {
|
|
177
|
+
return {
|
|
178
|
+
async generate(task, context) {
|
|
179
|
+
const system = buildSystemPrompt(context);
|
|
180
|
+
const prompt = buildUserPrompt(task);
|
|
181
|
+
const { text } = await generateText({
|
|
182
|
+
model,
|
|
183
|
+
messages: [
|
|
184
|
+
{
|
|
185
|
+
role: 'system',
|
|
186
|
+
content: system,
|
|
187
|
+
providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' } } },
|
|
188
|
+
},
|
|
189
|
+
{ role: 'user', content: prompt },
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
const code = parseCodeBlocks(text);
|
|
193
|
+
const history = {
|
|
194
|
+
messages: [
|
|
195
|
+
{ role: 'user', content: prompt },
|
|
196
|
+
{ role: 'assistant', content: text },
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
return { code, history };
|
|
200
|
+
},
|
|
201
|
+
async refine(feedback, context, history) {
|
|
202
|
+
const system = buildSystemPrompt(context);
|
|
203
|
+
const feedbackPrompt = buildFeedbackPrompt(feedback);
|
|
204
|
+
const { text } = await generateText({
|
|
205
|
+
model,
|
|
206
|
+
messages: [
|
|
207
|
+
{
|
|
208
|
+
role: 'system',
|
|
209
|
+
content: system,
|
|
210
|
+
providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' } } },
|
|
211
|
+
},
|
|
212
|
+
...history.messages,
|
|
213
|
+
{ role: 'user', content: feedbackPrompt },
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
const newCode = parseCodeBlocks(text);
|
|
217
|
+
const newHistory = {
|
|
218
|
+
messages: [
|
|
219
|
+
...history.messages,
|
|
220
|
+
{ role: 'user', content: feedbackPrompt },
|
|
221
|
+
{ role: 'assistant', content: text },
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
return { code: newCode, history: newHistory };
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=component-generator.js.map
|