@auto-engineer/component-implementor-react 1.102.0 → 1.103.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +54 -0
- package/dist/src/commands/implement-component.d.ts.map +1 -1
- package/dist/src/commands/implement-component.js +4 -0
- package/dist/src/commands/implement-component.js.map +1 -1
- package/dist/src/commands/implement-component.test.js +52 -0
- package/dist/src/commands/implement-component.test.js.map +1 -1
- package/dist/src/pipeline/run-pipeline.d.ts +8 -0
- package/dist/src/pipeline/run-pipeline.d.ts.map +1 -1
- package/dist/src/pipeline/run-pipeline.js +8 -0
- package/dist/src/pipeline/run-pipeline.js.map +1 -1
- package/dist/src/pipeline/run-pipeline.test.js +39 -3
- package/dist/src/pipeline/run-pipeline.test.js.map +1 -1
- package/dist/src/pipeline/steps/generate-component.test.js +4 -0
- package/dist/src/pipeline/steps/generate-component.test.js.map +1 -1
- package/dist/src/pipeline/steps/generate-story.test.js +4 -0
- package/dist/src/pipeline/steps/generate-story.test.js.map +1 -1
- package/dist/src/pipeline/steps/generate-test.test.js +4 -0
- package/dist/src/pipeline/steps/generate-test.test.js.map +1 -1
- package/dist/src/pipeline/steps/lint-fix-loop.d.ts.map +1 -1
- package/dist/src/pipeline/steps/lint-fix-loop.js +4 -3
- package/dist/src/pipeline/steps/lint-fix-loop.js.map +1 -1
- package/dist/src/pipeline/steps/lint-fix-loop.test.js +17 -0
- package/dist/src/pipeline/steps/lint-fix-loop.test.js.map +1 -1
- package/dist/src/pipeline/steps/story-fix-loop.d.ts.map +1 -1
- package/dist/src/pipeline/steps/story-fix-loop.js +1 -0
- package/dist/src/pipeline/steps/story-fix-loop.js.map +1 -1
- package/dist/src/pipeline/steps/story-fix-loop.test.js +4 -0
- package/dist/src/pipeline/steps/story-fix-loop.test.js.map +1 -1
- package/dist/src/pipeline/steps/storybook-test.test.js +4 -0
- package/dist/src/pipeline/steps/storybook-test.test.js.map +1 -1
- package/dist/src/pipeline/steps/test-fix-loop.d.ts.map +1 -1
- package/dist/src/pipeline/steps/test-fix-loop.js +1 -0
- package/dist/src/pipeline/steps/test-fix-loop.js.map +1 -1
- package/dist/src/pipeline/steps/test-fix-loop.test.js +4 -0
- package/dist/src/pipeline/steps/test-fix-loop.test.js.map +1 -1
- package/dist/src/pipeline/steps/type-fix-loop.d.ts.map +1 -1
- package/dist/src/pipeline/steps/type-fix-loop.js +1 -0
- package/dist/src/pipeline/steps/type-fix-loop.js.map +1 -1
- package/dist/src/pipeline/steps/type-fix-loop.test.js +4 -0
- package/dist/src/pipeline/steps/type-fix-loop.test.js.map +1 -1
- package/dist/src/prompt.d.ts +3 -3
- package/dist/src/prompt.d.ts.map +1 -1
- package/dist/src/prompt.js +29 -9
- package/dist/src/prompt.js.map +1 -1
- package/dist/src/prompt.test.js +143 -0
- package/dist/src/prompt.test.js.map +1 -1
- package/dist/src/tools/lint-runner.d.ts.map +1 -1
- package/dist/src/tools/lint-runner.js +9 -4
- package/dist/src/tools/lint-runner.js.map +1 -1
- package/dist/src/tools/lint-runner.test.js +3 -7
- package/dist/src/tools/lint-runner.test.js.map +1 -1
- package/dist/src/tools/test-runner.js +8 -4
- package/dist/src/tools/test-runner.js.map +1 -1
- package/dist/src/tools/test-runner.test.js +49 -3
- package/dist/src/tools/test-runner.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/scripts/generate-all.ts +673 -0
- package/src/commands/implement-component.test.ts +52 -0
- package/src/commands/implement-component.ts +4 -0
- package/src/pipeline/run-pipeline.test.ts +39 -3
- package/src/pipeline/run-pipeline.ts +16 -0
- package/src/pipeline/steps/generate-component.test.ts +4 -0
- package/src/pipeline/steps/generate-story.test.ts +4 -0
- package/src/pipeline/steps/generate-test.test.ts +4 -0
- package/src/pipeline/steps/lint-fix-loop.test.ts +21 -0
- package/src/pipeline/steps/lint-fix-loop.ts +5 -3
- package/src/pipeline/steps/story-fix-loop.test.ts +4 -0
- package/src/pipeline/steps/story-fix-loop.ts +1 -0
- package/src/pipeline/steps/storybook-test.test.ts +4 -0
- package/src/pipeline/steps/test-fix-loop.test.ts +4 -0
- package/src/pipeline/steps/test-fix-loop.ts +1 -0
- package/src/pipeline/steps/type-fix-loop.test.ts +4 -0
- package/src/pipeline/steps/type-fix-loop.ts +1 -0
- package/src/prompt.test.ts +176 -0
- package/src/prompt.ts +29 -9
- package/src/tools/lint-runner.test.ts +6 -7
- package/src/tools/lint-runner.ts +10 -4
- package/src/tools/test-runner.test.ts +55 -3
- package/src/tools/test-runner.ts +8 -4
|
@@ -70,6 +70,10 @@ describe('implement-component', () => {
|
|
|
70
70
|
success: true,
|
|
71
71
|
llmCalls: 4,
|
|
72
72
|
fixIterations: 1,
|
|
73
|
+
typeFixIterations: 0,
|
|
74
|
+
testFixIterations: 0,
|
|
75
|
+
lintFixIterations: 0,
|
|
76
|
+
storyFixIterations: 0,
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
const result = await handleImplementComponent(makeCommand());
|
|
@@ -102,6 +106,10 @@ describe('implement-component', () => {
|
|
|
102
106
|
error: 'Step "Type Fix Loop" failed: errors remain',
|
|
103
107
|
llmCalls: 5,
|
|
104
108
|
fixIterations: 3,
|
|
109
|
+
typeFixIterations: 0,
|
|
110
|
+
testFixIterations: 0,
|
|
111
|
+
lintFixIterations: 0,
|
|
112
|
+
storyFixIterations: 0,
|
|
105
113
|
});
|
|
106
114
|
|
|
107
115
|
const result = await handleImplementComponent(makeCommand());
|
|
@@ -125,6 +133,10 @@ describe('implement-component', () => {
|
|
|
125
133
|
success: true,
|
|
126
134
|
llmCalls: 3,
|
|
127
135
|
fixIterations: 0,
|
|
136
|
+
typeFixIterations: 0,
|
|
137
|
+
testFixIterations: 0,
|
|
138
|
+
lintFixIterations: 0,
|
|
139
|
+
storyFixIterations: 0,
|
|
128
140
|
});
|
|
129
141
|
|
|
130
142
|
const command = makeCommand({
|
|
@@ -182,6 +194,10 @@ describe('implement-component', () => {
|
|
|
182
194
|
success: true,
|
|
183
195
|
llmCalls: 4,
|
|
184
196
|
fixIterations: 1,
|
|
197
|
+
typeFixIterations: 0,
|
|
198
|
+
testFixIterations: 0,
|
|
199
|
+
lintFixIterations: 0,
|
|
200
|
+
storyFixIterations: 0,
|
|
185
201
|
});
|
|
186
202
|
|
|
187
203
|
const command = {
|
|
@@ -220,6 +236,10 @@ describe('implement-component', () => {
|
|
|
220
236
|
success: true,
|
|
221
237
|
llmCalls: 2,
|
|
222
238
|
fixIterations: 0,
|
|
239
|
+
typeFixIterations: 0,
|
|
240
|
+
testFixIterations: 0,
|
|
241
|
+
lintFixIterations: 0,
|
|
242
|
+
storyFixIterations: 0,
|
|
223
243
|
});
|
|
224
244
|
|
|
225
245
|
const command = {
|
|
@@ -262,6 +282,10 @@ describe('implement-component', () => {
|
|
|
262
282
|
success: true,
|
|
263
283
|
llmCalls: 4,
|
|
264
284
|
fixIterations: 0,
|
|
285
|
+
typeFixIterations: 0,
|
|
286
|
+
testFixIterations: 0,
|
|
287
|
+
lintFixIterations: 0,
|
|
288
|
+
storyFixIterations: 0,
|
|
265
289
|
});
|
|
266
290
|
|
|
267
291
|
const command = makeCommand({
|
|
@@ -308,6 +332,10 @@ describe('implement-component', () => {
|
|
|
308
332
|
success: true,
|
|
309
333
|
llmCalls: 2,
|
|
310
334
|
fixIterations: 0,
|
|
335
|
+
typeFixIterations: 0,
|
|
336
|
+
testFixIterations: 0,
|
|
337
|
+
lintFixIterations: 0,
|
|
338
|
+
storyFixIterations: 0,
|
|
311
339
|
});
|
|
312
340
|
|
|
313
341
|
await handleImplementComponent(makeCommand());
|
|
@@ -346,6 +374,10 @@ describe('implement-component', () => {
|
|
|
346
374
|
success: true,
|
|
347
375
|
llmCalls: 0,
|
|
348
376
|
fixIterations: 0,
|
|
377
|
+
typeFixIterations: 0,
|
|
378
|
+
testFixIterations: 0,
|
|
379
|
+
lintFixIterations: 0,
|
|
380
|
+
storyFixIterations: 0,
|
|
349
381
|
});
|
|
350
382
|
|
|
351
383
|
const result = await commandHandler.handle(makeCommand());
|
|
@@ -361,6 +393,10 @@ describe('implement-component', () => {
|
|
|
361
393
|
success: true,
|
|
362
394
|
llmCalls: 0,
|
|
363
395
|
fixIterations: 0,
|
|
396
|
+
typeFixIterations: 0,
|
|
397
|
+
testFixIterations: 0,
|
|
398
|
+
lintFixIterations: 0,
|
|
399
|
+
storyFixIterations: 0,
|
|
364
400
|
});
|
|
365
401
|
|
|
366
402
|
const command = makeCommand({
|
|
@@ -400,6 +436,10 @@ describe('implement-component', () => {
|
|
|
400
436
|
success: true,
|
|
401
437
|
llmCalls: 1,
|
|
402
438
|
fixIterations: 0,
|
|
439
|
+
typeFixIterations: 0,
|
|
440
|
+
testFixIterations: 0,
|
|
441
|
+
lintFixIterations: 0,
|
|
442
|
+
storyFixIterations: 0,
|
|
403
443
|
});
|
|
404
444
|
|
|
405
445
|
const command = makeCommand({
|
|
@@ -438,6 +478,10 @@ describe('implement-component', () => {
|
|
|
438
478
|
success: true,
|
|
439
479
|
llmCalls: 1,
|
|
440
480
|
fixIterations: 0,
|
|
481
|
+
typeFixIterations: 0,
|
|
482
|
+
testFixIterations: 0,
|
|
483
|
+
lintFixIterations: 0,
|
|
484
|
+
storyFixIterations: 0,
|
|
441
485
|
});
|
|
442
486
|
|
|
443
487
|
const command = makeCommand({
|
|
@@ -475,6 +519,10 @@ describe('implement-component', () => {
|
|
|
475
519
|
success: false,
|
|
476
520
|
llmCalls: 0,
|
|
477
521
|
fixIterations: 0,
|
|
522
|
+
typeFixIterations: 0,
|
|
523
|
+
testFixIterations: 0,
|
|
524
|
+
lintFixIterations: 0,
|
|
525
|
+
storyFixIterations: 0,
|
|
478
526
|
});
|
|
479
527
|
|
|
480
528
|
const result = await handleImplementComponent(makeCommand());
|
|
@@ -515,6 +563,10 @@ describe('implement-component', () => {
|
|
|
515
563
|
success: true,
|
|
516
564
|
llmCalls: 0,
|
|
517
565
|
fixIterations: 0,
|
|
566
|
+
typeFixIterations: 0,
|
|
567
|
+
testFixIterations: 0,
|
|
568
|
+
lintFixIterations: 0,
|
|
569
|
+
storyFixIterations: 0,
|
|
518
570
|
});
|
|
519
571
|
|
|
520
572
|
const command = makeCommand({
|
|
@@ -223,6 +223,10 @@ export async function handleImplementComponent(
|
|
|
223
223
|
storyVariants: payload.storyVariants,
|
|
224
224
|
llmCalls: 0,
|
|
225
225
|
fixIterations: 0,
|
|
226
|
+
typeFixIterations: 0,
|
|
227
|
+
testFixIterations: 0,
|
|
228
|
+
lintFixIterations: 0,
|
|
229
|
+
storyFixIterations: 0,
|
|
226
230
|
};
|
|
227
231
|
|
|
228
232
|
const config = buildPipelineConfig();
|
|
@@ -65,6 +65,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
65
65
|
isModify: false,
|
|
66
66
|
llmCalls: 0,
|
|
67
67
|
fixIterations: 0,
|
|
68
|
+
typeFixIterations: 0,
|
|
69
|
+
testFixIterations: 0,
|
|
70
|
+
lintFixIterations: 0,
|
|
71
|
+
storyFixIterations: 0,
|
|
68
72
|
...overrides,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
@@ -106,7 +110,15 @@ describe('runPipeline', () => {
|
|
|
106
110
|
|
|
107
111
|
const result = await runPipeline(steps, makeCtx());
|
|
108
112
|
|
|
109
|
-
expect(result).toEqual({
|
|
113
|
+
expect(result).toEqual({
|
|
114
|
+
success: true,
|
|
115
|
+
llmCalls: 0,
|
|
116
|
+
fixIterations: 0,
|
|
117
|
+
typeFixIterations: 0,
|
|
118
|
+
testFixIterations: 0,
|
|
119
|
+
lintFixIterations: 0,
|
|
120
|
+
storyFixIterations: 0,
|
|
121
|
+
});
|
|
110
122
|
expect(executionOrder).toEqual(['A', 'B', 'C']);
|
|
111
123
|
});
|
|
112
124
|
|
|
@@ -137,6 +149,10 @@ describe('runPipeline', () => {
|
|
|
137
149
|
error: 'Step "Step B" failed: something broke',
|
|
138
150
|
llmCalls: 0,
|
|
139
151
|
fixIterations: 0,
|
|
152
|
+
typeFixIterations: 0,
|
|
153
|
+
testFixIterations: 0,
|
|
154
|
+
lintFixIterations: 0,
|
|
155
|
+
storyFixIterations: 0,
|
|
140
156
|
});
|
|
141
157
|
expect(executionOrder).toEqual(['A']);
|
|
142
158
|
});
|
|
@@ -144,7 +160,15 @@ describe('runPipeline', () => {
|
|
|
144
160
|
it('returns success with empty steps', async () => {
|
|
145
161
|
const result = await runPipeline([], makeCtx());
|
|
146
162
|
|
|
147
|
-
expect(result).toEqual({
|
|
163
|
+
expect(result).toEqual({
|
|
164
|
+
success: true,
|
|
165
|
+
llmCalls: 0,
|
|
166
|
+
fixIterations: 0,
|
|
167
|
+
typeFixIterations: 0,
|
|
168
|
+
testFixIterations: 0,
|
|
169
|
+
lintFixIterations: 0,
|
|
170
|
+
storyFixIterations: 0,
|
|
171
|
+
});
|
|
148
172
|
});
|
|
149
173
|
|
|
150
174
|
it('reports accumulated llmCalls and fixIterations from context', async () => {
|
|
@@ -153,7 +177,15 @@ describe('runPipeline', () => {
|
|
|
153
177
|
|
|
154
178
|
const result = await runPipeline(steps, ctx);
|
|
155
179
|
|
|
156
|
-
expect(result).toEqual({
|
|
180
|
+
expect(result).toEqual({
|
|
181
|
+
success: true,
|
|
182
|
+
llmCalls: 5,
|
|
183
|
+
fixIterations: 3,
|
|
184
|
+
typeFixIterations: 0,
|
|
185
|
+
testFixIterations: 0,
|
|
186
|
+
lintFixIterations: 0,
|
|
187
|
+
storyFixIterations: 0,
|
|
188
|
+
});
|
|
157
189
|
});
|
|
158
190
|
|
|
159
191
|
it('reports context metrics on failure', async () => {
|
|
@@ -167,6 +199,10 @@ describe('runPipeline', () => {
|
|
|
167
199
|
error: 'Step "Broken" failed: oops',
|
|
168
200
|
llmCalls: 2,
|
|
169
201
|
fixIterations: 1,
|
|
202
|
+
typeFixIterations: 0,
|
|
203
|
+
testFixIterations: 0,
|
|
204
|
+
lintFixIterations: 0,
|
|
205
|
+
storyFixIterations: 0,
|
|
170
206
|
});
|
|
171
207
|
});
|
|
172
208
|
});
|
|
@@ -44,6 +44,10 @@ export type PipelineContext = {
|
|
|
44
44
|
storyCode?: string;
|
|
45
45
|
llmCalls: number;
|
|
46
46
|
fixIterations: number;
|
|
47
|
+
typeFixIterations: number;
|
|
48
|
+
testFixIterations: number;
|
|
49
|
+
lintFixIterations: number;
|
|
50
|
+
storyFixIterations: number;
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
export type ModelConfig = {
|
|
@@ -79,6 +83,10 @@ export type PipelineResult = {
|
|
|
79
83
|
error?: string;
|
|
80
84
|
llmCalls: number;
|
|
81
85
|
fixIterations: number;
|
|
86
|
+
typeFixIterations: number;
|
|
87
|
+
testFixIterations: number;
|
|
88
|
+
lintFixIterations: number;
|
|
89
|
+
storyFixIterations: number;
|
|
82
90
|
};
|
|
83
91
|
|
|
84
92
|
export function buildPipelineSteps(
|
|
@@ -146,6 +154,10 @@ export async function runPipeline(steps: PipelineStep[], ctx: PipelineContext):
|
|
|
146
154
|
error: `Step "${step.name}" failed: ${result.error}`,
|
|
147
155
|
llmCalls: ctx.llmCalls,
|
|
148
156
|
fixIterations: ctx.fixIterations,
|
|
157
|
+
typeFixIterations: ctx.typeFixIterations,
|
|
158
|
+
testFixIterations: ctx.testFixIterations,
|
|
159
|
+
lintFixIterations: ctx.lintFixIterations,
|
|
160
|
+
storyFixIterations: ctx.storyFixIterations,
|
|
149
161
|
};
|
|
150
162
|
}
|
|
151
163
|
|
|
@@ -156,5 +168,9 @@ export async function runPipeline(steps: PipelineStep[], ctx: PipelineContext):
|
|
|
156
168
|
success: true,
|
|
157
169
|
llmCalls: ctx.llmCalls,
|
|
158
170
|
fixIterations: ctx.fixIterations,
|
|
171
|
+
typeFixIterations: ctx.typeFixIterations,
|
|
172
|
+
testFixIterations: ctx.testFixIterations,
|
|
173
|
+
lintFixIterations: ctx.lintFixIterations,
|
|
174
|
+
storyFixIterations: ctx.storyFixIterations,
|
|
159
175
|
};
|
|
160
176
|
}
|
|
@@ -37,6 +37,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
37
37
|
isModify: false,
|
|
38
38
|
llmCalls: 0,
|
|
39
39
|
fixIterations: 0,
|
|
40
|
+
typeFixIterations: 0,
|
|
41
|
+
testFixIterations: 0,
|
|
42
|
+
lintFixIterations: 0,
|
|
43
|
+
storyFixIterations: 0,
|
|
40
44
|
testCode: 'import { render } from "@testing-library/react";',
|
|
41
45
|
...overrides,
|
|
42
46
|
};
|
|
@@ -22,6 +22,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
22
22
|
isModify: false,
|
|
23
23
|
llmCalls: 0,
|
|
24
24
|
fixIterations: 0,
|
|
25
|
+
typeFixIterations: 0,
|
|
26
|
+
testFixIterations: 0,
|
|
27
|
+
lintFixIterations: 0,
|
|
28
|
+
storyFixIterations: 0,
|
|
25
29
|
...overrides,
|
|
26
30
|
};
|
|
27
31
|
}
|
|
@@ -36,6 +36,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
36
36
|
isModify: false,
|
|
37
37
|
llmCalls: 0,
|
|
38
38
|
fixIterations: 0,
|
|
39
|
+
typeFixIterations: 0,
|
|
40
|
+
testFixIterations: 0,
|
|
41
|
+
lintFixIterations: 0,
|
|
42
|
+
storyFixIterations: 0,
|
|
39
43
|
...overrides,
|
|
40
44
|
};
|
|
41
45
|
}
|
|
@@ -38,6 +38,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
38
38
|
isModify: false,
|
|
39
39
|
llmCalls: 0,
|
|
40
40
|
fixIterations: 0,
|
|
41
|
+
typeFixIterations: 0,
|
|
42
|
+
testFixIterations: 0,
|
|
43
|
+
lintFixIterations: 0,
|
|
44
|
+
storyFixIterations: 0,
|
|
41
45
|
componentCode: 'original component',
|
|
42
46
|
testCode: 'original test',
|
|
43
47
|
...overrides,
|
|
@@ -132,6 +136,23 @@ describe('lintFixLoop', () => {
|
|
|
132
136
|
expect(result).toEqual({ success: true });
|
|
133
137
|
});
|
|
134
138
|
|
|
139
|
+
it('re-runs auto-fix after each LLM write to handle formatting issues', async () => {
|
|
140
|
+
vi.mocked(runLintFix).mockReturnValue({ passed: true, errors: [] });
|
|
141
|
+
vi.mocked(readFile).mockResolvedValue('auto-fixed' as never);
|
|
142
|
+
vi.mocked(runLint)
|
|
143
|
+
.mockReturnValueOnce({ passed: false, errors: ['format error'] })
|
|
144
|
+
.mockReturnValueOnce({ passed: true, errors: [] });
|
|
145
|
+
|
|
146
|
+
vi.mocked(generateText).mockResolvedValue({
|
|
147
|
+
text: '```tsx\nfixed component\n```\n```tsx\nfixed test\n```',
|
|
148
|
+
} as Awaited<ReturnType<typeof generateText>>);
|
|
149
|
+
vi.mocked(writeFile).mockResolvedValue(undefined);
|
|
150
|
+
|
|
151
|
+
await lintFixLoop('mock-model' as never, makeCtx(), 2);
|
|
152
|
+
|
|
153
|
+
expect(runLintFix).toHaveBeenCalledTimes(2);
|
|
154
|
+
});
|
|
155
|
+
|
|
135
156
|
it('uses empty string fallback when componentCode and testCode are undefined after readFile', async () => {
|
|
136
157
|
vi.mocked(runLintFix).mockReturnValue({ passed: true, errors: [] });
|
|
137
158
|
vi.mocked(readFile).mockResolvedValue(undefined as never);
|
|
@@ -34,17 +34,19 @@ export async function lintFixLoop(
|
|
|
34
34
|
const { text } = await generateText({ model, system, prompt });
|
|
35
35
|
ctx.llmCalls++;
|
|
36
36
|
ctx.fixIterations++;
|
|
37
|
+
ctx.lintFixIterations++;
|
|
37
38
|
|
|
38
39
|
const blocks = extractCodeBlocks(text);
|
|
39
40
|
if (blocks.length >= 2) {
|
|
40
|
-
ctx.componentCode = blocks[0];
|
|
41
|
-
ctx.testCode = blocks[1];
|
|
42
41
|
await writeFile(ctx.componentPath, blocks[0], 'utf-8');
|
|
43
42
|
await writeFile(ctx.testPath, blocks[1], 'utf-8');
|
|
44
43
|
} else if (blocks.length === 1) {
|
|
45
|
-
ctx.componentCode = blocks[0];
|
|
46
44
|
await writeFile(ctx.componentPath, blocks[0], 'utf-8');
|
|
47
45
|
}
|
|
46
|
+
|
|
47
|
+
runLintFix(filePaths, ctx.targetDir);
|
|
48
|
+
ctx.componentCode = await readFile(ctx.componentPath, 'utf-8');
|
|
49
|
+
ctx.testCode = await readFile(ctx.testPath, 'utf-8');
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
const finalResult = runLint(filePaths, ctx.targetDir);
|
|
@@ -37,6 +37,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
37
37
|
isModify: false,
|
|
38
38
|
llmCalls: 0,
|
|
39
39
|
fixIterations: 0,
|
|
40
|
+
typeFixIterations: 0,
|
|
41
|
+
testFixIterations: 0,
|
|
42
|
+
lintFixIterations: 0,
|
|
43
|
+
storyFixIterations: 0,
|
|
40
44
|
storyCode: 'original story',
|
|
41
45
|
componentCode: 'original component',
|
|
42
46
|
...overrides,
|
|
@@ -27,6 +27,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
27
27
|
isModify: false,
|
|
28
28
|
llmCalls: 0,
|
|
29
29
|
fixIterations: 0,
|
|
30
|
+
typeFixIterations: 0,
|
|
31
|
+
testFixIterations: 0,
|
|
32
|
+
lintFixIterations: 0,
|
|
33
|
+
storyFixIterations: 0,
|
|
30
34
|
...overrides,
|
|
31
35
|
};
|
|
32
36
|
}
|
|
@@ -37,6 +37,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
37
37
|
isModify: false,
|
|
38
38
|
llmCalls: 0,
|
|
39
39
|
fixIterations: 0,
|
|
40
|
+
typeFixIterations: 0,
|
|
41
|
+
testFixIterations: 0,
|
|
42
|
+
lintFixIterations: 0,
|
|
43
|
+
storyFixIterations: 0,
|
|
40
44
|
componentCode: 'original component',
|
|
41
45
|
testCode: 'original test',
|
|
42
46
|
...overrides,
|
|
@@ -37,6 +37,10 @@ function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
|
|
|
37
37
|
isModify: false,
|
|
38
38
|
llmCalls: 0,
|
|
39
39
|
fixIterations: 0,
|
|
40
|
+
typeFixIterations: 0,
|
|
41
|
+
testFixIterations: 0,
|
|
42
|
+
lintFixIterations: 0,
|
|
43
|
+
storyFixIterations: 0,
|
|
40
44
|
componentCode: 'original component',
|
|
41
45
|
testCode: 'original test',
|
|
42
46
|
...overrides,
|
package/src/prompt.test.ts
CHANGED
|
@@ -40,6 +40,34 @@ describe('prompt builders', () => {
|
|
|
40
40
|
expect(system).toContain('QUALITY CHECKLIST');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
it('system prompt contains strict package boundary rule', () => {
|
|
44
|
+
const { system } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
45
|
+
expect(system).toContain('Strict package boundary');
|
|
46
|
+
expect(system).toContain('package.json');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('system prompt contains UI barrel import rule', () => {
|
|
50
|
+
const { system } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
51
|
+
expect(system).toContain('UI components from barrel only');
|
|
52
|
+
expect(system).toContain('src/components/ui/index.ts');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('system prompt contains page navigation callback rule', () => {
|
|
56
|
+
const { system } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
57
|
+
expect(system).toContain('callback props for navigation');
|
|
58
|
+
expect(system).toContain('Never import a router library');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('system prompt contains controlled input initialization rule', () => {
|
|
62
|
+
const { system } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
63
|
+
expect(system).toContain("always initialize text/number inputs with an empty string (`useState('')`)");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('system prompt contains array key rule preventing index-based keys', () => {
|
|
67
|
+
const { system } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
68
|
+
expect(system).toContain("Biome's `noArrayIndexKey` rule flags both forms");
|
|
69
|
+
});
|
|
70
|
+
|
|
43
71
|
it('user prompt includes component name and spec deltas', () => {
|
|
44
72
|
const { prompt } = buildComponentPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
45
73
|
expect(prompt).toContain('**Card**');
|
|
@@ -111,6 +139,64 @@ describe('prompt builders', () => {
|
|
|
111
139
|
expect(system).toContain('Verify semantic elements from component source');
|
|
112
140
|
expect(system).toContain('getByText');
|
|
113
141
|
});
|
|
142
|
+
|
|
143
|
+
it('system prompt contains strict package boundary rule', () => {
|
|
144
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
145
|
+
expect(system).toContain('Strict package boundary');
|
|
146
|
+
expect(system).toContain('@testing-library/react');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('system prompt contains jsdom limitations rule', () => {
|
|
150
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
151
|
+
expect(system).toContain('jsdom limitations');
|
|
152
|
+
expect(system).toContain('DataTransfer');
|
|
153
|
+
expect(system).toContain('scrollIntoView');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('system prompt contains individual class testing rule', () => {
|
|
157
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
158
|
+
expect(system).toContain('Test Tailwind classes individually');
|
|
159
|
+
expect(system).toContain('toHaveClass');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('system prompt contains mock composed components rule', () => {
|
|
163
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
164
|
+
expect(system).toContain('Mock composed components');
|
|
165
|
+
expect(system).toContain('vi.mock');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('system prompt warns against spreading props in mocks', () => {
|
|
169
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
170
|
+
expect(system).toContain('Do NOT spread props');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('system prompt warns page tests not to query mocked child elements', () => {
|
|
174
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
175
|
+
expect(system).toContain('expect.anything()');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('system prompt requires mock.calls direct access for mock component prop assertions', () => {
|
|
179
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
180
|
+
expect(system).toContain(
|
|
181
|
+
'NEVER use `toHaveBeenCalledWith` — React passes `(props, ref)` where `ref` is `undefined`',
|
|
182
|
+
);
|
|
183
|
+
expect(system).toContain('MockChild.mock.calls[0]');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('system prompt forbids @testing-library/dom import', () => {
|
|
187
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
188
|
+
expect(system).toContain('@testing-library/dom');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('system prompt contains vi.mock hoisting rule', () => {
|
|
192
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
193
|
+
expect(system).toContain('hoisted');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('system prompt contains semantic HTML role mapping rule', () => {
|
|
197
|
+
const { system } = buildTestPrompt({ componentName: 'Card', specDeltas, projectSection });
|
|
198
|
+
expect(system).toContain('`<section>` has role="region"');
|
|
199
|
+
});
|
|
114
200
|
});
|
|
115
201
|
|
|
116
202
|
describe('buildStoryPrompt', () => {
|
|
@@ -366,6 +452,76 @@ describe('prompt builders', () => {
|
|
|
366
452
|
expect(result.prompt).toContain('## Component Code');
|
|
367
453
|
expect(result.prompt).toContain('## Test Code');
|
|
368
454
|
});
|
|
455
|
+
|
|
456
|
+
it('system prompt contains unresolvable import guidance', () => {
|
|
457
|
+
const result = buildTestFixPrompt({
|
|
458
|
+
componentCode: 'export function Card() {}',
|
|
459
|
+
testCode: 'test code',
|
|
460
|
+
failures: [],
|
|
461
|
+
testOutput: '',
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
expect(result.system).toContain('Unresolvable imports');
|
|
465
|
+
expect(result.system).toContain('Failed to resolve import');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('system prompt contains missing UI component guidance', () => {
|
|
469
|
+
const result = buildTestFixPrompt({
|
|
470
|
+
componentCode: 'export function Card() {}',
|
|
471
|
+
testCode: 'test code',
|
|
472
|
+
failures: [],
|
|
473
|
+
testOutput: '',
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
expect(result.system).toContain('Missing UI component imports');
|
|
477
|
+
expect(result.system).toContain('@/components/ui');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('system prompt contains vi.mock hoisting guidance', () => {
|
|
481
|
+
const result = buildTestFixPrompt({
|
|
482
|
+
componentCode: 'export function Card() {}',
|
|
483
|
+
testCode: 'test code',
|
|
484
|
+
failures: [],
|
|
485
|
+
testOutput: '',
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
expect(result.system).toContain('hoisted');
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('system prompt contains mock spy direct access fix guidance', () => {
|
|
492
|
+
const result = buildTestFixPrompt({
|
|
493
|
+
componentCode: 'export function Card() {}',
|
|
494
|
+
testCode: 'test code',
|
|
495
|
+
failures: [],
|
|
496
|
+
testOutput: '',
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
expect(result.system).toContain('replace `toHaveBeenCalledWith` with direct mock call access');
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('system prompt contains toHaveStyle replacement guidance', () => {
|
|
503
|
+
const result = buildTestFixPrompt({
|
|
504
|
+
componentCode: 'export function Card() {}',
|
|
505
|
+
testCode: 'test code',
|
|
506
|
+
failures: [],
|
|
507
|
+
testOutput: '',
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
expect(result.system).toContain('toHaveStyle()');
|
|
511
|
+
expect(result.system).toContain('toHaveClass');
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('system prompt contains ReferenceError mock import guidance', () => {
|
|
515
|
+
const result = buildTestFixPrompt({
|
|
516
|
+
componentCode: 'export function Card() {}',
|
|
517
|
+
testCode: 'test code',
|
|
518
|
+
failures: [],
|
|
519
|
+
testOutput: '',
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
expect(result.system).toContain('ReferenceError');
|
|
523
|
+
expect(result.system).toContain('Add the import after the `vi.mock` call');
|
|
524
|
+
});
|
|
369
525
|
});
|
|
370
526
|
|
|
371
527
|
describe('buildLintFixPrompt', () => {
|
|
@@ -385,6 +541,26 @@ describe('prompt builders', () => {
|
|
|
385
541
|
expect(result.prompt).toContain('## Test Code');
|
|
386
542
|
expect(result.prompt).toContain('var y = 2');
|
|
387
543
|
});
|
|
544
|
+
|
|
545
|
+
it('system prompt contains noArrayIndexKey guidance', () => {
|
|
546
|
+
const result = buildLintFixPrompt({
|
|
547
|
+
componentCode: 'const x = 1',
|
|
548
|
+
testCode: 'const y = 2',
|
|
549
|
+
errors: [],
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
expect(result.system).toContain('noArrayIndexKey');
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('system prompt covers derived keys like template literals in noArrayIndexKey guidance', () => {
|
|
556
|
+
const result = buildLintFixPrompt({
|
|
557
|
+
componentCode: 'const x = 1',
|
|
558
|
+
testCode: 'const y = 2',
|
|
559
|
+
errors: [],
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
expect(result.system).toContain('inside a template literal');
|
|
563
|
+
});
|
|
388
564
|
});
|
|
389
565
|
|
|
390
566
|
describe('buildStoryFixPrompt', () => {
|