@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.
Files changed (233) 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 +92 -0
  5. package/dist/src/commands/implement-component.d.ts +19 -0
  6. package/dist/src/commands/implement-component.d.ts.map +1 -1
  7. package/dist/src/commands/implement-component.js +109 -30
  8. package/dist/src/commands/implement-component.js.map +1 -1
  9. package/dist/src/commands/implement-component.test.js +259 -69
  10. package/dist/src/commands/implement-component.test.js.map +1 -1
  11. package/dist/src/extract-exports.d.ts +6 -0
  12. package/dist/src/extract-exports.d.ts.map +1 -0
  13. package/dist/src/extract-exports.js +46 -0
  14. package/dist/src/extract-exports.js.map +1 -0
  15. package/dist/src/generate-story-deterministic.d.ts +30 -0
  16. package/dist/src/generate-story-deterministic.d.ts.map +1 -0
  17. package/dist/src/generate-story-deterministic.js +229 -0
  18. package/dist/src/generate-story-deterministic.js.map +1 -0
  19. package/dist/src/index.d.ts +4 -0
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +3 -0
  22. package/dist/src/index.js.map +1 -1
  23. package/dist/src/pipeline/run-pipeline.d.ts +69 -0
  24. package/dist/src/pipeline/run-pipeline.d.ts.map +1 -0
  25. package/dist/src/pipeline/run-pipeline.js +78 -0
  26. package/dist/src/pipeline/run-pipeline.js.map +1 -0
  27. package/dist/src/pipeline/run-pipeline.test.d.ts +2 -0
  28. package/dist/src/pipeline/run-pipeline.test.d.ts.map +1 -0
  29. package/dist/src/pipeline/run-pipeline.test.js +247 -0
  30. package/dist/src/pipeline/run-pipeline.test.js.map +1 -0
  31. package/dist/src/pipeline/steps/generate-component.d.ts +4 -0
  32. package/dist/src/pipeline/steps/generate-component.d.ts.map +1 -0
  33. package/dist/src/pipeline/steps/generate-component.js +50 -0
  34. package/dist/src/pipeline/steps/generate-component.js.map +1 -0
  35. package/dist/src/pipeline/steps/generate-component.test.d.ts.map +1 -0
  36. package/dist/src/pipeline/steps/generate-component.test.js +106 -0
  37. package/dist/src/pipeline/steps/generate-component.test.js.map +1 -0
  38. package/dist/src/pipeline/steps/generate-story.d.ts +3 -0
  39. package/dist/src/pipeline/steps/generate-story.d.ts.map +1 -0
  40. package/dist/src/pipeline/steps/generate-story.js +14 -0
  41. package/dist/src/pipeline/steps/generate-story.js.map +1 -0
  42. package/dist/src/pipeline/steps/generate-story.test.d.ts.map +1 -0
  43. package/dist/src/pipeline/steps/generate-story.test.js +41 -0
  44. package/dist/src/pipeline/steps/generate-story.test.js.map +1 -0
  45. package/dist/src/pipeline/steps/generate-test.d.ts +4 -0
  46. package/dist/src/pipeline/steps/generate-test.d.ts.map +1 -0
  47. package/dist/src/pipeline/steps/generate-test.js +19 -0
  48. package/dist/src/pipeline/steps/generate-test.js.map +1 -0
  49. package/dist/src/pipeline/steps/generate-test.test.d.ts.map +1 -0
  50. package/dist/src/pipeline/steps/generate-test.test.js +60 -0
  51. package/dist/src/pipeline/steps/generate-test.test.js.map +1 -0
  52. package/dist/src/pipeline/steps/lint-fix-loop.d.ts +4 -0
  53. package/dist/src/pipeline/steps/lint-fix-loop.d.ts.map +1 -0
  54. package/dist/src/pipeline/steps/lint-fix-loop.js +45 -0
  55. package/dist/src/pipeline/steps/lint-fix-loop.js.map +1 -0
  56. package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts +2 -0
  57. package/dist/src/pipeline/steps/lint-fix-loop.test.d.ts.map +1 -0
  58. package/dist/src/pipeline/steps/lint-fix-loop.test.js +119 -0
  59. package/dist/src/pipeline/steps/lint-fix-loop.test.js.map +1 -0
  60. package/dist/src/pipeline/steps/story-fix-loop.d.ts +4 -0
  61. package/dist/src/pipeline/steps/story-fix-loop.d.ts.map +1 -0
  62. package/dist/src/pipeline/steps/story-fix-loop.js +34 -0
  63. package/dist/src/pipeline/steps/story-fix-loop.js.map +1 -0
  64. package/dist/src/pipeline/steps/story-fix-loop.test.d.ts +2 -0
  65. package/dist/src/pipeline/steps/story-fix-loop.test.d.ts.map +1 -0
  66. package/dist/src/pipeline/steps/story-fix-loop.test.js +94 -0
  67. package/dist/src/pipeline/steps/story-fix-loop.test.js.map +1 -0
  68. package/dist/src/pipeline/steps/storybook-test.d.ts +3 -0
  69. package/dist/src/pipeline/steps/storybook-test.d.ts.map +1 -0
  70. package/dist/src/pipeline/steps/storybook-test.js +22 -0
  71. package/dist/src/pipeline/steps/storybook-test.js.map +1 -0
  72. package/dist/src/pipeline/steps/storybook-test.test.d.ts +2 -0
  73. package/dist/src/pipeline/steps/storybook-test.test.d.ts.map +1 -0
  74. package/dist/src/pipeline/steps/storybook-test.test.js +66 -0
  75. package/dist/src/pipeline/steps/storybook-test.test.js.map +1 -0
  76. package/dist/src/pipeline/steps/test-fix-loop.d.ts +4 -0
  77. package/dist/src/pipeline/steps/test-fix-loop.d.ts.map +1 -0
  78. package/dist/src/pipeline/steps/test-fix-loop.js +44 -0
  79. package/dist/src/pipeline/steps/test-fix-loop.js.map +1 -0
  80. package/dist/src/pipeline/steps/test-fix-loop.test.d.ts +2 -0
  81. package/dist/src/pipeline/steps/test-fix-loop.test.d.ts.map +1 -0
  82. package/dist/src/pipeline/steps/test-fix-loop.test.js +168 -0
  83. package/dist/src/pipeline/steps/test-fix-loop.test.js.map +1 -0
  84. package/dist/src/pipeline/steps/type-fix-loop.d.ts +4 -0
  85. package/dist/src/pipeline/steps/type-fix-loop.d.ts.map +1 -0
  86. package/dist/src/pipeline/steps/type-fix-loop.js +43 -0
  87. package/dist/src/pipeline/steps/type-fix-loop.js.map +1 -0
  88. package/dist/src/pipeline/steps/type-fix-loop.test.d.ts +2 -0
  89. package/dist/src/pipeline/steps/type-fix-loop.test.d.ts.map +1 -0
  90. package/dist/src/pipeline/steps/type-fix-loop.test.js +112 -0
  91. package/dist/src/pipeline/steps/type-fix-loop.test.js.map +1 -0
  92. package/dist/src/pipeline/steps/visual-test.d.ts +3 -0
  93. package/dist/src/pipeline/steps/visual-test.d.ts.map +1 -0
  94. package/dist/src/pipeline/steps/visual-test.js +4 -0
  95. package/dist/src/pipeline/steps/visual-test.js.map +1 -0
  96. package/dist/src/pipeline/steps/visual-test.test.d.ts +2 -0
  97. package/dist/src/pipeline/steps/visual-test.test.d.ts.map +1 -0
  98. package/dist/src/pipeline/steps/visual-test.test.js +9 -0
  99. package/dist/src/pipeline/steps/visual-test.test.js.map +1 -0
  100. package/dist/src/project-context.d.ts +10 -0
  101. package/dist/src/project-context.d.ts.map +1 -0
  102. package/dist/src/project-context.js +178 -0
  103. package/dist/src/project-context.js.map +1 -0
  104. package/dist/src/prompt.d.ts +39 -7
  105. package/dist/src/prompt.d.ts.map +1 -1
  106. package/dist/src/prompt.js +233 -23
  107. package/dist/src/prompt.js.map +1 -1
  108. package/dist/src/prompt.test.js +154 -9
  109. package/dist/src/prompt.test.js.map +1 -1
  110. package/dist/src/scaffold.d.ts +49 -0
  111. package/dist/src/scaffold.d.ts.map +1 -0
  112. package/dist/src/scaffold.js +208 -0
  113. package/dist/src/scaffold.js.map +1 -0
  114. package/dist/src/tools/lint-runner.d.ts +7 -0
  115. package/dist/src/tools/lint-runner.d.ts.map +1 -0
  116. package/dist/src/tools/lint-runner.js +48 -0
  117. package/dist/src/tools/lint-runner.js.map +1 -0
  118. package/dist/src/tools/lint-runner.test.d.ts +2 -0
  119. package/dist/src/tools/lint-runner.test.d.ts.map +1 -0
  120. package/dist/src/tools/lint-runner.test.js +90 -0
  121. package/dist/src/tools/lint-runner.test.js.map +1 -0
  122. package/dist/src/tools/storybook-runner.d.ts +6 -0
  123. package/dist/src/tools/storybook-runner.d.ts.map +1 -0
  124. package/dist/src/tools/storybook-runner.js +25 -0
  125. package/dist/src/tools/storybook-runner.js.map +1 -0
  126. package/dist/src/tools/storybook-runner.test.d.ts +2 -0
  127. package/dist/src/tools/storybook-runner.test.d.ts.map +1 -0
  128. package/dist/src/tools/storybook-runner.test.js +43 -0
  129. package/dist/src/tools/storybook-runner.test.js.map +1 -0
  130. package/dist/src/tools/test-runner.d.ts +9 -0
  131. package/dist/src/tools/test-runner.d.ts.map +1 -0
  132. package/dist/src/tools/test-runner.js +74 -0
  133. package/dist/src/tools/test-runner.js.map +1 -0
  134. package/dist/src/tools/test-runner.test.d.ts +2 -0
  135. package/dist/src/tools/test-runner.test.d.ts.map +1 -0
  136. package/dist/src/tools/test-runner.test.js +177 -0
  137. package/dist/src/tools/test-runner.test.js.map +1 -0
  138. package/dist/src/tools/type-checker.d.ts +6 -0
  139. package/dist/src/tools/type-checker.d.ts.map +1 -0
  140. package/dist/src/tools/type-checker.js +36 -0
  141. package/dist/src/tools/type-checker.js.map +1 -0
  142. package/dist/src/tools/type-checker.test.d.ts +2 -0
  143. package/dist/src/tools/type-checker.test.d.ts.map +1 -0
  144. package/dist/src/tools/type-checker.test.js +96 -0
  145. package/dist/src/tools/type-checker.test.js.map +1 -0
  146. package/dist/tsconfig.tsbuildinfo +1 -1
  147. package/inputs/model-a/spec-deltas.json +1460 -0
  148. package/inputs/model-b/spec-deltas.json +1424 -0
  149. package/inputs/model-c/spec-deltas.json +1432 -0
  150. package/inputs/model-d/spec-deltas.json +967 -0
  151. package/inputs/model-e/spec-deltas.json +2292 -0
  152. package/ketchup-plan.md +43 -8
  153. package/package.json +3 -3
  154. package/scoring-heuristic.md +138 -0
  155. package/scripts/improve.ts +23 -18
  156. package/src/commands/implement-component.test.ts +309 -76
  157. package/src/commands/implement-component.ts +155 -31
  158. package/src/extract-exports.ts +53 -0
  159. package/src/generate-story-deterministic.ts +267 -0
  160. package/src/index.ts +12 -0
  161. package/src/pipeline/run-pipeline.test.ts +292 -0
  162. package/src/pipeline/run-pipeline.ts +160 -0
  163. package/src/pipeline/steps/generate-component.test.ts +130 -0
  164. package/src/pipeline/steps/generate-component.ts +60 -0
  165. package/src/pipeline/steps/generate-story.test.ts +54 -0
  166. package/src/pipeline/steps/generate-story.ts +17 -0
  167. package/src/pipeline/steps/generate-test.test.ts +75 -0
  168. package/src/pipeline/steps/generate-test.ts +25 -0
  169. package/src/pipeline/steps/lint-fix-loop.test.ts +155 -0
  170. package/src/pipeline/steps/lint-fix-loop.ts +59 -0
  171. package/src/pipeline/steps/story-fix-loop.test.ts +123 -0
  172. package/src/pipeline/steps/story-fix-loop.ts +47 -0
  173. package/src/pipeline/steps/storybook-test.test.ts +82 -0
  174. package/src/pipeline/steps/storybook-test.ts +27 -0
  175. package/src/pipeline/steps/test-fix-loop.test.ts +201 -0
  176. package/src/pipeline/steps/test-fix-loop.ts +56 -0
  177. package/src/pipeline/steps/type-fix-loop.test.ts +145 -0
  178. package/src/pipeline/steps/type-fix-loop.ts +55 -0
  179. package/src/pipeline/steps/visual-test.test.ts +10 -0
  180. package/src/pipeline/steps/visual-test.ts +5 -0
  181. package/src/project-context.ts +205 -0
  182. package/src/prompt.test.ts +174 -8
  183. package/src/prompt.ts +301 -23
  184. package/src/scaffold.ts +281 -0
  185. package/src/tools/lint-runner.test.ts +112 -0
  186. package/src/tools/lint-runner.ts +52 -0
  187. package/src/tools/storybook-runner.test.ts +53 -0
  188. package/src/tools/storybook-runner.ts +29 -0
  189. package/src/tools/test-runner.test.ts +213 -0
  190. package/src/tools/test-runner.ts +84 -0
  191. package/src/tools/type-checker.test.ts +120 -0
  192. package/src/tools/type-checker.ts +42 -0
  193. package/vitest.config.ts +9 -1
  194. package/dist/src/generate-component.d.ts +0 -4
  195. package/dist/src/generate-component.d.ts.map +0 -1
  196. package/dist/src/generate-component.js +0 -14
  197. package/dist/src/generate-component.js.map +0 -1
  198. package/dist/src/generate-component.test.d.ts.map +0 -1
  199. package/dist/src/generate-component.test.js +0 -73
  200. package/dist/src/generate-component.test.js.map +0 -1
  201. package/dist/src/generate-story.d.ts +0 -4
  202. package/dist/src/generate-story.d.ts.map +0 -1
  203. package/dist/src/generate-story.js +0 -14
  204. package/dist/src/generate-story.js.map +0 -1
  205. package/dist/src/generate-story.test.d.ts.map +0 -1
  206. package/dist/src/generate-story.test.js +0 -58
  207. package/dist/src/generate-story.test.js.map +0 -1
  208. package/dist/src/generate-test.d.ts +0 -4
  209. package/dist/src/generate-test.d.ts.map +0 -1
  210. package/dist/src/generate-test.js +0 -14
  211. package/dist/src/generate-test.js.map +0 -1
  212. package/dist/src/generate-test.test.d.ts.map +0 -1
  213. package/dist/src/generate-test.test.js +0 -77
  214. package/dist/src/generate-test.test.js.map +0 -1
  215. package/dist/src/reconcile.d.ts +0 -8
  216. package/dist/src/reconcile.d.ts.map +0 -1
  217. package/dist/src/reconcile.js +0 -18
  218. package/dist/src/reconcile.js.map +0 -1
  219. package/dist/src/reconcile.test.d.ts +0 -2
  220. package/dist/src/reconcile.test.d.ts.map +0 -1
  221. package/dist/src/reconcile.test.js +0 -108
  222. package/dist/src/reconcile.test.js.map +0 -1
  223. package/src/generate-component.test.ts +0 -89
  224. package/src/generate-component.ts +0 -16
  225. package/src/generate-story.test.ts +0 -71
  226. package/src/generate-story.ts +0 -16
  227. package/src/generate-test.test.ts +0 -93
  228. package/src/generate-test.ts +0 -16
  229. package/src/reconcile.test.ts +0 -127
  230. package/src/reconcile.ts +0 -27
  231. /package/dist/src/{generate-component.test.d.ts → pipeline/steps/generate-component.test.d.ts} +0 -0
  232. /package/dist/src/{generate-story.test.d.ts → pipeline/steps/generate-story.test.d.ts} +0 -0
  233. /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 — Verify completeness
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. **Code only.** Return ONLY the component file code. No markdown fences, no commentary, no file names.`;
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. The file must contain:
271
+ Return a single Storybook story file using CSF3 format. Follow this exact file structure:
257
272
 
258
- 1. Import \`Meta\` and \`StoryObj\` types from \`@storybook/react\`
259
- 2. Import the component as a named import from \`./ComponentName\`
260
- 3. A \`meta\` object (typed as \`Meta<typeof ComponentName>\`) as the default export
261
- 4. Named story exports typed as \`StoryObj<typeof ComponentName>\`
262
- 5. At minimum: a \`Default\` story plus one story per distinct visual/behavioral state`;
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.** Use \`export default meta\` and named story exports. No CSF2 \`Template.bind({})\` pattern.
288
- 2. **Named import.** Import the component as \`import { ComponentName } from './ComponentName'\`.
289
- 3. **Type annotations.** Type meta as \`Meta<typeof ComponentName>\` and stories as \`StoryObj<typeof ComponentName>\`.
290
- 4. **Prop names from spec language.** Use the exact prop names implied by the spec, matching the component and test agents.
291
- 5. **Args over render.** Prefer \`args: { ... }\` over custom \`render\` functions. Use \`render\` only when you need to wrap the component or demonstrate interaction.
292
- 6. **Action callbacks.** Use \`action('onEventName')\` from \`@storybook/addon-actions\` for callback props, or simply omit them if not critical to the story.
293
- 7. **Descriptive story names.** PascalCase names that describe the state: \`Loading\`, \`Empty\`, \`WithLongContent\`, \`Disabled\`, \`ErrorState\`.
294
- 8. **Default story first.** Always include a \`Default\` story that shows the component in its most common/ready state.
295
- 9. **No mock data in meta.** Put shared default args in \`meta.args\` only if they apply to all stories.
296
- 10. **Code only.** Return ONLY the story file code. No markdown fences, no commentary, no file names.`;
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
+ }