@auto-engineer/component-implementer 0.10.5

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.
@@ -0,0 +1,216 @@
1
+ import { type Command, defineCommandHandler, type Event } from '@auto-engineer/message-bus';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import createDebug from 'debug';
5
+ import { callAI, getProjectContext, loadScheme } from '../agent';
6
+
7
+ const debug = createDebug('frontend-implementer:implement-component');
8
+
9
+ export type ImplementComponentCommand = Command<
10
+ 'ImplementComponent',
11
+ {
12
+ projectDir: string;
13
+ iaSchemeDir: string;
14
+ designSystemPath: string;
15
+ componentType: 'atom' | 'molecule' | 'organism' | 'page';
16
+ componentName: string;
17
+ failures?: string[];
18
+ }
19
+ >;
20
+
21
+ export type ComponentImplementedEvent = Event<
22
+ 'ComponentImplemented',
23
+ {
24
+ filePath: string;
25
+ componentType: string;
26
+ componentName: string;
27
+ composition: string[];
28
+ specs: string[];
29
+ }
30
+ >;
31
+
32
+ export type ComponentImplementationFailedEvent = Event<
33
+ 'ComponentImplementationFailed',
34
+ {
35
+ error: string;
36
+ componentType: string;
37
+ componentName: string;
38
+ }
39
+ >;
40
+
41
+ export const commandHandler = defineCommandHandler<
42
+ ImplementComponentCommand,
43
+ (command: ImplementComponentCommand) => Promise<ComponentImplementedEvent | ComponentImplementationFailedEvent>
44
+ >({
45
+ name: 'ImplementComponent',
46
+ alias: 'implement:component',
47
+ description: 'AI implements a single component (atom, molecule, organism, or page)',
48
+ category: 'implement',
49
+ icon: 'layers',
50
+ fields: {
51
+ projectDir: { description: 'Project directory path', required: true },
52
+ iaSchemeDir: { description: 'IA schema directory path', required: true },
53
+ designSystemPath: { description: 'Design system file path', required: true },
54
+ componentType: {
55
+ description: 'Type of component: atom|molecule|organism|page',
56
+ required: true,
57
+ },
58
+ componentName: { description: 'Name of component to implement', required: true },
59
+ failures: {
60
+ description: 'Any failures from previous implementations',
61
+ required: false,
62
+ },
63
+ },
64
+ examples: [
65
+ '$ auto implement:component --project-dir=./client --ia-scheme-dir=./.context --design-system-path=./design-system.md --component-type=molecule --component-name=SurveyCard',
66
+ ],
67
+ events: ['ComponentImplemented', 'ComponentImplementationFailed'],
68
+ handle: async (
69
+ command: ImplementComponentCommand,
70
+ ): Promise<ComponentImplementedEvent | ComponentImplementationFailedEvent> => {
71
+ const result = await handleImplementComponentCommandInternal(command);
72
+ if (result.type === 'ComponentImplemented') {
73
+ debug(
74
+ 'Component implemented: %s/%s at %s',
75
+ result.data.componentType,
76
+ result.data.componentName,
77
+ result.data.filePath,
78
+ );
79
+ } else {
80
+ debug('Failed: %s', result.data.error);
81
+ }
82
+ return result;
83
+ },
84
+ });
85
+
86
+ async function handleImplementComponentCommandInternal(
87
+ command: ImplementComponentCommand,
88
+ ): Promise<ComponentImplementedEvent | ComponentImplementationFailedEvent> {
89
+ const { projectDir, iaSchemeDir, designSystemPath, componentType, componentName, failures = [] } = command.data;
90
+
91
+ try {
92
+ const userPreferencesFile = path.resolve(projectDir, 'design-system-principles.md');
93
+ const [userPreferences, designSystem] = await Promise.all([
94
+ fs.readFile(userPreferencesFile, 'utf-8'),
95
+ fs.readFile(designSystemPath, 'utf-8'),
96
+ ]);
97
+
98
+ const scheme = await loadScheme(iaSchemeDir);
99
+ if (!scheme) {
100
+ throw new Error('IA scheme not found');
101
+ }
102
+
103
+ const pluralKey = `${componentType}s`;
104
+ const collection = (scheme as Record<string, unknown>)[pluralKey];
105
+ if (!isValidCollection(collection)) {
106
+ throw new Error(`Invalid IA schema structure for ${pluralKey}`);
107
+ }
108
+
109
+ const items = (collection as { items: Record<string, unknown> }).items;
110
+ const componentDef = items[componentName] as Record<string, unknown> | undefined;
111
+ if (!componentDef) {
112
+ throw new Error(`Component ${componentType}:${componentName} not found in IA schema`);
113
+ }
114
+
115
+ const ctx = await getProjectContext(projectDir, iaSchemeDir, userPreferences, designSystem, failures);
116
+
117
+ const prompt = makeComponentPrompt(ctx, componentType, componentName, componentDef);
118
+
119
+ const code = await callAI(prompt);
120
+
121
+ const outPath = path.join(projectDir, 'src/components', `${componentType}s`, `${componentName}.tsx`);
122
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
123
+ await fs.writeFile(outPath, code, 'utf-8');
124
+
125
+ return {
126
+ type: 'ComponentImplemented',
127
+ data: {
128
+ filePath: outPath,
129
+ componentType,
130
+ componentName,
131
+ composition: extractComposition(componentDef),
132
+ specs: extractSpecs(componentDef),
133
+ },
134
+ timestamp: new Date(),
135
+ requestId: command.requestId,
136
+ correlationId: command.correlationId,
137
+ };
138
+ } catch (error: unknown) {
139
+ return {
140
+ type: 'ComponentImplementationFailed',
141
+ data: {
142
+ error: error instanceof Error ? error.message : String(error),
143
+ componentType,
144
+ componentName,
145
+ },
146
+ timestamp: new Date(),
147
+ requestId: command.requestId,
148
+ correlationId: command.correlationId,
149
+ };
150
+ }
151
+ }
152
+
153
+ function extractComposition(componentDef: Record<string, unknown>): string[] {
154
+ if ('composition' in componentDef && Boolean(componentDef.composition)) {
155
+ const comp = componentDef.composition as Record<string, unknown>;
156
+ if ('atoms' in comp && Array.isArray(comp.atoms)) {
157
+ return comp.atoms as string[];
158
+ }
159
+ if ('molecules' in comp && Array.isArray(comp.molecules)) {
160
+ return comp.molecules as string[];
161
+ }
162
+ }
163
+ return [];
164
+ }
165
+
166
+ function extractSpecs(componentDef: Record<string, unknown>): string[] {
167
+ if ('specs' in componentDef && Array.isArray(componentDef.specs)) {
168
+ return componentDef.specs as string[];
169
+ }
170
+ return [];
171
+ }
172
+
173
+ function makeComponentPrompt(
174
+ ctx: {
175
+ fileTreeSummary: string[];
176
+ atoms: unknown;
177
+ graphqlOperations: Record<string, string>;
178
+ theme: string;
179
+ },
180
+ componentType: string,
181
+ componentName: string,
182
+ componentDef: Record<string, unknown>,
183
+ ): string {
184
+ return `
185
+ You are Auto, a masterful frontend engineer.
186
+ Implement the following ${componentType}: **${componentName}** from the IA schema.
187
+
188
+ Component Definition:
189
+ ${JSON.stringify(componentDef, null, 2)}
190
+
191
+ Project Snapshot:
192
+ ${JSON.stringify(ctx.fileTreeSummary, null, 2)}
193
+
194
+ Available Atoms:
195
+ ${JSON.stringify(ctx.atoms, null, 2)}
196
+
197
+ GraphQL Operations:
198
+ ${Object.keys(ctx.graphqlOperations).join(', ')}
199
+
200
+ Theme:
201
+ ${ctx.theme}
202
+
203
+ Output: ONLY the full TypeScript React component code (no markdown, no explanations).
204
+ `;
205
+ }
206
+
207
+ function isValidCollection(collection: unknown): collection is { items: Record<string, unknown> } {
208
+ if (collection === null || collection === undefined) return false;
209
+ if (typeof collection !== 'object') return false;
210
+ if (!('items' in collection)) return false;
211
+
212
+ const items = (collection as { items: unknown }).items;
213
+ return typeof items === 'object' && items !== null;
214
+ }
215
+
216
+ export default commandHandler;
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Command exports
2
+ import { commandHandler as implementComponentHandler } from './commands/implement-component.js';
3
+
4
+ export const COMMANDS = [implementComponentHandler];
5
+
6
+ export type {
7
+ ImplementComponentCommand,
8
+ ComponentImplementedEvent,
9
+ ComponentImplementationFailedEvent,
10
+ } from './commands/implement-component.js';
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "outDir": "./dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist"],
9
+ "references": [{ "path": "../ai-gateway" }, { "path": "../message-bus" }]
10
+ }