@auto-engineer/component-implementor-react 1.107.0 → 1.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +6 -6
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +69 -0
  5. package/dist/src/commands/implement-component.d.ts +3 -0
  6. package/dist/src/commands/implement-component.d.ts.map +1 -1
  7. package/dist/src/commands/implement-component.js +11 -33
  8. package/dist/src/commands/implement-component.js.map +1 -1
  9. package/dist/src/commands/implement-component.test.js +6 -66
  10. package/dist/src/commands/implement-component.test.js.map +1 -1
  11. package/dist/src/pipeline/run-pipeline.d.ts +5 -25
  12. package/dist/src/pipeline/run-pipeline.d.ts.map +1 -1
  13. package/dist/src/pipeline/run-pipeline.js +17 -47
  14. package/dist/src/pipeline/run-pipeline.js.map +1 -1
  15. package/dist/src/pipeline/run-pipeline.test.js +29 -132
  16. package/dist/src/pipeline/run-pipeline.test.js.map +1 -1
  17. package/dist/src/pipeline/steps/fix-from-feedback.d.ts +4 -0
  18. package/dist/src/pipeline/steps/fix-from-feedback.d.ts.map +1 -0
  19. package/dist/src/pipeline/steps/fix-from-feedback.js +94 -0
  20. package/dist/src/pipeline/steps/fix-from-feedback.js.map +1 -0
  21. package/dist/src/pipeline/steps/generate-component.test.js +1 -5
  22. package/dist/src/pipeline/steps/generate-component.test.js.map +1 -1
  23. package/dist/src/pipeline/steps/generate-story.test.js +1 -5
  24. package/dist/src/pipeline/steps/generate-story.test.js.map +1 -1
  25. package/dist/src/pipeline/steps/generate-test.test.js +1 -5
  26. package/dist/src/pipeline/steps/generate-test.test.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +3 -3
  29. package/src/commands/implement-component.test.ts +6 -66
  30. package/src/commands/implement-component.ts +16 -47
  31. package/src/pipeline/run-pipeline.test.ts +32 -141
  32. package/src/pipeline/run-pipeline.ts +22 -74
  33. package/src/pipeline/steps/fix-from-feedback.ts +105 -0
  34. package/src/pipeline/steps/generate-component.test.ts +1 -5
  35. package/src/pipeline/steps/generate-story.test.ts +1 -5
  36. package/src/pipeline/steps/generate-test.test.ts +1 -5
  37. package/dist/src/pipeline/steps/lint-fix-loop.d.ts +0 -4
  38. package/dist/src/pipeline/steps/lint-fix-loop.d.ts.map +0 -1
  39. package/dist/src/pipeline/steps/lint-fix-loop.js +0 -46
  40. package/dist/src/pipeline/steps/lint-fix-loop.js.map +0 -1
  41. package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts +0 -2
  42. package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts.map +0 -1
  43. package/dist/src/pipeline/steps/lint-fix-loop.test.js +0 -136
  44. package/dist/src/pipeline/steps/lint-fix-loop.test.js.map +0 -1
  45. package/dist/src/pipeline/steps/story-fix-loop.d.ts +0 -4
  46. package/dist/src/pipeline/steps/story-fix-loop.d.ts.map +0 -1
  47. package/dist/src/pipeline/steps/story-fix-loop.js +0 -35
  48. package/dist/src/pipeline/steps/story-fix-loop.js.map +0 -1
  49. package/dist/src/pipeline/steps/story-fix-loop.test.d.ts +0 -2
  50. package/dist/src/pipeline/steps/story-fix-loop.test.d.ts.map +0 -1
  51. package/dist/src/pipeline/steps/story-fix-loop.test.js +0 -98
  52. package/dist/src/pipeline/steps/story-fix-loop.test.js.map +0 -1
  53. package/dist/src/pipeline/steps/storybook-test.d.ts +0 -3
  54. package/dist/src/pipeline/steps/storybook-test.d.ts.map +0 -1
  55. package/dist/src/pipeline/steps/storybook-test.js +0 -22
  56. package/dist/src/pipeline/steps/storybook-test.js.map +0 -1
  57. package/dist/src/pipeline/steps/storybook-test.test.d.ts +0 -2
  58. package/dist/src/pipeline/steps/storybook-test.test.d.ts.map +0 -1
  59. package/dist/src/pipeline/steps/storybook-test.test.js +0 -70
  60. package/dist/src/pipeline/steps/storybook-test.test.js.map +0 -1
  61. package/dist/src/pipeline/steps/test-fix-loop.d.ts +0 -4
  62. package/dist/src/pipeline/steps/test-fix-loop.d.ts.map +0 -1
  63. package/dist/src/pipeline/steps/test-fix-loop.js +0 -45
  64. package/dist/src/pipeline/steps/test-fix-loop.js.map +0 -1
  65. package/dist/src/pipeline/steps/test-fix-loop.test.d.ts +0 -2
  66. package/dist/src/pipeline/steps/test-fix-loop.test.d.ts.map +0 -1
  67. package/dist/src/pipeline/steps/test-fix-loop.test.js +0 -172
  68. package/dist/src/pipeline/steps/test-fix-loop.test.js.map +0 -1
  69. package/dist/src/pipeline/steps/type-fix-loop.d.ts +0 -4
  70. package/dist/src/pipeline/steps/type-fix-loop.d.ts.map +0 -1
  71. package/dist/src/pipeline/steps/type-fix-loop.js +0 -44
  72. package/dist/src/pipeline/steps/type-fix-loop.js.map +0 -1
  73. package/dist/src/pipeline/steps/type-fix-loop.test.d.ts +0 -2
  74. package/dist/src/pipeline/steps/type-fix-loop.test.d.ts.map +0 -1
  75. package/dist/src/pipeline/steps/type-fix-loop.test.js +0 -116
  76. package/dist/src/pipeline/steps/type-fix-loop.test.js.map +0 -1
  77. package/dist/src/pipeline/steps/visual-test.d.ts +0 -3
  78. package/dist/src/pipeline/steps/visual-test.d.ts.map +0 -1
  79. package/dist/src/pipeline/steps/visual-test.js +0 -4
  80. package/dist/src/pipeline/steps/visual-test.js.map +0 -1
  81. package/dist/src/pipeline/steps/visual-test.test.d.ts +0 -2
  82. package/dist/src/pipeline/steps/visual-test.test.d.ts.map +0 -1
  83. package/dist/src/pipeline/steps/visual-test.test.js +0 -9
  84. package/dist/src/pipeline/steps/visual-test.test.js.map +0 -1
  85. package/src/pipeline/steps/lint-fix-loop.test.ts +0 -176
  86. package/src/pipeline/steps/lint-fix-loop.ts +0 -61
  87. package/src/pipeline/steps/story-fix-loop.test.ts +0 -127
  88. package/src/pipeline/steps/story-fix-loop.ts +0 -48
  89. package/src/pipeline/steps/storybook-test.test.ts +0 -86
  90. package/src/pipeline/steps/storybook-test.ts +0 -27
  91. package/src/pipeline/steps/test-fix-loop.test.ts +0 -205
  92. package/src/pipeline/steps/test-fix-loop.ts +0 -57
  93. package/src/pipeline/steps/type-fix-loop.test.ts +0 -149
  94. package/src/pipeline/steps/type-fix-loop.ts +0 -56
  95. package/src/pipeline/steps/visual-test.test.ts +0 -10
  96. package/src/pipeline/steps/visual-test.ts +0 -5
