@penpair/core 0.3.7 → 0.4.1

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 (142) hide show
  1. package/dist/adapters.d.ts +1 -1
  2. package/dist/adapters.js +4 -0
  3. package/dist/approvals.d.ts +4 -1
  4. package/dist/approvals.js +7 -4
  5. package/dist/baseline-coverage.js +3 -3
  6. package/dist/baselines-config.d.ts +3 -0
  7. package/dist/baselines-config.js +3 -0
  8. package/dist/baselines.d.ts +7 -0
  9. package/dist/baselines.js +90 -21
  10. package/dist/bounded-file.d.ts +2 -0
  11. package/dist/bounded-file.js +38 -0
  12. package/dist/browser-wake.d.ts +22 -0
  13. package/dist/browser-wake.js +241 -0
  14. package/dist/cli-main.js +193 -18
  15. package/dist/design-commands/context.d.ts +9 -0
  16. package/dist/design-commands/context.js +1 -0
  17. package/dist/design-commands/index.d.ts +3 -0
  18. package/dist/design-commands/index.js +7 -0
  19. package/dist/design-commands/map-command.d.ts +4 -0
  20. package/dist/design-commands/map-command.js +685 -0
  21. package/dist/design-commands/release-command.d.ts +3 -0
  22. package/dist/design-commands/release-command.js +239 -0
  23. package/dist/design-releases/artifact-paths.d.ts +6 -0
  24. package/dist/design-releases/artifact-paths.js +82 -0
  25. package/dist/design-releases/assemble.d.ts +3 -0
  26. package/dist/design-releases/assemble.js +176 -0
  27. package/dist/design-releases/check.d.ts +16 -0
  28. package/dist/design-releases/check.js +323 -0
  29. package/dist/design-releases/context.d.ts +2 -0
  30. package/dist/design-releases/context.js +119 -0
  31. package/dist/design-releases/contract-graph.d.ts +13 -0
  32. package/dist/design-releases/contract-graph.js +75 -0
  33. package/dist/design-releases/fidelity.d.ts +23 -0
  34. package/dist/design-releases/fidelity.js +328 -0
  35. package/dist/design-releases/finalize.d.ts +4 -0
  36. package/dist/design-releases/finalize.js +201 -0
  37. package/dist/design-releases/index.d.ts +8 -0
  38. package/dist/design-releases/index.js +7 -0
  39. package/dist/design-releases/live.d.ts +22 -0
  40. package/dist/design-releases/live.js +97 -0
  41. package/dist/design-releases/snippets.d.ts +5 -0
  42. package/dist/design-releases/snippets.js +89 -0
  43. package/dist/design-releases/store.d.ts +10 -0
  44. package/dist/design-releases/store.js +205 -0
  45. package/dist/design-releases/types.d.ts +161 -0
  46. package/dist/design-releases/types.js +1 -0
  47. package/dist/design-releases/validation.d.ts +6 -0
  48. package/dist/design-releases/validation.js +384 -0
  49. package/dist/design-releases/verify.d.ts +3 -0
  50. package/dist/design-releases/verify.js +148 -0
  51. package/dist/errors.js +1 -1
  52. package/dist/image-codec.d.ts +3 -0
  53. package/dist/image-codec.js +78 -0
  54. package/dist/image-evidence.d.ts +15 -0
  55. package/dist/image-evidence.js +121 -0
  56. package/dist/index.d.ts +5 -0
  57. package/dist/index.js +5 -0
  58. package/dist/inspection.js +13 -5
  59. package/dist/instance/config-schema.d.ts +4 -0
  60. package/dist/instance/config-schema.js +7 -0
  61. package/dist/instance/connection.d.ts +2 -0
  62. package/dist/instance/connection.js +15 -3
  63. package/dist/kit-source.js +1 -1
  64. package/dist/layout-policy.js +21 -11
  65. package/dist/layout-settling.d.ts +1 -0
  66. package/dist/layout-settling.js +51 -17
  67. package/dist/layout.d.ts +7 -1
  68. package/dist/layout.js +19 -3
  69. package/dist/map-penpot/content-validation.d.ts +3 -0
  70. package/dist/map-penpot/content-validation.js +57 -0
  71. package/dist/map-penpot/content.d.ts +22 -0
  72. package/dist/map-penpot/content.js +173 -0
  73. package/dist/map-penpot/details.d.ts +4 -0
  74. package/dist/map-penpot/details.js +84 -0
  75. package/dist/map-penpot/evidence-error.d.ts +5 -0
  76. package/dist/map-penpot/evidence-error.js +9 -0
  77. package/dist/map-penpot/icons.d.ts +5 -0
  78. package/dist/map-penpot/icons.js +79 -0
  79. package/dist/map-penpot/import-content.d.ts +2 -0
  80. package/dist/map-penpot/import-content.js +150 -0
  81. package/dist/map-penpot/import.d.ts +5 -0
  82. package/dist/map-penpot/import.js +255 -0
  83. package/dist/map-penpot/index.d.ts +11 -0
  84. package/dist/map-penpot/index.js +10 -0
  85. package/dist/map-penpot/path-geometry.d.ts +5 -0
  86. package/dist/map-penpot/path-geometry.js +51 -0
  87. package/dist/map-penpot/presentation.d.ts +20 -0
  88. package/dist/map-penpot/presentation.js +205 -0
  89. package/dist/map-penpot/projection.d.ts +8 -0
  90. package/dist/map-penpot/projection.js +176 -0
  91. package/dist/map-penpot/publish.d.ts +4 -0
  92. package/dist/map-penpot/publish.js +301 -0
  93. package/dist/map-penpot/release-maps.d.ts +21 -0
  94. package/dist/map-penpot/release-maps.js +104 -0
  95. package/dist/map-penpot/snippets.d.ts +4 -0
  96. package/dist/map-penpot/snippets.js +164 -0
  97. package/dist/map-penpot/status-labels.d.ts +7 -0
  98. package/dist/map-penpot/status-labels.js +7 -0
  99. package/dist/map-penpot/types.d.ts +161 -0
  100. package/dist/map-penpot/types.js +1 -0
  101. package/dist/maps/annotation-layout.d.ts +24 -0
  102. package/dist/maps/annotation-layout.js +85 -0
  103. package/dist/maps/context.d.ts +13 -0
  104. package/dist/maps/context.js +207 -0
  105. package/dist/maps/index.d.ts +9 -0
  106. package/dist/maps/index.js +9 -0
  107. package/dist/maps/layout.d.ts +35 -0
  108. package/dist/maps/layout.js +364 -0
  109. package/dist/maps/proposal.d.ts +5 -0
  110. package/dist/maps/proposal.js +67 -0
  111. package/dist/maps/render.d.ts +4 -0
  112. package/dist/maps/render.js +331 -0
  113. package/dist/maps/review.d.ts +16 -0
  114. package/dist/maps/review.js +90 -0
  115. package/dist/maps/store.d.ts +21 -0
  116. package/dist/maps/store.js +222 -0
  117. package/dist/maps/types.d.ts +206 -0
  118. package/dist/maps/types.js +1 -0
  119. package/dist/maps/validation.d.ts +10 -0
  120. package/dist/maps/validation.js +375 -0
  121. package/dist/plugin-health.d.ts +13 -0
  122. package/dist/plugin-health.js +39 -0
  123. package/dist/skill-routing.js +4 -0
  124. package/dist/version.d.ts +1 -1
  125. package/dist/version.js +1 -1
  126. package/package.json +2 -1
  127. package/skills/penpair-design-release/SKILL.md +55 -0
  128. package/skills/penpair-design-release/agents/openai.yaml +6 -0
  129. package/skills/penpair-design-release/references/workflow.md +55 -0
  130. package/skills/penpair-diagrams/SKILL.md +52 -0
  131. package/skills/penpair-diagrams/agents/openai.yaml +6 -0
  132. package/skills/penpair-diagrams/references/workflow.md +84 -0
  133. package/skills/penpair-handoff/SKILL.md +7 -0
  134. package/skills/penpair-handoff/references/handoff-flow.md +7 -1
  135. package/skills/penpair-integration-map/SKILL.md +44 -0
  136. package/skills/penpair-integration-map/agents/openai.yaml +6 -0
  137. package/skills/penpair-integration-map/references/workflow.md +33 -0
  138. package/skills/penpair-journey-map/SKILL.md +42 -0
  139. package/skills/penpair-journey-map/agents/openai.yaml +6 -0
  140. package/skills/penpair-journey-map/references/workflow.md +35 -0
  141. package/skills/penpair-verify/SKILL.md +7 -0
  142. package/skills/penpair-verify/references/browser-evidence.md +10 -0
