@kbediako/codex-orchestrator 0.1.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/LICENSE +7 -0
- package/README.md +238 -0
- package/dist/bin/codex-orchestrator.js +507 -0
- package/dist/orchestrator/src/agents/builder.js +16 -0
- package/dist/orchestrator/src/agents/index.js +4 -0
- package/dist/orchestrator/src/agents/planner.js +17 -0
- package/dist/orchestrator/src/agents/reviewer.js +13 -0
- package/dist/orchestrator/src/agents/tester.js +13 -0
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
- package/dist/orchestrator/src/cli/adapters/index.js +4 -0
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
- package/dist/orchestrator/src/cli/doctor.js +48 -0
- package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
- package/dist/orchestrator/src/cli/exec/command.js +56 -0
- package/dist/orchestrator/src/cli/exec/context.js +108 -0
- package/dist/orchestrator/src/cli/exec/experience.js +77 -0
- package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
- package/dist/orchestrator/src/cli/exec/learning.js +62 -0
- package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
- package/dist/orchestrator/src/cli/exec/summary.js +109 -0
- package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
- package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
- package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
- package/dist/orchestrator/src/cli/exec/types.js +1 -0
- package/dist/orchestrator/src/cli/init.js +64 -0
- package/dist/orchestrator/src/cli/mcp.js +124 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
- package/dist/orchestrator/src/cli/orchestrator.js +554 -0
- package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
- package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
- package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
- package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
- package/dist/orchestrator/src/cli/run/environment.js +24 -0
- package/dist/orchestrator/src/cli/run/manifest.js +367 -0
- package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
- package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
- package/dist/orchestrator/src/cli/selfCheck.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
- package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
- package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
- package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
- package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
- package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
- package/dist/orchestrator/src/cli/types.js +1 -0
- package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
- package/dist/orchestrator/src/cli/ui/controller.js +26 -0
- package/dist/orchestrator/src/cli/ui/store.js +240 -0
- package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
- package/dist/orchestrator/src/cli/utils/fs.js +8 -0
- package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
- package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
- package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
- package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
- package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
- package/dist/orchestrator/src/cli/utils/runId.js +7 -0
- package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
- package/dist/orchestrator/src/cli/utils/strings.js +8 -0
- package/dist/orchestrator/src/cli/utils/time.js +6 -0
- package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
- package/dist/orchestrator/src/control-plane/index.js +3 -0
- package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
- package/dist/orchestrator/src/control-plane/types.js +1 -0
- package/dist/orchestrator/src/control-plane/validator.js +50 -0
- package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
- package/dist/orchestrator/src/events/EventBus.js +25 -0
- package/dist/orchestrator/src/learning/crystalizer.js +108 -0
- package/dist/orchestrator/src/learning/harvester.js +146 -0
- package/dist/orchestrator/src/learning/manifest.js +56 -0
- package/dist/orchestrator/src/learning/runner.js +177 -0
- package/dist/orchestrator/src/learning/validator.js +164 -0
- package/dist/orchestrator/src/logger.js +20 -0
- package/dist/orchestrator/src/manager.js +388 -0
- package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
- package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
- package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
- package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
- package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
- package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
- package/dist/orchestrator/src/persistence/lockFile.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
- package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
- package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
- package/dist/orchestrator/src/privacy/guard.js +111 -0
- package/dist/orchestrator/src/scheduler/index.js +1 -0
- package/dist/orchestrator/src/scheduler/plan.js +171 -0
- package/dist/orchestrator/src/scheduler/types.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
- package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
- package/dist/orchestrator/src/types.js +1 -0
- package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
- package/dist/orchestrator/src/utils/errorMessage.js +14 -0
- package/dist/orchestrator/src/utils/executionMode.js +69 -0
- package/dist/packages/control-plane-schemas/src/index.js +1 -0
- package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
- package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
- package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
- package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
- package/dist/packages/orchestrator/src/index.js +3 -0
- package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
- package/dist/packages/orchestrator/src/notifications/index.js +74 -0
- package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
- package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
- package/dist/packages/sdk-node/src/orchestrator.js +195 -0
- package/dist/packages/shared/config/designConfig.js +495 -0
- package/dist/packages/shared/config/env.js +37 -0
- package/dist/packages/shared/config/index.js +2 -0
- package/dist/packages/shared/design-artifacts/writer.js +221 -0
- package/dist/packages/shared/events/serializer.js +84 -0
- package/dist/packages/shared/events/types.js +1 -0
- package/dist/packages/shared/manifest/artifactUtils.js +36 -0
- package/dist/packages/shared/manifest/designArtifacts.js +665 -0
- package/dist/packages/shared/manifest/fileIO.js +29 -0
- package/dist/packages/shared/manifest/toolRuns.js +78 -0
- package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
- package/dist/packages/shared/manifest/types.js +5 -0
- package/dist/packages/shared/manifest/validator.js +73 -0
- package/dist/packages/shared/manifest/writer.js +2 -0
- package/dist/packages/shared/streams/stdio.js +112 -0
- package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
- package/dist/scripts/design/pipeline/componentize.js +74 -0
- package/dist/scripts/design/pipeline/context.js +34 -0
- package/dist/scripts/design/pipeline/extract.js +249 -0
- package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
- package/dist/scripts/design/pipeline/prepare.js +46 -0
- package/dist/scripts/design/pipeline/reference.js +94 -0
- package/dist/scripts/design/pipeline/state.js +206 -0
- package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
- package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
- package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
- package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
- package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
- package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
- package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
- package/dist/scripts/design/pipeline/visual-regression.js +137 -0
- package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
- package/package.json +97 -0
- package/schemas/manifest.json +1064 -0
- package/templates/README.md +12 -0
- package/templates/codex/mcp-client.json +8 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { loadDesignContext } from '../context.js';
|
|
5
|
+
import { appendToolkitArtifacts, ensureToolkitState, loadDesignRunState, saveDesignRunState, upsertStage, upsertToolkitContext } from '../state.js';
|
|
6
|
+
import { stageArtifacts } from '../../../../orchestrator/src/persistence/ArtifactStager.js';
|
|
7
|
+
import { buildRetentionMetadata } from './common.js';
|
|
8
|
+
import { computeColorPalette, computeFontFamilies, normalizeSentenceSpacing } from './snapshot.js';
|
|
9
|
+
async function main() {
|
|
10
|
+
const context = await loadDesignContext();
|
|
11
|
+
const state = await loadDesignRunState(context.statePath);
|
|
12
|
+
const stageId = 'design-toolkit-tokens';
|
|
13
|
+
const toolkitState = ensureToolkitState(state);
|
|
14
|
+
const contexts = toolkitState.contexts;
|
|
15
|
+
if (contexts.length === 0) {
|
|
16
|
+
upsertStage(state, {
|
|
17
|
+
id: stageId,
|
|
18
|
+
title: 'Toolkit token + style guide generation',
|
|
19
|
+
status: 'skipped',
|
|
20
|
+
notes: ['No toolkit contexts available. Run the extract stage first.']
|
|
21
|
+
});
|
|
22
|
+
await saveDesignRunState(context.statePath, state);
|
|
23
|
+
console.log('[design-toolkit-tokens] skipped — no contexts found');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const retention = toolkitState.retention ?? {
|
|
27
|
+
days: state.retention?.days ?? 30,
|
|
28
|
+
autoPurge: state.retention?.autoPurge ?? false,
|
|
29
|
+
policy: state.retention?.policy ?? 'design.config.retention'
|
|
30
|
+
};
|
|
31
|
+
const tmpRoot = join(tmpdir(), `design-toolkit-tokens-${Date.now()}`);
|
|
32
|
+
await mkdir(tmpRoot, { recursive: true });
|
|
33
|
+
const artifacts = [];
|
|
34
|
+
const failures = [];
|
|
35
|
+
let processed = 0;
|
|
36
|
+
for (const entry of contexts) {
|
|
37
|
+
try {
|
|
38
|
+
const outputs = await buildTokenOutputs({ entry, repoRoot: context.repoRoot, tmpRoot });
|
|
39
|
+
const tokensRetention = buildRetentionMetadata(retention, new Date());
|
|
40
|
+
const stagedTokens = await stageArtifacts({
|
|
41
|
+
taskId: context.taskId,
|
|
42
|
+
runId: context.runId,
|
|
43
|
+
artifacts: [
|
|
44
|
+
{
|
|
45
|
+
path: relative(process.cwd(), outputs.tokensPath),
|
|
46
|
+
description: `Token bundle for ${entry.slug}`
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
options: {
|
|
50
|
+
relativeDir: `design-toolkit/tokens/${entry.slug}`,
|
|
51
|
+
overwrite: true
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const stagedGuide = await stageArtifacts({
|
|
55
|
+
taskId: context.taskId,
|
|
56
|
+
runId: context.runId,
|
|
57
|
+
artifacts: [
|
|
58
|
+
{
|
|
59
|
+
path: relative(process.cwd(), outputs.styleGuidePath),
|
|
60
|
+
description: `Style guide for ${entry.slug}`
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
options: {
|
|
64
|
+
relativeDir: `design-toolkit/styleguide/${entry.slug}`,
|
|
65
|
+
overwrite: true
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
artifacts.push({
|
|
69
|
+
id: `${entry.slug}-tokens`,
|
|
70
|
+
stage: 'tokens',
|
|
71
|
+
status: 'succeeded',
|
|
72
|
+
relative_path: stagedTokens[0].path,
|
|
73
|
+
description: `Design tokens for ${entry.slug}`,
|
|
74
|
+
retention: tokensRetention,
|
|
75
|
+
metrics: {
|
|
76
|
+
token_count: outputs.tokenCount,
|
|
77
|
+
semantic_alias_count: outputs.semanticCount
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
artifacts.push({
|
|
81
|
+
id: `${entry.slug}-styleguide`,
|
|
82
|
+
stage: 'styleguide',
|
|
83
|
+
status: 'succeeded',
|
|
84
|
+
relative_path: stagedGuide[0].path,
|
|
85
|
+
description: `Style guide for ${entry.slug}`,
|
|
86
|
+
retention: tokensRetention,
|
|
87
|
+
metrics: {
|
|
88
|
+
styleguide_pages: outputs.pageCount
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
upsertToolkitContext(state, {
|
|
92
|
+
...entry,
|
|
93
|
+
tokensPath: stagedTokens[0].path,
|
|
94
|
+
styleguidePath: stagedGuide[0].path
|
|
95
|
+
});
|
|
96
|
+
processed += 1;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
failures.push(`${entry.slug}: ${message}`);
|
|
101
|
+
console.error(`[design-toolkit-tokens] failed for ${entry.slug}: ${message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (artifacts.length > 0) {
|
|
105
|
+
appendToolkitArtifacts(state, artifacts);
|
|
106
|
+
}
|
|
107
|
+
const status = failures.length === 0 && processed > 0 ? 'succeeded' : 'failed';
|
|
108
|
+
upsertStage(state, {
|
|
109
|
+
id: stageId,
|
|
110
|
+
title: 'Toolkit token + style guide generation',
|
|
111
|
+
status,
|
|
112
|
+
notes: failures.length > 0 ? failures : undefined,
|
|
113
|
+
metrics: {
|
|
114
|
+
context_count: contexts.length,
|
|
115
|
+
processed
|
|
116
|
+
},
|
|
117
|
+
artifacts: artifacts.map((artifact) => ({
|
|
118
|
+
relative_path: artifact.relative_path,
|
|
119
|
+
stage: artifact.stage,
|
|
120
|
+
status: artifact.status,
|
|
121
|
+
description: artifact.description ?? undefined
|
|
122
|
+
}))
|
|
123
|
+
});
|
|
124
|
+
await saveDesignRunState(context.statePath, state);
|
|
125
|
+
if (status === 'failed') {
|
|
126
|
+
throw new Error('Token or style guide generation failed.');
|
|
127
|
+
}
|
|
128
|
+
console.log(`[design-toolkit-tokens] generated tokens for ${processed} contexts`);
|
|
129
|
+
}
|
|
130
|
+
async function buildTokenOutputs(options) {
|
|
131
|
+
const { entry, repoRoot, tmpRoot } = options;
|
|
132
|
+
const desiredColorCount = 16;
|
|
133
|
+
const aggregatedCss = await loadAggregatedCss(entry, repoRoot);
|
|
134
|
+
const paletteFromFile = await loadPaletteFromFile(entry, repoRoot);
|
|
135
|
+
const palettePreview = Array.isArray(entry.palettePreview) ? mergeUniqueStrings(entry.palettePreview) : [];
|
|
136
|
+
const runtimeCanvasColors = Array.isArray(entry.runtimeCanvasColors) ? mergeUniqueStrings(entry.runtimeCanvasColors) : [];
|
|
137
|
+
const cssColors = computeColorPalette(aggregatedCss);
|
|
138
|
+
const palette = buildPalette(mergeUniqueStrings([...paletteFromFile, ...palettePreview, ...cssColors, ...runtimeCanvasColors]), desiredColorCount);
|
|
139
|
+
const sections = await loadSections(entry, repoRoot);
|
|
140
|
+
const cssFonts = computeFontFamilies(aggregatedCss);
|
|
141
|
+
const capturedFonts = Array.isArray(entry.fontFamilies) ? mergeUniqueStrings(entry.fontFamilies) : [];
|
|
142
|
+
const runtimeFonts = Array.isArray(entry.resolvedFonts) ? mergeUniqueStrings(entry.resolvedFonts) : [];
|
|
143
|
+
const mergedFonts = mergeUniqueStrings([...cssFonts, ...capturedFonts, ...runtimeFonts]);
|
|
144
|
+
const semanticColors = deriveSemanticColors(palette);
|
|
145
|
+
const semanticTokens = buildSemanticTokens(palette, semanticColors);
|
|
146
|
+
const tokens = {
|
|
147
|
+
"$schema": "https://design-tokens.github.io/community-group/format/module.json",
|
|
148
|
+
tokenSetOrder: ['global', 'semantic'],
|
|
149
|
+
global: {
|
|
150
|
+
color: buildColorTokens(palette),
|
|
151
|
+
radius: {
|
|
152
|
+
sm: { value: '4px' },
|
|
153
|
+
md: { value: '8px' },
|
|
154
|
+
lg: { value: '16px' }
|
|
155
|
+
},
|
|
156
|
+
spacing: {
|
|
157
|
+
xs: { value: '4px' },
|
|
158
|
+
sm: { value: '8px' },
|
|
159
|
+
md: { value: '16px' },
|
|
160
|
+
lg: { value: '24px' }
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
semantic: semanticTokens,
|
|
164
|
+
metadata: {
|
|
165
|
+
source: entry.url,
|
|
166
|
+
reference: entry.referenceUrl ?? entry.url,
|
|
167
|
+
css_path: entry.snapshotCssPath ?? null,
|
|
168
|
+
palette_sources: {
|
|
169
|
+
file: paletteFromFile.length,
|
|
170
|
+
extracted: cssColors.length,
|
|
171
|
+
preview: palettePreview.length,
|
|
172
|
+
runtime_canvas: runtimeCanvasColors.length
|
|
173
|
+
},
|
|
174
|
+
generated_at: new Date().toISOString(),
|
|
175
|
+
fonts: mergedFonts,
|
|
176
|
+
runtime_fonts: runtimeFonts,
|
|
177
|
+
palette_sample: palette.slice(0, 8),
|
|
178
|
+
runtime_canvas_colors: runtimeCanvasColors
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const cssVariables = buildCssVariables(entry.slug, palette, semanticColors);
|
|
182
|
+
const styleGuide = buildStyleGuide(entry, palette, mergedFonts, sections, runtimeFonts, runtimeCanvasColors, semanticColors, {
|
|
183
|
+
paletteFromFileCount: paletteFromFile.length,
|
|
184
|
+
palettePreviewCount: palettePreview.length,
|
|
185
|
+
cssColorCount: cssColors.length
|
|
186
|
+
});
|
|
187
|
+
const slugDir = join(tmpRoot, entry.slug);
|
|
188
|
+
await mkdir(slugDir, { recursive: true });
|
|
189
|
+
const tokensPath = join(slugDir, 'tokens.json');
|
|
190
|
+
const cssPath = join(slugDir, 'tokens.css');
|
|
191
|
+
const styleGuidePath = join(slugDir, 'STYLE_GUIDE.md');
|
|
192
|
+
await Promise.all([
|
|
193
|
+
writeFile(tokensPath, JSON.stringify(tokens, null, 2), 'utf8'),
|
|
194
|
+
writeFile(cssPath, `:root\n{\n${cssVariables}\n}\n`, 'utf8'),
|
|
195
|
+
writeFile(styleGuidePath, styleGuide, 'utf8')
|
|
196
|
+
]);
|
|
197
|
+
return {
|
|
198
|
+
tokensPath,
|
|
199
|
+
cssPath,
|
|
200
|
+
styleGuidePath,
|
|
201
|
+
tokenCount: palette.length,
|
|
202
|
+
semanticCount: Object.keys(semanticTokens).length,
|
|
203
|
+
pageCount: 1
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function loadAggregatedCss(entry, repoRoot) {
|
|
207
|
+
if (!entry.snapshotCssPath) {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const absolute = join(repoRoot, entry.snapshotCssPath);
|
|
212
|
+
return await readFile(absolute, 'utf8');
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.warn(`[design-toolkit-tokens] Failed to read aggregated CSS for ${entry.slug}:`, error);
|
|
216
|
+
return '';
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function loadPaletteFromFile(entry, repoRoot) {
|
|
220
|
+
if (!entry.palettePath) {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const absolute = join(repoRoot, entry.palettePath);
|
|
225
|
+
const raw = await readFile(absolute, 'utf8');
|
|
226
|
+
const parsed = JSON.parse(raw);
|
|
227
|
+
if (Array.isArray(parsed)) {
|
|
228
|
+
return parsed
|
|
229
|
+
.map((value) => (typeof value === 'string' ? value.trim() : null))
|
|
230
|
+
.filter((value) => Boolean(value));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.warn(`[design-toolkit-tokens] Failed to read palette for ${entry.slug}:`, error);
|
|
235
|
+
}
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
async function loadSections(entry, repoRoot) {
|
|
239
|
+
if (!entry.sectionsPath) {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const absolute = join(repoRoot, entry.sectionsPath);
|
|
244
|
+
const raw = await readFile(absolute, 'utf8');
|
|
245
|
+
const parsed = JSON.parse(raw);
|
|
246
|
+
return parsed
|
|
247
|
+
.map((section) => ({
|
|
248
|
+
title: normalizeSentenceSpacing(section.title ?? 'Section')
|
|
249
|
+
.replace(/\s+/g, ' ')
|
|
250
|
+
.trim() || 'Section',
|
|
251
|
+
description: normalizeSentenceSpacing(section.description ?? '')
|
|
252
|
+
.replace(/\s+/g, ' ')
|
|
253
|
+
.trim()
|
|
254
|
+
}))
|
|
255
|
+
.filter((section) => section.description.length > 0);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.warn(`[design-toolkit-tokens] Failed to read sections for ${entry.slug}:`, error);
|
|
259
|
+
}
|
|
260
|
+
return [];
|
|
261
|
+
}
|
|
262
|
+
function buildPalette(candidates, desired) {
|
|
263
|
+
const normalized = mergeUniqueStrings(candidates.map(normalizeHexColor));
|
|
264
|
+
if (normalized.length >= desired) {
|
|
265
|
+
return normalized.slice(0, desired);
|
|
266
|
+
}
|
|
267
|
+
if (normalized.length === 0) {
|
|
268
|
+
return ['#111111', '#ffffff'].slice(0, desired);
|
|
269
|
+
}
|
|
270
|
+
const padded = [...normalized];
|
|
271
|
+
const anchor = normalized[normalized.length - 1];
|
|
272
|
+
padded.push(...padPalette(anchor, desired - normalized.length));
|
|
273
|
+
return padded.slice(0, desired);
|
|
274
|
+
}
|
|
275
|
+
function padPalette(anchor, additional) {
|
|
276
|
+
const rgb = hexToRgb(anchor);
|
|
277
|
+
if (!rgb) {
|
|
278
|
+
return Array.from({ length: additional }, () => anchor);
|
|
279
|
+
}
|
|
280
|
+
const { h, s, l } = rgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
281
|
+
const results = [];
|
|
282
|
+
for (let step = 1; step <= additional; step += 1) {
|
|
283
|
+
const offset = Math.min(0.45, step * 0.08);
|
|
284
|
+
const lightness = Math.min(1, Math.max(0, l + (step % 2 === 0 ? -offset : offset)));
|
|
285
|
+
results.push(hslToHex(h, s, lightness));
|
|
286
|
+
}
|
|
287
|
+
return results;
|
|
288
|
+
}
|
|
289
|
+
function normalizeHexColor(value) {
|
|
290
|
+
if (!value.startsWith('#')) {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
return value.length === 4
|
|
294
|
+
? `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`.toLowerCase()
|
|
295
|
+
: value.toLowerCase();
|
|
296
|
+
}
|
|
297
|
+
function buildStyleGuide(entry, palette, fonts, sections, runtimeFonts, runtimeCanvasColors, semanticColors, paletteSources) {
|
|
298
|
+
const paletteList = palette.map((hex) => `- ${hex}`).join('\n');
|
|
299
|
+
const fontList = fonts.map((font) => `- ${font}`).join('\n');
|
|
300
|
+
const runtimeFontList = runtimeFonts.map((font) => `- ${font}`).join('\n');
|
|
301
|
+
const runtimeCanvasList = runtimeCanvasColors.map((hex) => `- ${hex}`).join('\n');
|
|
302
|
+
const sectionList = sections.length > 0
|
|
303
|
+
? sections.map((section) => `- **${section.title}** — ${section.description}`).join('\n')
|
|
304
|
+
: '- (No sections detected)';
|
|
305
|
+
const canvasSection = runtimeCanvasList ? `\n## Canvas Samples\n${runtimeCanvasList}\n` : '\n';
|
|
306
|
+
const runtimeFontSection = runtimeFontList ? `\n## Resolved Typekit Fonts\n${runtimeFontList}\n` : '\n';
|
|
307
|
+
const paletteSourceNotes = [
|
|
308
|
+
`- Aggregated CSS colors: ${paletteSources.cssColorCount}`,
|
|
309
|
+
`- Palette file swatches: ${paletteSources.paletteFromFileCount}`,
|
|
310
|
+
`- Preview sample swatches: ${paletteSources.palettePreviewCount}`
|
|
311
|
+
].join('\n');
|
|
312
|
+
return `# ${entry.slug} Style Guide\n\nGenerated tokens for ${entry.url}.\n\n## Palette\n${paletteList}\n\n## Semantic Roles\n- Background: ${semanticColors.background}\n- Foreground: ${semanticColors.foreground}\n- Accent: ${semanticColors.accent}\n\n## Palette Inputs\n${paletteSourceNotes}${canvasSection}\n## Typography (CSS)\n${fontList}${runtimeFontSection}\n## Layout Sections\n${sectionList}\n`;
|
|
313
|
+
}
|
|
314
|
+
function mergeUniqueStrings(values) {
|
|
315
|
+
const unique = [];
|
|
316
|
+
const seen = new Set();
|
|
317
|
+
for (const value of values) {
|
|
318
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
319
|
+
if (!text || seen.has(text)) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
seen.add(text);
|
|
323
|
+
unique.push(text);
|
|
324
|
+
}
|
|
325
|
+
return unique;
|
|
326
|
+
}
|
|
327
|
+
function deriveSemanticColors(palette) {
|
|
328
|
+
if (palette.length === 0) {
|
|
329
|
+
return { foreground: '#0b0b0b', background: '#ffffff', accent: '#1d4ed8' };
|
|
330
|
+
}
|
|
331
|
+
const scoring = palette.map((hex) => {
|
|
332
|
+
const rgb = hexToRgb(hex);
|
|
333
|
+
if (!rgb) {
|
|
334
|
+
return { hex, lightness: 0.5, saturation: 0.5 };
|
|
335
|
+
}
|
|
336
|
+
const { l, s } = rgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
337
|
+
return { hex, lightness: l, saturation: s };
|
|
338
|
+
});
|
|
339
|
+
const background = scoring.reduce((lightest, current) => (current.lightness > lightest.lightness ? current : lightest));
|
|
340
|
+
const foreground = scoring.reduce((darkest, current) => (current.lightness < darkest.lightness ? current : darkest));
|
|
341
|
+
const accentCandidate = scoring
|
|
342
|
+
.filter((entry) => entry.hex !== background.hex && entry.hex !== foreground.hex)
|
|
343
|
+
.reduce((vibrant, current) => (current.saturation > vibrant.saturation ? current : vibrant), scoring[0]);
|
|
344
|
+
return {
|
|
345
|
+
foreground: foreground.hex,
|
|
346
|
+
background: background.hex,
|
|
347
|
+
accent: accentCandidate.hex
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function buildSemanticTokens(palette, semantic) {
|
|
351
|
+
const alias = (hex) => {
|
|
352
|
+
const index = palette.findIndex((value) => value.toLowerCase() === hex.toLowerCase());
|
|
353
|
+
if (index >= 0) {
|
|
354
|
+
return `{global.color.color-${index + 1}.value}`;
|
|
355
|
+
}
|
|
356
|
+
return hex;
|
|
357
|
+
};
|
|
358
|
+
return {
|
|
359
|
+
foreground: { value: alias(semantic.foreground) },
|
|
360
|
+
background: { value: alias(semantic.background) },
|
|
361
|
+
accent: { value: alias(semantic.accent) }
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function buildColorTokens(palette) {
|
|
365
|
+
return Object.fromEntries(palette.map((hex, idx) => [`color-${idx + 1}`, { value: hex }]));
|
|
366
|
+
}
|
|
367
|
+
function buildCssVariables(slug, palette, semantic) {
|
|
368
|
+
const colors = palette.map((hex, idx) => ` --${slug}-color-${idx + 1}: ${hex};`);
|
|
369
|
+
const semanticVars = [
|
|
370
|
+
` --${slug}-background: ${semantic.background};`,
|
|
371
|
+
` --${slug}-foreground: ${semantic.foreground};`,
|
|
372
|
+
` --${slug}-accent: ${semantic.accent};`
|
|
373
|
+
];
|
|
374
|
+
return [...colors, ...semanticVars].join('\n');
|
|
375
|
+
}
|
|
376
|
+
function hexToRgb(hex) {
|
|
377
|
+
const normalized = normalizeHexColor(hex);
|
|
378
|
+
if (!/^#([0-9a-f]{6})$/i.test(normalized)) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
const value = normalized.slice(1);
|
|
382
|
+
return {
|
|
383
|
+
r: parseInt(value.slice(0, 2), 16),
|
|
384
|
+
g: parseInt(value.slice(2, 4), 16),
|
|
385
|
+
b: parseInt(value.slice(4, 6), 16)
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function rgbToHsl(r, g, b) {
|
|
389
|
+
const nr = r / 255;
|
|
390
|
+
const ng = g / 255;
|
|
391
|
+
const nb = b / 255;
|
|
392
|
+
const max = Math.max(nr, ng, nb);
|
|
393
|
+
const min = Math.min(nr, ng, nb);
|
|
394
|
+
const delta = max - min;
|
|
395
|
+
let h = 0;
|
|
396
|
+
if (delta !== 0) {
|
|
397
|
+
if (max === nr) {
|
|
398
|
+
h = ((ng - nb) / delta) % 6;
|
|
399
|
+
}
|
|
400
|
+
else if (max === ng) {
|
|
401
|
+
h = (nb - nr) / delta + 2;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
h = (nr - ng) / delta + 4;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
h = Math.round(h * 60);
|
|
408
|
+
if (h < 0) {
|
|
409
|
+
h += 360;
|
|
410
|
+
}
|
|
411
|
+
const l = (max + min) / 2;
|
|
412
|
+
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
413
|
+
return { h, s, l };
|
|
414
|
+
}
|
|
415
|
+
function hslToHex(h, s, l) {
|
|
416
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
417
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
418
|
+
const m = l - c / 2;
|
|
419
|
+
let r = 0;
|
|
420
|
+
let g = 0;
|
|
421
|
+
let b = 0;
|
|
422
|
+
if (h >= 0 && h < 60) {
|
|
423
|
+
r = c;
|
|
424
|
+
g = x;
|
|
425
|
+
}
|
|
426
|
+
else if (h >= 60 && h < 120) {
|
|
427
|
+
r = x;
|
|
428
|
+
g = c;
|
|
429
|
+
}
|
|
430
|
+
else if (h >= 120 && h < 180) {
|
|
431
|
+
g = c;
|
|
432
|
+
b = x;
|
|
433
|
+
}
|
|
434
|
+
else if (h >= 180 && h < 240) {
|
|
435
|
+
g = x;
|
|
436
|
+
b = c;
|
|
437
|
+
}
|
|
438
|
+
else if (h >= 240 && h < 300) {
|
|
439
|
+
r = x;
|
|
440
|
+
b = c;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
r = c;
|
|
444
|
+
b = x;
|
|
445
|
+
}
|
|
446
|
+
const toHex = (value) => {
|
|
447
|
+
const scaled = Math.round((value + m) * 255);
|
|
448
|
+
return scaled.toString(16).padStart(2, '0');
|
|
449
|
+
};
|
|
450
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
451
|
+
}
|
|
452
|
+
main().catch((error) => {
|
|
453
|
+
console.error('[design-toolkit-tokens] failed to generate tokens');
|
|
454
|
+
console.error(error instanceof Error ? error.stack ?? error.message : error);
|
|
455
|
+
process.exitCode = 1;
|
|
456
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { join, relative } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { loadDesignContext } from './context.js';
|
|
7
|
+
import { appendArtifacts, loadDesignRunState, saveDesignRunState, upsertStage } from './state.js';
|
|
8
|
+
import { stageArtifacts } from '../../../orchestrator/src/persistence/ArtifactStager.js';
|
|
9
|
+
const DESIGN_SYSTEM_DIR = 'packages/design-system';
|
|
10
|
+
const SUMMARY_FILE = join(DESIGN_SYSTEM_DIR, '.codex', 'visual-regression-summary.json');
|
|
11
|
+
async function main() {
|
|
12
|
+
const context = await loadDesignContext();
|
|
13
|
+
const state = await loadDesignRunState(context.statePath);
|
|
14
|
+
const stageId = 'design-visual-regression';
|
|
15
|
+
const designSystemExists = await pathExists(DESIGN_SYSTEM_DIR);
|
|
16
|
+
if (!designSystemExists) {
|
|
17
|
+
upsertStage(state, {
|
|
18
|
+
id: stageId,
|
|
19
|
+
title: 'Visual regression tests',
|
|
20
|
+
status: 'skipped',
|
|
21
|
+
notes: ['packages/design-system not present; skipping visual regression']
|
|
22
|
+
});
|
|
23
|
+
await saveDesignRunState(context.statePath, state);
|
|
24
|
+
console.log('[design-visual-regression] skipped — packages/design-system not found');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const exitCode = await runVisualRegression();
|
|
28
|
+
const summaryData = await loadSummaryData(exitCode);
|
|
29
|
+
const tmpRoot = join(tmpdir(), `design-visual-${Date.now()}`);
|
|
30
|
+
await mkdir(tmpRoot, { recursive: true });
|
|
31
|
+
const tempFile = join(tmpRoot, 'visual-regression-summary.json');
|
|
32
|
+
await writeFile(tempFile, JSON.stringify(summaryData, null, 2), 'utf8');
|
|
33
|
+
const [staged] = await stageArtifacts({
|
|
34
|
+
taskId: context.taskId,
|
|
35
|
+
runId: context.runId,
|
|
36
|
+
artifacts: [
|
|
37
|
+
{
|
|
38
|
+
path: relative(process.cwd(), tempFile),
|
|
39
|
+
description: 'Visual regression summary'
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
options: {
|
|
43
|
+
relativeDir: 'design/visual-regression',
|
|
44
|
+
overwrite: true
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const artifact = {
|
|
48
|
+
stage: 'visual-regression',
|
|
49
|
+
status: exitCode === 0 ? 'succeeded' : 'failed',
|
|
50
|
+
relative_path: staged.path,
|
|
51
|
+
type: 'visual-regression-summary',
|
|
52
|
+
description: 'Visual regression results',
|
|
53
|
+
metadata: summaryData.report ? { report: summaryData.report } : undefined
|
|
54
|
+
};
|
|
55
|
+
appendArtifacts(state, [artifact]);
|
|
56
|
+
upsertStage(state, {
|
|
57
|
+
id: stageId,
|
|
58
|
+
title: 'Visual regression tests',
|
|
59
|
+
status: exitCode === 0 ? 'succeeded' : 'failed',
|
|
60
|
+
metrics: {
|
|
61
|
+
status: exitCode === 0 ? 'passed' : 'failed',
|
|
62
|
+
total: summaryData.metrics.total,
|
|
63
|
+
failed: summaryData.metrics.failed
|
|
64
|
+
},
|
|
65
|
+
artifacts: [
|
|
66
|
+
{
|
|
67
|
+
relative_path: staged.path,
|
|
68
|
+
stage: 'visual-regression',
|
|
69
|
+
status: exitCode === 0 ? 'succeeded' : 'failed',
|
|
70
|
+
description: 'visual-regression-summary.json'
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
await saveDesignRunState(context.statePath, state);
|
|
75
|
+
const statusText = exitCode === 0 ? 'passed' : 'failed';
|
|
76
|
+
console.log(`[design-visual-regression] ${statusText}; summary staged at ${staged.path}`);
|
|
77
|
+
}
|
|
78
|
+
async function pathExists(path) {
|
|
79
|
+
try {
|
|
80
|
+
await access(path, constants.F_OK);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function runVisualRegression() {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const child = spawn('npm', ['--prefix', DESIGN_SYSTEM_DIR, 'run', 'test:visual'], {
|
|
90
|
+
stdio: 'inherit'
|
|
91
|
+
});
|
|
92
|
+
child.on('close', (code) => {
|
|
93
|
+
resolve(code ?? 1);
|
|
94
|
+
});
|
|
95
|
+
child.on('error', () => {
|
|
96
|
+
resolve(1);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async function loadSummaryData(exitCode) {
|
|
101
|
+
try {
|
|
102
|
+
const raw = await readFile(SUMMARY_FILE, 'utf8');
|
|
103
|
+
const parsed = JSON.parse(raw);
|
|
104
|
+
const metrics = parsed.metrics ?? {
|
|
105
|
+
total: exitCode === 0 ? 1 : 1,
|
|
106
|
+
failed: exitCode === 0 ? 0 : 1,
|
|
107
|
+
passed: exitCode === 0 ? 1 : 0
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
generatedAt: new Date().toISOString(),
|
|
111
|
+
status: exitCode === 0 ? 'passed' : 'failed',
|
|
112
|
+
metrics: {
|
|
113
|
+
total: metrics.total ?? (exitCode === 0 ? 1 : 1),
|
|
114
|
+
failed: metrics.failed ?? (exitCode === 0 ? 0 : 1),
|
|
115
|
+
passed: metrics.passed ?? (exitCode === 0 ? 1 : 0)
|
|
116
|
+
},
|
|
117
|
+
report: parsed.report
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return {
|
|
122
|
+
generatedAt: new Date().toISOString(),
|
|
123
|
+
status: exitCode === 0 ? 'passed' : 'failed',
|
|
124
|
+
metrics: {
|
|
125
|
+
total: exitCode === 0 ? 0 : 1,
|
|
126
|
+
failed: exitCode === 0 ? 0 : 1,
|
|
127
|
+
passed: exitCode === 0 ? 1 : 0
|
|
128
|
+
},
|
|
129
|
+
report: null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
main().catch((error) => {
|
|
134
|
+
console.error('[design-visual-regression] failed to execute visual regression tests');
|
|
135
|
+
console.error(error instanceof Error ? error.stack ?? error.message : error);
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { loadDesignContext } from './context.js';
|
|
2
|
+
import { ensureToolkitState, loadDesignRunState, saveDesignRunState, upsertStage } from './state.js';
|
|
3
|
+
import { writeDesignSummary } from '../../../packages/shared/design-artifacts/writer.js';
|
|
4
|
+
async function main() {
|
|
5
|
+
const context = await loadDesignContext();
|
|
6
|
+
const state = await loadDesignRunState(context.statePath);
|
|
7
|
+
const stageId = 'design-artifact-writer';
|
|
8
|
+
const toolkitState = ensureToolkitState(state);
|
|
9
|
+
const retention = state.retention ?? {
|
|
10
|
+
days: 30,
|
|
11
|
+
autoPurge: false,
|
|
12
|
+
policy: 'design.config.retention'
|
|
13
|
+
};
|
|
14
|
+
const privacy = state.privacy ?? {
|
|
15
|
+
allowThirdParty: false,
|
|
16
|
+
requireApproval: true,
|
|
17
|
+
maskSelectors: [],
|
|
18
|
+
approver: null
|
|
19
|
+
};
|
|
20
|
+
const result = await writeDesignSummary({
|
|
21
|
+
context: {
|
|
22
|
+
taskId: context.taskId,
|
|
23
|
+
runId: context.runId,
|
|
24
|
+
manifestPath: context.manifestPath,
|
|
25
|
+
repoRoot: context.repoRoot
|
|
26
|
+
},
|
|
27
|
+
stages: state.stages,
|
|
28
|
+
artifacts: state.artifacts,
|
|
29
|
+
configSnapshot: state.configSnapshot ?? null,
|
|
30
|
+
retention,
|
|
31
|
+
privacy,
|
|
32
|
+
approvals: state.approvals,
|
|
33
|
+
metrics: state.metrics,
|
|
34
|
+
designPlan: state.designPlan ?? undefined,
|
|
35
|
+
designGuardrail: state.designGuardrail ?? undefined,
|
|
36
|
+
designHistory: state.designHistory ?? undefined,
|
|
37
|
+
designStyleProfile: state.designStyleProfile ?? undefined,
|
|
38
|
+
designMetrics: state.designMetrics ?? undefined,
|
|
39
|
+
toolkitArtifacts: toolkitState.artifacts,
|
|
40
|
+
toolkitSummary: toolkitState.summary ?? undefined,
|
|
41
|
+
outDir: context.outRoot,
|
|
42
|
+
now: new Date()
|
|
43
|
+
});
|
|
44
|
+
toolkitState.summary =
|
|
45
|
+
result.summary.design_toolkit_summary ?? null;
|
|
46
|
+
upsertStage(state, {
|
|
47
|
+
id: stageId,
|
|
48
|
+
title: 'Persist design artifact summary',
|
|
49
|
+
status: 'succeeded',
|
|
50
|
+
metrics: {
|
|
51
|
+
summary_path: result.summaryPath
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
await saveDesignRunState(context.statePath, state);
|
|
55
|
+
console.log(`[design-artifact-writer] summary written to ${result.summaryPath}`);
|
|
56
|
+
}
|
|
57
|
+
main().catch((error) => {
|
|
58
|
+
console.error('[design-artifact-writer] failed to persist design artifact summary');
|
|
59
|
+
console.error(error instanceof Error ? error.stack ?? error.message : error);
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
});
|