Generates production-ready React components, tests, and Storybook stories from structured spec deltas using an LLM-powered multi-stage pipeline.
Without this package, you would have to manually write every React component, its Vitest test file, and its Storybook story from scratch, then iterate through type errors, test failures, and lint issues by hand. This package automates the entire flow: it scaffolds the component skeleton, calls an LLM to fill in the implementation body, generates tests and stories in parallel, runs type-check/test/lint verification loops, and scores the result -- all in a single command invocation.
| Name | Alias | Description |
|---|---|---|
ImplementComponent |
implement:component |
Generate a React component with test and Storybook story from structured spec deltas |
ComponentImplemented-- The component, test, and story were generated and verified successfully.ComponentImplementationFailed-- Generation failed with an error message.
The input specification is expressed as four arrays of requirement strings:
- structure -- DOM structure and component composition requirements
- rendering -- What the component renders given various prop combinations
- interaction -- User interaction behavior (clicks, hovers, keyboard)
- styling -- Visual appearance, Tailwind classes, responsive rules
When props metadata is provided, the package generates a deterministic scaffold (imports, props type, function signature) with a __BODY__ placeholder. The LLM only fills in the component body, keeping the type-safe skeleton locked down.
After initial generation, the pipeline runs automated fix loops for:
- Type errors --
tscagainst the component and story files - Test failures --
vitest runagainst the generated test file - Lint errors --
biome checkwith auto-fix, then LLM-assisted fix - Story type errors --
tscagainst the story file specifically
Each loop has a configurable max iteration count (env vars MAX_TYPE_FIX_ITERATIONS, MAX_TEST_FIX_ITERATIONS, MAX_LINT_FIX_ITERATIONS, MAX_STORY_FIX_ITERATIONS).
A final LLM pass scores the generated output against the original spec deltas, producing a structured EvaluationResult with per-category scores.
pnpm add @auto-engineer/component-implementor-reactimport { COMMANDS } from '@auto-engineer/component-implementor-react';
// Register the ImplementComponent handler with your message bus
for (const handler of COMMANDS) {
bus.register(handler);
}import { handleImplementComponent, type ImplementComponentCommand } from '@auto-engineer/component-implementor-react';
const command: ImplementComponentCommand = {
type: 'ImplementComponent',
data: {
targetDir: './client',
job: {
id: 'job_1',
dependsOn: [],
target: 'ImplementComponent',
payload: {
componentId: 'milestone-badge',
structure: ['Renders a badge with an icon and label'],
rendering: ['Shows milestone name and completion percentage'],
interaction: ['Clicking the badge navigates to milestone details'],
styling: ['Uses rounded-full with indigo background'],
storybookPath: 'src/components/MilestoneBadge.stories.tsx',
files: { create: ['src/components/MilestoneBadge.tsx'] },
composes: [],
},
},
},
timestamp: new Date(),
requestId: 'req-1',
correlationId: 'corr-1',
};
const result = await handleImplementComponent(command);
if (result.type === 'ComponentImplemented') {
console.log(`Created ${result.data.componentPath}`);
console.log(`LLM calls: ${result.data.llmCalls}, fix iterations: ${result.data.fixIterations}`);
console.log(`Evaluation score: ${result.data.evaluation?.totalScore}`);
}import { ComponentPipelineAgent, type PipelineInput } from '@auto-engineer/component-implementor-react';
import { InMemorySessionService, Runner } from '@google/adk';
const input: PipelineInput = {
componentName: 'MilestoneBadge',
componentPath: '/abs/path/to/MilestoneBadge.tsx',
testPath: '/abs/path/to/MilestoneBadge.test.tsx',
storyPath: '/abs/path/to/MilestoneBadge.stories.tsx',
componentImportPath: '@/components/MilestoneBadge',
targetDir: '/abs/path/to/client',
specDeltas: { structure: ['...'], rendering: ['...'], interaction: [], styling: [] },
projectSection: '',
composes: [],
isModify: false,
};
const agent = new ComponentPipelineAgent(model, input);
const runner = new Runner({ appName: 'my-app', agent, sessionService: new InMemorySessionService() });
for await (const event of runner.runEphemeral({
userId: 'pipeline',
newMessage: { role: 'user', parts: [{ text: 'Implement component MilestoneBadge' }] },
})) {
// consume events
}
console.log(agent.metrics); // { llmCalls, fixIterations, evaluation, ... }Include a props array in the job payload to get a locked-down scaffold. The LLM only fills the body:
payload: {
// ...
props: [
{ name: 'label', type: 'string', required: true, description: 'Badge text', category: 'data' },
{ name: 'variant', type: "'default' | 'success'", required: false, default: "'default'", description: 'Color variant', category: 'visual' },
],
}When storyVariants is supplied, stories are scaffolded without an LLM call:
payload: {
// ...
storyVariants: [
{ name: 'Default', description: 'Default state', args: { label: 'Sprint 1' } },
{ name: 'Completed', description: 'Completed milestone', args: { label: 'Done', variant: 'success' } },
],
}Set the file path under files.modify instead of files.create. The pipeline reads the existing source and instructs the LLM to preserve existing behavior while applying the new spec deltas:
payload: {
files: { modify: ['src/components/MilestoneBadge.tsx'] },
// ...
}Set environment variables to control how many times each fix loop retries:
MAX_TYPE_FIX_ITERATIONS=3 # default: 3
MAX_TEST_FIX_ITERATIONS=3 # default: 3
MAX_LINT_FIX_ITERATIONS=2 # default: 2
MAX_STORY_FIX_ITERATIONS=2 # default: 2The package uses adk-llm-bridge and reads three environment variables:
CUSTOM_PROVIDER_DEFAULT_MODEL=claude-sonnet-4-20250514
CUSTOM_PROVIDER_BASE_URL=https://api.anthropic.com/v1
CUSTOM_PROVIDER_API_KEY=sk-...// Command handler for message-bus registration
export const COMMANDS: CommandHandler[];
export const implementComponentHandler: CommandHandler;
// Direct invocation
export function handleImplementComponent(
command: ImplementComponentCommand,
): Promise<ComponentImplementedEvent | ComponentImplementationFailedEvent>;
// Pipeline agent (Google ADK BaseAgent)
export class ComponentPipelineAgent extends BaseAgent {
constructor(model: BaseLlm, input: PipelineInput);
get metrics(): PipelineResult;
}
// Scaffold utilities
export function generateScaffold(input: ScaffoldInput): ScaffoldResult;
export function scaffoldStoryFile(input: ScaffoldStoryInput): string;
export function generatePlaceholderComponent(input: PlaceholderInput): string;
export function generatePlaceholderStory(componentName: string, importPath: string): string;interface PipelineInput {
componentName: string;
componentPath: string;
testPath: string;
storyPath: string;
componentImportPath: string;
targetDir: string;
specDeltas: SpecDeltas;
projectSection: string;
props?: Array<{ name: string; type: string; required: boolean; default?: string; description: string; category: string }>;
composes: Array<{ id: string; path: string }>;
storyVariants?: StoryVariant[];
existingComponent?: string;
isModify: boolean;
}
interface PipelineResult {
success: boolean;
llmCalls: number;
fixIterations: number;
typeFixIterations: number;
testFixIterations: number;
lintFixIterations: number;
storyFixIterations: number;
evaluation: EvaluationResult | null;
}
interface EvaluationResult {
totalScore: number;
categories: Record<string, { score: number; maxScore: number; findings: string[] }>;
}
interface SpecDeltas {
structure: string[];
rendering: string[];
interaction: string[];
styling: string[];
}The package is built on the Google ADK agent framework. The ComponentPipelineAgent orchestrates a sequence of sub-agents, each responsible for one stage.
flowchart TD
CMD[ImplementComponent Command] --> PIPELINE[ComponentPipelineAgent]
subgraph "Stage 1 — Generate (parallel)"
BODY[ComponentBodyAgent]
TEST[TestGenerationAgent]
end
subgraph "Stage 2 — Story"
STORY[StoryAgent / scaffoldStoryFile]
end
subgraph "Stage 3 — Verify & Fix"
TC[TypeFixLoopAgent]
TF[TestFixLoopAgent]
LF[LintFixLoopAgent]
SF[StoryFixLoopAgent]
end
subgraph "Stage 4 — Score"
EVAL[EvaluationAgent]
end
PIPELINE --> BODY
PIPELINE --> TEST
BODY --> STORY
TEST --> STORY
STORY --> TC
TC --> TF
TF --> LF
LF --> SF
SF --> EVAL
EVAL --> RESULT[ComponentImplemented Event]
Each fix loop uses synchronous shell tool runners to check the target project:
| Runner | Command | Purpose |
|---|---|---|
type-checker |
pnpm type-check |
Run tsc --noEmit on the target project |
test-runner |
npx vitest run --reporter=json |
Run tests and parse JSON results |
lint-runner |
npx @biomejs/biome check |
Run Biome lint/format checks |
storybook-runner |
npx storybook test |
Run Storybook interaction tests |
Before calling the LLM, the pipeline assembles a project context section from the target directory containing:
- Allowed packages (from
package.jsondependencies) - UI component barrel file exports (
src/components/ui/index.ts) - Project CSS/Tailwind tokens (
src/index.css) - Composed component signatures (extracted via TS AST, not full source)