@design-ai/cli 4.55.0 → 4.56.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 (52) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +39 -0
  3. package/README.ko.md +7 -7
  4. package/README.md +9 -9
  5. package/cli/lib/mcp-server.mjs +208 -10
  6. package/cli/lib/site-analysis.mjs +297 -0
  7. package/cli/lib/site-args.mjs +433 -0
  8. package/cli/lib/site-bundle-build.mjs +127 -0
  9. package/cli/lib/site-bundle-check.mjs +454 -0
  10. package/cli/lib/site-bundle-commands.mjs +95 -0
  11. package/cli/lib/site-bundle-compare.mjs +157 -0
  12. package/cli/lib/site-bundle-contract.mjs +79 -0
  13. package/cli/lib/site-bundle-files.mjs +87 -0
  14. package/cli/lib/site-bundle-handoff-runbook-actions.mjs +113 -0
  15. package/cli/lib/site-bundle-handoff-runbook-evidence-fields.mjs +164 -0
  16. package/cli/lib/site-bundle-handoff-runbook-evidence.mjs +334 -0
  17. package/cli/lib/site-bundle-handoff-runbook-format.mjs +31 -0
  18. package/cli/lib/site-bundle-handoff-runbook-stage-metadata.mjs +84 -0
  19. package/cli/lib/site-bundle-handoff-runbook.mjs +1331 -0
  20. package/cli/lib/site-bundle-handoff-summary.mjs +183 -0
  21. package/cli/lib/site-bundle-handoff.mjs +271 -0
  22. package/cli/lib/site-bundle-readme.mjs +98 -0
  23. package/cli/lib/site-bundle-repair-report.mjs +143 -0
  24. package/cli/lib/site-bundle-repair.mjs +68 -0
  25. package/cli/lib/site-content.mjs +399 -0
  26. package/cli/lib/site-evidence.mjs +35 -0
  27. package/cli/lib/site-mcp-commands.mjs +28 -0
  28. package/cli/lib/site-mcp-probes.mjs +159 -0
  29. package/cli/lib/site-mcp-readiness.mjs +157 -0
  30. package/cli/lib/site-mcp-report.mjs +324 -0
  31. package/cli/lib/site-next-actions.mjs +333 -0
  32. package/cli/lib/site-options.mjs +104 -0
  33. package/cli/lib/site-prompts.mjs +332 -0
  34. package/cli/lib/site-starter.mjs +153 -0
  35. package/cli/lib/site-strings.mjs +23 -0
  36. package/cli/lib/site-tasks.mjs +93 -0
  37. package/cli/lib/site-workflow-graph.mjs +309 -0
  38. package/cli/lib/site-workspace.mjs +492 -0
  39. package/cli/lib/site.mjs +108 -6617
  40. package/docs/DISTRIBUTION.ko.md +35 -6
  41. package/docs/DISTRIBUTION.md +35 -8
  42. package/docs/RELEASE-CHECKLIST.md +20 -3
  43. package/docs/ROADMAP.md +2179 -0
  44. package/docs/external-status.md +22 -7
  45. package/docs/integrations/design-ai-mcp-server.md +32 -0
  46. package/docs/integrations/vscode-walkthrough.ko.md +3 -3
  47. package/docs/integrations/vscode-walkthrough.md +3 -3
  48. package/docs/site-overrides/main.html +1 -1
  49. package/package.json +1 -1
  50. package/tools/audit/package-smoke.py +106 -0
  51. package/tools/audit/registry-smoke.py +378 -10
  52. package/tools/audit/smoke_assertions.py +83 -22