package/dist/cli-main.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { auditDesignRules } from "./audit-rules.js";
2
2
  import { projectStatus, projectFileUrl, openProject } from "./project-status.js";
3
+ import { wakeBrowserTarget } from "./browser-wake.js";
4
+ import { pluginHealthFromError } from "./plugin-health.js";
3
5
  import { artifactSchemas, migrateProjectConfig } from "./formats.js";
4
6
  import { importDtcg, exportDtcg } from "./portable-tokens.js";
5
7
  import { captureStructuralSnapshot, writeStructuralSnapshot, readStructuralSnapshot, diffStructuralSnapshots, renderStructuralDiff, } from "./structure-snapshot.js";
@@ -23,7 +25,8 @@ import { resolveProjectOutput } from "./project-input.js";
23
25
  import { mkdir } from "node:fs/promises";
24
26
  import { captureShots, exportShapeByName, findShapesByName } from "./shots.js";
25
27
  import { exportShapePng } from "./export.js";
26
- import { exportBaselines } from "./baselines.js";
28
+ import { exportBaselines, resolveBaselineImageFormat } from "./baselines.js";
29
+ import { encodeBaselineImage } from "./image-codec.js";
27
30
  import { archiveOperation, runOnPage, runOperation } from "./operation.js";
28
31
  import { formatDeepReport, runDeepCheck } from "./deep-check.js";
