create-auto-app 0.13.3 → 0.15.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.
@@ -1,424 +1,272 @@
1
- import { autoConfig, on, dispatch } from '@auto-engineer/cli';
2
- import type { ExportSchemaEvents } from '@auto-engineer/narrative';
3
- import type {
4
- GenerateServerCommand,
5
- GenerateServerEvents,
6
- SliceGeneratedEvent,
7
- } from '@auto-engineer/server-generator-apollo-emmett';
8
- import type { ImplementSliceEvents, ImplementSliceCommand } from '@auto-engineer/server-implementer';
9
- import type {
10
- CheckTestsCommand,
11
- CheckTypesCommand,
12
- CheckLintCommand,
13
- TestsCheckFailedEvent,
14
- TypeCheckFailedEvent,
15
- LintCheckFailedEvent,
16
- } from '@auto-engineer/server-checks';
17
- import type { GenerateIACommand, GenerateIAEvents } from '@auto-engineer/information-architect';
18
- import type { ImplementClientCommand } from '@auto-engineer/frontend-implementer';
19
- import type { GenerateClientCommand, GenerateClientEvents } from '@auto-engineer/frontend-generator-react-graphql';
20
- import type { CheckClientEvents } from '@auto-engineer/frontend-checks';
21
- import type {
22
- ImplementComponentCommand,
23
- ComponentImplementedEvent,
24
- ComponentImplementationFailedEvent,
25
- } from '@auto-engineer/component-implementer';
26
- import type { StartServerCommand, StartClientCommand } from '@auto-engineer/dev-server';
27
- import * as path from 'path';
28
- import createDebug from 'debug';
29
-
30
- const debug = createDebug('auto:config:component');
1
+ import * as path from 'node:path';
2
+ import type { Event } from '@auto-engineer/message-bus';
3
+ import { define } from '@auto-engineer/pipeline';
31
4
 
32
- const sliceRetryState = new Map<string, number>();
33
- const MAX_RETRIES = 4;
34
-
35
- type ComponentType = 'molecule' | 'organism' | 'page';
36
- const componentPhaseOrder: ComponentType[] = ['molecule', 'organism', 'page'];
37
-
38
- let clientComponents: Array<{ type: string; filePath: string }> = [];
39
- let clientTargetDir = '';
40
- const processedComponents = new Set<string>();
41
- const dispatchedPhases = new Set<string>();
42
- const failedComponents = new Set<string>();
5
+ interface Component {
6
+ type: 'molecule' | 'organism' | 'page';
7
+ filePath: string;
8
+ }
43
9
 