@@ -0,0 +1,433 @@
1
+ // CLI argument parsing for `design-ai site`.
2
+
3
+ import { parseOutputFlags } from "./output.mjs";
4
+ import { unknownOptionMessage } from "./suggest.mjs";
5
+ import { SITE_PROMPT_TEMPLATE_IDS } from "./site-content.mjs";
6
+ import {
7
+ CMS_OPTIONS,
8
+ DATABASE_OPTIONS,
9
+ DEPLOY_OPTIONS,
10
+ SITE_INTAKE_TEMPLATE_LANGUAGE_OPTIONS,
11
+ VIEWPORT_OPTIONS,
12
+ } from "./site-options.mjs";
13
+
14
+ export const SITE_OPTIONS = [
15
+ "-h",
16
+ "--help",
17
+ "--json",
18
+ "--stdin",
19
+ "--init",
20
+ "--name",
21
+ "--live-url",
22
+ "--repo-url",
23
+ "--local-path",
24
+ "--figma-url",
25
+ "--brand-notes",
26
+ "--deploy",
27
+ "--sentry",
28
+ "--cms",
29
+ "--database",
30
+ "--page",
31
+ "--flow",
32
+ "--viewport",
33
+ "--from-intake",
34
+ "--language",
35
+ "--intake-template",
36
+ "--sample",
37
+ "--tasks",
38
+ "--bundle",
39
+ "--bundle-check",
40
+ "--bundle-compare",
41
+ "--bundle-handoff",
42
+ "--bundle-repair",
43
+ "--next-actions",
44
+ "--prompt-list",
45
+ "--mcp-check",
46
+ "--mcp-plan",
47
+ "--graph",
48
+ "--probes",
49
+ "--prompt",
50
+ "--task",
51
+ "--strict",
52
+ "--report",
53
+ "--prompts",
54
+ "--out",
55
+ "--output",
56
+ "--force",
57
+ "--yes",
58
+ ];
59
+
60
+ function readOptionValue(args, index, flag) {
61
+ const value = args[index + 1];
62
+ if (!value || value.startsWith("--")) {
63
+ throw new Error(`${flag} requires a value`);
64
+ }
65
+ return value;
66
+ }
67
+
68
+ export function parseSiteArgs(args) {
69
+ const out = {
70
+ target: "",
71
+ stdin: false,
72
+ init: false,
73
+ initProfile: {
74
+ name: "",
75
+ liveUrl: "",
76
+ repoUrl: "",
77
+ localPath: "",
78
+ figmaUrl: "",
79
+ brandNotes: "",
80
+ deployProvider: "none",
81
+ sentryProject: "",
82
+ cms: "none",
83
+ database: "none",
84
+ pages: [],
85
+ userFlows: [],
86
+ viewports: [],
87
+ },
88
+ fromIntake: false,
89
+ fromIntakePath: "",
90
+ intakeTemplate: false,
91
+ language: "en",
92
+ languageProvided: false,
93
+ sample: false,
94
+ tasks: false,
95
+ bundle: false,
96
+ bundleCheck: false,
97
+ bundleCompareTarget: "",
98
+ bundleHandoff: false,
99
+ bundleRepair: false,
100
+ nextActions: false,
101
+ promptList: false,
102
+ mcpCheck: false,
103
+ mcpPlan: false,
104
+ graph: false,
105
+ probes: false,
106
+ promptTemplate: "",
107
+ taskSelector: "",
108
+ json: false,
109
+ strict: false,
110
+ report: false,
111
+ prompts: false,
112
+ outPath: "",
113
+ force: false,
114
+ yes: false,
115
+ help: false,
116
+ };
117
+
118
+ for (let i = 0; i < args.length; i += 1) {
119
+ const arg = args[i];
120
+ out.index = i;
121
+
122
+ if (arg === "-h" || arg === "--help") {
123
+ out.help = true;
124
+ } else if (arg === "--json") {
125
+ out.json = true;
126
+ } else if (arg === "--stdin") {
127
+ out.stdin = true;
128
+ } else if (arg === "--init") {
129
+ out.init = true;
130
+ } else if (arg === "--name") {
131
+ out.initProfile.name = readOptionValue(args, i, "--name");
132
+ i += 1;
133
+ } else if (arg === "--live-url") {
134
+ out.initProfile.liveUrl = readOptionValue(args, i, "--live-url");
135
+ i += 1;
136
+ } else if (arg === "--repo-url") {
137
+ out.initProfile.repoUrl = readOptionValue(args, i, "--repo-url");
138
+ i += 1;
139
+ } else if (arg === "--local-path") {
140
+ out.initProfile.localPath = readOptionValue(args, i, "--local-path");
141
+ i += 1;
142
+ } else if (arg === "--figma-url") {
143
+ out.initProfile.figmaUrl = readOptionValue(args, i, "--figma-url");
144
+ i += 1;
145
+ } else if (arg === "--brand-notes") {
146
+ out.initProfile.brandNotes = readOptionValue(args, i, "--brand-notes");
147
+ i += 1;
148
+ } else if (arg === "--deploy") {
149
+ const value = readOptionValue(args, i, "--deploy");
150
+ if (!DEPLOY_OPTIONS.includes(value)) {
151
+ throw new Error(`--deploy must be one of: ${DEPLOY_OPTIONS.join(", ")}`);
152
+ }
153
+ out.initProfile.deployProvider = value;
154
+ i += 1;
155
+ } else if (arg === "--sentry") {
156
+ out.initProfile.sentryProject = readOptionValue(args, i, "--sentry");
157
+ i += 1;
158
+ } else if (arg === "--cms") {
159
+ const value = readOptionValue(args, i, "--cms");
160
+ if (!CMS_OPTIONS.includes(value)) {
161
+ throw new Error(`--cms must be one of: ${CMS_OPTIONS.join(", ")}`);
162
+ }
163
+ out.initProfile.cms = value;
164
+ i += 1;
165
+ } else if (arg === "--database") {
166
+ const value = readOptionValue(args, i, "--database");
167
+ if (!DATABASE_OPTIONS.includes(value)) {
168
+ throw new Error(`--database must be one of: ${DATABASE_OPTIONS.join(", ")}`);
169
+ }
170
+ out.initProfile.database = value;
171
+ i += 1;
172
+ } else if (arg === "--page") {
173
+ out.initProfile.pages.push(readOptionValue(args, i, "--page"));
174
+ i += 1;
175
+ } else if (arg === "--flow") {
176
+ out.initProfile.userFlows.push(readOptionValue(args, i, "--flow"));
177
+ i += 1;
178
+ } else if (arg === "--viewport") {
179
+ const value = readOptionValue(args, i, "--viewport");
180
+ if (!VIEWPORT_OPTIONS.includes(value)) {
181
+ throw new Error(`--viewport must be one of: ${VIEWPORT_OPTIONS.join(", ")}`);
182
+ }
183
+ out.initProfile.viewports.push(value);
184
+ i += 1;
185
+ } else if (arg === "--from-intake") {
186
+ out.fromIntake = true;
187
+ const value = args[i + 1];
188
+ if (value && !value.startsWith("--")) {
189
+ out.fromIntakePath = value;
190
+ i += 1;
191
+ }
192
+ } else if (arg === "--language") {
193
+ const value = readOptionValue(args, i, "--language");
194
+ if (!SITE_INTAKE_TEMPLATE_LANGUAGE_OPTIONS.includes(value)) {
195
+ throw new Error(`--language must be one of: ${SITE_INTAKE_TEMPLATE_LANGUAGE_OPTIONS.join(", ")}`);
196
+ }
197
+ out.language = value;
198
+ out.languageProvided = true;
199
+ i += 1;
200
+ } else if (arg === "--intake-template") {
201
+ out.intakeTemplate = true;
202
+ } else if (arg === "--sample") {
203
+ out.sample = true;
204
+ } else if (arg === "--tasks") {
205
+ out.tasks = true;
206
+ } else if (arg === "--bundle") {
207
+ out.bundle = true;
208
+ } else if (arg === "--bundle-check") {
209
+ out.bundleCheck = true;
210
+ } else if (arg === "--bundle-compare") {
211
+ const value = args[i + 1];
212
+ if (!value || value.startsWith("--")) {
213
+ throw new Error("--bundle-compare requires a second handoff bundle directory path");
214
+ }
215
+ out.bundleCompareTarget = value;
216
+ i += 1;
217
+ } else if (arg === "--bundle-handoff") {
218
+ out.bundleHandoff = true;
219
+ } else if (arg === "--bundle-repair") {
220
+ out.bundleRepair = true;
221
+ } else if (arg === "--next-actions") {
222
+ out.nextActions = true;
223
+ } else if (arg === "--prompt-list") {
224
+ out.promptList = true;
225
+ } else if (arg === "--mcp-check") {
226
+ out.mcpCheck = true;
227
+ } else if (arg === "--mcp-plan") {
228
+ out.mcpPlan = true;
229
+ } else if (arg === "--graph") {
230
+ out.graph = true;
231
+ } else if (arg === "--probes") {
232
+ out.probes = true;
233
+ } else if (arg === "--prompt") {
234
+ const value = args[i + 1];
235
+ if (!value || value.startsWith("--")) {
236
+ throw new Error("--prompt requires a template id");
237
+ }
238
+ if (!SITE_PROMPT_TEMPLATE_IDS.includes(value)) {
239
+ throw new Error(`--prompt must be one of: ${SITE_PROMPT_TEMPLATE_IDS.join(", ")}`);
240
+ }
241
+ out.promptTemplate = value;
242
+ i += 1;
243
+ } else if (arg === "--task") {
244
+ const value = args[i + 1];
245
+ if (!value || value.startsWith("--")) {
246
+ throw new Error("--task requires a refactor task id or 1-based task number");
247
+ }
248
+ out.taskSelector = value;
249
+ i += 1;
250
+ } else if (arg === "--strict") {
251
+ out.strict = true;
252
+ } else if (arg === "--report") {
253
+ out.report = true;
254
+ } else if (arg === "--prompts") {
255
+ out.prompts = true;
256
+ } else if (arg === "--yes") {
257
+ out.yes = true;
258
+ } else if (parseOutputFlags(args, out)) {
259
+ i = out.index;
260
+ } else if (arg.startsWith("--")) {
261
+ throw new Error(unknownOptionMessage("site", arg, SITE_OPTIONS));
262
+ } else if (!out.target) {
263
+ out.target = arg;
264
+ } else {
265
+ throw new Error(`Unexpected argument: ${arg}`);
266
+ }
267
+ }
268
+
269
+ const sources = [out.target ? "file path" : "", out.stdin ? "--stdin" : ""].filter(Boolean);
270
+ if (sources.length > 1) {
271
+ throw new Error("Use either a workspace JSON file path or --stdin, not both");
272
+ }
273
+ const hasInitProfileFields = Boolean(
274
+ out.initProfile.name
275
+ || out.initProfile.liveUrl
276
+ || out.initProfile.repoUrl
277
+ || out.initProfile.localPath
278
+ || out.initProfile.figmaUrl
279
+ || out.initProfile.brandNotes
280
+ || out.initProfile.sentryProject
281
+ || out.initProfile.deployProvider !== "none"
282
+ || out.initProfile.cms !== "none"
283
+ || out.initProfile.database !== "none"
284
+ || out.initProfile.pages.length > 0
285
+ || out.initProfile.userFlows.length > 0
286
+ || out.initProfile.viewports.length > 0,
287
+ );
288
+ if (hasInitProfileFields && !out.init) {
289
+ throw new Error("Use --name, --live-url, --repo-url, --local-path, --figma-url, --brand-notes, --deploy, --sentry, --cms, --database, --page, --flow, or --viewport only with --init");
290
+ }
291
+ if (out.init && sources.length > 0) {
292
+ throw new Error("Use --init without a workspace JSON file path or --stdin");
293
+ }
294
+ if (out.fromIntake && out.target) {
295
+ throw new Error("Use --from-intake without a workspace JSON file path");
296
+ }
297
+ if (out.fromIntake && out.stdin && out.fromIntakePath) {
298
+ throw new Error("Use --from-intake with either a file path or --stdin, not both");
299
+ }
300
+ if (out.fromIntake && !out.fromIntakePath && !out.stdin) {
301
+ throw new Error("--from-intake requires a file path or --stdin");
302
+ }
303
+ if (out.fromIntake && (out.init || hasInitProfileFields)) {
304
+ throw new Error("Use --from-intake without --init or init profile fields");
305
+ }
306
+ if (out.intakeTemplate && (sources.length > 0 || out.init || hasInitProfileFields)) {
307
+ throw new Error("Use --intake-template without a workspace JSON file path, --stdin, --init, or init profile fields");
308
+ }
309
+ if (out.languageProvided && !out.intakeTemplate) {
310
+ throw new Error("Use --language only with --intake-template");
311
+ }
312
+ if (out.intakeTemplate && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.nextActions || out.promptList || out.mcpCheck || out.mcpPlan || out.graph || out.probes || out.report || out.prompts || out.promptTemplate || out.strict || out.yes)) {
313
+ throw new Error("Use --intake-template only with --language, --json, --out, or --force");
314
+ }
315
+ if (out.init && !out.initProfile.name.trim()) {
316
+ throw new Error("--init requires --name");
317
+ }
318
+ if (out.init && !out.initProfile.liveUrl.trim()) {
319
+ throw new Error("--init requires --live-url");
320
+ }
321
+ if (out.init && (out.sample || out.tasks || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.promptList || out.mcpCheck || out.mcpPlan || out.graph || out.report || out.prompts || out.promptTemplate)) {
322
+ throw new Error("Use --init without --sample, --tasks, --bundle-check, --bundle-compare, --bundle-handoff, --bundle-repair, --prompt-list, --mcp-check, --mcp-plan, --graph, --report, --prompts, or --prompt");
323
+ }
324
+ if (out.init && out.strict && !(out.nextActions || out.bundle)) {
325
+ throw new Error("Use --init --strict only with --next-actions or --bundle");
326
+ }
327
+ if (out.fromIntake && (out.intakeTemplate || out.sample || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.promptList || out.mcpCheck || out.mcpPlan || out.graph || out.report || out.prompts || out.promptTemplate || out.yes)) {
328
+ throw new Error("Use --from-intake only with --json, --next-actions, --tasks, --bundle, --out, --strict, or --force");
329
+ }
330
+ if (out.fromIntake && out.strict && !(out.nextActions || out.tasks || out.bundle)) {
331
+ throw new Error("Use --from-intake --strict only with --next-actions, --tasks, or --bundle");
332
+ }
333
+ if (out.sample && sources.length > 0) {
334
+ throw new Error("Use --sample without a workspace JSON file path or --stdin");
335
+ }
336
+ if (out.promptList && sources.length > 0) {
337
+ throw new Error("Use --prompt-list without a workspace JSON file path or --stdin");
338
+ }
339
+ if (out.sample && (out.report || out.prompts || out.promptTemplate || out.nextActions || out.graph)) {
340
+ throw new Error("Use --sample without --report, --prompts, --prompt, --next-actions, or --graph");
341
+ }
342
+ if (out.promptList && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.nextActions || out.mcpCheck || out.mcpPlan || out.graph || out.report || out.prompts || out.promptTemplate || out.strict)) {
343
+ throw new Error("Use --prompt-list without --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, --bundle-repair, --next-actions, --mcp-check, --mcp-plan, --graph, --report, --prompts, --prompt, or --strict");
344
+ }
345
+ if (out.mcpCheck && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate)) {
346
+ throw new Error("Use --mcp-check without --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, --bundle-repair, --next-actions, --graph, --report, --prompts, or --prompt");
347
+ }
348
+ if (out.mcpPlan && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate)) {
349
+ throw new Error("Use --mcp-plan without --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, --bundle-repair, --next-actions, --graph, --report, --prompts, or --prompt");
350
+ }
351
+ if (out.probes && !(out.mcpCheck || out.mcpPlan)) {
352
+ throw new Error("Use --probes only with --mcp-check or --mcp-plan");
353
+ }
354
+ if (out.bundle && (out.sample || (out.tasks && !out.fromIntake) || out.graph || out.report || out.prompts || out.promptTemplate)) {
355
+ throw new Error("Use --bundle without --sample, --tasks, --graph, --report, --prompts, or --prompt");
356
+ }
357
+ if (out.bundleCheck && out.stdin) {
358
+ throw new Error("Use --bundle-check with a handoff bundle directory path, not --stdin");
359
+ }
360
+ if (out.bundleCheck && (out.sample || out.tasks || out.bundle || out.bundleHandoff || out.bundleRepair || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate || out.promptList || out.mcpCheck || out.mcpPlan)) {
361
+ throw new Error("Use --bundle-check without --sample, --tasks, --bundle, --bundle-handoff, --bundle-repair, --next-actions, --graph, --report, --prompts, --prompt, --prompt-list, --mcp-check, or --mcp-plan");
362
+ }
363
+ if (out.bundleCompareTarget && out.stdin) {
364
+ throw new Error("Use --bundle-compare with handoff bundle directory paths, not --stdin");
365
+ }
366
+ if (out.bundleCompareTarget && !out.target) {
367
+ throw new Error("--bundle-compare requires a primary handoff bundle directory path");
368
+ }
369
+ if (out.bundleCompareTarget && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleHandoff || out.bundleRepair || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate || out.promptList || out.mcpCheck || out.mcpPlan)) {
370
+ throw new Error("Use --bundle-compare without --sample, --tasks, --bundle, --bundle-check, --bundle-handoff, --bundle-repair, --next-actions, --graph, --report, --prompts, --prompt, --prompt-list, --mcp-check, or --mcp-plan");
371
+ }
372
+ if (out.bundleHandoff && out.stdin) {
373
+ throw new Error("Use --bundle-handoff with a handoff bundle directory path, not --stdin");
374
+ }
375
+ if (out.bundleHandoff && !out.target) {
376
+ throw new Error("--bundle-handoff requires a handoff bundle directory path");
377
+ }
378
+ if (out.bundleHandoff && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleRepair || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate || out.promptList || out.mcpCheck || out.mcpPlan)) {
379
+ throw new Error("Use --bundle-handoff without --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-repair, --next-actions, --graph, --report, --prompts, --prompt, --prompt-list, --mcp-check, or --mcp-plan");
380
+ }
381
+ if (out.bundleRepair && out.stdin) {
382
+ throw new Error("Use --bundle-repair with a handoff bundle directory path, not --stdin");
383
+ }
384
+ if (out.bundleRepair && !out.target) {
385
+ throw new Error("--bundle-repair requires a handoff bundle directory path");
386
+ }
387
+ if (out.bundleRepair && (out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.nextActions || out.graph || out.report || out.prompts || out.promptTemplate || out.promptList || out.mcpCheck || out.mcpPlan)) {
388
+ throw new Error("Use --bundle-repair without --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, --next-actions, --graph, --report, --prompts, --prompt, --prompt-list, --mcp-check, or --mcp-plan");
389
+ }
390
+ if (out.yes && !out.bundleRepair) {
391
+ throw new Error("Use --yes only with --bundle-repair");
392
+ }
393
+ const initBundleMode = out.init && out.bundle;
394
+ const fromIntakeBundleMode = out.fromIntake && out.bundle;
395
+ const fromIntakeTasksMode = out.fromIntake && out.tasks;
396
+ if (!initBundleMode && !fromIntakeBundleMode && !fromIntakeTasksMode && [out.init, out.fromIntake, out.intakeTemplate, out.sample, out.tasks, out.bundle].filter(Boolean).length > 1) {
397
+ throw new Error("Use only one generated workspace mode: --init, --from-intake, --intake-template, --sample, --tasks, or --bundle");
398
+ }
399
+ if (out.sample && out.strict) {
400
+ throw new Error("Use --sample without --strict; validate the generated file in a separate command");
401
+ }
402
+ if (out.taskSelector && !out.promptTemplate && !out.bundleHandoff) {
403
+ throw new Error("Use --task only with --prompt or --bundle-handoff");
404
+ }
405
+ if (out.taskSelector && out.promptTemplate && out.promptTemplate !== "codex-implementation") {
406
+ throw new Error("Use --task only with --prompt codex-implementation");
407
+ }
408
+ if (out.tasks && (out.json || out.nextActions || out.graph || out.report || out.prompts)) {
409
+ throw new Error("Use --tasks without --json, --next-actions, --graph, --report, or --prompts; validate the generated file in a separate command");
410
+ }
411
+ if (out.tasks && out.promptTemplate) {
412
+ throw new Error("Use --tasks without --prompt; generate tasks in a separate command first");
413
+ }
414
+ if (out.bundle && out.json) {
415
+ throw new Error("--json is not supported with --bundle; use --bundle --out dir for bundle artifacts");
416
+ }
417
+ if (out.bundle && !out.outPath) {
418
+ throw new Error("--bundle requires --out directory");
419
+ }
420
+ const outputModes = [out.report ? "--report" : "", out.prompts ? "--prompts" : "", out.promptTemplate ? "--prompt" : "", out.nextActions ? "--next-actions" : "", out.mcpCheck ? "--mcp-check" : "", out.mcpPlan ? "--mcp-plan" : "", out.graph ? "--graph" : "", out.bundle ? "--bundle" : "", out.bundleCheck ? "--bundle-check" : "", out.bundleCompareTarget ? "--bundle-compare" : "", out.bundleHandoff ? "--bundle-handoff" : "", out.bundleRepair ? "--bundle-repair" : ""].filter(Boolean);
421
+ if (outputModes.length > 1) {
422
+ throw new Error("Use only one output mode: --report, --prompts, --prompt, --next-actions, --mcp-check, --mcp-plan, --graph, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, or --bundle-repair");
423
+ }
424
+ if (out.json && (out.report || out.prompts || out.promptTemplate)) {
425
+ throw new Error("--json is only supported for the site summary, --next-actions, --mcp-check, --mcp-plan, --graph, --bundle-check, --bundle-compare, --bundle-handoff, or --bundle-repair; use --out with --report, --prompts, or --prompt for Markdown artifacts");
426
+ }
427
+ if (out.outPath && !(out.json || out.report || out.prompts || out.promptTemplate || out.init || out.fromIntake || out.intakeTemplate || out.sample || out.tasks || out.bundle || out.bundleCheck || out.bundleCompareTarget || out.bundleHandoff || out.bundleRepair || out.nextActions || out.promptList || out.mcpCheck || out.mcpPlan || out.graph)) {
428
+ throw new Error("--out requires --json, --report, --prompts, --prompt, --init, --from-intake, --intake-template, --sample, --tasks, --bundle, --bundle-check, --bundle-compare, --bundle-handoff, --bundle-repair, --next-actions, --prompt-list, --mcp-check, --mcp-plan, or --graph");
429
+ }
430
+
431
+ const { index, languageProvided, ...parsed } = out;
432
+ return parsed;
433
+ }
@@ -0,0 +1,127 @@
1
+ // Website Improvement handoff bundle builder.
2
+
3
+ import {
4
+ buildSiteMcpActionPlan,
5
+ buildSiteMcpCheckReport,
6
+ combineStatuses,
7
+ formatSiteMcpCheckJson,
8
+ } from "./site-mcp-report.mjs";
9
+ import { buildSiteMcpProbeReport } from "./site-mcp-probes.mjs";
10
+ import {
11
+ buildSiteBundleImplementationPrompt,
12
+ buildSiteHandoffReport,
13
+ buildSitePromptBundle,
14
+ } from "./site-prompts.mjs";
15
+ import { countImplementationEvidence } from "./site-evidence.mjs";
16
+ import {
17
+ SITE_BUNDLE_FILES,
18
+ } from "./site-content.mjs";
19
+ import { analyzeSiteWorkspace } from "./site-analysis.mjs";
20
+ import { generateSiteRefactorTasks } from "./site-tasks.mjs";
21
+ import { buildBundleChecksums } from "./site-bundle-files.mjs";
22
+ import {
23
+ buildSiteBundleHandoffGuidance,
24
+ buildSiteBundleReadme,
25
+ } from "./site-bundle-readme.mjs";
26
+
27
+ export function buildSiteHandoffBundle(workspace, summary = {}) {
28
+ const taskResult = generateSiteRefactorTasks(workspace);
29
+ const taskWorkspace = taskResult.workspace;
30
+ const source = summary.filePath || "workspace.json";
31
+ const { summary: taskSummary } = analyzeSiteWorkspace(taskWorkspace, { filePath: source });
32
+ const mcpReport = buildSiteMcpCheckReport(taskWorkspace, taskSummary);
33
+ const mcpProbeReport = buildSiteMcpProbeReport(taskWorkspace);
34
+ const filePaths = SITE_BUNDLE_FILES;
35
+ const bundleStatus = combineStatuses(mcpReport.status, mcpProbeReport.status);
36
+ const handoffGuidance = buildSiteBundleHandoffGuidance(bundleStatus);
37
+ const bundleSummary = {
38
+ version: 1,
39
+ generatedAt: taskWorkspace.updatedAt,
40
+ source,
41
+ status: bundleStatus,
42
+ workspaceStatus: taskSummary.status,
43
+ site: taskSummary.site,
44
+ counts: taskSummary.counts,
45
+ taskGeneration: {
46
+ createdCount: taskResult.created.length,
47
+ skippedCount: taskResult.skippedCount,
48
+ totalTasks: taskWorkspace.refactorTasks.length,
49
+ created: taskResult.created.map((task) => ({
50
+ id: task.id,
51
+ title: task.title,
52
+ category: task.category,
53
+ priority: task.priority,
54
+ })),
55
+ },
56
+ implementationEvidence: countImplementationEvidence(taskWorkspace.implementationEvidence),
57
+ mcp: {
58
+ status: mcpReport.status,
59
+ counts: mcpReport.counts,
60
+ taskGaps: mcpReport.taskGaps.length,
61
+ probeStatus: mcpProbeReport.status,
62
+ probeCounts: {
63
+ count: mcpProbeReport.count,
64
+ pass: mcpProbeReport.pass,
65
+ warn: mcpProbeReport.warn,
66
+ fail: mcpProbeReport.fail,
67
+ },
68
+ },
69
+ files: filePaths,
70
+ handoff: handoffGuidance,
71
+ boundaries: [
72
+ "deterministic-local",
73
+ "no-external-mcp-calls",
74
+ "no-target-repo-mutation",
75
+ "no-lighthouse-axe-visual-diff",
76
+ ],
77
+ };
78
+
79
+ const contentFiles = [
80
+ {
81
+ path: "README.md",
82
+ content: `${buildSiteBundleReadme(taskWorkspace, bundleSummary, mcpReport, mcpProbeReport, filePaths)}\n`,
83
+ },
84
+ {
85
+ path: "website-workspace.tasks.json",
86
+ content: `${JSON.stringify(taskWorkspace, null, 2)}\n`,
87
+ },
88
+ {
89
+ path: "mcp-check.json",
90
+ content: `${formatSiteMcpCheckJson(mcpReport)}\n`,
91
+ },
92
+ {
93
+ path: "mcp-probes.json",
94
+ content: `${JSON.stringify(mcpProbeReport, null, 2)}\n`,
95
+ },
96
+ {
97
+ path: "mcp-action-plan.md",
98
+ content: `${buildSiteMcpActionPlan(taskWorkspace, taskSummary)}\n`,
99
+ },
100
+ {
101
+ path: "website-handoff.md",
102
+ content: `${buildSiteHandoffReport(taskWorkspace)}\n`,
103
+ },
104
+ {
105
+ path: "website-prompts.md",
106
+ content: `${buildSitePromptBundle(taskWorkspace)}\n`,
107
+ },
108
+ {
109
+ path: "codex-implementation.md",
110
+ content: `${buildSiteBundleImplementationPrompt(taskWorkspace)}\n`,
111
+ },
112
+ ];
113
+ bundleSummary.checksums = buildBundleChecksums(contentFiles);
114
+
115
+ return {
116
+ status: bundleStatus,
117
+ summary: bundleSummary,
118
+ files: [
119
+ contentFiles.find((file) => file.path === "README.md"),
120
+ {
121
+ path: "summary.json",
122
+ content: `${JSON.stringify(bundleSummary, null, 2)}\n`,
123
+ },
124
+ ...contentFiles.filter((file) => file.path !== "README.md"),
125
+ ],
126
+ };
127
+ }