@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.
Files changed (150) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +238 -0
  3. package/dist/bin/codex-orchestrator.js +507 -0
  4. package/dist/orchestrator/src/agents/builder.js +16 -0
  5. package/dist/orchestrator/src/agents/index.js +4 -0
  6. package/dist/orchestrator/src/agents/planner.js +17 -0
  7. package/dist/orchestrator/src/agents/reviewer.js +13 -0
  8. package/dist/orchestrator/src/agents/tester.js +13 -0
  9. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
  10. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
  11. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
  12. package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
  13. package/dist/orchestrator/src/cli/adapters/index.js +4 -0
  14. package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
  15. package/dist/orchestrator/src/cli/doctor.js +48 -0
  16. package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
  17. package/dist/orchestrator/src/cli/exec/command.js +56 -0
  18. package/dist/orchestrator/src/cli/exec/context.js +108 -0
  19. package/dist/orchestrator/src/cli/exec/experience.js +77 -0
  20. package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
  21. package/dist/orchestrator/src/cli/exec/learning.js +62 -0
  22. package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
  23. package/dist/orchestrator/src/cli/exec/summary.js +109 -0
  24. package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
  25. package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
  26. package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
  27. package/dist/orchestrator/src/cli/exec/types.js +1 -0
  28. package/dist/orchestrator/src/cli/init.js +64 -0
  29. package/dist/orchestrator/src/cli/mcp.js +124 -0
  30. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
  31. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
  32. package/dist/orchestrator/src/cli/orchestrator.js +554 -0
  33. package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
  34. package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
  35. package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
  36. package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
  37. package/dist/orchestrator/src/cli/run/environment.js +24 -0
  38. package/dist/orchestrator/src/cli/run/manifest.js +367 -0
  39. package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
  40. package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
  41. package/dist/orchestrator/src/cli/selfCheck.js +12 -0
  42. package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
  43. package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
  44. package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
  45. package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
  46. package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
  47. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
  48. package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
  49. package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
  50. package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
  51. package/dist/orchestrator/src/cli/types.js +1 -0
  52. package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
  53. package/dist/orchestrator/src/cli/ui/controller.js +26 -0
  54. package/dist/orchestrator/src/cli/ui/store.js +240 -0
  55. package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
  56. package/dist/orchestrator/src/cli/utils/fs.js +8 -0
  57. package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
  58. package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
  59. package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
  60. package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
  61. package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
  62. package/dist/orchestrator/src/cli/utils/runId.js +7 -0
  63. package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
  64. package/dist/orchestrator/src/cli/utils/strings.js +8 -0
  65. package/dist/orchestrator/src/cli/utils/time.js +6 -0
  66. package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
  67. package/dist/orchestrator/src/control-plane/index.js +3 -0
  68. package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
  69. package/dist/orchestrator/src/control-plane/types.js +1 -0
  70. package/dist/orchestrator/src/control-plane/validator.js +50 -0
  71. package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
  72. package/dist/orchestrator/src/events/EventBus.js +25 -0
  73. package/dist/orchestrator/src/learning/crystalizer.js +108 -0
  74. package/dist/orchestrator/src/learning/harvester.js +146 -0
  75. package/dist/orchestrator/src/learning/manifest.js +56 -0
  76. package/dist/orchestrator/src/learning/runner.js +177 -0
  77. package/dist/orchestrator/src/learning/validator.js +164 -0
  78. package/dist/orchestrator/src/logger.js +20 -0
  79. package/dist/orchestrator/src/manager.js +388 -0
  80. package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
  81. package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
  82. package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
  83. package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
  84. package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
  85. package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
  86. package/dist/orchestrator/src/persistence/lockFile.js +26 -0
  87. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
  88. package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
  89. package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
  90. package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
  91. package/dist/orchestrator/src/privacy/guard.js +111 -0
  92. package/dist/orchestrator/src/scheduler/index.js +1 -0
  93. package/dist/orchestrator/src/scheduler/plan.js +171 -0
  94. package/dist/orchestrator/src/scheduler/types.js +1 -0
  95. package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
  96. package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
  97. package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
  98. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
  99. package/dist/orchestrator/src/types.js +1 -0
  100. package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
  101. package/dist/orchestrator/src/utils/errorMessage.js +14 -0
  102. package/dist/orchestrator/src/utils/executionMode.js +69 -0
  103. package/dist/packages/control-plane-schemas/src/index.js +1 -0
  104. package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
  105. package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
  106. package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
  107. package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
  108. package/dist/packages/orchestrator/src/index.js +3 -0
  109. package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
  110. package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
  111. package/dist/packages/orchestrator/src/notifications/index.js +74 -0
  112. package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
  113. package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
  114. package/dist/packages/sdk-node/src/orchestrator.js +195 -0
  115. package/dist/packages/shared/config/designConfig.js +495 -0
  116. package/dist/packages/shared/config/env.js +37 -0
  117. package/dist/packages/shared/config/index.js +2 -0
  118. package/dist/packages/shared/design-artifacts/writer.js +221 -0
  119. package/dist/packages/shared/events/serializer.js +84 -0
  120. package/dist/packages/shared/events/types.js +1 -0
  121. package/dist/packages/shared/manifest/artifactUtils.js +36 -0
  122. package/dist/packages/shared/manifest/designArtifacts.js +665 -0
  123. package/dist/packages/shared/manifest/fileIO.js +29 -0
  124. package/dist/packages/shared/manifest/toolRuns.js +78 -0
  125. package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
  126. package/dist/packages/shared/manifest/types.js +5 -0
  127. package/dist/packages/shared/manifest/validator.js +73 -0
  128. package/dist/packages/shared/manifest/writer.js +2 -0
  129. package/dist/packages/shared/streams/stdio.js +112 -0
  130. package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
  131. package/dist/scripts/design/pipeline/componentize.js +74 -0
  132. package/dist/scripts/design/pipeline/context.js +34 -0
  133. package/dist/scripts/design/pipeline/extract.js +249 -0
  134. package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
  135. package/dist/scripts/design/pipeline/prepare.js +46 -0
  136. package/dist/scripts/design/pipeline/reference.js +94 -0
  137. package/dist/scripts/design/pipeline/state.js +206 -0
  138. package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
  139. package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
  140. package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
  141. package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
  142. package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
  143. package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
  144. package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
  145. package/dist/scripts/design/pipeline/visual-regression.js +137 -0
  146. package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
  147. package/package.json +97 -0
  148. package/schemas/manifest.json +1064 -0
  149. package/templates/README.md +12 -0
  150. 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
+ });