@@ -1,27 +0,0 @@
1
- import { runStorybookTest } from '../../tools/storybook-runner';
2
- import { runTypeCheck } from '../../tools/type-checker';
3
- import type { PipelineContext, StepResult } from '../run-pipeline';
4
-
5
- export async function storybookTestStep(ctx: PipelineContext, enableStorybookCli: boolean): Promise<StepResult> {
6
- const typeResult = runTypeCheck(ctx.targetDir, [ctx.storyPath]);
7
-
8
- if (!typeResult.passed) {
9
- return {
10
- success: false,
11
- error: `Story type errors: ${typeResult.errors.join('; ')}`,
12
- };
13
- }
14
-
15
- if (enableStorybookCli) {
16
- const testResult = runStorybookTest(ctx.storyPath, ctx.targetDir);
17
-
18
- if (!testResult.passed) {
19
- return {
20
- success: false,
21
- error: `Storybook test failures: ${testResult.errors.join('; ')}`,
22
- };
23
- }
24
- }
25
-
26
- return { success: true };
27
- }
@@ -1,205 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- vi.mock('ai', () => ({
4
- generateText: vi.fn(),
5
- }));
6
-
7
- vi.mock('node:fs/promises', () => ({
8
- readFile: vi.fn(),
9
- writeFile: vi.fn(),
10
- }));
11
-
12
- vi.mock('../../tools/test-runner', () => ({
13
- runTests: vi.fn(),
14
- }));
15
-
16
- import { readFile, writeFile } from 'node:fs/promises';
17
- import { generateText } from 'ai';
18
- import { runTests } from '../../tools/test-runner';
19
- import type { PipelineContext } from '../run-pipeline';
20
- import { testFixLoop } from './test-fix-loop';
21
-
22
- afterEach(() => {
23
- vi.clearAllMocks();
24
- });
25
-
26
- function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
27
- return {
28
- componentName: 'MyButton',
29
- componentPath: '/project/src/MyButton.tsx',
30
- testPath: '/project/src/MyButton.test.tsx',
31
- storyPath: '/project/src/MyButton.stories.tsx',
32
- componentImportPath: '@/components/ui/MyButton',
33
- targetDir: '/project',
34
- specDeltas: { structure: [], rendering: [], interaction: [], styling: [] },
35
- projectSection: '',
36
- composes: [],
37
- isModify: false,
38
- llmCalls: 0,
39
- fixIterations: 0,
40
- typeFixIterations: 0,
41
- testFixIterations: 0,
42
- lintFixIterations: 0,
43
- storyFixIterations: 0,
44
- componentCode: 'original component',
45
- testCode: 'original test',
46
- ...overrides,
47
- };
48
- }
49
-
50
- describe('testFixLoop', () => {
51
- it('returns success immediately when all tests pass', async () => {
52
- vi.mocked(runTests).mockReturnValue({
53
- passed: true,
54
- numPassed: 3,
55
- numFailed: 0,
56
- failures: [],
57
- output: '',
58
- });
59
-
60
- const result = await testFixLoop('mock-model' as never, makeCtx(), 3);
61
-
62
- expect(result).toEqual({ success: true });
63
- expect(generateText).not.toHaveBeenCalled();
64
- });
65
-
66
- it('calls LLM to fix failures and writes fixed files', async () => {
67
- vi.mocked(runTests)
68
- .mockReturnValueOnce({
69
- passed: false,
70
- numPassed: 1,
71
- numFailed: 1,
72
- failures: ['renders button: element not found'],
73
- output: 'FAIL',
74
- })
75
- .mockReturnValueOnce({
76
- passed: true,
77
- numPassed: 2,
78
- numFailed: 0,
79
- failures: [],
80
- output: 'PASS',
81
- });
82
-
83
- vi.mocked(generateText).mockResolvedValue({
84
- text: '```tsx\nfixed component\n```\n```tsx\nfixed test\n```',
85
- } as Awaited<ReturnType<typeof generateText>>);
86
- vi.mocked(writeFile).mockResolvedValue(undefined);
87
-
88
- const result = await testFixLoop('mock-model' as never, makeCtx(), 3);
89
-
90
- expect(result).toEqual({ success: true });
91
- expect(generateText).toHaveBeenCalledTimes(1);
92
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.tsx', 'fixed component', 'utf-8');
93
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.test.tsx', 'fixed test', 'utf-8');
94
- });
95
-
96
- it('returns failure after exhausting max iterations', async () => {
97
- vi.mocked(runTests).mockReturnValue({
98
- passed: false,
99
- numPassed: 0,
100
- numFailed: 1,
101
- failures: ['persistent failure'],
102
- output: 'FAIL',
103
- });
104
-
105
- vi.mocked(generateText).mockResolvedValue({
106
- text: '```tsx\nstill broken\n```\n```tsx\nstill broken test\n```',
107
- } as Awaited<ReturnType<typeof generateText>>);
108
- vi.mocked(writeFile).mockResolvedValue(undefined);
109
- vi.mocked(readFile).mockResolvedValue('still broken' as never);
110
-
111
- const result = await testFixLoop('mock-model' as never, makeCtx(), 1);
112
-
113
- expect(result).toEqual({
114
- success: false,
115
- error: expect.stringContaining('Test failures remain after 1 iterations'),
116
- });
117
- });
118
-
119
- it('handles single code block response by updating only component', async () => {
120
- vi.mocked(runTests)
121
- .mockReturnValueOnce({
122
- passed: false,
123
- numPassed: 0,
124
- numFailed: 1,
125
- failures: ['failure'],
126
- output: 'FAIL',
127
- })
128
- .mockReturnValueOnce({
129
- passed: true,
130
- numPassed: 1,
131
- numFailed: 0,
132
- failures: [],
133
- output: 'PASS',
134
- });
135
-
136
- vi.mocked(generateText).mockResolvedValue({
137
- text: '```tsx\nfixed component only\n```',
138
- } as Awaited<ReturnType<typeof generateText>>);
139
- vi.mocked(writeFile).mockResolvedValue(undefined);
140
-
141
- const result = await testFixLoop('mock-model' as never, makeCtx(), 3);
142
-
143
- expect(result).toEqual({ success: true });
144
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.tsx', 'fixed component only', 'utf-8');
145
- expect(writeFile).toHaveBeenCalledTimes(1);
146
- });
147
-
148
- it('returns success when final check passes after last iteration fix', async () => {
149
- vi.mocked(runTests)
150
- .mockReturnValueOnce({
151
- passed: false,
152
- numPassed: 0,
153
- numFailed: 1,
154
- failures: ['failure'],
155
- output: 'FAIL',
156
- })
157
- .mockReturnValueOnce({
158
- passed: true,
159
- numPassed: 1,
160
- numFailed: 0,
161
- failures: [],
162
- output: 'PASS',
163
- });
164
-
165
- vi.mocked(generateText).mockResolvedValue({
166
- text: '```tsx\nfixed\n```\n```tsx\nfixed test\n```',
167
- } as Awaited<ReturnType<typeof generateText>>);
168
- vi.mocked(writeFile).mockResolvedValue(undefined);
169
-
170
- const result = await testFixLoop('mock-model' as never, makeCtx(), 1);
171
-
172
- expect(result).toEqual({ success: true });
173
- });
174
-
175
- it('uses empty string fallback when componentCode and testCode are undefined', async () => {
176
- vi.mocked(runTests)
177
- .mockReturnValueOnce({
178
- passed: false,
179
- numPassed: 0,
180
- numFailed: 1,
181
- failures: ['failure'],
182
- output: 'FAIL',
183
- })
184
- .mockReturnValueOnce({
185
- passed: true,
186
- numPassed: 1,
187
- numFailed: 0,
188
- failures: [],
189
- output: 'PASS',
190
- });
191
-
192
- vi.mocked(generateText).mockResolvedValue({
193
- text: '```tsx\nfixed\n```\n```tsx\nfixed test\n```',
194
- } as Awaited<ReturnType<typeof generateText>>);
195
- vi.mocked(writeFile).mockResolvedValue(undefined);
196
-
197
- const result = await testFixLoop(
198
- 'mock-model' as never,
199
- makeCtx({ componentCode: undefined, testCode: undefined }),
200
- 1,
201
- );
202
-
203
- expect(result).toEqual({ success: true });
204
- });
205
- });
@@ -1,57 +0,0 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
- import type { LanguageModel } from 'ai';
3
- import { generateText } from 'ai';
4
- import { extractCodeBlocks } from '../../extract-code-block';
5
- import { buildTestFixPrompt } from '../../prompt';
6
- import { runTests } from '../../tools/test-runner';
7
- import type { PipelineContext, StepResult } from '../run-pipeline';
8
-
9
- export async function testFixLoop(
10
- model: LanguageModel,
11
- ctx: PipelineContext,
12
- maxIterations: number,
13
- ): Promise<StepResult> {
14
- for (let i = 0; i < maxIterations; i++) {
15
- const result = runTests(ctx.testPath, ctx.targetDir);
16
-
17
- if (result.passed) {
18
- return { success: true };
19
- }
20
-
21
- const { system, prompt } = buildTestFixPrompt({
22
- componentCode: ctx.componentCode ?? '',
23
- testCode: ctx.testCode ?? '',
24
- failures: result.failures,
25
- testOutput: result.output,
26
- });
27
-
28
- const { text } = await generateText({ model, system, prompt });
29
- ctx.llmCalls++;
30
- ctx.fixIterations++;
31
- ctx.testFixIterations++;
32
-
33
- const blocks = extractCodeBlocks(text);
34
- if (blocks.length >= 2) {
35
- ctx.componentCode = blocks[0];
36
- ctx.testCode = blocks[1];
37
- await writeFile(ctx.componentPath, blocks[0], 'utf-8');
38
- await writeFile(ctx.testPath, blocks[1], 'utf-8');
39
- } else if (blocks.length === 1) {
40
- ctx.componentCode = blocks[0];
41
- await writeFile(ctx.componentPath, blocks[0], 'utf-8');
42
- }
43
- }
44
-
45
- const finalResult = runTests(ctx.testPath, ctx.targetDir);
46
- if (finalResult.passed) {
47
- return { success: true };
48
- }
49
-
50
- ctx.componentCode = await readFile(ctx.componentPath, 'utf-8');
51
- ctx.testCode = await readFile(ctx.testPath, 'utf-8');
52
-
53
- return {
54
- success: false,
55
- error: `Test failures remain after ${maxIterations} iterations: ${finalResult.failures.join('; ')}`,
56
- };
57
- }
@@ -1,149 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- vi.mock('ai', () => ({
4
- generateText: vi.fn(),
5
- }));
6
-
7
- vi.mock('node:fs/promises', () => ({
8
- readFile: vi.fn(),
9
- writeFile: vi.fn(),
10
- }));
11
-
12
- vi.mock('../../tools/type-checker', () => ({
13
- runTypeCheck: vi.fn(),
14
- }));
15
-
16
- import { readFile, writeFile } from 'node:fs/promises';
17
- import { generateText } from 'ai';
18
- import { runTypeCheck } from '../../tools/type-checker';
19
- import type { PipelineContext } from '../run-pipeline';
20
- import { typeFixLoop } from './type-fix-loop';
21
-
22
- afterEach(() => {
23
- vi.clearAllMocks();
24
- });
25
-
26
- function makeCtx(overrides: Partial<PipelineContext> = {}): PipelineContext {
27
- return {
28
- componentName: 'MyButton',
29
- componentPath: '/project/src/MyButton.tsx',
30
- testPath: '/project/src/MyButton.test.tsx',
31
- storyPath: '/project/src/MyButton.stories.tsx',
32
- componentImportPath: '@/components/ui/MyButton',
33
- targetDir: '/project',
34
- specDeltas: { structure: [], rendering: [], interaction: [], styling: [] },
35
- projectSection: '',
36
- composes: [],
37
- isModify: false,
38
- llmCalls: 0,
39
- fixIterations: 0,
40
- typeFixIterations: 0,
41
- testFixIterations: 0,
42
- lintFixIterations: 0,
43
- storyFixIterations: 0,
44
- componentCode: 'original component',
45
- testCode: 'original test',
46
- ...overrides,
47
- };
48
- }
49
-
50
- describe('typeFixLoop', () => {
51
- it('returns success immediately when type check passes', async () => {
52
- vi.mocked(runTypeCheck).mockReturnValue({ passed: true, errors: [] });
53
-
54
- const result = await typeFixLoop('mock-model' as never, makeCtx(), 3);
55
-
56
- expect(result).toEqual({ success: true });
57
- expect(generateText).not.toHaveBeenCalled();
58
- });
59
-
60
- it('calls LLM to fix type errors and writes fixed files', async () => {
61
- vi.mocked(runTypeCheck)
62
- .mockReturnValueOnce({ passed: false, errors: ['error TS2307: Module not found'] })
63
- .mockReturnValueOnce({ passed: true, errors: [] });
64
-
65
- vi.mocked(generateText).mockResolvedValue({
66
- text: '```tsx\nfixed component\n```\n```tsx\nfixed test\n```',
67
- } as Awaited<ReturnType<typeof generateText>>);
68
- vi.mocked(writeFile).mockResolvedValue(undefined);
69
-
70
- const result = await typeFixLoop('mock-model' as never, makeCtx(), 3);
71
-
72
- expect(result).toEqual({ success: true });
73
- expect(generateText).toHaveBeenCalledTimes(1);
74
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.tsx', 'fixed component', 'utf-8');
75
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.test.tsx', 'fixed test', 'utf-8');
76
- });
77
-
78
- it('returns failure after exhausting max iterations', async () => {
79
- vi.mocked(runTypeCheck).mockReturnValue({
80
- passed: false,
81
- errors: ['persistent error'],
82
- });
83
-
84
- vi.mocked(generateText).mockResolvedValue({
85
- text: '```tsx\nstill broken\n```\n```tsx\nstill broken test\n```',
86
- } as Awaited<ReturnType<typeof generateText>>);
87
- vi.mocked(writeFile).mockResolvedValue(undefined);
88
- vi.mocked(readFile).mockResolvedValue('still broken' as never);
89
-
90
- const result = await typeFixLoop('mock-model' as never, makeCtx(), 2);
91
-
92
- expect(result).toEqual({
93
- success: false,
94
- error: expect.stringContaining('Type errors remain after 2 iterations'),
95
- });
96
- expect(generateText).toHaveBeenCalledTimes(2);
97
- });
98
-
99
- it('handles single code block response by updating only component', async () => {
100
- vi.mocked(runTypeCheck)
101
- .mockReturnValueOnce({ passed: false, errors: ['error in component'] })
102
- .mockReturnValueOnce({ passed: true, errors: [] });
103
-
104
- vi.mocked(generateText).mockResolvedValue({
105
- text: '```tsx\nfixed component only\n```',
106
- } as Awaited<ReturnType<typeof generateText>>);
107
- vi.mocked(writeFile).mockResolvedValue(undefined);
108
-
109
- const result = await typeFixLoop('mock-model' as never, makeCtx(), 3);
110
-
111
- expect(result).toEqual({ success: true });
112
- expect(writeFile).toHaveBeenCalledWith('/project/src/MyButton.tsx', 'fixed component only', 'utf-8');
113
- expect(writeFile).toHaveBeenCalledTimes(1);
114
- });
115
-
116
- it('returns success when final check passes after last iteration fix', async () => {
117
- vi.mocked(runTypeCheck)
118
- .mockReturnValueOnce({ passed: false, errors: ['error'] })
119
- .mockReturnValueOnce({ passed: true, errors: [] });
120
-
121
- vi.mocked(generateText).mockResolvedValue({
122
- text: '```tsx\nfixed\n```\n```tsx\nfixed test\n```',
123
- } as Awaited<ReturnType<typeof generateText>>);
124
- vi.mocked(writeFile).mockResolvedValue(undefined);
125
-
126
- const result = await typeFixLoop('mock-model' as never, makeCtx(), 1);
127
-
128
- expect(result).toEqual({ success: true });
129
- });
130
-
131
- it('uses empty string fallback when componentCode and testCode are undefined', async () => {
132
- vi.mocked(runTypeCheck)
133
- .mockReturnValueOnce({ passed: false, errors: ['error'] })
134
- .mockReturnValueOnce({ passed: true, errors: [] });
135
-
136
- vi.mocked(generateText).mockResolvedValue({
137
- text: '```tsx\nfixed\n```\n```tsx\nfixed test\n```',
138
- } as Awaited<ReturnType<typeof generateText>>);
139
- vi.mocked(writeFile).mockResolvedValue(undefined);
140
-
141
- const result = await typeFixLoop(
142
- 'mock-model' as never,
143
- makeCtx({ componentCode: undefined, testCode: undefined }),
144
- 1,
145
- );
146
-
147
- expect(result).toEqual({ success: true });
148
- });
149
- });
@@ -1,56 +0,0 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
- import type { LanguageModel } from 'ai';
3
- import { generateText } from 'ai';
4
- import { extractCodeBlocks } from '../../extract-code-block';
5
- import { buildTypeFixPrompt } from '../../prompt';
6
- import { runTypeCheck } from '../../tools/type-checker';
7
- import type { PipelineContext, StepResult } from '../run-pipeline';
8
-
9
- export async function typeFixLoop(
10
- model: LanguageModel,
11
- ctx: PipelineContext,
12
- maxIterations: number,
13
- ): Promise<StepResult> {
14
- for (let i = 0; i < maxIterations; i++) {
15
- const result = runTypeCheck(ctx.targetDir, [ctx.componentPath, ctx.testPath]);
16
-
17
- if (result.passed) {
18
- return { success: true };
19
- }
20
-
21
- const { system, prompt } = buildTypeFixPrompt({
22
- componentCode: ctx.componentCode ?? '',
23
- testCode: ctx.testCode ?? '',
24
- errors: result.errors,
25
- });
26
-
27
- const { text } = await generateText({ model, system, prompt });
28
- ctx.llmCalls++;
29
- ctx.fixIterations++;
30
- ctx.typeFixIterations++;
31
-
32
- const blocks = extractCodeBlocks(text);
33
- if (blocks.length >= 2) {
34
- ctx.componentCode = blocks[0];
35
- ctx.testCode = blocks[1];
36
- await writeFile(ctx.componentPath, blocks[0], 'utf-8');
37
- await writeFile(ctx.testPath, blocks[1], 'utf-8');
38
- } else if (blocks.length === 1) {
39
- ctx.componentCode = blocks[0];
40
- await writeFile(ctx.componentPath, blocks[0], 'utf-8');
41
- }
42
- }
43
-
44
- const finalResult = runTypeCheck(ctx.targetDir, [ctx.componentPath, ctx.testPath]);
45
- if (finalResult.passed) {
46
- return { success: true };
47
- }
48
-
49
- ctx.componentCode = await readFile(ctx.componentPath, 'utf-8');
50
- ctx.testCode = await readFile(ctx.testPath, 'utf-8');
51
-
52
- return {
53
- success: false,
54
- error: `Type errors remain after ${maxIterations} iterations: ${finalResult.errors.join('; ')}`,
55
- };
56
- }
@@ -1,10 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { visualTestStep } from './visual-test';
3
-
4
- describe('visualTestStep', () => {
5
- it('returns success (placeholder)', async () => {
6
- const result = await visualTestStep();
7
-
8
- expect(result).toEqual({ success: true });
9
- });
10
- });
@@ -1,5 +0,0 @@
1
- import type { StepResult } from '../run-pipeline';
2
-
3
- export async function visualTestStep(): Promise<StepResult> {
4
- return { success: true };
5
- }