aub-workspace 0.3.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 (152) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +23 -0
  3. package/bin/aub-workspace.mjs +246 -0
  4. package/package.json +32 -0
  5. package/vendor/aub/apps/editor/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
  6. package/vendor/aub/apps/editor/dist/assets/angular-importer.lib-dB_jK4mR.js +32 -0
  7. package/vendor/aub/apps/editor/dist/assets/canvas-tools-CuYC7cA2.js +364 -0
  8. package/vendor/aub/apps/editor/dist/assets/design-bridge.lib-DJvaK6AX.js +1 -0
  9. package/vendor/aub/apps/editor/dist/assets/export-agent-prompt.lib-BsP0KNqo.js +2 -0
  10. package/vendor/aub/apps/editor/dist/assets/export-md.lib-DdmdeWgO.js +3 -0
  11. package/vendor/aub/apps/editor/dist/assets/handoff-package.lib-DDYpcEma.js +20 -0
  12. package/vendor/aub/apps/editor/dist/assets/implementation-report.lib-CmsSB_8s.js +1 -0
  13. package/vendor/aub/apps/editor/dist/assets/index-BCH-ek3h.js +2 -0
  14. package/vendor/aub/apps/editor/dist/assets/index-lAnc928Q.css +1 -0
  15. package/vendor/aub/apps/editor/dist/assets/index-vt1nM1M4.js +507 -0
  16. package/vendor/aub/apps/editor/dist/assets/jszip.min-CRfXyL92.js +12 -0
  17. package/vendor/aub/apps/editor/dist/assets/react-vendor-ByX9Pqse.js +40 -0
  18. package/vendor/aub/apps/editor/dist/brand/android-chrome-192x192.png +0 -0
  19. package/vendor/aub/apps/editor/dist/brand/android-chrome-512x512.png +0 -0
  20. package/vendor/aub/apps/editor/dist/brand/app-icon-1024.png +0 -0
  21. package/vendor/aub/apps/editor/dist/brand/app-icon-192.png +0 -0
  22. package/vendor/aub/apps/editor/dist/brand/app-icon-512.png +0 -0
  23. package/vendor/aub/apps/editor/dist/brand/apple-touch-icon.png +0 -0
  24. package/vendor/aub/apps/editor/dist/brand/aub-logo-mark.svg +28 -0
  25. package/vendor/aub/apps/editor/dist/brand/favicon-16x16.png +0 -0
  26. package/vendor/aub/apps/editor/dist/brand/favicon-32x32.png +0 -0
  27. package/vendor/aub/apps/editor/dist/brand/favicon-48x48.png +0 -0
  28. package/vendor/aub/apps/editor/dist/brand/favicon.ico +0 -0
  29. package/vendor/aub/apps/editor/dist/brand/favicon.svg +9 -0
  30. package/vendor/aub/apps/editor/dist/brand/maskable-icon-512.png +0 -0
  31. package/vendor/aub/apps/editor/dist/brand/mstile-150x150.png +0 -0
  32. package/vendor/aub/apps/editor/dist/brand/safari-pinned-tab.svg +8 -0
  33. package/vendor/aub/apps/editor/dist/browserconfig.xml +9 -0
  34. package/vendor/aub/apps/editor/dist/index.html +22 -0
  35. package/vendor/aub/apps/editor/dist/manifest.webmanifest +28 -0
  36. package/vendor/aub/apps/editor/dist/template-previews/admin-table.png +0 -0
  37. package/vendor/aub/apps/editor/dist/template-previews/booking.png +0 -0
  38. package/vendor/aub/apps/editor/dist/template-previews/calendar.png +0 -0
  39. package/vendor/aub/apps/editor/dist/template-previews/catalog.png +0 -0
  40. package/vendor/aub/apps/editor/dist/template-previews/chat.png +0 -0
  41. package/vendor/aub/apps/editor/dist/template-previews/checkout.png +0 -0
  42. package/vendor/aub/apps/editor/dist/template-previews/crm.png +0 -0
  43. package/vendor/aub/apps/editor/dist/template-previews/dashboard.png +0 -0
  44. package/vendor/aub/apps/editor/dist/template-previews/feed.png +0 -0
  45. package/vendor/aub/apps/editor/dist/template-previews/files.png +0 -0
  46. package/vendor/aub/apps/editor/dist/template-previews/kanban.png +0 -0
  47. package/vendor/aub/apps/editor/dist/template-previews/landing.png +0 -0
  48. package/vendor/aub/apps/editor/dist/template-previews/mail.png +0 -0
  49. package/vendor/aub/apps/editor/dist/template-previews/onboarding.png +0 -0
  50. package/vendor/aub/apps/editor/dist/template-previews/pricing.png +0 -0
  51. package/vendor/aub/apps/editor/dist/template-previews/product-detail.png +0 -0
  52. package/vendor/aub/apps/editor/dist/template-previews/settings.png +0 -0
  53. package/vendor/aub/apps/editor/dist/template-previews/wiki.png +0 -0
  54. package/vendor/aub/apps/mcp-server/dist/aub.js +15 -0
  55. package/vendor/aub/apps/mcp-server/dist/context.js +1 -0
  56. package/vendor/aub/apps/mcp-server/dist/http.js +123 -0
  57. package/vendor/aub/apps/mcp-server/dist/index.js +23 -0
  58. package/vendor/aub/apps/mcp-server/dist/repo.js +17 -0
  59. package/vendor/aub/apps/mcp-server/dist/schema.js +42 -0
  60. package/vendor/aub/apps/mcp-server/dist/server.js +80 -0
  61. package/vendor/aub/apps/mcp-server/dist/tools/approve-component-candidate.js +27 -0
  62. package/vendor/aub/apps/mcp-server/dist/tools/diff-blueprints.js +27 -0
  63. package/vendor/aub/apps/mcp-server/dist/tools/export-handoff.js +87 -0
  64. package/vendor/aub/apps/mcp-server/dist/tools/export-prompt.js +35 -0
  65. package/vendor/aub/apps/mcp-server/dist/tools/export-template-authoring-prompt.js +13 -0
  66. package/vendor/aub/apps/mcp-server/dist/tools/generate-template-from-source.js +25 -0
  67. package/vendor/aub/apps/mcp-server/dist/tools/get-aub-session.js +13 -0
  68. package/vendor/aub/apps/mcp-server/dist/tools/get-blueprint.js +28 -0
  69. package/vendor/aub/apps/mcp-server/dist/tools/get-project.js +45 -0
  70. package/vendor/aub/apps/mcp-server/dist/tools/get-workspace-status.js +10 -0
  71. package/vendor/aub/apps/mcp-server/dist/tools/import-design-bridge.js +62 -0
  72. package/vendor/aub/apps/mcp-server/dist/tools/list-blueprints.js +11 -0
  73. package/vendor/aub/apps/mcp-server/dist/tools/list-projects.js +11 -0
  74. package/vendor/aub/apps/mcp-server/dist/tools/lock-blueprint.js +33 -0
  75. package/vendor/aub/apps/mcp-server/dist/tools/migrate-blueprint.js +38 -0
  76. package/vendor/aub/apps/mcp-server/dist/tools/resolve-component.js +51 -0
  77. package/vendor/aub/apps/mcp-server/dist/tools/scaffold-blueprint.js +53 -0
  78. package/vendor/aub/apps/mcp-server/dist/tools/scan-project-ui.js +18 -0
  79. package/vendor/aub/apps/mcp-server/dist/tools/submit-report.js +48 -0
  80. package/vendor/aub/apps/mcp-server/dist/tools/update-aub-session.js +14 -0
  81. package/vendor/aub/apps/mcp-server/dist/tools/validate-blueprint.js +67 -0
  82. package/vendor/aub/apps/mcp-server/dist/tools/validate-project.js +74 -0
  83. package/vendor/aub/apps/mcp-server/dist/tools/write-blueprint.js +72 -0
  84. package/vendor/aub/apps/mcp-server/dist/workspace.js +138 -0
  85. package/vendor/aub/docs/agent-handoff.md +85 -0
  86. package/vendor/aub/docs/agent-handoff.zh-Hant.md +85 -0
  87. package/vendor/aub/docs/template-authoring-agent.md +86 -0
  88. package/vendor/aub/schema/aub-ci.schema.json +34 -0
  89. package/vendor/aub/schema/aub.registry.schema.json +118 -0
  90. package/vendor/aub/schema/design-bridge.schema.json +44 -0
  91. package/vendor/aub/schema/implementation-report.schema.json +93 -0
  92. package/vendor/aub/schema/project-types.ts +72 -0
  93. package/vendor/aub/schema/registry/components.json +118 -0
  94. package/vendor/aub/schema/types.js +13 -0
  95. package/vendor/aub/schema/types.ts +348 -0
  96. package/vendor/aub/schema/ui-blueprint-lock.schema.json +61 -0
  97. package/vendor/aub/schema/ui-blueprint.schema.json +1339 -0
  98. package/vendor/aub/schema/ui-project.schema.json +139 -0
  99. package/vendor/aub/scripts/agent-implementation-benchmark.lib.mjs +125 -0
  100. package/vendor/aub/scripts/angular-importer.lib.mjs +982 -0
  101. package/vendor/aub/scripts/check-editor-bundle-budget.mjs +36 -0
  102. package/vendor/aub/scripts/ci-verify.lib.mjs +256 -0
  103. package/vendor/aub/scripts/ci-verify.mjs +45 -0
  104. package/vendor/aub/scripts/create-authoring-kit.mjs +84 -0
  105. package/vendor/aub/scripts/create-implementation-report.mjs +24 -0
  106. package/vendor/aub/scripts/design-bridge.lib.d.mts +32 -0
  107. package/vendor/aub/scripts/design-bridge.lib.mjs +69 -0
  108. package/vendor/aub/scripts/diff-blueprint.lib.d.mts +18 -0
  109. package/vendor/aub/scripts/diff-blueprint.lib.mjs +148 -0
  110. package/vendor/aub/scripts/diff-blueprint.mjs +25 -0
  111. package/vendor/aub/scripts/export-agent-prompt.lib.d.mts +10 -0
  112. package/vendor/aub/scripts/export-agent-prompt.lib.mjs +160 -0
  113. package/vendor/aub/scripts/export-agent-prompt.mjs +79 -0
  114. package/vendor/aub/scripts/export-md.lib.d.mts +3 -0
  115. package/vendor/aub/scripts/export-md.lib.mjs +302 -0
  116. package/vendor/aub/scripts/export-md.mjs +43 -0
  117. package/vendor/aub/scripts/generate-registry-artifacts.lib.mjs +118 -0
  118. package/vendor/aub/scripts/generate-registry-artifacts.mjs +65 -0
  119. package/vendor/aub/scripts/generate-site-locales.mjs +545 -0
  120. package/vendor/aub/scripts/handoff-package.lib.d.mts +20 -0
  121. package/vendor/aub/scripts/handoff-package.lib.mjs +111 -0
  122. package/vendor/aub/scripts/implementation-report.lib.d.mts +21 -0
  123. package/vendor/aub/scripts/implementation-report.lib.mjs +97 -0
  124. package/vendor/aub/scripts/import-angular-component.mjs +72 -0
  125. package/vendor/aub/scripts/import-design-bridge.mjs +59 -0
  126. package/vendor/aub/scripts/lock-blueprint.lib.d.mts +23 -0
  127. package/vendor/aub/scripts/lock-blueprint.lib.mjs +58 -0
  128. package/vendor/aub/scripts/lock-blueprint.mjs +36 -0
  129. package/vendor/aub/scripts/migrate-blueprint-cli.mjs +28 -0
  130. package/vendor/aub/scripts/migrate-blueprint.d.mts +5 -0
  131. package/vendor/aub/scripts/migrate-blueprint.mjs +95 -0
  132. package/vendor/aub/scripts/package-workspace-cli.mjs +34 -0
  133. package/vendor/aub/scripts/project.lib.d.mts +44 -0
  134. package/vendor/aub/scripts/project.lib.mjs +175 -0
  135. package/vendor/aub/scripts/project.mjs +332 -0
  136. package/vendor/aub/scripts/registry.lib.d.mts +52 -0
  137. package/vendor/aub/scripts/registry.lib.mjs +222 -0
  138. package/vendor/aub/scripts/run-agent-implementation.mjs +423 -0
  139. package/vendor/aub/scripts/run-agent-readability.mjs +145 -0
  140. package/vendor/aub/scripts/run-ollama-prompt.mjs +30 -0
  141. package/vendor/aub/scripts/scaffold-blueprint.lib.d.mts +38 -0
  142. package/vendor/aub/scripts/scaffold-blueprint.lib.mjs +316 -0
  143. package/vendor/aub/scripts/scaffold-blueprint.mjs +86 -0
  144. package/vendor/aub/scripts/score-agent-implementation.mjs +27 -0
  145. package/vendor/aub/scripts/score-agent-readability.mjs +54 -0
  146. package/vendor/aub/scripts/sync-brand-assets.mjs +33 -0
  147. package/vendor/aub/scripts/validate-blueprint.lib.d.mts +14 -0
  148. package/vendor/aub/scripts/validate-blueprint.lib.mjs +136 -0
  149. package/vendor/aub/scripts/validate.mjs +128 -0
  150. package/vendor/aub/scripts/verify-implementation-report.mjs +36 -0
  151. package/vendor/aub/scripts/workspace-loop.lib.d.mts +17 -0
  152. package/vendor/aub/scripts/workspace-loop.lib.mjs +674 -0
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { readdir, stat } from 'node:fs/promises';
3
+ import { join, relative, resolve } from 'node:path';
4
+
5
+ const repoRoot = resolve(import.meta.dirname, '..');
6
+ const assetsDir = resolve(repoRoot, 'apps/editor/dist/assets');
7
+ const maxChunkBytes = Number(process.env.AUB_EDITOR_MAX_CHUNK_BYTES ?? 500 * 1024);
8
+
9
+ const entries = await readdir(assetsDir, { withFileTypes: true });
10
+ const jsChunks = [];
11
+
12
+ for (const entry of entries) {
13
+ if (!entry.isFile() || !entry.name.endsWith('.js')) continue;
14
+ const path = join(assetsDir, entry.name);
15
+ const info = await stat(path);
16
+ jsChunks.push({ path, bytes: info.size });
17
+ }
18
+
19
+ const offenders = jsChunks
20
+ .filter((chunk) => chunk.bytes > maxChunkBytes)
21
+ .sort((a, b) => b.bytes - a.bytes);
22
+
23
+ if (offenders.length > 0) {
24
+ const limit = formatBytes(maxChunkBytes);
25
+ const details = offenders
26
+ .map((chunk) => `- ${relative(repoRoot, chunk.path)}: ${formatBytes(chunk.bytes)} > ${limit}`)
27
+ .join('\n');
28
+ throw new Error(`Editor bundle budget exceeded:\n${details}`);
29
+ }
30
+
31
+ console.log(`Editor bundle budget ok: ${jsChunks.length} JS chunks, max ${formatBytes(maxChunkBytes)}.`);
32
+
33
+ function formatBytes(bytes) {
34
+ if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(2)} MiB`;
35
+ return `${(bytes / 1024).toFixed(1)} KiB`;
36
+ }
@@ -0,0 +1,256 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import Ajv2020 from 'ajv/dist/2020.js';
6
+ import addFormats from 'ajv-formats';
7
+ import yaml from 'js-yaml';
8
+ import { buildKnownTypes } from './registry.lib.mjs';
9
+ import { validateBlueprintSemantics } from './validate-blueprint.lib.mjs';
10
+ import { loadProject, validateProjectSemantics } from './project.lib.mjs';
11
+ import { verifyImplementationReport } from './implementation-report.lib.mjs';
12
+
13
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
14
+
15
+ export async function verifyWorkspace({
16
+ workspace = process.cwd(),
17
+ configPath = '.aub/ci.json',
18
+ requireReports = false,
19
+ } = {}) {
20
+ const root = resolve(workspace);
21
+ const absoluteConfig = resolve(root, configPath);
22
+ const validators = await loadValidators();
23
+ const failures = [];
24
+ const checks = [];
25
+ let config;
26
+
27
+ if (existsSync(absoluteConfig)) {
28
+ config = JSON.parse(await readFile(absoluteConfig, 'utf8'));
29
+ if (!validators.validateConfig(config)) {
30
+ for (const error of formatAjvErrors(validators.validateConfig)) {
31
+ failures.push({ path: relativePath(root, absoluteConfig), message: `CI config: ${error}` });
32
+ }
33
+ return summarize({ root, configPath, checks, failures });
34
+ }
35
+ } else {
36
+ const discovered = await discoverAubFiles(root);
37
+ config = {
38
+ version: '1.0.0',
39
+ blueprints: discovered.blueprints,
40
+ projects: discovered.projects,
41
+ reports: [],
42
+ };
43
+ }
44
+
45
+ const blueprintRefs = config.blueprints ?? [];
46
+ const projectRefs = config.projects ?? [];
47
+ const reportRefs = config.reports ?? [];
48
+ const reportTargets = new Set(reportRefs.map((entry) => normalizeRef(entry.blueprint)));
49
+
50
+ if (blueprintRefs.length === 0 && projectRefs.length === 0) {
51
+ failures.push({ path: configPath, message: 'No Blueprint or project files were configured or discovered.' });
52
+ }
53
+
54
+ for (const ref of blueprintRefs) {
55
+ const result = await verifyBlueprintFile(root, ref, validators);
56
+ checks.push(result);
57
+ failures.push(...result.failures);
58
+ if (requireReports && !reportTargets.has(normalizeRef(ref))) {
59
+ failures.push({ path: ref, message: 'No implementation report is configured for this Blueprint.' });
60
+ }
61
+ }
62
+
63
+ for (const ref of projectRefs) {
64
+ const result = await verifyProjectFile(root, ref, validators);
65
+ checks.push(result);
66
+ failures.push(...result.failures);
67
+ }
68
+
69
+ for (const entry of reportRefs) {
70
+ const result = await verifyReportFile(root, entry, validators);
71
+ checks.push(result);
72
+ failures.push(...result.failures);
73
+ }
74
+
75
+ return summarize({ root, configPath: existsSync(absoluteConfig) ? configPath : null, checks, failures });
76
+ }
77
+
78
+ async function loadValidators() {
79
+ const [blueprint, project, report, config] = await Promise.all([
80
+ readJson(join(ROOT, 'schema', 'ui-blueprint.schema.json')),
81
+ readJson(join(ROOT, 'schema', 'ui-project.schema.json')),
82
+ readJson(join(ROOT, 'schema', 'implementation-report.schema.json')),
83
+ readJson(join(ROOT, 'schema', 'aub-ci.schema.json')),
84
+ ]);
85
+ const ajv = new Ajv2020({ allErrors: true, strict: true });
86
+ addFormats(ajv);
87
+ return {
88
+ validateBlueprint: ajv.compile(blueprint),
89
+ validateProject: ajv.compile(project),
90
+ validateReport: ajv.compile(report),
91
+ validateConfig: ajv.compile(config),
92
+ };
93
+ }
94
+
95
+ async function verifyBlueprintFile(root, ref, validators) {
96
+ const path = resolveRef(root, ref);
97
+ const failures = [];
98
+ try {
99
+ const blueprint = await readDocument(path);
100
+ const schemaOk = validators.validateBlueprint(blueprint);
101
+ if (!schemaOk) {
102
+ for (const error of formatAjvErrors(validators.validateBlueprint)) {
103
+ failures.push({ path: ref, message: `Blueprint schema: ${error}` });
104
+ }
105
+ } else {
106
+ try {
107
+ const { knownTypes } = await buildKnownTypes({ startDir: dirname(path) });
108
+ for (const error of validateBlueprintSemantics(blueprint, { knownTypes })) {
109
+ failures.push({ path: ref, message: `Blueprint semantics: ${error}` });
110
+ }
111
+ } catch (error) {
112
+ failures.push({ path: ref, message: `Component registry: ${error.message}` });
113
+ }
114
+ }
115
+ } catch (error) {
116
+ failures.push({ path: ref, message: error.message });
117
+ }
118
+ return { kind: 'blueprint', path: ref, passed: failures.length === 0, failures };
119
+ }
120
+
121
+ async function verifyProjectFile(root, ref, validators) {
122
+ const path = resolveRef(root, ref);
123
+ const failures = [];
124
+ try {
125
+ const loaded = await loadProject(path);
126
+ if (!validators.validateProject(loaded.project)) {
127
+ for (const error of formatAjvErrors(validators.validateProject)) {
128
+ failures.push({ path: ref, message: `Project schema: ${error}` });
129
+ }
130
+ }
131
+ for (const error of validateProjectSemantics(loaded.project, { screensById: loaded.screensById })) {
132
+ failures.push({ path: ref, message: `Project semantics: ${error}` });
133
+ }
134
+ for (const error of loaded.errors) failures.push({ path: ref, message: error });
135
+ for (const screen of loaded.screens) {
136
+ if (!screen.blueprint) continue;
137
+ const screenRef = relativePath(root, screen.path);
138
+ if (!validators.validateBlueprint(screen.blueprint)) {
139
+ for (const error of formatAjvErrors(validators.validateBlueprint)) {
140
+ failures.push({ path: screenRef, message: `Blueprint schema: ${error}` });
141
+ }
142
+ continue;
143
+ }
144
+ try {
145
+ const { knownTypes } = await buildKnownTypes({ startDir: dirname(screen.path) });
146
+ for (const error of validateBlueprintSemantics(screen.blueprint, { knownTypes })) {
147
+ failures.push({ path: screenRef, message: `Blueprint semantics: ${error}` });
148
+ }
149
+ } catch (error) {
150
+ failures.push({ path: screenRef, message: `Component registry: ${error.message}` });
151
+ }
152
+ }
153
+ } catch (error) {
154
+ failures.push({ path: ref, message: error.message });
155
+ }
156
+ return { kind: 'project', path: ref, passed: failures.length === 0, failures };
157
+ }
158
+
159
+ async function verifyReportFile(root, entry, validators) {
160
+ const failures = [];
161
+ try {
162
+ const [blueprint, report] = await Promise.all([
163
+ readDocument(resolveRef(root, entry.blueprint)),
164
+ readJson(resolveRef(root, entry.report)),
165
+ ]);
166
+ if (!validators.validateReport(report)) {
167
+ for (const error of formatAjvErrors(validators.validateReport)) {
168
+ failures.push({ path: entry.report, message: `Report schema: ${error}` });
169
+ }
170
+ } else {
171
+ const result = verifyImplementationReport(blueprint, report);
172
+ for (const error of result.errors) {
173
+ failures.push({ path: entry.report, message: `Implementation report: ${error}` });
174
+ }
175
+ }
176
+ } catch (error) {
177
+ failures.push({ path: entry.report, message: error.message });
178
+ }
179
+ return {
180
+ kind: 'report',
181
+ path: entry.report,
182
+ blueprint: entry.blueprint,
183
+ passed: failures.length === 0,
184
+ failures,
185
+ };
186
+ }
187
+
188
+ async function discoverAubFiles(root) {
189
+ const blueprints = [];
190
+ const projects = [];
191
+ await walk(root, async (path) => {
192
+ const rel = relativePath(root, path);
193
+ if (/\.aub\.project\.(json|ya?ml)$/i.test(path)) projects.push(rel);
194
+ else if (/\.ui\.(json|ya?ml)$/i.test(path)) blueprints.push(rel);
195
+ });
196
+ return { blueprints: blueprints.sort(), projects: projects.sort() };
197
+ }
198
+
199
+ async function walk(dir, visit) {
200
+ const { readdir } = await import('node:fs/promises');
201
+ const ignored = new Set(['node_modules', 'dist', '.git', '.pnpm-store', '_site']);
202
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
203
+ if (entry.name.startsWith('.') && entry.name !== '.aub') continue;
204
+ const path = join(dir, entry.name);
205
+ if (entry.isDirectory()) {
206
+ if (!ignored.has(entry.name)) await walk(path, visit);
207
+ } else if (entry.isFile()) {
208
+ await visit(path);
209
+ }
210
+ }
211
+ }
212
+
213
+ function summarize({ root, configPath, checks, failures }) {
214
+ return {
215
+ valid: failures.length === 0,
216
+ workspace: root,
217
+ configPath,
218
+ summary: {
219
+ checks: checks.length,
220
+ passed: checks.filter((check) => check.passed).length,
221
+ failed: checks.filter((check) => !check.passed).length,
222
+ failures: failures.length,
223
+ },
224
+ checks,
225
+ failures,
226
+ };
227
+ }
228
+
229
+ function resolveRef(root, ref) {
230
+ return isAbsolute(ref) ? ref : resolve(root, ref);
231
+ }
232
+
233
+ function normalizeRef(ref) {
234
+ return ref.replaceAll('\\', '/').replace(/^\.\//, '');
235
+ }
236
+
237
+ function relativePath(root, path) {
238
+ return relative(root, path).split(sep).join('/');
239
+ }
240
+
241
+ function formatAjvErrors(validate) {
242
+ return (validate.errors ?? []).map((error) => {
243
+ const path = error.instancePath || '(root)';
244
+ return `${path} ${error.message ?? 'invalid'}`;
245
+ });
246
+ }
247
+
248
+ async function readDocument(path) {
249
+ const text = await readFile(path, 'utf8');
250
+ const extension = extname(path).toLowerCase();
251
+ return extension === '.yaml' || extension === '.yml' ? yaml.load(text) : JSON.parse(text);
252
+ }
253
+
254
+ async function readJson(path) {
255
+ return JSON.parse(await readFile(path, 'utf8'));
256
+ }
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { appendFile } from 'node:fs/promises';
4
+ import { verifyWorkspace } from './ci-verify.lib.mjs';
5
+
6
+ const args = process.argv.slice(2);
7
+ const workspace = valueAfter(args, '--workspace') ?? process.cwd();
8
+ const configPath = valueAfter(args, '--config') ?? '.aub/ci.json';
9
+ const requireReports = args.includes('--require-reports');
10
+
11
+ const result = await verifyWorkspace({ workspace, configPath, requireReports });
12
+
13
+ for (const check of result.checks) {
14
+ console.log(`${check.passed ? '✓' : '✗'} ${check.kind}: ${check.path}`);
15
+ }
16
+ for (const failure of result.failures) {
17
+ console.error(` ${failure.path}: ${failure.message}`);
18
+ if (process.env.GITHUB_ACTIONS === 'true') {
19
+ console.error(`::error file=${failure.path}::${escapeAnnotation(failure.message)}`);
20
+ }
21
+ }
22
+ console.log(
23
+ `AUB CI: ${result.summary.passed}/${result.summary.checks} checks passed, ${result.summary.failures} failure(s).`
24
+ );
25
+
26
+ if (process.env.GITHUB_STEP_SUMMARY) {
27
+ const rows = result.checks
28
+ .map((check) => `| ${check.passed ? 'Pass' : 'Fail'} | ${check.kind} | \`${check.path}\` |`)
29
+ .join('\n');
30
+ await appendFile(
31
+ process.env.GITHUB_STEP_SUMMARY,
32
+ `## AUB contract verification\n\n| Status | Check | File |\n|---|---|---|\n${rows || '| Fail | configuration | No checks found |'}\n\n**${result.summary.failures} failure(s)**\n`
33
+ );
34
+ }
35
+
36
+ process.exit(result.valid ? 0 : 1);
37
+
38
+ function valueAfter(argv, flag) {
39
+ const index = argv.indexOf(flag);
40
+ return index >= 0 ? argv[index + 1] : undefined;
41
+ }
42
+
43
+ function escapeAnnotation(value) {
44
+ return value.replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A');
45
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from 'node:crypto';
4
+ import { readFile, writeFile } from 'node:fs/promises';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import JSZip from 'jszip';
8
+ import { migrateBlueprint } from './migrate-blueprint.mjs';
9
+
10
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
11
+ const outputPath = resolve(process.argv[2] || 'aub-authoring-kit.zip');
12
+ const schema = JSON.parse(await readFile(resolve(root, 'schema/ui-blueprint.schema.json'), 'utf8'));
13
+ const registry = JSON.parse(await readFile(resolve(root, 'schema/registry/components.json'), 'utf8'));
14
+ const example = migrateBlueprint(JSON.parse(await readFile(resolve(root, 'examples/dashboard.ui.json'), 'utf8')));
15
+ const files = {
16
+ 'AUTHORING.md': authoringGuide(),
17
+ 'ui-blueprint.schema.json': `${JSON.stringify(schema, null, 2)}\n`,
18
+ 'components.json': `${JSON.stringify(registry, null, 2)}\n`,
19
+ 'examples/canonical.ui.json': `${JSON.stringify(example, null, 2)}\n`,
20
+ 'prompts/author.md': authorPrompt(),
21
+ 'VALIDATE.md': validationGuide(),
22
+ };
23
+ const zip = new JSZip();
24
+ const manifestFiles = {};
25
+ for (const [path, content] of Object.entries(files)) {
26
+ const bytes = Buffer.from(content);
27
+ manifestFiles[path] = { sha256: createHash('sha256').update(bytes).digest('hex'), bytes: bytes.byteLength };
28
+ zip.file(path, content);
29
+ }
30
+ zip.file('manifest.json', `${JSON.stringify({
31
+ format: 'aub-authoring-kit',
32
+ format_version: '1.0.0',
33
+ blueprint_version: schema.properties.version.enum.at(-1),
34
+ files: manifestFiles,
35
+ }, null, 2)}\n`);
36
+ await writeFile(outputPath, await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' }));
37
+ console.error(`✓ wrote ${outputPath}`);
38
+
39
+ export function authoringGuide() {
40
+ return `# AUB Blueprint Authoring
41
+
42
+ Produce a single JSON document that validates against \`ui-blueprint.schema.json\`.
43
+
44
+ ## Rules
45
+
46
+ 1. Use only component types declared in \`components.json\`.
47
+ 2. Create exactly one root node with \`parent_id: null\`.
48
+ 3. Keep every \`parent_id\` and \`children\` relationship bidirectionally consistent.
49
+ 4. Read \`isContainer\` from \`components.json\`. Types with \`isContainer: false\` are leaves and MUST use \`children: []\`; represent labels and icons through \`content\`, not child nodes.
50
+ 5. Use \`layout.mode: auto\` for flex/grid flow and \`freeform\` only when exact placements are known.
51
+ 6. Size units are limited to \`px\`, \`%\`, \`rem\`, and \`vw\`. Never use \`fr\` in a size object; use \`grid_columns\` for fractional tracks.
52
+ 7. Declare interactions, responsive behavior, and at least five acceptance criteria.
53
+ 8. Include layout, interaction, responsive, and accessibility acceptance categories.
54
+ 9. Never invent uncertain product behavior. Put unresolved decisions in \`screen.notes\`.
55
+ 10. Before returning, verify leaf children, allowed units, unique ids, references, and acceptance coverage.
56
+ 11. Return JSON only when the caller requests a machine-readable Blueprint.
57
+ `;
58
+ }
59
+
60
+ export function authorPrompt() {
61
+ return `Create an AUB UI Blueprint from the supplied UI requirements.
62
+
63
+ Read AUTHORING.md, components.json, the JSON Schema, and the canonical example first.
64
+ Return a complete JSON object. Do not use unregistered component types.
65
+ Do not omit parent/children links, responsive rules, interactions, or acceptance criteria.
66
+ Use children only on registry types where isContainer is true. Every leaf must have children: [].
67
+ Use only px, %, rem, or vw size units; express fractional grids with grid_columns.
68
+ When source material is ambiguous, preserve the ambiguity in screen.notes instead of guessing.
69
+ Perform a final schema checklist before returning, then validate with the documented command.
70
+ `;
71
+ }
72
+
73
+ export function validationGuide() {
74
+ return `# Validation
75
+
76
+ From the AUB repository:
77
+
78
+ \`\`\`bash
79
+ pnpm validate path/to/screen.ui.json
80
+ \`\`\`
81
+
82
+ The command must exit with status 0. Fix schema and semantic errors before handoff.
83
+ `;
84
+ }
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { extname, resolve } from 'node:path';
5
+ import yaml from 'js-yaml';
6
+ import { createImplementationReportTemplate } from './implementation-report.lib.mjs';
7
+
8
+ const [inputPath, outputPath] = process.argv.slice(2);
9
+ if (!inputPath) {
10
+ console.error('Usage: node scripts/create-implementation-report.mjs <blueprint.ui.json|yaml> [report.json]');
11
+ process.exit(2);
12
+ }
13
+
14
+ const text = await readFile(resolve(inputPath), 'utf8');
15
+ const extension = extname(inputPath).toLowerCase();
16
+ const blueprint = extension === '.yaml' || extension === '.yml' ? yaml.load(text) : JSON.parse(text);
17
+ const output = `${JSON.stringify(createImplementationReportTemplate(blueprint), null, 2)}\n`;
18
+
19
+ if (outputPath && outputPath !== '-') {
20
+ await writeFile(resolve(outputPath), output, 'utf8');
21
+ console.error(`✓ wrote ${outputPath}`);
22
+ } else {
23
+ process.stdout.write(output);
24
+ }
@@ -0,0 +1,32 @@
1
+ import type { Blueprint } from '../schema/types.js';
2
+
3
+ export interface DesignBridgeSource {
4
+ kind: 'figma' | 'penpot';
5
+ document_id: string;
6
+ page_id?: string;
7
+ frame_id: string;
8
+ url?: string;
9
+ exported_at?: string;
10
+ }
11
+
12
+ export interface DesignBridgeDocument {
13
+ format: 'aub-design-bridge';
14
+ version: '1.0.0';
15
+ source: DesignBridgeSource;
16
+ blueprint: Blueprint;
17
+ node_map: Record<
18
+ string,
19
+ {
20
+ source_id: string;
21
+ source_name?: string;
22
+ component_key?: string;
23
+ }
24
+ >;
25
+ }
26
+
27
+ export const DESIGN_BRIDGE_VERSION: '1.0.0';
28
+ export function importDesignBridge(input: DesignBridgeDocument): {
29
+ blueprint: Blueprint;
30
+ source: DesignBridgeSource;
31
+ sourceMap: DesignBridgeDocument['node_map'];
32
+ };
@@ -0,0 +1,69 @@
1
+ export const DESIGN_BRIDGE_VERSION = '1.0.0';
2
+
3
+ export function importDesignBridge(input) {
4
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
5
+ throw new Error('Design Bridge input must be an object.');
6
+ }
7
+ if (input.format !== 'aub-design-bridge' || input.version !== DESIGN_BRIDGE_VERSION) {
8
+ throw new Error(`Design Bridge must declare format "aub-design-bridge" and version "${DESIGN_BRIDGE_VERSION}".`);
9
+ }
10
+ if (!['figma', 'penpot'].includes(input.source?.kind)) {
11
+ throw new Error('Design Bridge source.kind must be "figma" or "penpot".');
12
+ }
13
+ if (!input.blueprint || typeof input.blueprint !== 'object' || Array.isArray(input.blueprint)) {
14
+ throw new Error('Design Bridge must contain a complete blueprint object.');
15
+ }
16
+ if (!input.node_map || typeof input.node_map !== 'object' || Array.isArray(input.node_map)) {
17
+ throw new Error('Design Bridge must contain a node_map object.');
18
+ }
19
+
20
+ const blueprint = structuredClone(input.blueprint);
21
+ const nodes = Array.isArray(blueprint.nodes) ? blueprint.nodes : [];
22
+ const nodeIds = new Set(nodes.map((node) => node?.id).filter(Boolean));
23
+ const mappedIds = new Set(Object.keys(input.node_map));
24
+ const missing = [...nodeIds].filter((id) => !mappedIds.has(id));
25
+ const unknown = [...mappedIds].filter((id) => !nodeIds.has(id));
26
+ if (missing.length > 0 || unknown.length > 0) {
27
+ const details = [
28
+ missing.length > 0 ? `missing node mappings: ${missing.join(', ')}` : '',
29
+ unknown.length > 0 ? `unknown mapped nodes: ${unknown.join(', ')}` : '',
30
+ ].filter(Boolean);
31
+ throw new Error(`Design Bridge node_map must exactly cover blueprint nodes (${details.join('; ')}).`);
32
+ }
33
+
34
+ const sourceFile =
35
+ input.source.url ??
36
+ `${input.source.kind}://${input.source.document_id}/${input.source.page_id ?? 'page'}/${input.source.frame_id}`;
37
+ const sourceMap = {};
38
+ blueprint.nodes = nodes.map((node) => {
39
+ const mapping = input.node_map[node.id];
40
+ if (!mapping?.source_id) {
41
+ throw new Error(`Design Bridge node_map.${node.id}.source_id is required.`);
42
+ }
43
+ sourceMap[node.id] = {
44
+ source_id: mapping.source_id,
45
+ ...(mapping.source_name ? { source_name: mapping.source_name } : {}),
46
+ ...(mapping.component_key ? { component_key: mapping.component_key } : {}),
47
+ };
48
+ return {
49
+ ...node,
50
+ source: {
51
+ file: sourceFile,
52
+ selector: mapping.source_id,
53
+ },
54
+ };
55
+ });
56
+ blueprint.provenance = {
57
+ source_kind: input.source.kind,
58
+ framework: input.source.kind === 'figma' ? 'Figma' : 'Penpot',
59
+ importer_version: DESIGN_BRIDGE_VERSION,
60
+ entry_file: input.source.frame_id,
61
+ source_files: [sourceFile],
62
+ };
63
+
64
+ return {
65
+ blueprint,
66
+ source: structuredClone(input.source),
67
+ sourceMap,
68
+ };
69
+ }
@@ -0,0 +1,18 @@
1
+ export interface BlueprintDiff {
2
+ before: { version: string; screen_id: string; screen_name: string };
3
+ after: { version: string; screen_id: string; screen_name: string };
4
+ summary: Record<string, number>;
5
+ screen_changes: string[];
6
+ nodes: Record<string, any[]>;
7
+ interactions: Record<string, any[]>;
8
+ responsive: Record<string, any[]>;
9
+ acceptance: Record<string, any[]>;
10
+ viewports: Record<string, any[]>;
11
+ design_system_changes: string[];
12
+ }
13
+
14
+ export function diffBlueprints(
15
+ before: Record<string, any>,
16
+ after: Record<string, any>
17
+ ): BlueprintDiff;
18
+ export function renderBlueprintDiff(diff: BlueprintDiff): string;