29
32
  import { probe } from "./probe.js";
@@ -43,7 +46,7 @@ import { askHidden, askLine, openInBrowser } from "./instance/tty.js";
43
46
  import { validateSkillAgent } from "./skill-hosts.js";
44
47
  import { loadAdapters, readRawConfig, reservedCommands, } from "./adapters.js";
45
48
  import { requireLocalProfile } from "./local-profile.js";
46
- import { assertEndpointIsInstance, doctorExternal, LifecycleError, } from "./instance/connection.js";
49
+ import { assertEndpointIsInstance, doctorExternal, doctorEditor, frontendUrl, LifecycleError, } from "./instance/connection.js";
47
50
  import { nodeRunner } from "./instance/runner.js";
48
51
  import { version } from "./version.js";
49
52
  import { guardFile } from "./file-guard.js";
@@ -54,6 +57,11 @@ import { backupNoteCheck } from "./backup-note.js";
54
57
  import { createCheckpoint } from "./native-api.js";
55
58
  import { listJournals, readJournal } from "./journal.js";
56
59
  const usageHead = `penpair ${version}
60
+ Design agreements and release pages:
61
+ penpair journey-map check|render|publish|import|accept|approve|context --from FILE
62
+ penpair integration-map check|render|publish|import|accept|approve|context --from FILE
63
+ penpair design-release plan|assemble|verify|finalize|check|context --from FILE
64
+ (publication previews by default; --apply writes Penpot, --write records local acceptance/review)
57
65
  Start here (guided, plain language; safe to run again):
58
66
  penpair setup asks where your Penpot is (web is the default), sets everything up and checks it
59
67
  penpair setup --web [--external <address>] Penpot on the web: design.penpot.app, or your team's address; no questions about it
@@ -65,6 +73,7 @@ Connect / diagnose:
65
73
  penpair config check [--json] validate configuration offline
66
74
  penpair config schema print its JSON Schema
67
75
  penpair doctor [--json] [--timeout-ms 5000] origin, transport, fresh editor read and file pin (1000–30000 ms)
76
+ penpair wake --debug-port 9222 [--json] activate the pinned browser tab, then verify the editor; no reload or replay
68
77
  penpair status [--adapters] observed project health and optional companion readiness
69
78
  penpair open [--print] open the pinned public file URL, or print it
70
79
  penpair probe [--integrity] observed APIs and limits
@@ -78,7 +87,7 @@ Build:
78
87
  penpair snippets / snippets check list or validate configured trusted scripts
79
88
  penpair tokens add-set|set|remove|promote inspect token edits; --apply writes once
80
89
  penpair archive --page PAGE --name Archive --keep PREFIX [--apply]
81
- (run-file/export/shots accept --layout auto|native|geometry; default auto)
90
+ (run-file/run/archive/token edits/export/shots accept --layout auto|native|geometry, repeatable --layout-root ID, --layout-max-nodes 1..100000)
82
91
  (journaled writes accept --resume RUN_ID --apply, --checkpoint and --trace)
83
92
  Inspect / spec:
84
93
  penpair pages file identity, revision and pages
@@ -202,6 +211,12 @@ async function main(argv, cli) {
202
211
  case: {
203
212
  type: "string",
204
213
  },
214
+ format: {
215
+ type: "string",
216
+ },
217
+ "migrate-format": {
218
+ type: "boolean",
219
+ },
205
220
  "force-reason": {
206
221
  type: "string",
207
222
  },
@@ -223,9 +238,19 @@ async function main(argv, cli) {
223
238
  "timeout-ms": {
224
239
  type: "string",
225
240
  },
241
+ "debug-port": {
242
+ type: "string",
243
+ },
226
244
  layout: {
227
245
  type: "string",
228
246
  },
247
+ "layout-root": {
248
+ type: "string",
249
+ multiple: true,
250
+ },
251
+ "layout-max-nodes": {
252
+ type: "string",
253
+ },
229
254
  page: {
230
255
  type: "string",
231
256
  },
@@ -420,6 +445,11 @@ async function main(argv, cli) {
420
445
  "max-total-bytes",
421
446
  "capture-timeout-ms",
422
447
  ];
448
+ const layoutFlags = [
449
+ "layout",
450
+ "layout-root",
451
+ "layout-max-nodes",
452
+ ];
423
453
  const flags = {
424
454
  formats: [
425
455
  "out",
@@ -436,6 +466,7 @@ async function main(argv, cli) {
436
466
  "by",
437
467
  "case",
438
468
  "write",
469
+ "format",
439
470
  ],
440
471
  spec: [
441
472
  "from",
@@ -473,6 +504,7 @@ async function main(argv, cli) {
473
504
  "theme",
474
505
  ]
475
506
  : [
507
+ ...layoutFlags,
476
508
  "type",
477
509
  "value",
478
510
  "description",
@@ -486,16 +518,17 @@ async function main(argv, cli) {
486
518
  "page",
487
519
  "scale",
488
520
  "name",
489
- "layout",
521
+ ...layoutFlags,
490
522
  ],
491
523
  shots: [
492
524
  "page",
493
525
  "prefix",
494
526
  "out",
495
527
  "scale",
496
- "layout",
528
+ ...layoutFlags,
497
529
  ],
498
530
  archive: [
531
+ ...layoutFlags,
499
532
  "page",
500
533
  "name",
501
534
  "keep",
@@ -514,7 +547,7 @@ async function main(argv, cli) {
514
547
  "include",
515
548
  ],
516
549
  "run-file": [
517
- "layout",
550
+ ...layoutFlags,
518
551
  "include",
519
552
  "inspect",
520
553
  "readback",
@@ -527,6 +560,7 @@ async function main(argv, cli) {
527
560
  "trace",
528
561
  ],
529
562
  run: [
563
+ ...layoutFlags,
530
564
  "builtins-only",
531
565
  "param",
532
566
  "apply",
@@ -539,6 +573,8 @@ async function main(argv, cli) {
539
573
  "apply",
540
574
  "update",
541
575
  "force-reason",
576
+ "format",
577
+ "migrate-format",
542
578
  ],
543
579
  probe: [
544
580
  "deep",
@@ -579,6 +615,10 @@ async function main(argv, cli) {
579
615
  doctor: [
580
616
  "timeout-ms",
581
617
  ],
618
+ wake: [
619
+ "debug-port",
620
+ "timeout-ms",
621
+ ],
582
622
  up: [],
583
623
  down: [],
584
624
  open: [
@@ -629,6 +669,12 @@ async function main(argv, cli) {
629
669
  throw new Error(`--${key} is not supported by ${command || "this command"}`);
630
670
  }
631
671
  }
672
+ if (values.format && values.format !== "png" && values.format !== "webp") {
673
+ throw new CommandArgumentError("--format must be png or webp");
674
+ }
675
+ if (values["migrate-format"] && (!values.format || (values.apply && !values.update))) {
676
+ throw new CommandArgumentError("--migrate-format requires --format; applying it requires --update");
677
+ }
632
678
  for (const [key, min, max, integer] of [
633
679
  [
634
680
  "depth",
@@ -689,12 +735,31 @@ async function main(argv, cli) {
689
735
  ].includes(values.layout)) {
690
736
  throw new CommandArgumentError("--layout must be auto, native or geometry");
691
737
  }
692
- if (command === "doctor" &&
738
+ if (values["layout-max-nodes"] !== undefined &&
739
+ (!/^\d+$/.test(values["layout-max-nodes"]) ||
740
+ Number(values["layout-max-nodes"]) < 1 ||
741
+ Number(values["layout-max-nodes"]) > 100000)) {
742
+ throw new CommandArgumentError("--layout-max-nodes must be an integer from 1 to 100000");
743
+ }
744
+ if (values["layout-root"] &&
745
+ (values["layout-root"].some((id) => !id.trim()) ||
746
+ new Set(values["layout-root"]).size !== values["layout-root"].length)) {
747
+ throw new CommandArgumentError("--layout-root must name distinct nonempty shape IDs");
748
+ }
749
+ if ((command === "doctor" || command === "wake") &&
693
750
  values["timeout-ms"] !== undefined &&
694
751
  (!/^\d+$/.test(values["timeout-ms"]) ||
695
752
  Number(values["timeout-ms"]) < 1000 ||
696
753
  Number(values["timeout-ms"]) > 30000)) {
697
- throw new CommandArgumentError("doctor --timeout-ms must be an integer from 1000 to 30000");
754
+ throw new CommandArgumentError(`${command} --timeout-ms must be an integer from 1000 to 30000`);
755
+ }
756
+ if (command === "wake" &&
757
+ (rest.length ||
758
+ !values["debug-port"] ||
759
+ !/^\d+$/.test(values["debug-port"]) ||
760
+ Number(values["debug-port"]) < 1 ||
761
+ Number(values["debug-port"]) > 65535)) {
762
+ throw new CommandArgumentError("wake requires --debug-port with an integer from 1 to 65535");
698
763
  }
699
764
  let tokenEdit;
700
765
  if (command === "tokens" &&
@@ -1337,7 +1402,11 @@ async function main(argv, cli) {
1337
1402
  {
1338
1403
  check: "editor and plugin",
1339
1404
  ok: false,
1340
- detail: "No resolved project credential/connection; editor identity was not observed",
1405
+ detail: "No resolved project credential/connection; editor identity was not observed; heartbeat age unavailable; keep-alive unknown",
1406
+ evidence: {
1407
+ heartbeat: "unknown",
1408
+ pluginHealth: pluginHealthFromError(undefined),
1409
+ },
1341
1410
  remedy: "Configure the private credential source, connect the intended editor, then rerun doctor.",
1342
1411
  },
1343
1412
  ]
@@ -1371,6 +1440,57 @@ async function main(argv, cli) {
1371
1440
  }
1372
1441
  return ok ? 0 : 1;
1373
1442
  }
1443
+ if (command === "wake") {
1444
+ const config = await loadCommandConfig(root);
1445
+ if (!config.file?.id) {
1446
+ throw new CommandArgumentError("Pin the owner-confirmed file before waking its browser tab");
1447
+ }
1448
+ const mcp = await mcpProbeFor();
1449
+ if (!mcp) {
1450
+ throw new Error("Configure the project's MCP connection before waking and verifying its tab");
1451
+ }
1452
+ assertEndpointIsInstance({
1453
+ origin: mcp.origin,
1454
+ source: "project connection",
1455
+ }, config);
1456
+ const timeoutMs = Number(values["timeout-ms"] ?? 5000);
1457
+ const started = Date.now();
1458
+ const activation = await wakeBrowserTarget({
1459
+ origin: isExternal(config) ? config.profile.origin : frontendUrl(config),
1460
+ allowLoopbackAlias: !isExternal(config),
1461
+ fileId: config.file.id,
1462
+ port: Number(values["debug-port"]),
1463
+ timeoutMs,
1464
+ signal: commandSignal(),
1465
+ });
1466
+ const remaining = timeoutMs - (Date.now() - started);
1467
+ const checks = remaining > 0
1468
+ ? await doctorEditor(config, {
1469
+ mcp,
1470
+ timeoutMs: remaining,
1471
+ })
1472
+ : [
1473
+ {
1474
+ check: "editor and plugin",
1475
+ ok: false,
1476
+ detail: "Tab activation exhausted the verification deadline",
1477
+ },
1478
+ ];
1479
+ const ready = checks.length > 0 && checks.every((check) => check.ok);
1480
+ output.result({
1481
+ ...activation,
1482
+ ready,
1483
+ checks,
1484
+ });
1485
+ if (!values.json) {
1486
+ output.log("Browser activation acknowledged; sustained keep-alive: none. No write was replayed.");
1487
+ for (const check of checks) {
1488
+ output.log(`${check.ok ? "ok " : "FAIL"} ${check.check}: ${check.detail}`);
1489
+ }
1490
+ output.log("Reconcile any uncertain run independently before explicitly resuming it.");
1491
+ }
1492
+ return ready ? 0 : 1;
1493
+ }
1374
1494
  if (command === "open") {
1375
1495
  if (rest.length) {
1376
1496
  throw new CommandArgumentError("open accepts only --print for a URL preview");
@@ -1662,10 +1782,24 @@ async function main(argv, cli) {
1662
1782
  const client = await guarded();
1663
1783
  commandRedactor(endpointRedactor(endpoint));
1664
1784
  const options = {
1665
- ...(values.layout
1785
+ ...(values.layout || values["layout-root"] || values["layout-max-nodes"]
1666
1786
  ? {
1667
1787
  layout: {
1668
- strategy: values.layout,
1788
+ ...(values.layout
1789
+ ? {
1790
+ strategy: values.layout,
1791
+ }
1792
+ : {}),
1793
+ ...(values["layout-root"]
1794
+ ? {
1795
+ shapeIds: values["layout-root"],
1796
+ }
1797
+ : {}),
1798
+ ...(values["layout-max-nodes"]
1799
+ ? {
1800
+ maxNodes: Number(values["layout-max-nodes"]),
1801
+ }
1802
+ : {}),
1669
1803
  },
1670
1804
  }
1671
1805
  : {}),
@@ -1786,6 +1920,8 @@ async function main(argv, cli) {
1786
1920
  apply: values.apply === true,
1787
1921
  update: values.update === true,
1788
1922
  forceReason: values["force-reason"],
1923
+ format: values.format,
1924
+ migrateFormat: values["migrate-format"],
1789
1925
  });
1790
1926
  output.result(report);
1791
1927
  for (const r of report.results) {
@@ -1908,6 +2044,7 @@ async function main(argv, cli) {
1908
2044
  throw new CommandArgumentError("approve --page PAGE --frame ID [--case CASE_ID] previews evidence; --write requires --note exact-owner-words and --by identity");
1909
2045
  }
1910
2046
  let intent;
2047
+ let imageFormat = values.format ?? "webp";
1911
2048
  if (values.case) {
1912
2049
  const baseline = projectConfig?.baselines;
1913
2050
  const wanted = baseline?.frames.find((f) => f.id === values.case);
@@ -1926,6 +2063,9 @@ async function main(argv, cli) {
1926
2063
  }
1927
2064
  }
1928
2065
  intent = wanted.case;
2066
+ imageFormat = values.format
2067
+ ? values.format
2068
+ : await resolveBaselineImageFormat(root, baseline, wanted.id);
1929
2069
  }
1930
2070
  const captured = await exportShapePng(client, values.frame, {
1931
2071
  ...options,
@@ -1935,17 +2075,21 @@ async function main(argv, cli) {
1935
2075
  if (!captured.stable) {
1936
2076
  throw new Error("Approval export did not stabilize");
1937
2077
  }
1938
- const sha256 = artifactSha(captured.png);
2078
+ const approvalImage = await encodeBaselineImage(captured.png, imageFormat);
2079
+ const sha256 = artifactSha(approvalImage);
1939
2080
  const report = values.write
1940
2081
  ? await recordApproval(root, captured, {
1941
2082
  note: values.note,
1942
2083
  by: values.by,
1943
- }, intent, endpointRedactor(endpoint))
2084
+ }, intent, endpointRedactor(endpoint), {
2085
+ format: imageFormat,
2086
+ })
1944
2087
  : {
1945
2088
  schemaVersion: 1,
1946
2089
  kind: "approval-candidate",
1947
2090
  capture: captured.capture,
1948
2091
  sha256,
2092
+ imageFormat,
1949
2093
  fingerprint: approvalFingerprint(captured.capture, sha256, intent),
1950
2094
  recorded: false,
1951
2095
  };
@@ -2148,6 +2292,36 @@ async function runAdapterCommand(command, root, args) {
2148
2292
  async function runCliInternal(argv, options = {}) {
2149
2293
  try {
2150
2294
  const root = projectRoot();
2295
+ if (argv[0] === "journey-map" ||
2296
+ argv[0] === "integration-map" ||
2297
+ argv[0] === "design-release") {
2298
+ const { runDesignCommand } = await import("./design-commands/index.js");
2299
+ let designRedactor = (text) => redactEndpoint(text);
2300
+ return await runDesignCommand(argv[0], argv.slice(1), {
2301
+ root,
2302
+ output,
2303
+ redact: (text) => designRedactor(text),
2304
+ async withPenpot(work) {
2305
+ const config = await loadCommandConfig(root);
2306
+ if (!config.file?.id) {
2307
+ throw new Error("Live design commands require an explicitly pinned Penpot file");
2308
+ }
2309
+ const endpoint = await projectEndpoint(root);
2310
+ designRedactor = endpointRedactor(endpoint);
2311
+ commandRedactor(endpointRedactor(endpoint));
2312
+ assertEndpointIsInstance(endpoint.description, config);
2313
+ const client = guardFile(await connect(endpoint), config.file, {
2314
+ mode: "strict",
2315
+ });
2316
+ try {
2317
+ return await work(client);
2318
+ }
2319
+ finally {
2320
+ await client.close();
2321
+ }
2322
+ },
2323
+ });
2324
+ }
2151
2325
  if (argv[0] === "onboard") {
2152
2326
  if (argv.slice(1).some((arg) => arg !== "--json")) {
2153
2327
  throw new Error("usage: penpair onboard [--json]");
@@ -2330,15 +2504,16 @@ async function runCliBound(argv, options = {}) {
2330
2504
  ...argv,
2331
2505
  ];
2332
2506
  const timeoutAt = args.indexOf("--timeout-ms");
2507
+ const boundedDiagnosis = args[0] === "doctor" || args[0] === "wake";
2333
2508
  let invalidTimeout = false;
2334
2509
  if (timeoutAt !== -1) {
2335
2510
  const milliseconds = Number(args[timeoutAt + 1]);
2336
2511
  invalidTimeout =
2337
2512
  !Number.isInteger(milliseconds) ||
2338
- milliseconds < (args[0] === "doctor" ? 1000 : 1) ||
2339
- milliseconds > (args[0] === "doctor" ? 30000 : 1_800_000) ||
2513
+ milliseconds < (boundedDiagnosis ? 1000 : 1) ||
2514
+ milliseconds > (boundedDiagnosis ? 30000 : 1_800_000) ||
2340
2515
  args.indexOf("--timeout-ms", timeoutAt + 1) !== -1;
2341
- if (args[0] !== "doctor") {
2516
+ if (!boundedDiagnosis) {
2342
2517
  args.splice(timeoutAt, 2);
2343
2518
  }
2344
2519
  if (!invalidTimeout) {
@@ -2365,8 +2540,8 @@ async function runCliBound(argv, options = {}) {
2365
2540
  try {
2366
2541
  if (invalidTimeout) {
2367
2542
  const result = await collectCommand(args[0] ?? "help", async () => {
2368
- throw new Error(args[0] === "doctor"
2369
- ? "doctor --timeout-ms must be one integer from 1000 to 30000"
2543
+ throw new Error(boundedDiagnosis
2544
+ ? `${args[0]} --timeout-ms must be one integer from 1000 to 30000`
2370
2545
  : "--timeout-ms must be one integer from 1 to 1800000");
2371
2546
  });
2372
2547
  if (json) {
@@ -0,0 +1,9 @@
1
+ import type { CommandOutput } from "../command-result.js";
2
+ import type { PenpotToolClient } from "../client.js";
3
+ /** Project-owned artifact commands can work offline; a live operation opens a strictly pinned connection. */
4
+ export interface DesignCommandContext {
5
+ root: string;
6
+ output: CommandOutput;
7
+ redact(value: string): string;
8
+ withPenpot<T>(work: (client: PenpotToolClient) => Promise<T>): Promise<T>;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { DesignCommandContext } from "./context.js";
2
+ export type { DesignCommandContext } from "./context.js";
3
+ export declare function runDesignCommand(family: "journey-map" | "integration-map" | "design-release", args: string[], context: DesignCommandContext): Promise<number>;
@@ -0,0 +1,7 @@
1
+ import { runMapCommand } from "./map-command.js";
2
+ import { runDesignReleaseCommand } from "./release-command.js";
3
+ export async function runDesignCommand(family, args, context) {
4
+ return family === "design-release"
5
+ ? runDesignReleaseCommand(args, context)
6
+ : runMapCommand(family, args, context);
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { MapKind } from "../maps/types.js";
2
+ import type { DesignCommandContext } from "./context.js";
3
+ /** Explicit local artifact actions remain offline; native publication is preview-only until --apply. */
4
+ export declare function runMapCommand(kind: MapKind, args: string[], context: DesignCommandContext): Promise<number>;