@auto-engineer/component-implementor-react 1.98.0 → 1.100.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 +6 -6
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +92 -0
- package/dist/src/commands/implement-component.d.ts +19 -0
- package/dist/src/commands/implement-component.d.ts.map +1 -1
- package/dist/src/commands/implement-component.js +109 -30
- package/dist/src/commands/implement-component.js.map +1 -1
- package/dist/src/commands/implement-component.test.js +259 -69
- package/dist/src/commands/implement-component.test.js.map +1 -1
- package/dist/src/extract-exports.d.ts +6 -0
- package/dist/src/extract-exports.d.ts.map +1 -0
- package/dist/src/extract-exports.js +46 -0
- package/dist/src/extract-exports.js.map +1 -0
- package/dist/src/generate-story-deterministic.d.ts +30 -0
- package/dist/src/generate-story-deterministic.d.ts.map +1 -0
- package/dist/src/generate-story-deterministic.js +229 -0
- package/dist/src/generate-story-deterministic.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/pipeline/run-pipeline.d.ts +69 -0
- package/dist/src/pipeline/run-pipeline.d.ts.map +1 -0
- package/dist/src/pipeline/run-pipeline.js +78 -0
- package/dist/src/pipeline/run-pipeline.js.map +1 -0
- package/dist/src/pipeline/run-pipeline.test.d.ts +2 -0
- package/dist/src/pipeline/run-pipeline.test.d.ts.map +1 -0
- package/dist/src/pipeline/run-pipeline.test.js +247 -0
- package/dist/src/pipeline/run-pipeline.test.js.map +1 -0
- package/dist/src/pipeline/steps/generate-component.d.ts +4 -0
- package/dist/src/pipeline/steps/generate-component.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-component.js +50 -0
- package/dist/src/pipeline/steps/generate-component.js.map +1 -0
- package/dist/src/pipeline/steps/generate-component.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-component.test.js +106 -0
- package/dist/src/pipeline/steps/generate-component.test.js.map +1 -0
- package/dist/src/pipeline/steps/generate-story.d.ts +3 -0
- package/dist/src/pipeline/steps/generate-story.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-story.js +14 -0
- package/dist/src/pipeline/steps/generate-story.js.map +1 -0
- package/dist/src/pipeline/steps/generate-story.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-story.test.js +41 -0
- package/dist/src/pipeline/steps/generate-story.test.js.map +1 -0
- package/dist/src/pipeline/steps/generate-test.d.ts +4 -0
- package/dist/src/pipeline/steps/generate-test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-test.js +19 -0
- package/dist/src/pipeline/steps/generate-test.js.map +1 -0
- package/dist/src/pipeline/steps/generate-test.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/generate-test.test.js +60 -0
- package/dist/src/pipeline/steps/generate-test.test.js.map +1 -0
- package/dist/src/pipeline/steps/lint-fix-loop.d.ts +4 -0
- package/dist/src/pipeline/steps/lint-fix-loop.d.ts.map +1 -0
- package/dist/src/pipeline/steps/lint-fix-loop.js +45 -0
- package/dist/src/pipeline/steps/lint-fix-loop.js.map +1 -0
- package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts +2 -0
- package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/lint-fix-loop.test.js +119 -0
- package/dist/src/pipeline/steps/lint-fix-loop.test.js.map +1 -0
- package/dist/src/pipeline/steps/story-fix-loop.d.ts +4 -0
- package/dist/src/pipeline/steps/story-fix-loop.d.ts.map +1 -0
- package/dist/src/pipeline/steps/story-fix-loop.js +34 -0
- package/dist/src/pipeline/steps/story-fix-loop.js.map +1 -0
- package/dist/src/pipeline/steps/story-fix-loop.test.d.ts +2 -0
- package/dist/src/pipeline/steps/story-fix-loop.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/story-fix-loop.test.js +94 -0
- package/dist/src/pipeline/steps/story-fix-loop.test.js.map +1 -0
- package/dist/src/pipeline/steps/storybook-test.d.ts +3 -0
- package/dist/src/pipeline/steps/storybook-test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/storybook-test.js +22 -0
- package/dist/src/pipeline/steps/storybook-test.js.map +1 -0
- package/dist/src/pipeline/steps/storybook-test.test.d.ts +2 -0
- package/dist/src/pipeline/steps/storybook-test.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/storybook-test.test.js +66 -0
- package/dist/src/pipeline/steps/storybook-test.test.js.map +1 -0
- package/dist/src/pipeline/steps/test-fix-loop.d.ts +4 -0
- package/dist/src/pipeline/steps/test-fix-loop.d.ts.map +1 -0
- package/dist/src/pipeline/steps/test-fix-loop.js +44 -0
- package/dist/src/pipeline/steps/test-fix-loop.js.map +1 -0
- package/dist/src/pipeline/steps/test-fix-loop.test.d.ts +2 -0
- package/dist/src/pipeline/steps/test-fix-loop.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/test-fix-loop.test.js +168 -0
- package/dist/src/pipeline/steps/test-fix-loop.test.js.map +1 -0
- package/dist/src/pipeline/steps/type-fix-loop.d.ts +4 -0
- package/dist/src/pipeline/steps/type-fix-loop.d.ts.map +1 -0
- package/dist/src/pipeline/steps/type-fix-loop.js +43 -0
- package/dist/src/pipeline/steps/type-fix-loop.js.map +1 -0
- package/dist/src/pipeline/steps/type-fix-loop.test.d.ts +2 -0
- package/dist/src/pipeline/steps/type-fix-loop.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/type-fix-loop.test.js +112 -0
- package/dist/src/pipeline/steps/type-fix-loop.test.js.map +1 -0
- package/dist/src/pipeline/steps/visual-test.d.ts +3 -0
- package/dist/src/pipeline/steps/visual-test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/visual-test.js +4 -0
- package/dist/src/pipeline/steps/visual-test.js.map +1 -0
- package/dist/src/pipeline/steps/visual-test.test.d.ts +2 -0
- package/dist/src/pipeline/steps/visual-test.test.d.ts.map +1 -0
- package/dist/src/pipeline/steps/visual-test.test.js +9 -0
- package/dist/src/pipeline/steps/visual-test.test.js.map +1 -0
- package/dist/src/project-context.d.ts +10 -0
- package/dist/src/project-context.d.ts.map +1 -0
- package/dist/src/project-context.js +178 -0
- package/dist/src/project-context.js.map +1 -0
- package/dist/src/prompt.d.ts +39 -7
- package/dist/src/prompt.d.ts.map +1 -1
- package/dist/src/prompt.js +233 -23
- package/dist/src/prompt.js.map +1 -1
- package/dist/src/prompt.test.js +154 -9
- package/dist/src/prompt.test.js.map +1 -1
- package/dist/src/scaffold.d.ts +49 -0
- package/dist/src/scaffold.d.ts.map +1 -0
- package/dist/src/scaffold.js +208 -0
- package/dist/src/scaffold.js.map +1 -0
- package/dist/src/tools/lint-runner.d.ts +7 -0
- package/dist/src/tools/lint-runner.d.ts.map +1 -0
- package/dist/src/tools/lint-runner.js +48 -0
- package/dist/src/tools/lint-runner.js.map +1 -0
- package/dist/src/tools/lint-runner.test.d.ts +2 -0
- package/dist/src/tools/lint-runner.test.d.ts.map +1 -0
- package/dist/src/tools/lint-runner.test.js +90 -0
- package/dist/src/tools/lint-runner.test.js.map +1 -0
- package/dist/src/tools/storybook-runner.d.ts +6 -0
- package/dist/src/tools/storybook-runner.d.ts.map +1 -0
- package/dist/src/tools/storybook-runner.js +25 -0
- package/dist/src/tools/storybook-runner.js.map +1 -0
- package/dist/src/tools/storybook-runner.test.d.ts +2 -0
- package/dist/src/tools/storybook-runner.test.d.ts.map +1 -0
- package/dist/src/tools/storybook-runner.test.js +43 -0
- package/dist/src/tools/storybook-runner.test.js.map +1 -0
- package/dist/src/tools/test-runner.d.ts +9 -0
- package/dist/src/tools/test-runner.d.ts.map +1 -0
- package/dist/src/tools/test-runner.js +74 -0
- package/dist/src/tools/test-runner.js.map +1 -0
- package/dist/src/tools/test-runner.test.d.ts +2 -0
- package/dist/src/tools/test-runner.test.d.ts.map +1 -0
- package/dist/src/tools/test-runner.test.js +177 -0
- package/dist/src/tools/test-runner.test.js.map +1 -0
- package/dist/src/tools/type-checker.d.ts +6 -0
- package/dist/src/tools/type-checker.d.ts.map +1 -0
- package/dist/src/tools/type-checker.js +36 -0
- package/dist/src/tools/type-checker.js.map +1 -0
- package/dist/src/tools/type-checker.test.d.ts +2 -0
- package/dist/src/tools/type-checker.test.d.ts.map +1 -0
- package/dist/src/tools/type-checker.test.js +96 -0
- package/dist/src/tools/type-checker.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/inputs/model-a/spec-deltas.json +1460 -0
- package/inputs/model-b/spec-deltas.json +1424 -0
- package/inputs/model-c/spec-deltas.json +1432 -0
- package/inputs/model-d/spec-deltas.json +967 -0
- package/inputs/model-e/spec-deltas.json +2292 -0
- package/ketchup-plan.md +43 -8
- package/package.json +3 -3
- package/scoring-heuristic.md +138 -0
- package/scripts/improve.ts +23 -18
- package/src/commands/implement-component.test.ts +309 -76
- package/src/commands/implement-component.ts +155 -31
- package/src/extract-exports.ts +53 -0
- package/src/generate-story-deterministic.ts +267 -0
- package/src/index.ts +12 -0
- package/src/pipeline/run-pipeline.test.ts +292 -0
- package/src/pipeline/run-pipeline.ts +160 -0
- package/src/pipeline/steps/generate-component.test.ts +130 -0
- package/src/pipeline/steps/generate-component.ts +60 -0
- package/src/pipeline/steps/generate-story.test.ts +54 -0
- package/src/pipeline/steps/generate-story.ts +17 -0
- package/src/pipeline/steps/generate-test.test.ts +75 -0
- package/src/pipeline/steps/generate-test.ts +25 -0
- package/src/pipeline/steps/lint-fix-loop.test.ts +155 -0
- package/src/pipeline/steps/lint-fix-loop.ts +59 -0
- package/src/pipeline/steps/story-fix-loop.test.ts +123 -0
- package/src/pipeline/steps/story-fix-loop.ts +47 -0
- package/src/pipeline/steps/storybook-test.test.ts +82 -0
- package/src/pipeline/steps/storybook-test.ts +27 -0
- package/src/pipeline/steps/test-fix-loop.test.ts +201 -0
- package/src/pipeline/steps/test-fix-loop.ts +56 -0
- package/src/pipeline/steps/type-fix-loop.test.ts +145 -0
- package/src/pipeline/steps/type-fix-loop.ts +55 -0
- package/src/pipeline/steps/visual-test.test.ts +10 -0
- package/src/pipeline/steps/visual-test.ts +5 -0
- package/src/project-context.ts +205 -0
- package/src/prompt.test.ts +174 -8
- package/src/prompt.ts +301 -23
- package/src/scaffold.ts +281 -0
- package/src/tools/lint-runner.test.ts +112 -0
- package/src/tools/lint-runner.ts +52 -0
- package/src/tools/storybook-runner.test.ts +53 -0
- package/src/tools/storybook-runner.ts +29 -0
- package/src/tools/test-runner.test.ts +213 -0
- package/src/tools/test-runner.ts +84 -0
- package/src/tools/type-checker.test.ts +120 -0
- package/src/tools/type-checker.ts +42 -0
- package/vitest.config.ts +9 -1
- package/dist/src/generate-component.d.ts +0 -4
- package/dist/src/generate-component.d.ts.map +0 -1
- package/dist/src/generate-component.js +0 -14
- package/dist/src/generate-component.js.map +0 -1
- package/dist/src/generate-component.test.d.ts.map +0 -1
- package/dist/src/generate-component.test.js +0 -73
- package/dist/src/generate-component.test.js.map +0 -1
- package/dist/src/generate-story.d.ts +0 -4
- package/dist/src/generate-story.d.ts.map +0 -1
- package/dist/src/generate-story.js +0 -14
- package/dist/src/generate-story.js.map +0 -1
- package/dist/src/generate-story.test.d.ts.map +0 -1
- package/dist/src/generate-story.test.js +0 -58
- package/dist/src/generate-story.test.js.map +0 -1
- package/dist/src/generate-test.d.ts +0 -4
- package/dist/src/generate-test.d.ts.map +0 -1
- package/dist/src/generate-test.js +0 -14
- package/dist/src/generate-test.js.map +0 -1
- package/dist/src/generate-test.test.d.ts.map +0 -1
- package/dist/src/generate-test.test.js +0 -77
- package/dist/src/generate-test.test.js.map +0 -1
- package/dist/src/reconcile.d.ts +0 -8
- package/dist/src/reconcile.d.ts.map +0 -1
- package/dist/src/reconcile.js +0 -18
- package/dist/src/reconcile.js.map +0 -1
- package/dist/src/reconcile.test.d.ts +0 -2
- package/dist/src/reconcile.test.d.ts.map +0 -1
- package/dist/src/reconcile.test.js +0 -108
- package/dist/src/reconcile.test.js.map +0 -1
- package/src/generate-component.test.ts +0 -89
- package/src/generate-component.ts +0 -16
- package/src/generate-story.test.ts +0 -71
- package/src/generate-story.ts +0 -16
- package/src/generate-test.test.ts +0 -93
- package/src/generate-test.ts +0 -16
- package/src/reconcile.test.ts +0 -127
- package/src/reconcile.ts +0 -27
- /package/dist/src/{generate-component.test.d.ts → pipeline/steps/generate-component.test.d.ts} +0 -0
- /package/dist/src/{generate-story.test.d.ts → pipeline/steps/generate-story.test.d.ts} +0 -0
- /package/dist/src/{generate-test.test.d.ts → pipeline/steps/generate-test.test.d.ts} +0 -0
package/src/prompt.ts
CHANGED
|
@@ -64,14 +64,21 @@ Name props to match the spec language directly. If the spec says "shows loading
|
|
|
64
64
|
### Phase 3 — Implement the component body
|
|
65
65
|
Build the JSX tree to satisfy structure specs. Add conditional rendering for rendering specs. Wire event handlers for interaction specs. Apply Tailwind classes for styling specs.
|
|
66
66
|
|
|
67
|
-
### Phase 4 —
|
|
67
|
+
### Phase 4 — Align with the test file
|
|
68
|
+
If a test file is provided, it is the **source of truth**. Your component must pass every test. Read each test case and ensure:
|
|
69
|
+
- Your export name matches the test's import
|
|
70
|
+
- Your prop names match exactly what the tests pass
|
|
71
|
+
- Your rendered elements match what the tests query (getByRole, getByText)
|
|
72
|
+
- Your event handlers fire the callbacks the tests assert
|
|
73
|
+
|
|
74
|
+
### Phase 5 — Verify completeness
|
|
68
75
|
Walk through each spec delta and confirm the implementation covers it. Every spec string must trace to a specific line of code.`;
|
|
69
76
|
|
|
70
77
|
const COMPONENT_RULES = `─────────────────────────────────────────────
|
|
71
78
|
RULES
|
|
72
79
|
─────────────────────────────────────────────
|
|
73
80
|
|
|
74
|
-
1. **Functional components only.** Use \`function ComponentName\` with TypeScript. No class components.
|
|
81
|
+
1. **Functional components only.** Use \`function ComponentName\` with TypeScript. No class components. When the spec requires \`forwardRef\`, use \`export const ComponentName = React.forwardRef<HTMLElement, ComponentNameProps>(function ComponentName(props, ref) { ... })\` — never write \`export function ComponentName = forwardRef(...)\` (that is invalid syntax).
|
|
75
82
|
2. **Named exports.** Export the component as a named export. Export the Props type. No default exports.
|
|
76
83
|
3. **Tailwind CSS.** Use Tailwind utility classes for all styling. No inline styles, no CSS modules, no styled-components.
|
|
77
84
|
4. **ARIA roles.** Use semantic HTML elements and ARIA attributes for accessibility. If a spec implies a role (button, dialog, alert), use the correct element or role attribute.
|
|
@@ -80,7 +87,10 @@ RULES
|
|
|
80
87
|
7. **No side effects.** No fetch calls, no localStorage, no timers unless a spec explicitly requires them.
|
|
81
88
|
8. **No unnecessary state.** Derive from props where possible. Only use \`useState\` when the component genuinely manages internal state per spec.
|
|
82
89
|
9. **Composition-ready.** If structure specs mention slots or regions, accept them as children or named props (e.g. \`header\`, \`footer\`).
|
|
83
|
-
10. **
|
|
90
|
+
10. **Import paths.** Import composed UI components using the exact paths shown in the **Composed Components** section of the project context. Import utility functions from \`@/lib/utils\` (e.g. \`import { cn } from '@/lib/utils'\`). NEVER use made-up package names like \`'your-design-system'\`, \`'@company/ui'\`, or \`'@acme/components'\`. NEVER guess import paths — only use paths explicitly provided in the prompt.
|
|
91
|
+
11. **Code only.** Return ONLY the component file code. No markdown fences, no commentary, no file names.
|
|
92
|
+
12. **Numeric formatting.** Use the \`tabular-nums\` Tailwind class on all numeric displays (prices, counts, statistics). Use \`Intl.NumberFormat\` for currency formatting rather than string template literals with \`$\`.
|
|
93
|
+
13. **Type imports for composed component props.** When you need to reference a composed component's prop type (e.g. to type an array of items), use \`import type { XProps } from '...'\` with the exact import path from the Composed Components section. Never reference a type without importing it.`;
|
|
84
94
|
|
|
85
95
|
const COMPONENT_CHECKLIST = `─────────────────────────────────────────────
|
|
86
96
|
QUALITY CHECKLIST
|
|
@@ -95,7 +105,10 @@ Before returning, verify:
|
|
|
95
105
|
- [ ] Component is exported as a named export
|
|
96
106
|
- [ ] ARIA roles and semantic elements are used where specs imply them
|
|
97
107
|
- [ ] No dead code or unused imports
|
|
98
|
-
- [ ] Existing component behavior is preserved when modifying
|
|
108
|
+
- [ ] Existing component behavior is preserved when modifying
|
|
109
|
+
- [ ] Numeric values use tabular-nums and Intl.NumberFormat for currency
|
|
110
|
+
- [ ] forwardRef uses \`export const X = React.forwardRef(...)\` syntax, never \`export function X = forwardRef(...)\`
|
|
111
|
+
- [ ] All referenced types from composed components are imported with \`import type { ... }\``;
|
|
99
112
|
|
|
100
113
|
export const componentPromptSections = {
|
|
101
114
|
PREAMBLE: COMPONENT_PREAMBLE,
|
|
@@ -190,7 +203,9 @@ RULES
|
|
|
190
203
|
7. **No implementation testing.** Test behavior, not implementation details. Do not assert on internal state, hook calls, or component instance methods.
|
|
191
204
|
8. **Async awareness.** Use \`waitFor\` or \`findBy*\` queries when specs involve async behavior (loading states, delayed rendering).
|
|
192
205
|
9. **Test descriptions mirror specs.** Use spec language directly in test descriptions so traceability is obvious.
|
|
193
|
-
10. **Code only.** Return ONLY the test file code. No markdown fences, no commentary, no file names
|
|
206
|
+
10. **Code only.** Return ONLY the test file code. No markdown fences, no commentary, no file names.
|
|
207
|
+
11. **Tailwind classes, not inline styles.** The project uses Tailwind CSS. In JSDOM, Tailwind utility classes are just class names — they do NOT produce computed inline styles. Use \`toHaveClass('class-name')\` for class-based assertions. NEVER use \`toHaveStyle()\` for Tailwind utilities like \`tabular-nums\`, \`touch-manipulation\`, \`min-w-[44px]\`, etc. For hover states, transforms, transitions, and pseudo-elements — JSDOM cannot compute these. Skip those assertions entirely.
|
|
208
|
+
12. **Verify semantic elements from component source.** Some UI library components use unexpected elements (e.g., CardTitle renders a \`<div>\`, not a heading). If composed component source code is provided, check the actual rendered element before choosing your query strategy. Prefer \`getByText\` when the semantic element is uncertain.`;
|
|
194
209
|
|
|
195
210
|
const TEST_CHECKLIST = `─────────────────────────────────────────────
|
|
196
211
|
QUALITY CHECKLIST
|
|
@@ -253,13 +268,24 @@ const STORY_OUTPUT_FORMAT = `─────────────────
|
|
|
253
268
|
YOUR OUTPUT — A SINGLE .stories.tsx FILE
|
|
254
269
|
─────────────────────────────────────────────
|
|
255
270
|
|
|
256
|
-
Return a single Storybook story file using CSF3 format.
|
|
271
|
+
Return a single Storybook story file using CSF3 format. Follow this exact file structure:
|
|
257
272
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
273
|
+
\`\`\`
|
|
274
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
275
|
+
import { ComponentName } from '<import path from Component Import Path section>';
|
|
276
|
+
|
|
277
|
+
const meta: Meta<typeof ComponentName> = {
|
|
278
|
+
title: 'UI Components/ComponentName',
|
|
279
|
+
component: ComponentName,
|
|
280
|
+
};
|
|
281
|
+
export default meta; // ← REQUIRED — Storybook crashes without this line
|
|
282
|
+
|
|
283
|
+
type Story = StoryObj<typeof ComponentName>;
|
|
284
|
+
|
|
285
|
+
export const Default: Story = { args: { ... } };
|
|
286
|
+
\`\`\`
|
|
287
|
+
|
|
288
|
+
CRITICAL: The \`export default meta;\` line is **mandatory**. Without it Storybook will crash. Never omit it.`;
|
|
263
289
|
|
|
264
290
|
const STORY_METHODOLOGY = `─────────────────────────────────────────────
|
|
265
291
|
METHODOLOGY
|
|
@@ -284,16 +310,20 @@ const STORY_RULES = `───────────────────
|
|
|
284
310
|
RULES
|
|
285
311
|
─────────────────────────────────────────────
|
|
286
312
|
|
|
287
|
-
1. **CSF3 format.**
|
|
288
|
-
2. **
|
|
289
|
-
3. **
|
|
290
|
-
4. **
|
|
291
|
-
5. **
|
|
292
|
-
6. **
|
|
293
|
-
7. **
|
|
294
|
-
8. **
|
|
295
|
-
9. **
|
|
296
|
-
10. **
|
|
313
|
+
1. **CSF3 format with default export.** You MUST include \`export default meta;\` — Storybook crashes without it. Use named story exports. No CSF2 \`Template.bind({})\` pattern.
|
|
314
|
+
2. **Storybook imports.** Import types from \`@storybook/react-vite\`: \`import type { Meta, StoryObj } from '@storybook/react-vite'\`. NEVER import from \`@storybook/react\` — that package is not installed.
|
|
315
|
+
3. **Component import path.** Import the component using the exact path specified in the **Component Import Path** section of the user prompt. NEVER use relative imports like \`./ComponentName\` and NEVER guess the import path.
|
|
316
|
+
4. **Type annotations.** Type meta as \`Meta<typeof ComponentName>\`, then define \`type Story = StoryObj<typeof ComponentName>\` and use \`Story\` for all story exports.
|
|
317
|
+
5. **Prop names from spec language.** Use the exact prop names implied by the spec, matching the component and test agents.
|
|
318
|
+
6. **Args over render.** Prefer \`args: { ... }\` over custom \`render\` functions. Use \`render\` only when you need to wrap the component or demonstrate composition with other components.
|
|
319
|
+
7. **No action() or addon imports.** Do NOT import \`action\` from \`@storybook/addon-actions\` or \`fn\` from \`@storybook/test\` — these packages are not installed. For callback props, simply omit them from args (Storybook auto-detects and logs them), or use inline arrow functions like \`() => {}\`.
|
|
320
|
+
8. **Descriptive story names.** PascalCase names that describe the state: \`Loading\`, \`Empty\`, \`WithLongContent\`, \`Disabled\`, \`ErrorState\`.
|
|
321
|
+
9. **Default story first.** Always include a \`Default\` story that shows the component in its most common/ready state.
|
|
322
|
+
10. **1-3 stories maximum.** Keep it minimal: a \`Default\` story, plus at most 1-2 additional stories for the most important visual states (e.g. a key variant or a loading state). Do NOT create a story for every single prop combination — focus on what a designer needs to see at a glance.
|
|
323
|
+
11. **Meta fields.** Set \`title: 'UI Components/ComponentName'\` and \`component: ComponentName\` in meta. Both are required.
|
|
324
|
+
12. **No mock data in meta.** Put shared default args in \`meta.args\` only if they apply to all stories.
|
|
325
|
+
13. **Composed component imports.** When a story needs other UI components (e.g. for a \`render\` function demonstrating composition), import them using the exact paths shown in the **Composed Components** section of the project context. NEVER guess import paths.
|
|
326
|
+
14. **Code only.** Return ONLY the story file code. No markdown fences, no commentary, no file names.`;
|
|
297
327
|
|
|
298
328
|
const STORY_CHECKLIST = `─────────────────────────────────────────────
|
|
299
329
|
QUALITY CHECKLIST
|
|
@@ -305,9 +335,15 @@ Before returning, verify:
|
|
|
305
335
|
- [ ] Every styling variant (size, color, emphasis) has a dedicated story
|
|
306
336
|
- [ ] Story names are descriptive PascalCase
|
|
307
337
|
- [ ] Args use prop names that match spec language
|
|
308
|
-
- [ ] Meta has correct component reference and title
|
|
338
|
+
- [ ] Meta has correct component reference and \`title: 'UI Components/ComponentName'\`
|
|
339
|
+
- [ ] \`export default meta;\` is present (Storybook crashes without it)
|
|
340
|
+
- [ ] Meta has \`component: ComponentName\` field
|
|
309
341
|
- [ ] CSF3 format is used throughout
|
|
310
|
-
- [ ] No unnecessary render functions — args suffice where possible
|
|
342
|
+
- [ ] No unnecessary render functions — args suffice where possible
|
|
343
|
+
- [ ] Imports use \`@storybook/react-vite\` (NOT \`@storybook/react\`)
|
|
344
|
+
- [ ] Component imported using the exact path from the **Component Import Path** section (NOT relative path, NOT guessed)
|
|
345
|
+
- [ ] No \`action()\` from \`@storybook/addon-actions\` — omit callbacks or use inline arrows
|
|
346
|
+
- [ ] No imports from \`@storybook/test\` — \`fn()\`, \`userEvent\`, \`expect()\` are not available`;
|
|
311
347
|
|
|
312
348
|
export const storyPromptSections = {
|
|
313
349
|
PREAMBLE: STORY_PREAMBLE,
|
|
@@ -464,17 +500,23 @@ export interface ComponentPromptInput {
|
|
|
464
500
|
componentName: string;
|
|
465
501
|
specDeltas: SpecDeltas;
|
|
466
502
|
existingComponent?: string;
|
|
503
|
+
projectSection: string;
|
|
504
|
+
testCode?: string;
|
|
467
505
|
}
|
|
468
506
|
|
|
469
507
|
export interface TestPromptInput {
|
|
470
508
|
componentName: string;
|
|
471
509
|
specDeltas: SpecDeltas;
|
|
472
510
|
existingComponent?: string;
|
|
511
|
+
projectSection: string;
|
|
473
512
|
}
|
|
474
513
|
|
|
475
514
|
export interface StoryPromptInput {
|
|
476
515
|
componentName: string;
|
|
477
516
|
specDeltas: SpecDeltas;
|
|
517
|
+
projectSection: string;
|
|
518
|
+
componentCode?: string;
|
|
519
|
+
componentImportPath: string;
|
|
478
520
|
}
|
|
479
521
|
|
|
480
522
|
export interface ReconcilerPromptInput {
|
|
@@ -484,6 +526,7 @@ export interface ReconcilerPromptInput {
|
|
|
484
526
|
testCode: string;
|
|
485
527
|
storyCode: string;
|
|
486
528
|
existingComponent?: string;
|
|
529
|
+
projectSection: string;
|
|
487
530
|
}
|
|
488
531
|
|
|
489
532
|
export function buildComponentPrompt(input: ComponentPromptInput): PromptResult {
|
|
@@ -501,6 +544,23 @@ export function buildComponentPrompt(input: ComponentPromptInput): PromptResult
|
|
|
501
544
|
parts.push('```');
|
|
502
545
|
}
|
|
503
546
|
|
|
547
|
+
if (input.testCode) {
|
|
548
|
+
parts.push('\n\n## Test File (source of truth — your component MUST pass every test)\n');
|
|
549
|
+
parts.push('The test file below was generated first and is immutable. Study it carefully:');
|
|
550
|
+
parts.push('- Match the exact export name the tests import');
|
|
551
|
+
parts.push('- Accept every prop the tests pass (with exact names)');
|
|
552
|
+
parts.push('- Render elements the tests query (getByRole, getByText)');
|
|
553
|
+
parts.push('- Wire event handlers the tests fire (fireEvent.click → onClick prop)');
|
|
554
|
+
parts.push('- Produce the conditional rendering the tests expect\n');
|
|
555
|
+
parts.push('```tsx');
|
|
556
|
+
parts.push(input.testCode);
|
|
557
|
+
parts.push('```');
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (input.projectSection) {
|
|
561
|
+
parts.push('\n\n' + input.projectSection);
|
|
562
|
+
}
|
|
563
|
+
|
|
504
564
|
return {
|
|
505
565
|
system: buildComponentSystemPrompt(),
|
|
506
566
|
prompt: parts.join('\n'),
|
|
@@ -522,6 +582,10 @@ export function buildTestPrompt(input: TestPromptInput): PromptResult {
|
|
|
522
582
|
parts.push('```');
|
|
523
583
|
}
|
|
524
584
|
|
|
585
|
+
if (input.projectSection) {
|
|
586
|
+
parts.push('\n\n' + input.projectSection);
|
|
587
|
+
}
|
|
588
|
+
|
|
525
589
|
return {
|
|
526
590
|
system: buildTestSystemPrompt(),
|
|
527
591
|
prompt: parts.join('\n'),
|
|
@@ -535,9 +599,23 @@ export function buildStoryPrompt(input: StoryPromptInput): PromptResult {
|
|
|
535
599
|
`Write Storybook stories for the React component **${input.componentName}** from the following spec deltas.\n`,
|
|
536
600
|
);
|
|
537
601
|
|
|
602
|
+
parts.push('## Component Import Path\n');
|
|
603
|
+
parts.push(`Import the component as: \`import { ${input.componentName} } from '${input.componentImportPath}'\`\n`);
|
|
604
|
+
|
|
538
605
|
parts.push('## Spec Deltas\n');
|
|
539
606
|
parts.push(buildSpecText(input.specDeltas));
|
|
540
607
|
|
|
608
|
+
if (input.componentCode) {
|
|
609
|
+
parts.push('\n\n## Component Code (use this as the source of truth for props, exports, and types)\n');
|
|
610
|
+
parts.push('```tsx');
|
|
611
|
+
parts.push(input.componentCode);
|
|
612
|
+
parts.push('```');
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (input.projectSection) {
|
|
616
|
+
parts.push('\n\n' + input.projectSection);
|
|
617
|
+
}
|
|
618
|
+
|
|
541
619
|
return {
|
|
542
620
|
system: buildStorySystemPrompt(),
|
|
543
621
|
prompt: parts.join('\n'),
|
|
@@ -574,8 +652,208 @@ export function buildReconcilerPrompt(input: ReconcilerPromptInput): PromptResul
|
|
|
574
652
|
parts.push(input.storyCode);
|
|
575
653
|
parts.push('```');
|
|
576
654
|
|
|
655
|
+
if (input.projectSection) {
|
|
656
|
+
parts.push('\n\n' + input.projectSection);
|
|
657
|
+
}
|
|
658
|
+
|
|
577
659
|
return {
|
|
578
660
|
system: buildReconcilerSystemPrompt(),
|
|
579
661
|
prompt: parts.join('\n'),
|
|
580
662
|
};
|
|
581
663
|
}
|
|
664
|
+
|
|
665
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
666
|
+
// TYPE FIXER — fixes TypeScript compilation errors
|
|
667
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
668
|
+
|
|
669
|
+
const TYPE_FIX_SYSTEM = `You are a TypeScript error fixer. You receive a React component file, its test file, and TypeScript compiler errors. Your job is to make minimal surgical changes to fix the type errors.
|
|
670
|
+
|
|
671
|
+
Return EXACTLY two code blocks:
|
|
672
|
+
1. First block: the fixed component file
|
|
673
|
+
2. Second block: the fixed test file
|
|
674
|
+
|
|
675
|
+
Rules:
|
|
676
|
+
- Fix ONLY the errors listed. Do not refactor or improve unrelated code.
|
|
677
|
+
- Preserve all existing behavior and functionality.
|
|
678
|
+
- Prefer fixing the component over the test (test is source of truth).
|
|
679
|
+
- If a type error is in the test, only fix it if the test has an import/type mismatch — never change test behavior.
|
|
680
|
+
- Return complete files, not diffs.
|
|
681
|
+
- No commentary, no explanations. Just the two code blocks.`;
|
|
682
|
+
|
|
683
|
+
export interface TypeFixInput {
|
|
684
|
+
componentCode: string;
|
|
685
|
+
testCode: string;
|
|
686
|
+
errors: string[];
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export function buildTypeFixPrompt(input: TypeFixInput): PromptResult {
|
|
690
|
+
const parts: string[] = [];
|
|
691
|
+
|
|
692
|
+
parts.push('## TypeScript Errors\n');
|
|
693
|
+
parts.push('```');
|
|
694
|
+
parts.push(input.errors.join('\n'));
|
|
695
|
+
parts.push('```');
|
|
696
|
+
|
|
697
|
+
parts.push('\n\n## Component Code\n');
|
|
698
|
+
parts.push('```tsx');
|
|
699
|
+
parts.push(input.componentCode);
|
|
700
|
+
parts.push('```');
|
|
701
|
+
|
|
702
|
+
parts.push('\n\n## Test Code\n');
|
|
703
|
+
parts.push('```tsx');
|
|
704
|
+
parts.push(input.testCode);
|
|
705
|
+
parts.push('```');
|
|
706
|
+
|
|
707
|
+
return {
|
|
708
|
+
system: TYPE_FIX_SYSTEM,
|
|
709
|
+
prompt: parts.join('\n'),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
714
|
+
// TEST FIXER — fixes test failures by adjusting the component
|
|
715
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
716
|
+
|
|
717
|
+
const TEST_FIX_SYSTEM = `You are a test failure fixer. You receive a React component, its test file, and vitest failure output. The test is the source of truth — fix the component to pass the tests.
|
|
718
|
+
|
|
719
|
+
Return EXACTLY two code blocks:
|
|
720
|
+
1. First block: the fixed component file
|
|
721
|
+
2. Second block: the fixed test file (usually unchanged)
|
|
722
|
+
|
|
723
|
+
Rules:
|
|
724
|
+
- The test file is the source of truth. Prefer fixing the component.
|
|
725
|
+
- Only modify the test if it has a genuine bug (e.g. wrong import path, typo in query). Never weaken assertions.
|
|
726
|
+
- Make minimal changes to pass the failing tests without breaking passing ones.
|
|
727
|
+
- Return complete files, not diffs.
|
|
728
|
+
- No commentary, no explanations. Just the two code blocks.`;
|
|
729
|
+
|
|
730
|
+
export interface TestFixInput {
|
|
731
|
+
componentCode: string;
|
|
732
|
+
testCode: string;
|
|
733
|
+
failures: string[];
|
|
734
|
+
testOutput: string;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export function buildTestFixPrompt(input: TestFixInput): PromptResult {
|
|
738
|
+
const parts: string[] = [];
|
|
739
|
+
|
|
740
|
+
parts.push('## Test Failures\n');
|
|
741
|
+
for (const failure of input.failures) {
|
|
742
|
+
parts.push('```');
|
|
743
|
+
parts.push(failure);
|
|
744
|
+
parts.push('```\n');
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
parts.push('\n## Test Output\n');
|
|
748
|
+
parts.push('```');
|
|
749
|
+
parts.push(input.testOutput);
|
|
750
|
+
parts.push('```');
|
|
751
|
+
|
|
752
|
+
parts.push('\n\n## Component Code\n');
|
|
753
|
+
parts.push('```tsx');
|
|
754
|
+
parts.push(input.componentCode);
|
|
755
|
+
parts.push('```');
|
|
756
|
+
|
|
757
|
+
parts.push('\n\n## Test Code\n');
|
|
758
|
+
parts.push('```tsx');
|
|
759
|
+
parts.push(input.testCode);
|
|
760
|
+
parts.push('```');
|
|
761
|
+
|
|
762
|
+
return {
|
|
763
|
+
system: TEST_FIX_SYSTEM,
|
|
764
|
+
prompt: parts.join('\n'),
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
769
|
+
// LINT FIXER — fixes lint errors that biome --write couldn't auto-fix
|
|
770
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
771
|
+
|
|
772
|
+
const LINT_FIX_SYSTEM = `You are a lint error fixer. You receive source files and lint errors that could not be auto-fixed by biome. Fix the remaining lint issues with minimal changes.
|
|
773
|
+
|
|
774
|
+
Return EXACTLY two code blocks:
|
|
775
|
+
1. First block: the fixed component file
|
|
776
|
+
2. Second block: the fixed test file
|
|
777
|
+
|
|
778
|
+
Rules:
|
|
779
|
+
- Fix ONLY the lint errors listed. Do not refactor unrelated code.
|
|
780
|
+
- Preserve all behavior and functionality.
|
|
781
|
+
- Return complete files, not diffs.
|
|
782
|
+
- No commentary, no explanations. Just the two code blocks.`;
|
|
783
|
+
|
|
784
|
+
export interface LintFixInput {
|
|
785
|
+
componentCode: string;
|
|
786
|
+
testCode: string;
|
|
787
|
+
errors: string[];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export function buildLintFixPrompt(input: LintFixInput): PromptResult {
|
|
791
|
+
const parts: string[] = [];
|
|
792
|
+
|
|
793
|
+
parts.push('## Lint Errors\n');
|
|
794
|
+
parts.push('```');
|
|
795
|
+
parts.push(input.errors.join('\n'));
|
|
796
|
+
parts.push('```');
|
|
797
|
+
|
|
798
|
+
parts.push('\n\n## Component Code\n');
|
|
799
|
+
parts.push('```tsx');
|
|
800
|
+
parts.push(input.componentCode);
|
|
801
|
+
parts.push('```');
|
|
802
|
+
|
|
803
|
+
parts.push('\n\n## Test Code\n');
|
|
804
|
+
parts.push('```tsx');
|
|
805
|
+
parts.push(input.testCode);
|
|
806
|
+
parts.push('```');
|
|
807
|
+
|
|
808
|
+
return {
|
|
809
|
+
system: LINT_FIX_SYSTEM,
|
|
810
|
+
prompt: parts.join('\n'),
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
815
|
+
// STORY FIXER — fixes story type errors
|
|
816
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
817
|
+
|
|
818
|
+
const STORY_FIX_SYSTEM = `You are a Storybook story fixer. You receive a story file, the component it references, and TypeScript errors. Fix the story to compile correctly.
|
|
819
|
+
|
|
820
|
+
Return EXACTLY one code block: the fixed story file.
|
|
821
|
+
|
|
822
|
+
Rules:
|
|
823
|
+
- Fix ONLY the errors listed. Do not add stories or change story behavior.
|
|
824
|
+
- Ensure imports match the component's actual exports.
|
|
825
|
+
- Ensure args match the component's actual props.
|
|
826
|
+
- Use CSF3 format with \`export default meta;\`.
|
|
827
|
+
- Import types from \`@storybook/react-vite\`.
|
|
828
|
+
- Return the complete file, not a diff.
|
|
829
|
+
- No commentary, no explanations. Just the one code block.`;
|
|
830
|
+
|
|
831
|
+
export interface StoryFixInput {
|
|
832
|
+
storyCode: string;
|
|
833
|
+
componentCode: string;
|
|
834
|
+
errors: string[];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export function buildStoryFixPrompt(input: StoryFixInput): PromptResult {
|
|
838
|
+
const parts: string[] = [];
|
|
839
|
+
|
|
840
|
+
parts.push('## TypeScript Errors\n');
|
|
841
|
+
parts.push('```');
|
|
842
|
+
parts.push(input.errors.join('\n'));
|
|
843
|
+
parts.push('```');
|
|
844
|
+
|
|
845
|
+
parts.push('\n\n## Story Code\n');
|
|
846
|
+
parts.push('```tsx');
|
|
847
|
+
parts.push(input.storyCode);
|
|
848
|
+
parts.push('```');
|
|
849
|
+
|
|
850
|
+
parts.push('\n\n## Component Code (reference)\n');
|
|
851
|
+
parts.push('```tsx');
|
|
852
|
+
parts.push(input.componentCode);
|
|
853
|
+
parts.push('```');
|
|
854
|
+
|
|
855
|
+
return {
|
|
856
|
+
system: STORY_FIX_SYSTEM,
|
|
857
|
+
prompt: parts.join('\n'),
|
|
858
|
+
};
|
|
859
|
+
}
|