@hstm-labs/forge-apps-generator 0.1.11
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 +35 -0
- package/dist/apps-generate-stage.d.ts +57 -0
- package/dist/apps-generate-stage.d.ts.map +1 -0
- package/dist/apps-generate-stage.js +232 -0
- package/dist/apps-generate-stage.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/ui-generate-stage.d.ts +57 -0
- package/dist/ui-generate-stage.d.ts.map +1 -0
- package/dist/ui-generate-stage.js +232 -0
- package/dist/ui-generate-stage.js.map +1 -0
- package/dist/validator.d.ts +41 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +220 -0
- package/dist/validator.js.map +1 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @hstm-labs/forge-apps-generator
|
|
2
|
+
|
|
3
|
+
Apps (UI) layer generation stage for Forge — produces pages, components, layouts, forms, and navigation for web/ and mobile-web/ from specification workflows, architecture, and services (API) output.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hstm-labs/forge-apps-generator
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Public API
|
|
12
|
+
|
|
13
|
+
### Types
|
|
14
|
+
|
|
15
|
+
- `UiArtifact` — complete UI layer output
|
|
16
|
+
- `UiPage`, `UiComponent`, `UiLayout`, `UiForm`, `UiNavigation`, `UiRoute`
|
|
17
|
+
|
|
18
|
+
### Classes
|
|
19
|
+
|
|
20
|
+
- `AppsGenerateStage` — pipeline stage implementing `PipelineStage` interface
|
|
21
|
+
- `UiOutputValidator` — validates LLM-produced UI output
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { AppsGenerateStage } from '@hstm-labs/forge-apps-generator';
|
|
27
|
+
|
|
28
|
+
const stage = new AppsGenerateStage();
|
|
29
|
+
const result = await stage.execute(input);
|
|
30
|
+
// result.data contains UiArtifact
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces pages, components, layouts, forms, and navigation from
|
|
5
|
+
* specification workflows, architecture, API endpoints, and technology
|
|
6
|
+
* profile. Follows the same stage implementation pattern established by
|
|
7
|
+
* {@link ArchitectStage} and {@link ServicesGenerateStage}.
|
|
8
|
+
*
|
|
9
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
10
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
11
|
+
* files into subdirectories, and returns the parsed {@link UiArtifact}
|
|
12
|
+
* in the stage output data.
|
|
13
|
+
*
|
|
14
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
15
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
16
|
+
*/
|
|
17
|
+
import type { StageName } from '@hstm-labs/forge-common';
|
|
18
|
+
import type { PipelineStage, PipelineStageInput, PipelineStageOutput, PipelineContext, AgentPromptContext } from '@hstm-labs/forge-core';
|
|
19
|
+
/**
|
|
20
|
+
* Pipeline stage that generates the UI layer.
|
|
21
|
+
*
|
|
22
|
+
* Produces a {@link UiArtifact} containing pages, components, layouts,
|
|
23
|
+
* forms, and navigation configuration. Each element is written as a
|
|
24
|
+
* separate file artifact grouped into subdirectories.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const stage = new AppsGenerateStage();
|
|
29
|
+
* const output = await stage.execute(input, context);
|
|
30
|
+
* const ui = output.data?.ui as UiArtifact;
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare class AppsGenerateStage implements PipelineStage {
|
|
34
|
+
readonly name: StageName;
|
|
35
|
+
readonly dependsOn: StageName[];
|
|
36
|
+
readonly requiresLLM = true;
|
|
37
|
+
/**
|
|
38
|
+
* Return rendered agent-mode prompt context using `apps-generate-agent.hbs`.
|
|
39
|
+
*
|
|
40
|
+
* @param input - Input from prior stages
|
|
41
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
42
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
43
|
+
*/
|
|
44
|
+
getAgentContext(input: PipelineStageInput, context: PipelineContext): AgentPromptContext;
|
|
45
|
+
/**
|
|
46
|
+
* Execute the UI generation stage.
|
|
47
|
+
*
|
|
48
|
+
* @param input - Input from prior stages (expects validate + architect + services-generate output)
|
|
49
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
50
|
+
* @returns Stage output with UI artifacts and parsed UiArtifact in data
|
|
51
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
52
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
53
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
54
|
+
*/
|
|
55
|
+
execute(input: PipelineStageInput, context: PipelineContext): Promise<PipelineStageOutput>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=apps-generate-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps-generate-stage.d.ts","sourceRoot":"","sources":["../src/apps-generate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAEf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAY/B;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAmB;IAC3C,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAsC;IACrE,QAAQ,CAAC,WAAW,QAAQ;IAE5B;;;;;;OAMG;IACH,eAAe,CACb,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,kBAAkB;IA+CrB;;;;;;;;;OASG;IACG,OAAO,CACX,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CAqNhC"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces pages, components, layouts, forms, and navigation from
|
|
5
|
+
* specification workflows, architecture, API endpoints, and technology
|
|
6
|
+
* profile. Follows the same stage implementation pattern established by
|
|
7
|
+
* {@link ArchitectStage} and {@link ServicesGenerateStage}.
|
|
8
|
+
*
|
|
9
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
10
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
11
|
+
* files into subdirectories, and returns the parsed {@link UiArtifact}
|
|
12
|
+
* in the stage output data.
|
|
13
|
+
*
|
|
14
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
15
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
16
|
+
*/
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
19
|
+
import { ForgeError, ErrorCodes, hashContent } from '@hstm-labs/forge-common';
|
|
20
|
+
import { ApiStageExecutor } from '@hstm-labs/forge-core';
|
|
21
|
+
import { resolveApiStyle } from '@hstm-labs/forge-architect';
|
|
22
|
+
import { loadProfile } from '@hstm-labs/forge-profiles';
|
|
23
|
+
import { loadTemplate, renderTemplate } from '@hstm-labs/forge-templates';
|
|
24
|
+
import { UiOutputValidator } from './validator.js';
|
|
25
|
+
/**
|
|
26
|
+
* Pipeline stage that generates the UI layer.
|
|
27
|
+
*
|
|
28
|
+
* Produces a {@link UiArtifact} containing pages, components, layouts,
|
|
29
|
+
* forms, and navigation configuration. Each element is written as a
|
|
30
|
+
* separate file artifact grouped into subdirectories.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const stage = new AppsGenerateStage();
|
|
35
|
+
* const output = await stage.execute(input, context);
|
|
36
|
+
* const ui = output.data?.ui as UiArtifact;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class AppsGenerateStage {
|
|
40
|
+
name = 'apps-generate';
|
|
41
|
+
dependsOn = ['architect', 'services-generate'];
|
|
42
|
+
requiresLLM = true;
|
|
43
|
+
/**
|
|
44
|
+
* Return rendered agent-mode prompt context using `apps-generate-agent.hbs`.
|
|
45
|
+
*
|
|
46
|
+
* @param input - Input from prior stages
|
|
47
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
48
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
49
|
+
*/
|
|
50
|
+
getAgentContext(input, context) {
|
|
51
|
+
const parsedSpec = input['validate']?.data?.['parsedSpec'];
|
|
52
|
+
const architecture = input['architect']?.data?.['architecture'];
|
|
53
|
+
const api = input['services-generate']?.data?.['api'];
|
|
54
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
55
|
+
const resolvedApiStyle = parsedSpec
|
|
56
|
+
? resolveApiStyle(parsedSpec, profile)
|
|
57
|
+
: 'graphql';
|
|
58
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'apps-generate', 'artifacts');
|
|
59
|
+
const agentTemplate = loadTemplate('apps-generate-agent', context.workspace.rootDir);
|
|
60
|
+
const rendered = renderTemplate(agentTemplate, {
|
|
61
|
+
spec: (parsedSpec ?? {}),
|
|
62
|
+
profile: profile,
|
|
63
|
+
architecture: (architecture ?? {}),
|
|
64
|
+
api: (api ?? {}),
|
|
65
|
+
stage: { resolvedApiStyle, outputDir },
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
prompt: rendered.content,
|
|
69
|
+
outputSchema: { format: 'json' },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Execute the UI generation stage.
|
|
74
|
+
*
|
|
75
|
+
* @param input - Input from prior stages (expects validate + architect + services-generate output)
|
|
76
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
77
|
+
* @returns Stage output with UI artifacts and parsed UiArtifact in data
|
|
78
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
79
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
80
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
81
|
+
*/
|
|
82
|
+
async execute(input, context) {
|
|
83
|
+
// 1. Get parsed spec from validate stage output
|
|
84
|
+
const validateOutput = input['validate'];
|
|
85
|
+
if (validateOutput === undefined) {
|
|
86
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Apps generate stage requires 'validate' stage output, but it was not found. " +
|
|
87
|
+
'Ensure the validate stage runs before the apps-generate stage.');
|
|
88
|
+
}
|
|
89
|
+
const parsedSpec = validateOutput.data?.['parsedSpec'];
|
|
90
|
+
if (parsedSpec === undefined) {
|
|
91
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Validate stage did not produce a parsed specification in its output data. ' +
|
|
92
|
+
'Ensure the validate stage includes parsedSpec in its data output.');
|
|
93
|
+
}
|
|
94
|
+
// 2. Get architecture from architect stage output
|
|
95
|
+
const architectOutput = input['architect'];
|
|
96
|
+
if (architectOutput === undefined) {
|
|
97
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Apps generate stage requires 'architect' stage output, but it was not found. " +
|
|
98
|
+
'Ensure the architect stage runs before the apps-generate stage.');
|
|
99
|
+
}
|
|
100
|
+
const architecture = architectOutput.data?.['architecture'];
|
|
101
|
+
if (architecture === undefined) {
|
|
102
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Architect stage did not produce an architecture in its output data. ' +
|
|
103
|
+
'Ensure the architect stage includes architecture in its data output.');
|
|
104
|
+
}
|
|
105
|
+
// 3. Get API artifact from services-generate stage output
|
|
106
|
+
const servicesGenerateOutput = input['services-generate'];
|
|
107
|
+
if (servicesGenerateOutput === undefined) {
|
|
108
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Apps generate stage requires 'services-generate' stage output, but it was not found. " +
|
|
109
|
+
'Ensure the services-generate stage runs before the apps-generate stage.');
|
|
110
|
+
}
|
|
111
|
+
const api = servicesGenerateOutput.data?.['api'];
|
|
112
|
+
if (api === undefined) {
|
|
113
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Services generate stage did not produce an API artifact in its output data. ' +
|
|
114
|
+
'Ensure the services-generate stage includes api in its data output.');
|
|
115
|
+
}
|
|
116
|
+
// 4. Load profile, resolve API style
|
|
117
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
118
|
+
const resolvedApiStyle = resolveApiStyle(parsedSpec, profile);
|
|
119
|
+
// 5. Compute output directory
|
|
120
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'apps-generate', 'artifacts');
|
|
121
|
+
// 6. Load and render templates
|
|
122
|
+
const systemTemplate = loadTemplate('apps-generate-system', context.workspace.rootDir);
|
|
123
|
+
const userTemplate = loadTemplate('apps-generate-user', context.workspace.rootDir);
|
|
124
|
+
const templateContext = {
|
|
125
|
+
spec: parsedSpec,
|
|
126
|
+
profile: profile,
|
|
127
|
+
architecture: architecture,
|
|
128
|
+
api: api,
|
|
129
|
+
stage: {
|
|
130
|
+
resolvedApiStyle,
|
|
131
|
+
outputDir,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const systemPrompt = renderTemplate(systemTemplate, templateContext);
|
|
135
|
+
const userPrompt = renderTemplate(userTemplate, templateContext);
|
|
136
|
+
// 7. Execute via API mode StageExecutor
|
|
137
|
+
if (context.adapter === undefined) {
|
|
138
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Apps generate stage requires an LLM adapter in API mode, but none was provided. ' +
|
|
139
|
+
'Configure an LLM provider in forge.config.json or use agent mode.');
|
|
140
|
+
}
|
|
141
|
+
const entityNames = architecture.dataModel.map((e) => e.name);
|
|
142
|
+
const validator = new UiOutputValidator(entityNames);
|
|
143
|
+
const executor = new ApiStageExecutor({
|
|
144
|
+
adapter: context.adapter,
|
|
145
|
+
validator,
|
|
146
|
+
retryPolicy: {
|
|
147
|
+
maxRetries: 3,
|
|
148
|
+
backoffMs: 1000,
|
|
149
|
+
includeErrorInRetry: true,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
const result = await executor.execute({
|
|
153
|
+
prompt: userPrompt.content,
|
|
154
|
+
systemPrompt: systemPrompt.content,
|
|
155
|
+
stageName: 'apps-generate',
|
|
156
|
+
outputDir,
|
|
157
|
+
runId: context.runId,
|
|
158
|
+
mode: 'api',
|
|
159
|
+
outputSchema: { format: 'json' },
|
|
160
|
+
});
|
|
161
|
+
// 8. Parse UiArtifact from LLM output
|
|
162
|
+
const rawContent = result.artifacts[0]?.content ?? '';
|
|
163
|
+
const uiArtifact = JSON.parse(rawContent);
|
|
164
|
+
// 9. Write individual files to artifacts directory in subdirectories
|
|
165
|
+
const artifacts = [];
|
|
166
|
+
// Pages
|
|
167
|
+
const pagesDir = join(outputDir, 'pages');
|
|
168
|
+
mkdirSync(pagesDir, { recursive: true });
|
|
169
|
+
for (const page of uiArtifact.pages) {
|
|
170
|
+
writeFileSync(join(pagesDir, page.fileName), page.content, 'utf-8');
|
|
171
|
+
artifacts.push({
|
|
172
|
+
filePath: join('pages', page.fileName),
|
|
173
|
+
content: page.content,
|
|
174
|
+
contentHash: hashContent(page.content),
|
|
175
|
+
sizeBytes: Buffer.byteLength(page.content, 'utf-8'),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// Components
|
|
179
|
+
const componentsDir = join(outputDir, 'components');
|
|
180
|
+
mkdirSync(componentsDir, { recursive: true });
|
|
181
|
+
for (const component of uiArtifact.components) {
|
|
182
|
+
writeFileSync(join(componentsDir, component.fileName), component.content, 'utf-8');
|
|
183
|
+
artifacts.push({
|
|
184
|
+
filePath: join('components', component.fileName),
|
|
185
|
+
content: component.content,
|
|
186
|
+
contentHash: hashContent(component.content),
|
|
187
|
+
sizeBytes: Buffer.byteLength(component.content, 'utf-8'),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Layouts
|
|
191
|
+
const layoutsDir = join(outputDir, 'layouts');
|
|
192
|
+
mkdirSync(layoutsDir, { recursive: true });
|
|
193
|
+
for (const layout of uiArtifact.layouts) {
|
|
194
|
+
writeFileSync(join(layoutsDir, layout.fileName), layout.content, 'utf-8');
|
|
195
|
+
artifacts.push({
|
|
196
|
+
filePath: join('layouts', layout.fileName),
|
|
197
|
+
content: layout.content,
|
|
198
|
+
contentHash: hashContent(layout.content),
|
|
199
|
+
sizeBytes: Buffer.byteLength(layout.content, 'utf-8'),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Forms
|
|
203
|
+
const formsDir = join(outputDir, 'forms');
|
|
204
|
+
mkdirSync(formsDir, { recursive: true });
|
|
205
|
+
for (const form of uiArtifact.forms) {
|
|
206
|
+
writeFileSync(join(formsDir, form.fileName), form.content, 'utf-8');
|
|
207
|
+
artifacts.push({
|
|
208
|
+
filePath: join('forms', form.fileName),
|
|
209
|
+
content: form.content,
|
|
210
|
+
contentHash: hashContent(form.content),
|
|
211
|
+
sizeBytes: Buffer.byteLength(form.content, 'utf-8'),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Navigation
|
|
215
|
+
mkdirSync(outputDir, { recursive: true });
|
|
216
|
+
writeFileSync(join(outputDir, uiArtifact.navigation.fileName), uiArtifact.navigation.content, 'utf-8');
|
|
217
|
+
artifacts.push({
|
|
218
|
+
filePath: uiArtifact.navigation.fileName,
|
|
219
|
+
content: uiArtifact.navigation.content,
|
|
220
|
+
contentHash: hashContent(uiArtifact.navigation.content),
|
|
221
|
+
sizeBytes: Buffer.byteLength(uiArtifact.navigation.content, 'utf-8'),
|
|
222
|
+
});
|
|
223
|
+
// 10. Return PipelineStageOutput with all artifacts and data
|
|
224
|
+
return {
|
|
225
|
+
artifacts,
|
|
226
|
+
data: {
|
|
227
|
+
ui: uiArtifact,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=apps-generate-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps-generate-stage.js","sourceRoot":"","sources":["../src/apps-generate-stage.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,OAAO,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI artifact type definitions for the Forge UI generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the output schema produced by the UI generation stage,
|
|
5
|
+
* including pages, components, layouts, forms, and navigation.
|
|
6
|
+
*/
|
|
7
|
+
/** Complete UI layer output produced by the apps-generate stage. */
|
|
8
|
+
export interface UiArtifact {
|
|
9
|
+
/** Frontend framework used. */
|
|
10
|
+
framework: string;
|
|
11
|
+
/** Generated pages (route-level components). */
|
|
12
|
+
pages: UiPage[];
|
|
13
|
+
/** Shared/reusable components. */
|
|
14
|
+
components: UiComponent[];
|
|
15
|
+
/** Layout components (shells, navigation). */
|
|
16
|
+
layouts: UiLayout[];
|
|
17
|
+
/** Form definitions for entity CRUD. */
|
|
18
|
+
forms: UiForm[];
|
|
19
|
+
/** Navigation/routing configuration. */
|
|
20
|
+
navigation: UiNavigation;
|
|
21
|
+
}
|
|
22
|
+
/** A route-level page component. */
|
|
23
|
+
export interface UiPage {
|
|
24
|
+
/** Page name (e.g., "PetListPage"). */
|
|
25
|
+
name: string;
|
|
26
|
+
/** Route path (e.g., "/pets"). */
|
|
27
|
+
route: string;
|
|
28
|
+
/** File name (e.g., "PetListPage.tsx"). */
|
|
29
|
+
fileName: string;
|
|
30
|
+
/** File content (source code). */
|
|
31
|
+
content: string;
|
|
32
|
+
/** Entity this page relates to (if any). */
|
|
33
|
+
entity?: string | undefined;
|
|
34
|
+
/** Description. */
|
|
35
|
+
description: string;
|
|
36
|
+
}
|
|
37
|
+
/** A reusable UI component. */
|
|
38
|
+
export interface UiComponent {
|
|
39
|
+
/** Component name (e.g., "PetCard"). */
|
|
40
|
+
name: string;
|
|
41
|
+
/** File name. */
|
|
42
|
+
fileName: string;
|
|
43
|
+
/** File content. */
|
|
44
|
+
content: string;
|
|
45
|
+
/** Reusability scope: page-specific or shared. */
|
|
46
|
+
scope: 'shared' | 'page';
|
|
47
|
+
/** Description. */
|
|
48
|
+
description: string;
|
|
49
|
+
}
|
|
50
|
+
/** A layout/shell component. */
|
|
51
|
+
export interface UiLayout {
|
|
52
|
+
/** Layout name (e.g., "AppShell", "DashboardLayout"). */
|
|
53
|
+
name: string;
|
|
54
|
+
/** File name. */
|
|
55
|
+
fileName: string;
|
|
56
|
+
/** File content. */
|
|
57
|
+
content: string;
|
|
58
|
+
/** Description. */
|
|
59
|
+
description: string;
|
|
60
|
+
}
|
|
61
|
+
/** A form component for entity CRUD operations. */
|
|
62
|
+
export interface UiForm {
|
|
63
|
+
/** Form name (e.g., "CreatePetForm"). */
|
|
64
|
+
name: string;
|
|
65
|
+
/** Entity this form operates on. */
|
|
66
|
+
entity: string;
|
|
67
|
+
/** Operation: create, edit, or search. */
|
|
68
|
+
operation: 'create' | 'edit' | 'search';
|
|
69
|
+
/** File name. */
|
|
70
|
+
fileName: string;
|
|
71
|
+
/** File content. */
|
|
72
|
+
content: string;
|
|
73
|
+
}
|
|
74
|
+
/** Navigation/routing configuration. */
|
|
75
|
+
export interface UiNavigation {
|
|
76
|
+
/** Route definitions. */
|
|
77
|
+
routes: UiRoute[];
|
|
78
|
+
/** Navigation file name. */
|
|
79
|
+
fileName: string;
|
|
80
|
+
/** File content (router config). */
|
|
81
|
+
content: string;
|
|
82
|
+
}
|
|
83
|
+
/** A single route definition. */
|
|
84
|
+
export interface UiRoute {
|
|
85
|
+
/** Route path. */
|
|
86
|
+
path: string;
|
|
87
|
+
/** Page component name. */
|
|
88
|
+
page: string;
|
|
89
|
+
/** Display label in navigation. */
|
|
90
|
+
label: string;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kCAAkC;IAClC,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,wCAAwC;IACxC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wCAAwC;IACxC,UAAU,EAAE,YAAY,CAAC;CAC1B;AAMD,oCAAoC;AACpC,MAAM,WAAW,MAAM;IACrB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,+BAA+B;AAC/B,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,gCAAgC;AAChC,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACrB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IACxC,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iCAAiC;AACjC,MAAM,WAAW,OAAO;IACtB,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces pages, components, layouts, forms, and navigation from
|
|
5
|
+
* specification workflows, architecture, API endpoints, and technology
|
|
6
|
+
* profile. Follows the same stage implementation pattern established by
|
|
7
|
+
* {@link ArchitectStage} in Task 5.1 and {@link ApiGenerateStage} in Task 5.2.
|
|
8
|
+
*
|
|
9
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
10
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
11
|
+
* files into subdirectories, and returns the parsed {@link UiArtifact}
|
|
12
|
+
* in the stage output data.
|
|
13
|
+
*
|
|
14
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
15
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
16
|
+
*/
|
|
17
|
+
import type { StageName } from '@hstm-labs/forge-common';
|
|
18
|
+
import type { PipelineStage, PipelineStageInput, PipelineStageOutput, PipelineContext, AgentPromptContext } from '@hstm-labs/forge-core';
|
|
19
|
+
/**
|
|
20
|
+
* Pipeline stage that generates the UI layer.
|
|
21
|
+
*
|
|
22
|
+
* Produces a {@link UiArtifact} containing pages, components, layouts,
|
|
23
|
+
* forms, and navigation configuration. Each element is written as a
|
|
24
|
+
* separate file artifact grouped into subdirectories.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const stage = new UiGenerateStage();
|
|
29
|
+
* const output = await stage.execute(input, context);
|
|
30
|
+
* const ui = output.data?.ui as UiArtifact;
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare class UiGenerateStage implements PipelineStage {
|
|
34
|
+
readonly name: StageName;
|
|
35
|
+
readonly dependsOn: StageName[];
|
|
36
|
+
readonly requiresLLM = true;
|
|
37
|
+
/**
|
|
38
|
+
* Return rendered agent-mode prompt context using `ui-generate-agent.hbs`.
|
|
39
|
+
*
|
|
40
|
+
* @param input - Input from prior stages
|
|
41
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
42
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
43
|
+
*/
|
|
44
|
+
getAgentContext(input: PipelineStageInput, context: PipelineContext): AgentPromptContext;
|
|
45
|
+
/**
|
|
46
|
+
* Execute the UI generation stage.
|
|
47
|
+
*
|
|
48
|
+
* @param input - Input from prior stages (expects validate + architect + api-generate output)
|
|
49
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
50
|
+
* @returns Stage output with UI artifacts and parsed UiArtifact in data
|
|
51
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
52
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
53
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
54
|
+
*/
|
|
55
|
+
execute(input: PipelineStageInput, context: PipelineContext): Promise<PipelineStageOutput>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=ui-generate-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-generate-stage.d.ts","sourceRoot":"","sources":["../src/ui-generate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAEf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAY/B;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,YAAW,aAAa;IACnD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAiB;IACzC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAiC;IAChE,QAAQ,CAAC,WAAW,QAAQ;IAE5B;;;;;;OAMG;IACH,eAAe,CACb,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,kBAAkB;IA+CrB;;;;;;;;;OASG;IACG,OAAO,CACX,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CAqNhC"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces pages, components, layouts, forms, and navigation from
|
|
5
|
+
* specification workflows, architecture, API endpoints, and technology
|
|
6
|
+
* profile. Follows the same stage implementation pattern established by
|
|
7
|
+
* {@link ArchitectStage} in Task 5.1 and {@link ApiGenerateStage} in Task 5.2.
|
|
8
|
+
*
|
|
9
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
10
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
11
|
+
* files into subdirectories, and returns the parsed {@link UiArtifact}
|
|
12
|
+
* in the stage output data.
|
|
13
|
+
*
|
|
14
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
15
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
16
|
+
*/
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
19
|
+
import { ForgeError, ErrorCodes, hashContent } from '@hstm-labs/forge-common';
|
|
20
|
+
import { ApiStageExecutor } from '@hstm-labs/forge-core';
|
|
21
|
+
import { resolveApiStyle } from '@hstm-labs/forge-architect';
|
|
22
|
+
import { loadProfile } from '@hstm-labs/forge-profiles';
|
|
23
|
+
import { loadTemplate, renderTemplate } from '@hstm-labs/forge-templates';
|
|
24
|
+
import { UiOutputValidator } from './validator.js';
|
|
25
|
+
/**
|
|
26
|
+
* Pipeline stage that generates the UI layer.
|
|
27
|
+
*
|
|
28
|
+
* Produces a {@link UiArtifact} containing pages, components, layouts,
|
|
29
|
+
* forms, and navigation configuration. Each element is written as a
|
|
30
|
+
* separate file artifact grouped into subdirectories.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const stage = new UiGenerateStage();
|
|
35
|
+
* const output = await stage.execute(input, context);
|
|
36
|
+
* const ui = output.data?.ui as UiArtifact;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class UiGenerateStage {
|
|
40
|
+
name = 'ui-generate';
|
|
41
|
+
dependsOn = ['architect', 'api-generate'];
|
|
42
|
+
requiresLLM = true;
|
|
43
|
+
/**
|
|
44
|
+
* Return rendered agent-mode prompt context using `ui-generate-agent.hbs`.
|
|
45
|
+
*
|
|
46
|
+
* @param input - Input from prior stages
|
|
47
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
48
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
49
|
+
*/
|
|
50
|
+
getAgentContext(input, context) {
|
|
51
|
+
const parsedSpec = input['validate']?.data?.['parsedSpec'];
|
|
52
|
+
const architecture = input['architect']?.data?.['architecture'];
|
|
53
|
+
const api = input['api-generate']?.data?.['api'];
|
|
54
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
55
|
+
const resolvedApiStyle = parsedSpec
|
|
56
|
+
? resolveApiStyle(parsedSpec, profile)
|
|
57
|
+
: 'graphql';
|
|
58
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'ui-generate', 'artifacts');
|
|
59
|
+
const agentTemplate = loadTemplate('ui-generate-agent', context.workspace.rootDir);
|
|
60
|
+
const rendered = renderTemplate(agentTemplate, {
|
|
61
|
+
spec: (parsedSpec ?? {}),
|
|
62
|
+
profile: profile,
|
|
63
|
+
architecture: (architecture ?? {}),
|
|
64
|
+
api: (api ?? {}),
|
|
65
|
+
stage: { resolvedApiStyle, outputDir },
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
prompt: rendered.content,
|
|
69
|
+
outputSchema: { format: 'json' },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Execute the UI generation stage.
|
|
74
|
+
*
|
|
75
|
+
* @param input - Input from prior stages (expects validate + architect + api-generate output)
|
|
76
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
77
|
+
* @returns Stage output with UI artifacts and parsed UiArtifact in data
|
|
78
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
79
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
80
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
81
|
+
*/
|
|
82
|
+
async execute(input, context) {
|
|
83
|
+
// 1. Get parsed spec from validate stage output
|
|
84
|
+
const validateOutput = input['validate'];
|
|
85
|
+
if (validateOutput === undefined) {
|
|
86
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "UI generate stage requires 'validate' stage output, but it was not found. " +
|
|
87
|
+
'Ensure the validate stage runs before the ui-generate stage.');
|
|
88
|
+
}
|
|
89
|
+
const parsedSpec = validateOutput.data?.['parsedSpec'];
|
|
90
|
+
if (parsedSpec === undefined) {
|
|
91
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Validate stage did not produce a parsed specification in its output data. ' +
|
|
92
|
+
'Ensure the validate stage includes parsedSpec in its data output.');
|
|
93
|
+
}
|
|
94
|
+
// 2. Get architecture from architect stage output
|
|
95
|
+
const architectOutput = input['architect'];
|
|
96
|
+
if (architectOutput === undefined) {
|
|
97
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "UI generate stage requires 'architect' stage output, but it was not found. " +
|
|
98
|
+
'Ensure the architect stage runs before the ui-generate stage.');
|
|
99
|
+
}
|
|
100
|
+
const architecture = architectOutput.data?.['architecture'];
|
|
101
|
+
if (architecture === undefined) {
|
|
102
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Architect stage did not produce an architecture in its output data. ' +
|
|
103
|
+
'Ensure the architect stage includes architecture in its data output.');
|
|
104
|
+
}
|
|
105
|
+
// 3. Get API artifact from api-generate stage output
|
|
106
|
+
const apiGenerateOutput = input['api-generate'];
|
|
107
|
+
if (apiGenerateOutput === undefined) {
|
|
108
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "UI generate stage requires 'api-generate' stage output, but it was not found. " +
|
|
109
|
+
'Ensure the api-generate stage runs before the ui-generate stage.');
|
|
110
|
+
}
|
|
111
|
+
const api = apiGenerateOutput.data?.['api'];
|
|
112
|
+
if (api === undefined) {
|
|
113
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'API generate stage did not produce an API artifact in its output data. ' +
|
|
114
|
+
'Ensure the api-generate stage includes api in its data output.');
|
|
115
|
+
}
|
|
116
|
+
// 4. Load profile, resolve API style
|
|
117
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
118
|
+
const resolvedApiStyle = resolveApiStyle(parsedSpec, profile);
|
|
119
|
+
// 5. Compute output directory
|
|
120
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'ui-generate', 'artifacts');
|
|
121
|
+
// 6. Load and render templates
|
|
122
|
+
const systemTemplate = loadTemplate('ui-generate-system', context.workspace.rootDir);
|
|
123
|
+
const userTemplate = loadTemplate('ui-generate-user', context.workspace.rootDir);
|
|
124
|
+
const templateContext = {
|
|
125
|
+
spec: parsedSpec,
|
|
126
|
+
profile: profile,
|
|
127
|
+
architecture: architecture,
|
|
128
|
+
api: api,
|
|
129
|
+
stage: {
|
|
130
|
+
resolvedApiStyle,
|
|
131
|
+
outputDir,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const systemPrompt = renderTemplate(systemTemplate, templateContext);
|
|
135
|
+
const userPrompt = renderTemplate(userTemplate, templateContext);
|
|
136
|
+
// 7. Execute via API mode StageExecutor
|
|
137
|
+
if (context.adapter === undefined) {
|
|
138
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'UI generate stage requires an LLM adapter in API mode, but none was provided. ' +
|
|
139
|
+
'Configure an LLM provider in forge.config.json or use agent mode.');
|
|
140
|
+
}
|
|
141
|
+
const entityNames = architecture.dataModel.map((e) => e.name);
|
|
142
|
+
const validator = new UiOutputValidator(entityNames);
|
|
143
|
+
const executor = new ApiStageExecutor({
|
|
144
|
+
adapter: context.adapter,
|
|
145
|
+
validator,
|
|
146
|
+
retryPolicy: {
|
|
147
|
+
maxRetries: 3,
|
|
148
|
+
backoffMs: 1000,
|
|
149
|
+
includeErrorInRetry: true,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
const result = await executor.execute({
|
|
153
|
+
prompt: userPrompt.content,
|
|
154
|
+
systemPrompt: systemPrompt.content,
|
|
155
|
+
stageName: 'ui-generate',
|
|
156
|
+
outputDir,
|
|
157
|
+
runId: context.runId,
|
|
158
|
+
mode: 'api',
|
|
159
|
+
outputSchema: { format: 'json' },
|
|
160
|
+
});
|
|
161
|
+
// 8. Parse UiArtifact from LLM output
|
|
162
|
+
const rawContent = result.artifacts[0]?.content ?? '';
|
|
163
|
+
const uiArtifact = JSON.parse(rawContent);
|
|
164
|
+
// 9. Write individual files to artifacts directory in subdirectories
|
|
165
|
+
const artifacts = [];
|
|
166
|
+
// Pages
|
|
167
|
+
const pagesDir = join(outputDir, 'pages');
|
|
168
|
+
mkdirSync(pagesDir, { recursive: true });
|
|
169
|
+
for (const page of uiArtifact.pages) {
|
|
170
|
+
writeFileSync(join(pagesDir, page.fileName), page.content, 'utf-8');
|
|
171
|
+
artifacts.push({
|
|
172
|
+
filePath: join('pages', page.fileName),
|
|
173
|
+
content: page.content,
|
|
174
|
+
contentHash: hashContent(page.content),
|
|
175
|
+
sizeBytes: Buffer.byteLength(page.content, 'utf-8'),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// Components
|
|
179
|
+
const componentsDir = join(outputDir, 'components');
|
|
180
|
+
mkdirSync(componentsDir, { recursive: true });
|
|
181
|
+
for (const component of uiArtifact.components) {
|
|
182
|
+
writeFileSync(join(componentsDir, component.fileName), component.content, 'utf-8');
|
|
183
|
+
artifacts.push({
|
|
184
|
+
filePath: join('components', component.fileName),
|
|
185
|
+
content: component.content,
|
|
186
|
+
contentHash: hashContent(component.content),
|
|
187
|
+
sizeBytes: Buffer.byteLength(component.content, 'utf-8'),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Layouts
|
|
191
|
+
const layoutsDir = join(outputDir, 'layouts');
|
|
192
|
+
mkdirSync(layoutsDir, { recursive: true });
|
|
193
|
+
for (const layout of uiArtifact.layouts) {
|
|
194
|
+
writeFileSync(join(layoutsDir, layout.fileName), layout.content, 'utf-8');
|
|
195
|
+
artifacts.push({
|
|
196
|
+
filePath: join('layouts', layout.fileName),
|
|
197
|
+
content: layout.content,
|
|
198
|
+
contentHash: hashContent(layout.content),
|
|
199
|
+
sizeBytes: Buffer.byteLength(layout.content, 'utf-8'),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Forms
|
|
203
|
+
const formsDir = join(outputDir, 'forms');
|
|
204
|
+
mkdirSync(formsDir, { recursive: true });
|
|
205
|
+
for (const form of uiArtifact.forms) {
|
|
206
|
+
writeFileSync(join(formsDir, form.fileName), form.content, 'utf-8');
|
|
207
|
+
artifacts.push({
|
|
208
|
+
filePath: join('forms', form.fileName),
|
|
209
|
+
content: form.content,
|
|
210
|
+
contentHash: hashContent(form.content),
|
|
211
|
+
sizeBytes: Buffer.byteLength(form.content, 'utf-8'),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Navigation
|
|
215
|
+
mkdirSync(outputDir, { recursive: true });
|
|
216
|
+
writeFileSync(join(outputDir, uiArtifact.navigation.fileName), uiArtifact.navigation.content, 'utf-8');
|
|
217
|
+
artifacts.push({
|
|
218
|
+
filePath: uiArtifact.navigation.fileName,
|
|
219
|
+
content: uiArtifact.navigation.content,
|
|
220
|
+
contentHash: hashContent(uiArtifact.navigation.content),
|
|
221
|
+
sizeBytes: Buffer.byteLength(uiArtifact.navigation.content, 'utf-8'),
|
|
222
|
+
});
|
|
223
|
+
// 10. Return PipelineStageOutput with all artifacts and data
|
|
224
|
+
return {
|
|
225
|
+
artifacts,
|
|
226
|
+
data: {
|
|
227
|
+
ui: uiArtifact,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=ui-generate-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-generate-stage.js","sourceRoot":"","sources":["../src/ui-generate-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI output validator for the Forge UI generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Validates LLM-generated UI output against the
|
|
5
|
+
* {@link UiArtifact} schema with structural and semantic checks.
|
|
6
|
+
*/
|
|
7
|
+
import type { OutputValidator, OutputSchema, ValidationResult } from '@hstm-labs/forge-core';
|
|
8
|
+
/**
|
|
9
|
+
* Validator for UI generation stage output.
|
|
10
|
+
*
|
|
11
|
+
* Performs the following checks:
|
|
12
|
+
* 1. JSON parse validation
|
|
13
|
+
* 2. Required top-level keys
|
|
14
|
+
* 3. Non-empty pages with required fields
|
|
15
|
+
* 4. Entity coverage (every entity has at least one page)
|
|
16
|
+
* 5. Form coverage (every entity has create and edit forms)
|
|
17
|
+
* 6. Navigation routes non-empty and cover all pages
|
|
18
|
+
* 7. Non-empty layouts (at least one)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const validator = new UiOutputValidator(['User', 'Post']);
|
|
23
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class UiOutputValidator implements OutputValidator {
|
|
27
|
+
private readonly entityNames;
|
|
28
|
+
/**
|
|
29
|
+
* @param entityNames - Entity names from architecture data model for coverage checks
|
|
30
|
+
*/
|
|
31
|
+
constructor(entityNames: string[]);
|
|
32
|
+
/**
|
|
33
|
+
* Validate UI output against the UiArtifact schema.
|
|
34
|
+
*
|
|
35
|
+
* @param output - Raw LLM output text
|
|
36
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
37
|
+
* @returns Validation result with descriptive errors
|
|
38
|
+
*/
|
|
39
|
+
validate(output: string, _schema: OutputSchema): ValidationResult;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEjB,MAAM,uBAAuB,CAAC;AAmB/B;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IAEvC;;OAEG;gBACS,WAAW,EAAE,MAAM,EAAE;IAIjC;;;;;;OAMG;IAEH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,gBAAgB;CAkLlE"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI output validator for the Forge UI generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Validates LLM-generated UI output against the
|
|
5
|
+
* {@link UiArtifact} schema with structural and semantic checks.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Constants
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const REQUIRED_TOP_LEVEL_KEYS = [
|
|
11
|
+
'framework',
|
|
12
|
+
'pages',
|
|
13
|
+
'components',
|
|
14
|
+
'layouts',
|
|
15
|
+
'forms',
|
|
16
|
+
'navigation',
|
|
17
|
+
];
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Implementation
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Validator for UI generation stage output.
|
|
23
|
+
*
|
|
24
|
+
* Performs the following checks:
|
|
25
|
+
* 1. JSON parse validation
|
|
26
|
+
* 2. Required top-level keys
|
|
27
|
+
* 3. Non-empty pages with required fields
|
|
28
|
+
* 4. Entity coverage (every entity has at least one page)
|
|
29
|
+
* 5. Form coverage (every entity has create and edit forms)
|
|
30
|
+
* 6. Navigation routes non-empty and cover all pages
|
|
31
|
+
* 7. Non-empty layouts (at least one)
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const validator = new UiOutputValidator(['User', 'Post']);
|
|
36
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class UiOutputValidator {
|
|
40
|
+
entityNames;
|
|
41
|
+
/**
|
|
42
|
+
* @param entityNames - Entity names from architecture data model for coverage checks
|
|
43
|
+
*/
|
|
44
|
+
constructor(entityNames) {
|
|
45
|
+
this.entityNames = entityNames;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validate UI output against the UiArtifact schema.
|
|
49
|
+
*
|
|
50
|
+
* @param output - Raw LLM output text
|
|
51
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
52
|
+
* @returns Validation result with descriptive errors
|
|
53
|
+
*/
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
55
|
+
validate(output, _schema) {
|
|
56
|
+
const errors = [];
|
|
57
|
+
// 1. Parse as JSON
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(output);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
errors.push({
|
|
64
|
+
message: 'UI output is not valid JSON. Ensure the LLM produces only raw JSON without markdown fences or explanatory text.',
|
|
65
|
+
severity: 'error',
|
|
66
|
+
});
|
|
67
|
+
return { valid: false, errors };
|
|
68
|
+
}
|
|
69
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
70
|
+
errors.push({
|
|
71
|
+
message: 'UI output must be a JSON object, not an array or primitive.',
|
|
72
|
+
severity: 'error',
|
|
73
|
+
});
|
|
74
|
+
return { valid: false, errors };
|
|
75
|
+
}
|
|
76
|
+
const obj = parsed;
|
|
77
|
+
// 2. Verify required top-level keys
|
|
78
|
+
for (const key of REQUIRED_TOP_LEVEL_KEYS) {
|
|
79
|
+
if (!(key in obj)) {
|
|
80
|
+
errors.push({
|
|
81
|
+
message: `Missing required top-level key: '${key}'.`,
|
|
82
|
+
path: key,
|
|
83
|
+
severity: 'error',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
return { valid: false, errors };
|
|
89
|
+
}
|
|
90
|
+
// 3. Verify pages is non-empty array with required fields
|
|
91
|
+
const pages = obj['pages'];
|
|
92
|
+
if (!Array.isArray(pages) || pages.length === 0) {
|
|
93
|
+
errors.push({
|
|
94
|
+
message: "Key 'pages' must be a non-empty array of page definitions.",
|
|
95
|
+
path: 'pages',
|
|
96
|
+
severity: 'error',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
for (let i = 0; i < pages.length; i++) {
|
|
101
|
+
const page = pages[i];
|
|
102
|
+
if (page === undefined || typeof page !== 'object' || page === null) {
|
|
103
|
+
errors.push({
|
|
104
|
+
message: `pages[${String(i)}] is not a valid object.`,
|
|
105
|
+
path: `pages[${String(i)}]`,
|
|
106
|
+
severity: 'error',
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
for (const field of ['name', 'route', 'fileName', 'content']) {
|
|
111
|
+
if (typeof page[field] !== 'string' || page[field].length === 0) {
|
|
112
|
+
errors.push({
|
|
113
|
+
message: `pages[${String(i)}] is missing required field '${field}'.`,
|
|
114
|
+
path: `pages[${String(i)}].${field}`,
|
|
115
|
+
severity: 'error',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 4. Entity coverage — every entity has at least one page
|
|
122
|
+
if (Array.isArray(pages) && pages.length > 0) {
|
|
123
|
+
const pageEntities = new Set();
|
|
124
|
+
for (const page of pages) {
|
|
125
|
+
// Check page.entity field
|
|
126
|
+
if (typeof page['entity'] === 'string' && page['entity'].length > 0) {
|
|
127
|
+
pageEntities.add(page['entity'].toLowerCase());
|
|
128
|
+
}
|
|
129
|
+
// Also check page name containing entity name
|
|
130
|
+
if (typeof page['name'] === 'string') {
|
|
131
|
+
const pageName = page['name'].toLowerCase();
|
|
132
|
+
for (const entityName of this.entityNames) {
|
|
133
|
+
if (pageName.includes(entityName.toLowerCase())) {
|
|
134
|
+
pageEntities.add(entityName.toLowerCase());
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const entityName of this.entityNames) {
|
|
140
|
+
if (!pageEntities.has(entityName.toLowerCase())) {
|
|
141
|
+
errors.push({
|
|
142
|
+
message: `Entity '${entityName}' has no corresponding page. Every entity must have at least one page.`,
|
|
143
|
+
path: 'pages',
|
|
144
|
+
severity: 'error',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// 5. Form coverage — every entity has create and edit forms
|
|
150
|
+
const forms = obj['forms'];
|
|
151
|
+
if (!Array.isArray(forms) || forms.length === 0) {
|
|
152
|
+
errors.push({
|
|
153
|
+
message: "Key 'forms' must be a non-empty array of form definitions.",
|
|
154
|
+
path: 'forms',
|
|
155
|
+
severity: 'error',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const entityFormOps = new Map();
|
|
160
|
+
for (const form of forms) {
|
|
161
|
+
const entity = typeof form['entity'] === 'string' ? form['entity'].toLowerCase() : '';
|
|
162
|
+
const operation = typeof form['operation'] === 'string' ? form['operation'] : '';
|
|
163
|
+
if (entity.length > 0 && operation.length > 0) {
|
|
164
|
+
if (!entityFormOps.has(entity)) {
|
|
165
|
+
entityFormOps.set(entity, new Set());
|
|
166
|
+
}
|
|
167
|
+
entityFormOps.get(entity).add(operation);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const entityName of this.entityNames) {
|
|
171
|
+
const ops = entityFormOps.get(entityName.toLowerCase());
|
|
172
|
+
if (ops === undefined || !ops.has('create')) {
|
|
173
|
+
errors.push({
|
|
174
|
+
message: `Entity '${entityName}' is missing a 'create' form.`,
|
|
175
|
+
path: 'forms',
|
|
176
|
+
severity: 'error',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (ops === undefined || !ops.has('edit')) {
|
|
180
|
+
errors.push({
|
|
181
|
+
message: `Entity '${entityName}' is missing an 'edit' form.`,
|
|
182
|
+
path: 'forms',
|
|
183
|
+
severity: 'error',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// 6. Navigation routes non-empty and cover all pages
|
|
189
|
+
const navigation = obj['navigation'];
|
|
190
|
+
if (typeof navigation !== 'object' || navigation === null || Array.isArray(navigation)) {
|
|
191
|
+
errors.push({
|
|
192
|
+
message: "Key 'navigation' must be an object with routes and content.",
|
|
193
|
+
path: 'navigation',
|
|
194
|
+
severity: 'error',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const nav = navigation;
|
|
199
|
+
const routes = nav['routes'];
|
|
200
|
+
if (!Array.isArray(routes) || routes.length === 0) {
|
|
201
|
+
errors.push({
|
|
202
|
+
message: "Navigation 'routes' must be a non-empty array.",
|
|
203
|
+
path: 'navigation.routes',
|
|
204
|
+
severity: 'error',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// 7. Verify layouts is non-empty
|
|
209
|
+
const layouts = obj['layouts'];
|
|
210
|
+
if (!Array.isArray(layouts) || layouts.length === 0) {
|
|
211
|
+
errors.push({
|
|
212
|
+
message: "Key 'layouts' must be a non-empty array — at least one layout is required.",
|
|
213
|
+
path: 'layouts',
|
|
214
|
+
severity: 'error',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return { valid: errors.length === 0, errors };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hstm-labs/forge-apps-generator",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@hstm-labs/forge-common": "0.1.11",
|
|
20
|
+
"@hstm-labs/forge-core": "0.1.11",
|
|
21
|
+
"@hstm-labs/forge-architect": "0.1.11",
|
|
22
|
+
"@hstm-labs/forge-services-generator": "0.1.11",
|
|
23
|
+
"@hstm-labs/forge-spec-parser": "0.1.11",
|
|
24
|
+
"@hstm-labs/forge-profiles": "0.1.11",
|
|
25
|
+
"@hstm-labs/forge-templates": "0.1.11"
|
|
26
|
+
}
|
|
27
|
+
}
|