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.
- package/README.md +145 -132
- package/dist/scripts/copy-templates.js +26 -2
- package/dist/scripts/copy-templates.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +93 -176
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -5
- package/templates/kanban-todo/README.md +161 -0
- package/templates/kanban-todo/auto.config.ts +241 -393
- package/templates/kanban-todo/narratives/homepage.narrative.ts +2 -1
- package/templates/kanban-todo/narratives/structure.narrative.ts +2 -1
- package/templates/kanban-todo/narratives/todo-list.narrative.ts +4 -3
- package/templates/kanban-todo/package.json +3 -2
- package/templates/questionnaires/README.md +161 -0
- package/templates/questionnaires/.gitignore +0 -4
- package/templates/questionnaires/auto.config.ts +0 -239
- package/templates/questionnaires/narratives/homepage.narrative.ts +0 -103
- package/templates/questionnaires/narratives/questionnaires.narrative.ts +0 -393
- package/templates/questionnaires/narratives/structure.narrative.ts +0 -16
- package/templates/questionnaires/package.json +0 -30
- package/templates/questionnaires/template.json +0 -7
- package/templates/questionnaires/tsconfig.json +0 -15
|
@@ -1,424 +1,272 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
lintCheckFailed?: LintCheckFailedEvent;
|
|
10
|
+
interface SchemaExportedData {
|
|
11
|
+
directory: string;
|
|
12
|
+
outputPath: string;
|
|
48
13
|
}
|
|
49
14
|
|
|
50
|
-
|
|
51
|
-
string
|
|
52
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
19
|
+
interface SliceImplementedData {
|
|
20
|
+
slicePath: string;
|
|
21
|
+
}
|
|
86
22
|
|
|
87
|
-
|
|
23
|
+
interface ClientGeneratedData {
|
|
24
|
+
components: Component[];
|
|
25
|
+
targetDir: string;
|
|
26
|
+
}
|
|
88
27
|
|
|
89
|
-
|
|
28
|
+
interface CheckEventData {
|
|
29
|
+
targetDirectory?: string;
|
|
30
|
+
errors?: string;
|
|
31
|
+
}
|
|
90
32
|
|
|
91
|
-
|
|
33
|
+
interface ValidationError {
|
|
34
|
+
component: string;
|
|
35
|
+
type: 'molecule' | 'organism';
|
|
36
|
+
field: string;
|
|
37
|
+
invalidReferences: string[];
|
|
38
|
+
message: string;
|
|
39
|
+
}
|
|
92
40
|
|
|
93
|
-
|
|
94
|
-
|
|
41
|
+
interface IAValidationFailedData {
|
|
42
|
+
errors: ValidationError[];
|
|
43
|
+
outputDir: string;
|
|
44
|
+
modelPath: string;
|
|
45
|
+
}
|
|
95
46
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
133
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
296
|
-
|
|
75
|
+
function shouldRetry(slicePath: string): boolean {
|
|
76
|
+
const attempts = sliceRetryState.get(slicePath) ?? 0;
|
|
77
|
+
return attempts < MAX_RETRIES;
|
|
78
|
+
}
|
|
297
79
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
|
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
|
|
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
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
errorMessages.push(failures.testsCheckFailed.data.errors);
|
|
94
|
+
function resolvePath(relativePath: string): string {
|
|
95
|
+
if (projectRoot === '') {
|
|
96
|
+
return relativePath;
|
|
370
97
|
}
|
|
371
|
-
if (
|
|
372
|
-
|
|
98
|
+
if (relativePath.startsWith('/')) {
|
|
99
|
+
return relativePath;
|
|
373
100
|
}
|
|
374
|
-
if (
|
|
375
|
-
|
|
101
|
+
if (relativePath.startsWith('./')) {
|
|
102
|
+
return `${projectRoot}/${relativePath.slice(2)}`;
|
|
376
103
|
}
|
|
377
|
-
return
|
|
104
|
+
return `${projectRoot}/${relativePath}`;
|
|
378
105
|
}
|
|
379
106
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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 {
|
|
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');
|