44
- interface CheckFailures {
45
- testsCheckFailed?: TestsCheckFailedEvent;
46
- typeCheckFailed?: TypeCheckFailedEvent;
47
- lintCheckFailed?: LintCheckFailedEvent;
10
+ interface SchemaExportedData {
11
+ directory: string;
12
+ outputPath: string;
48
13
  }
49
14
 
50
- type EventsType = Record<
51
- string,
52
- Array<{
53
- type: string;
54
- data: { targetDirectory?: string; errors?: string };
55
- }>
56
- >;
57
-
58
- export default autoConfig({
59
- fileId: 'todoK4nB2',
60
-
61
- plugins: [
62
- '@auto-engineer/server-checks',
63
- '@auto-engineer/design-system-importer',
64
- '@auto-engineer/server-generator-apollo-emmett',
65
- '@auto-engineer/narrative',
66
- '@auto-engineer/frontend-checks',
67
- '@auto-engineer/frontend-implementer',
68
- '@auto-engineer/component-implementer',
69
- '@auto-engineer/information-architect',
70
- '@auto-engineer/frontend-generator-react-graphql',
71
- '@auto-engineer/server-implementer',
72
- '@auto-engineer/dev-server',
73
- ],
74
- aliases: {
75
- // Resolve command name conflicts between packages
76
- // 'test:types': checkTypesCommandHandler,
77
- },
78
- pipeline: () => {
79
- function getComponentsOfType(type: string) {
80
- return clientComponents.filter((c) => c.type === type);
81
- }
15
+ interface SliceGeneratedData {
16
+ slicePath: string;
17
+ }
82
18
 
83
- function areAllProcessed(type: string): boolean {
84
- const components = getComponentsOfType(type);
85
- if (components.length === 0) return false;
19
+ interface SliceImplementedData {
20
+ slicePath: string;
21
+ }
86
22
 
87
- const allDone = components.every((c) => processedComponents.has(c.filePath) || failedComponents.has(c.filePath));
23
+ interface ClientGeneratedData {
24
+ components: Component[];
25
+ targetDir: string;
26
+ }
88
27
 
89
- if (!allDone) return false;
28
+ interface CheckEventData {
29
+ targetDirectory?: string;
30
+ errors?: string;
31
+ }
90
32
 
91
- const anyFailed = components.some((c) => failedComponents.has(c.filePath));
33
+ interface ValidationError {
34
+ component: string;
35
+ type: 'molecule' | 'organism';
36
+ field: string;
37
+ invalidReferences: string[];
38
+ message: string;
39
+ }
92
40
 
93
- return !anyFailed;
94
- }
41
+ interface IAValidationFailedData {
42
+ errors: ValidationError[];
43
+ outputDir: string;
44
+ modelPath: string;
45
+ }
95
46
 
96
- function dispatchComponentsOfType(type: ComponentType) {
97
- const components = getComponentsOfType(type);
98
- const commands = components.map((component) => {
99
- const componentName = path.basename(component.filePath).replace('.tsx', '');
100
- return dispatch<ImplementComponentCommand>('ImplementComponent', {
101
- projectDir: clientTargetDir,
102
- iaSchemeDir: './.context',
103
- designSystemPath: './.context/design-system.md',
104
- componentType: type,
105
- filePath: component.filePath,
106
- componentName,
107
- aiOptions: { maxTokens: 3000 },
108
- });
109
- });
110
- return dispatch.parallel(commands);
111
- }
47
+ const MAX_RETRIES = 4;
48
+ const MAX_IA_RETRIES = 3;
49
+ const sliceRetryState = new Map<string, number>();
50
+ let iaRetryCount = 0;
51
+ let projectRoot = '';
112
52
 
113
- function tryAdvanceToNextPhase() {
114
- for (let i = 0; i < componentPhaseOrder.length - 1; i++) {
115
- const currentPhase = componentPhaseOrder[i];
116
- const nextPhase = componentPhaseOrder[i + 1];
117
- const allProcessed = areAllProcessed(currentPhase);
118
- const alreadyDispatched = dispatchedPhases.has(nextPhase);
119
- if (allProcessed && !alreadyDispatched) {
120
- dispatchedPhases.add(nextPhase);
121
- return dispatchComponentsOfType(nextPhase);
122
- }
123
- }
124
- return [];
125
- }
53
+ function hasAnyFailures(events: Event[]): boolean {
54
+ return events.some((e) => e.type.includes('Failed'));
55
+ }
126
56
 
127
- on<ExportSchemaEvents>('SchemaExported', () =>
128
- dispatch<GenerateServerCommand>('GenerateServer', {
129
- modelPath: './.context/schema.json',
130
- destination: '.',
131
- }),
132
- );
133
- on<SliceGeneratedEvent>('SliceGenerated', (e) =>
134
- dispatch<ImplementSliceCommand>('ImplementSlice', {
135
- slicePath: e.data.slicePath,
136
- context: {
137
- previousOutputs: 'errors',
138
- attemptNumber: 0,
139
- },
140
- aiOptions: { maxTokens: 2000 },
141
- }),
142
- );
143
-
144
- on<ImplementSliceEvents>('SliceImplemented', (e) =>
145
- dispatch<CheckTestsCommand>('CheckTests', {
146
- targetDirectory: e.data.slicePath,
147
- scope: 'slice',
148
- }),
149
- );
150
-
151
- on<ImplementSliceEvents>('SliceImplemented', (e) =>
152
- dispatch<CheckTypesCommand>('CheckTypes', {
153
- targetDirectory: e.data.slicePath,
154
- scope: 'slice',
155
- }),
156
- );
157
-
158
- on<ImplementSliceEvents>('SliceImplemented', (e) =>
159
- dispatch<CheckLintCommand>('CheckLint', {
160
- targetDirectory: e.data.slicePath,
161
- scope: 'slice',
162
- fix: true,
163
- }),
164
- );
165
-
166
- on.settled<CheckTestsCommand, CheckTypesCommand, CheckLintCommand>(
167
- ['CheckTests', 'CheckTypes', 'CheckLint'],
168
- dispatch<ImplementSliceCommand>(['ImplementSlice'], (events, send) => {
169
- const failures = findCheckFailures(events);
170
- const slicePath = getSlicePath(failures, events);
171
-
172
- if (!hasAnyFailures(failures)) {
173
- sliceRetryState.delete(slicePath);
174
- return { persist: false };
175
- }
176
-
177
- const currentAttempt = sliceRetryState.get(slicePath) ?? 0;
178
-
179
- if (currentAttempt >= MAX_RETRIES) {
180
- sliceRetryState.delete(slicePath);
181
- return { persist: false };
182
- }
183
-
184
- sliceRetryState.set(slicePath, currentAttempt + 1);
185
-
186
- send({
187
- type: 'ImplementSlice',
188
- data: {
189
- slicePath,
190
- context: {
191
- previousOutputs: collectErrorMessages(failures),
192
- attemptNumber: currentAttempt + 1,
193
- },
194
- aiOptions: { maxTokens: 2000 },
195
- },
196
- });
197
-
198
- return { persist: true };
199
- }),
200
- );
201
-
202
- on<GenerateServerEvents>('ServerGenerated', () => [
203
- dispatch<GenerateIACommand>('GenerateIA', {
204
- modelPath: './.context/schema.json',
205
- outputDir: './.context',
206
- }),
207
- dispatch<StartServerCommand>('StartServer', {
208
- serverDirectory: './server',
209
- }),
210
- ]);
211
-
212
- on<GenerateIAEvents>('IAGenerated', () =>
213
- dispatch<GenerateClientCommand>('GenerateClient', {
214
- targetDir: './client',
215
- iaSchemaPath: './.context/auto-ia-scheme.json',
216
- gqlSchemaPath: './.context/schema.graphql',
217
- figmaVariablesPath: './.context/figma-file.json',
218
- }),
219
- );
220
-
221
- on<GenerateClientEvents>('ClientGenerated', (e) => {
222
- if (e.type !== 'ClientGenerated') return;
223
-
224
- // this is purley for display purposes the pipeline
225
- if (e.data === null || e.data === undefined || !Array.isArray(e.data.components)) {
226
- return [
227
- dispatch<ImplementComponentCommand>('ImplementComponent', {
228
- projectDir: './client',
229
- iaSchemeDir: './.context',
230
- designSystemPath: './.context/design-system.md',
231
- componentType: 'molecule',
232
- filePath: 'client/src/components/molecules/Example.tsx',
233
- componentName: 'Example.tsx',
234
- aiOptions: { maxTokens: 3000 },
235
- }),
236
- dispatch<StartClientCommand>('StartClient', {
237
- clientDirectory: './client',
238
- }),
239
- ];
240
- }
241
-
242
- debug('ClientGenerated event received');
243
- debug('Total components: %d', e.data.components.length);
244
- debug(
245
- 'Component types: %o',
246
- e.data.components.map((c) => c.type),
247
- );
248
-
249
- clientComponents = e.data.components;
250
- clientTargetDir = e.data.targetDir;
251
- processedComponents.clear();
252
- dispatchedPhases.clear();
253
- failedComponents.clear();
254
-
255
- const molecules = clientComponents.filter((c) => c.type === 'molecule');
256
- debug('Found %d molecules', molecules.length);
257
- debug(
258
- 'Molecule paths: %o',
259
- molecules.map((m) => m.filePath),
260
- );
261
-
262
- dispatchedPhases.add('molecule');
263
-
264
- const componentCommands = molecules.map((component) => {
265
- const componentName = path.basename(component.filePath).replace('.tsx', '');
266
- return dispatch<ImplementComponentCommand>('ImplementComponent', {
267
- projectDir: clientTargetDir,
268
- iaSchemeDir: './.context',
269
- designSystemPath: './.context/design-system.md',
270
- componentType: 'molecule',
271
- filePath: component.filePath,
272
- componentName,
273
- aiOptions: { maxTokens: 3000 },
274
- });
275
- });
276
-
277
- const startClientCommand = dispatch<StartClientCommand>('StartClient', {
278
- clientDirectory: './client',
279
- });
280
-
281
- return dispatch.parallel([...componentCommands, startClientCommand]);
282
- });
57
+ function collectErrors(events: Event[]): string {
58
+ return events
59
+ .filter((e) => e.type.includes('Failed'))
60
+ .map((e) => (e.data as CheckEventData).errors ?? '')
61
+ .filter((s) => s.length > 0)
62
+ .join('\n');
63
+ }
283
64
 
284
- const handleComponentProcessed = (e: ComponentImplementedEvent | ComponentImplementationFailedEvent) => {
285
- if (e.data === null || e.data === undefined || e.data.filePath === null || e.data.filePath === undefined) {
286
- return [];
287
- }
65
+ function extractSlicePath(events: Record<string, Event[]>): string {
66
+ const firstEvent = events.CheckTests?.[0] ?? events.CheckTypes?.[0] ?? events.CheckLint?.[0];
67
+ const data = firstEvent?.data as CheckEventData | undefined;
68
+ return data?.targetDirectory ?? '';
69
+ }
288
70
 
289
- if (e.type === 'ComponentImplemented') {
290
- processedComponents.add(e.data.filePath);
291
- } else {
292
- failedComponents.add(e.data.filePath);
293
- }
71
+ function gatherAllCheckEvents(events: Record<string, Event[]>): Event[] {
72
+ return [...(events.CheckTests ?? []), ...(events.CheckTypes ?? []), ...(events.CheckLint ?? [])];
73
+ }
294
74
 
295
- return tryAdvanceToNextPhase();
296
- };
75
+ function shouldRetry(slicePath: string): boolean {
76
+ const attempts = sliceRetryState.get(slicePath) ?? 0;
77
+ return attempts < MAX_RETRIES;
78
+ }
297
79
 
298
- on<ComponentImplementedEvent>('ComponentImplemented', handleComponentProcessed);
299
- on<ComponentImplementationFailedEvent>('ComponentImplementationFailed', handleComponentProcessed);
300
-
301
- // on<ImplementClientEvents>('ClientImplemented', () =>
302
- // dispatch<CheckClientCommand>('CheckClient', {
303
- // clientDirectory: './client',
304
- // skipBrowserChecks: true,
305
- // }),
306
- // );
307
-
308
- on<CheckClientEvents>('ClientChecked', (e) => {
309
- if (e.type === 'ClientChecked') {
310
- const hasErrors = e.data.tsErrors > 0 || e.data.buildErrors > 0 || e.data.consoleErrors > 0;
311
-
312
- if (hasErrors) {
313
- const failures = [
314
- ...(e.data.tsErrorDetails || []),
315
- ...(e.data.buildErrorDetails || []),
316
- ...(e.data.consoleErrorDetails || []),
317
- ];
318
- return dispatch<ImplementClientCommand>('ImplementClient', {
319
- projectDir: './client',
320
- iaSchemeDir: './.context',
321
- designSystemPath: './.context/design-system.md',
322
- failures,
323
- });
324
- }
325
- }
326
- });
327
- },
328
- });
329
-
330
- function findCheckFailures(events: EventsType): CheckFailures {
331
- const checkTests = events.CheckTests as Array<
332
- TestsCheckFailedEvent | { type: string; data: { targetDirectory: string } }
333
- >;
334
- const checkTypes = events.CheckTypes as Array<
335
- TypeCheckFailedEvent | { type: string; data: { targetDirectory: string } }
336
- >;
337
- const checkLint = events.CheckLint as Array<
338
- LintCheckFailedEvent | { type: string; data: { targetDirectory: string } }
339
- >;
340
-
341
- return {
342
- testsCheckFailed: checkTests.find((e): e is TestsCheckFailedEvent => e.type === 'TestsCheckFailed'),
343
- typeCheckFailed: checkTypes.find((e): e is TypeCheckFailedEvent => e.type === 'TypeCheckFailed'),
344
- lintCheckFailed: checkLint.find((e): e is LintCheckFailedEvent => e.type === 'LintCheckFailed'),
345
- };
80
+ function incrementRetryCount(slicePath: string): number {
81
+ const attempts = sliceRetryState.get(slicePath) ?? 0;
82
+ sliceRetryState.set(slicePath, attempts + 1);
83
+ return attempts + 1;
346
84
  }
347
85
 
348
- function hasAnyFailures(failures: CheckFailures): boolean {
349
- return (
350
- failures.testsCheckFailed !== undefined ||
351
- failures.typeCheckFailed !== undefined ||
352
- failures.lintCheckFailed !== undefined
353
- );
86
+ function hasValidComponents(e: { data: ClientGeneratedData | null }): boolean {
87
+ return e.data !== null && Array.isArray(e.data.components) && e.data.components.length > 0;
354
88
  }
355
89
 
356
- function getSlicePath(failures: CheckFailures, events: EventsType): string {
357
- return (
358
- failures.testsCheckFailed?.data.targetDirectory ??
359
- failures.typeCheckFailed?.data.targetDirectory ??
360
- failures.lintCheckFailed?.data.targetDirectory ??
361
- (events.CheckTests[0]?.data.targetDirectory as string) ??
362
- ''
363
- );
90
+ function hasInvalidComponents(e: { data: ClientGeneratedData | null }): boolean {
91
+ return !hasValidComponents(e);
364
92
  }
365
93
 
366
- function collectErrorMessages(failures: CheckFailures): string {
367
- const errorMessages: string[] = [];
368
- if (failures.testsCheckFailed !== undefined) {
369
- errorMessages.push(failures.testsCheckFailed.data.errors);
94
+ function resolvePath(relativePath: string): string {
95
+ if (projectRoot === '') {
96
+ return relativePath;
370
97
  }
371
- if (failures.typeCheckFailed !== undefined) {
372
- errorMessages.push(failures.typeCheckFailed.data.errors);
98
+ if (relativePath.startsWith('/')) {
99
+ return relativePath;
373
100
  }
374
- if (failures.lintCheckFailed !== undefined) {
375
- errorMessages.push(failures.lintCheckFailed.data.errors);
101
+ if (relativePath.startsWith('./')) {
102
+ return `${projectRoot}/${relativePath.slice(2)}`;
376
103
  }
377
- return errorMessages.join('\n');
104
+ return `${projectRoot}/${relativePath}`;
378
105
  }
379
106
 
380
- /*
381
-
382
- rm -rf server client .context/schema.json .context/schema.graphql .context/auto-ia-scheme.json
383
- pnpm auto export:schema
384
- pnpm auto generate:ia --output-dir=./.context --flow-files=./narratives/questionnaires.narrative.ts
385
- pnpm auto generate:server --schema-path=./.context/schema.json --destination=.
386
- pnpm auto generate:client --starter-dir=../../packages/frontend-generator-react-graphql/shadcn-starter --target-dir=./client --ia-schema-path=./.context/auto-ia-scheme.json --gql-schema-path=./.context/schema.graphql --figma-variables-path=./.context/figma-file.json
387
- pnpm auto implement:client --project-dir=./questionnaires/client --ia-scheme-dir=./questionnaires/.context --design-system-path=./questionnaires/.context/design-system.md
388
-
389
-
390
- // make this emit one slice at a time
391
- pnpm auto generate:server --schema-path=./.context/schema.json --destination=.
392
-
393
- // TODO remove the AI part and make it mathematical
394
- pnpm auto generate:client --starter-dir=/Users/sam/WebstormProjects/top/auto-engineer/packages/frontend-generator-react-graphql/shadcn-starter --target-dir=./client --ia-schema-path=./.context/auto-ia-scheme.json --gql-schema-path=./.context/schema.graphql --figma-variables-path=./.context/figma-file.json
395
-
396
- // run this per slice in parallel
397
- pnpm auto implement:slice --slice-path=./questionnaires/server/src/domain/narratives/questionnaires/submits-the-questionnaire
398
- // add checks
399
- // add retry logic tore-implement failed slices with a retry count
400
-
401
- // slice these up
402
- pnpm auto implement:client --project-dir=./questionnaires/client --ia-scheme-dir=./questionnaires/.context --design-system-path=./questionnaires/.context/design-system.md
403
-
404
-
405
- // implement atoms in parallel - how do I know all atoms are done?
406
- // implement molecules in parallel - how do I know all molecules are done?
407
- // implement organisms in parallel - how do I know all organisms are done?
408
- // implement pages in parallel - how do I know all pages are done?
409
-
107
+ export const fileId = 'kanbanNew1';
108
+
109
+ export const plugins = [
110
+ '@auto-engineer/server-checks',
111
+ '@auto-engineer/design-system-importer',
112
+ '@auto-engineer/server-generator-apollo-emmett',
113
+ '@auto-engineer/narrative',
114
+ '@auto-engineer/frontend-checks',
115
+ '@auto-engineer/frontend-implementer',
116
+ '@auto-engineer/component-implementer',
117
+ '@auto-engineer/information-architect',
118
+ '@auto-engineer/frontend-generator-react-graphql',
119
+ '@auto-engineer/server-implementer',
120
+ '@auto-engineer/dev-server',
121
+ ];
122
+
123
+ export const pipeline = define('kanban-todo')
124
+ .on('SchemaExported')
125
+ .emit('GenerateServer', (e: { data: SchemaExportedData }) => {
126
+ projectRoot = e.data.directory;
127
+ return {
128
+ modelPath: e.data.outputPath,
129
+ destination: e.data.directory,
130
+ };
131
+ })
132
+
133
+ .on('SliceGenerated')
134
+ .emit('ImplementSlice', (e: { data: SliceGeneratedData }) => ({
135
+ slicePath: resolvePath(e.data.slicePath),
136
+ context: { previousOutputs: 'errors', attemptNumber: 0 },
137
+ aiOptions: { maxTokens: 2000 },
138
+ }))
139
+
140
+ .on('SliceImplemented')
141
+ .emit('CheckTests', (e: { data: SliceImplementedData }) => ({
142
+ targetDirectory: e.data.slicePath,
143
+ scope: 'slice',
144
+ }))
145
+ .emit('CheckTypes', (e: { data: SliceImplementedData }) => ({
146
+ targetDirectory: e.data.slicePath,
147
+ scope: 'slice',
148
+ }))
149
+ .emit('CheckLint', (e: { data: SliceImplementedData }) => ({
150
+ targetDirectory: e.data.slicePath,
151
+ scope: 'slice',
152
+ fix: true,
153
+ }))
154
+
155
+ .settled(['CheckTests', 'CheckTypes', 'CheckLint'])
156
+ .dispatch({ dispatches: ['ImplementSlice'] }, (events, send) => {
157
+ const allEvents = gatherAllCheckEvents(events);
158
+
159
+ if (!hasAnyFailures(allEvents)) {
160
+ const slicePath = extractSlicePath(events);
161
+ sliceRetryState.delete(slicePath);
162
+ return;
163
+ }
410
164
 
411
- // generate slice > implement slice > check slice > retry failure 3 times >
412
- // generate slice > implement slice > check slice > retry failure 3 times >
413
- // generate slice > implement slice > check slice > retry failure 3 times >
165
+ const slicePath = extractSlicePath(events);
414
166
 
415
- cd ~/WebstormProjects/top/auto-engineer/examples/questionnaires &&\
416
- pnpm -w build &&\
417
- rm -rf server client .context/schema.json .context/schema.graphql .context/auto-ia-scheme.json &&\
418
- DEBUG=* pnpm auto export:schema &&\
419
- DEBUG=* pnpm auto generate:server --schema-path=./.context/schema.json --destination=. &&\
420
- DEBUG=* pnpm auto generate:ia --output-dir=./.context --flow-files=./narratives/questionnaires.narrative.ts &&\
421
- DEBUG=* pnpm auto generate:client --starter-dir=../../packages/frontend-generator-react-graphql/shadcn-starter --target-dir=./client --ia-schema-path=./.context/auto-ia-scheme.json --gql-schema-path=./.context/schema.graphql --figma-variables-path=./.context/figma-file.json
167
+ if (!shouldRetry(slicePath)) {
168
+ sliceRetryState.delete(slicePath);
169
+ return;
170
+ }
422
171
 
172
+ const retryAttempt = incrementRetryCount(slicePath);
173
+ send('ImplementSlice', {
174
+ slicePath,
175
+ context: { previousOutputs: collectErrors(allEvents), attemptNumber: retryAttempt },
176
+ aiOptions: { maxTokens: 2000 },
177
+ });
178
+ return { persist: true };
179
+ })
180
+
181
+ .on('ServerGenerated')
182
+ .emit('GenerateIA', () => {
183
+ iaRetryCount = 0;
184
+ return {
185
+ modelPath: resolvePath('./.context/schema.json'),
186
+ outputDir: resolvePath('./.context'),
187
+ };
188
+ })
189
+ .emit('StartServer', () => ({
190
+ serverDirectory: resolvePath('./server'),
191
+ }))
192
+
193
+ .on('IAValidationFailed')
194
+ .emit('GenerateIA', (e: { data: IAValidationFailedData }) => {
195
+ iaRetryCount += 1;
196
+ if (iaRetryCount > MAX_IA_RETRIES) {
197
+ console.error('IA validation failed after max retries. Errors:', e.data.errors);
198
+ return null;
199
+ }
200
+ const errorSummary = e.data.errors.map((err) => err.message).join('\n');
201
+ console.log(`IA validation failed (attempt ${iaRetryCount}/${MAX_IA_RETRIES}). Retrying...`);
202
+ console.log('Errors:\n', errorSummary);
203
+ return {
204
+ modelPath: e.data.modelPath,
205
+ outputDir: e.data.outputDir,
206
+ previousErrors: errorSummary,
207
+ };
208
+ })
209
+
210
+ .on('IAGenerated')
211
+ .emit('GenerateClient', () => ({
212
+ targetDir: resolvePath('./client'),
213
+ iaSchemaPath: resolvePath('./.context/auto-ia-scheme.json'),
214
+ gqlSchemaPath: resolvePath('./.context/schema.graphql'),
215
+ figmaVariablesPath: resolvePath('./.context/figma-file.json'),
216
+ }))
217
+
218
+ .on('ClientGenerated')
219
+ .when(hasValidComponents)
220
+ .emit('StartClient', () => ({
221
+ clientDirectory: resolvePath('./client'),
222
+ }))
223
+
224
+ .on('ClientGenerated')
225
+ .when(hasInvalidComponents)
226
+ .emit('ImplementComponent', () => ({
227
+ projectDir: resolvePath('./client'),
228
+ iaSchemeDir: resolvePath('./.context'),
229
+ designSystemPath: resolvePath('./.context/design-system.md'),
230
+ componentType: 'molecule',
231
+ filePath: resolvePath('client/src/components/molecules/Example.tsx'),
232
+ componentName: 'Example.tsx',
233
+ aiOptions: { maxTokens: 3000 },
234
+ }))
235
+
236
+ .on('ClientGenerated')
237
+ .when(hasValidComponents)
238
+
239
+
240
+ // build dependency chain
241
+ // page >> org1 && org 2
242
+ // org2 >> mol1 && mol2
243
+ // process dependency tree
244
+
245
+ .forEach((e: { data: ClientGeneratedData }) => e.data.components)
246
+ .groupInto(['molecule', 'organism', 'page'], (c) => c.type) // group into phased chain
247
+ .process('ImplementComponent', (c: Component) => ({ // processPhase
248
+ projectDir: resolvePath('./client'),
249
+ iaSchemeDir: resolvePath('./.context'),
250
+ designSystemPath: resolvePath('./.context/design-system.md'),
251
+ componentType: c.type ?? 'molecule',
252
+ filePath: resolvePath(c.filePath ?? ''),
253
+ componentName: (c.filePath ?? '').split('/').pop()?.replace('.tsx', '') ?? '',
254
+ aiOptions: { maxTokens: 3000 },
255
+ }))
256
+ .onComplete({
257
+ success: { name: 'AllComponentsImplemented', displayName: 'All Components Implemented' },
258
+ failure: { name: 'ComponentsFailed', displayName: 'Components Failed' },
259
+ itemKey: (e) => (e.data as { filePath?: string }).filePath ?? '',
260
+ })
261
+
262
+ .build();
263
+
264
+ export function resetState(): void {
265
+ sliceRetryState.clear();
266
+ iaRetryCount = 0;
267
+ projectRoot = '';
268
+ }
423
269
 
424
- */
270
+ export function setProjectRoot(root: string): void {
271
+ projectRoot = root;
272
+ }
@@ -1,4 +1,5 @@
1
- import { experience, narrative, it, describe } from '@auto-engineer/narrative';
1
+ import { describe, experience, it, narrative } from '@auto-engineer/narrative';
2
+
2
3
  narrative('Todo Dashboard', 'H6i9Rs2Gz', () => {
3
4
  experience('Kanban Board View', 'K7j0St3Hz').client(() => {
4
5
  it('display three distinct columns: To Do, In Progress, and Done');