@1agh/maude 0.15.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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +166 -0
  3. package/cli/bin/maude.exe +15 -0
  4. package/cli/bin/maude.mjs +45 -0
  5. package/cli/bin/mdcc.exe +10 -0
  6. package/cli/bin/mdcc.mjs +7 -0
  7. package/cli/cli-wrapper.cjs +67 -0
  8. package/cli/commands/config.mjs +94 -0
  9. package/cli/commands/design.mjs +386 -0
  10. package/cli/commands/help.mjs +57 -0
  11. package/cli/commands/init.mjs +178 -0
  12. package/cli/commands/version.mjs +7 -0
  13. package/cli/install.cjs +113 -0
  14. package/cli/lib/argv.mjs +37 -0
  15. package/cli/lib/argv.test.mjs +46 -0
  16. package/cli/lib/copy-tree.mjs +78 -0
  17. package/package.json +94 -0
  18. package/plugins/design/dev-server/annotations-context-toolbar.tsx +397 -0
  19. package/plugins/design/dev-server/annotations-layer.tsx +1717 -0
  20. package/plugins/design/dev-server/api.ts +674 -0
  21. package/plugins/design/dev-server/bin/_screenshot-playwright.mjs +50 -0
  22. package/plugins/design/dev-server/bin/bootstrap-check.sh +83 -0
  23. package/plugins/design/dev-server/bin/canvas-edit.sh +48 -0
  24. package/plugins/design/dev-server/bin/handoff.sh +27 -0
  25. package/plugins/design/dev-server/bin/screenshot.sh +232 -0
  26. package/plugins/design/dev-server/bin/server-up.sh +135 -0
  27. package/plugins/design/dev-server/bin/slug.sh +22 -0
  28. package/plugins/design/dev-server/bin/smoke.sh +272 -0
  29. package/plugins/design/dev-server/build.ts +267 -0
  30. package/plugins/design/dev-server/canvas-build.ts +219 -0
  31. package/plugins/design/dev-server/canvas-edit.ts +388 -0
  32. package/plugins/design/dev-server/canvas-header.ts +165 -0
  33. package/plugins/design/dev-server/canvas-icons.tsx +131 -0
  34. package/plugins/design/dev-server/canvas-lib-inline.ts +260 -0
  35. package/plugins/design/dev-server/canvas-lib-resolver.ts +85 -0
  36. package/plugins/design/dev-server/canvas-lib.tsx +1995 -0
  37. package/plugins/design/dev-server/canvas-meta.schema.json +181 -0
  38. package/plugins/design/dev-server/canvas-pipeline.ts +270 -0
  39. package/plugins/design/dev-server/canvas-shell.tsx +813 -0
  40. package/plugins/design/dev-server/client/app.jsx +2027 -0
  41. package/plugins/design/dev-server/client/hmr.mjs +85 -0
  42. package/plugins/design/dev-server/client/iframe-lazy.mjs +121 -0
  43. package/plugins/design/dev-server/client/index.html +15 -0
  44. package/plugins/design/dev-server/client/styles/0-reset.css +18 -0
  45. package/plugins/design/dev-server/client/styles/1-tokens.css +297 -0
  46. package/plugins/design/dev-server/client/styles/2-layout.css +35 -0
  47. package/plugins/design/dev-server/client/styles/3-shell.css +906 -0
  48. package/plugins/design/dev-server/client/styles/4-components.css +1268 -0
  49. package/plugins/design/dev-server/client/styles/5-utilities.css +4 -0
  50. package/plugins/design/dev-server/client/styles/_index.css +24 -0
  51. package/plugins/design/dev-server/client/styles.css +1419 -0
  52. package/plugins/design/dev-server/config.schema.json +147 -0
  53. package/plugins/design/dev-server/context-menu.tsx +343 -0
  54. package/plugins/design/dev-server/context.ts +173 -0
  55. package/plugins/design/dev-server/dist/client.bundle.js +20323 -0
  56. package/plugins/design/dev-server/dist/styles.css +2875 -0
  57. package/plugins/design/dev-server/examples/README.md +9 -0
  58. package/plugins/design/dev-server/examples/perf-100-artboards.tsx +113 -0
  59. package/plugins/design/dev-server/fs-watch.ts +63 -0
  60. package/plugins/design/dev-server/handoff.ts +721 -0
  61. package/plugins/design/dev-server/history.ts +125 -0
  62. package/plugins/design/dev-server/hmr-broadcast.ts +114 -0
  63. package/plugins/design/dev-server/http.ts +413 -0
  64. package/plugins/design/dev-server/input-router.tsx +485 -0
  65. package/plugins/design/dev-server/inspect.ts +365 -0
  66. package/plugins/design/dev-server/locator.ts +159 -0
  67. package/plugins/design/dev-server/mem.ts +97 -0
  68. package/plugins/design/dev-server/runtime-bundle.ts +235 -0
  69. package/plugins/design/dev-server/server.mjs +1246 -0
  70. package/plugins/design/dev-server/server.ts +131 -0
  71. package/plugins/design/dev-server/test/_helpers.ts +81 -0
  72. package/plugins/design/dev-server/test/active-state.test.ts +145 -0
  73. package/plugins/design/dev-server/test/annotations-api.test.ts +146 -0
  74. package/plugins/design/dev-server/test/annotations-layer.test.ts +419 -0
  75. package/plugins/design/dev-server/test/binary-smoke.test.ts +47 -0
  76. package/plugins/design/dev-server/test/bundle-smoke.test.ts +29 -0
  77. package/plugins/design/dev-server/test/canvas-build.test.ts +78 -0
  78. package/plugins/design/dev-server/test/canvas-edit.test.ts +139 -0
  79. package/plugins/design/dev-server/test/canvas-header.test.ts +127 -0
  80. package/plugins/design/dev-server/test/canvas-lib-inline.test.ts +146 -0
  81. package/plugins/design/dev-server/test/canvas-lib-resolver.test.ts +112 -0
  82. package/plugins/design/dev-server/test/canvas-meta-api.test.ts +236 -0
  83. package/plugins/design/dev-server/test/canvas-pipeline.test.ts +180 -0
  84. package/plugins/design/dev-server/test/canvas-route.test.ts +176 -0
  85. package/plugins/design/dev-server/test/fs-watch.test.ts +41 -0
  86. package/plugins/design/dev-server/test/handoff-static-frames.test.ts +215 -0
  87. package/plugins/design/dev-server/test/handoff.test.ts +281 -0
  88. package/plugins/design/dev-server/test/history-rollback.test.ts +62 -0
  89. package/plugins/design/dev-server/test/hmr-broadcast.test.ts +108 -0
  90. package/plugins/design/dev-server/test/input-router.test.ts +316 -0
  91. package/plugins/design/dev-server/test/locator.test.ts +214 -0
  92. package/plugins/design/dev-server/test/perf-harness.ts +193 -0
  93. package/plugins/design/dev-server/test/phase-3.6-smoke.test.ts +77 -0
  94. package/plugins/design/dev-server/test/runtime-bundle.test.ts +69 -0
  95. package/plugins/design/dev-server/test/server-lifecycle.test.ts +28 -0
  96. package/plugins/design/dev-server/test/tool-palette.test.tsx +55 -0
  97. package/plugins/design/dev-server/test/use-annotation-selection.test.tsx +77 -0
  98. package/plugins/design/dev-server/test/use-artboard-drag.test.ts +325 -0
  99. package/plugins/design/dev-server/test/use-selection-set.test.tsx +166 -0
  100. package/plugins/design/dev-server/test/use-snap-guides.test.ts +190 -0
  101. package/plugins/design/dev-server/test/use-tool-mode.test.tsx +93 -0
  102. package/plugins/design/dev-server/test/ws-handshake.test.ts +33 -0
  103. package/plugins/design/dev-server/tool-palette.tsx +278 -0
  104. package/plugins/design/dev-server/tsconfig.json +26 -0
  105. package/plugins/design/dev-server/use-annotation-selection.tsx +92 -0
  106. package/plugins/design/dev-server/use-annotations-visibility.tsx +43 -0
  107. package/plugins/design/dev-server/use-artboard-drag.tsx +445 -0
  108. package/plugins/design/dev-server/use-selection-set.tsx +224 -0
  109. package/plugins/design/dev-server/use-snap-guides.tsx +215 -0
  110. package/plugins/design/dev-server/use-tool-mode.tsx +114 -0
  111. package/plugins/design/dev-server/ws.ts +90 -0
  112. package/plugins/design/templates/_shell.html +177 -0
  113. package/plugins/design/templates/canvas.tsx.template +54 -0
  114. package/plugins/design/templates/design-system-inspiration/_MAPPING.md +277 -0
  115. package/plugins/design/templates/design-system-inspiration/_README.md +71 -0
  116. package/plugins/design/templates/design-system-inspiration/audience-consumer/components-banner.html +68 -0
  117. package/plugins/design/templates/design-system-inspiration/audience-consumer/components-empty-state-generous.html +39 -0
  118. package/plugins/design/templates/design-system-inspiration/audience-consumer/components-feature-grid.html +62 -0
  119. package/plugins/design/templates/design-system-inspiration/audience-consumer/components-marketing-card.html +63 -0
  120. package/plugins/design/templates/design-system-inspiration/audience-consumer/components-testimonial.html +80 -0
  121. package/plugins/design/templates/design-system-inspiration/audience-developer/components-code-block.html +71 -0
  122. package/plugins/design/templates/design-system-inspiration/audience-developer/components-diff-view.html +65 -0
  123. package/plugins/design/templates/design-system-inspiration/audience-developer/components-log-stream.html +62 -0
  124. package/plugins/design/templates/design-system-inspiration/audience-developer/components-monospace-table.html +57 -0
  125. package/plugins/design/templates/design-system-inspiration/audience-developer/components-terminal-pane.html +67 -0
  126. package/plugins/design/templates/design-system-inspiration/audience-developer/type-mono.html +57 -0
  127. package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +74 -0
  128. package/plugins/design/templates/design-system-inspiration/audience-pro/components-command-palette.html +90 -0
  129. package/plugins/design/templates/design-system-inspiration/audience-pro/components-keyboard.html +51 -0
  130. package/plugins/design/templates/design-system-inspiration/audience-pro/components-list.html +89 -0
  131. package/plugins/design/templates/design-system-inspiration/audience-pro/components-shortcuts-overlay.html +85 -0
  132. package/plugins/design/templates/design-system-inspiration/audience-pro/components-toast-menu.html +80 -0
  133. package/plugins/design/templates/design-system-inspiration/core/INDEX.md.tpl +25 -0
  134. package/plugins/design/templates/design-system-inspiration/core/README.orchestration.md.tpl +71 -0
  135. package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +77 -0
  136. package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +50 -0
  137. package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +111 -0
  138. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +28 -0
  139. package/plugins/design/templates/design-system-inspiration/core/preview/_layout.css +113 -0
  140. package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +48 -0
  141. package/plugins/design/templates/design-system-inspiration/core/preview/colors-surfaces.html +49 -0
  142. package/plugins/design/templates/design-system-inspiration/core/preview/colors-text.html +52 -0
  143. package/plugins/design/templates/design-system-inspiration/core/preview/components-buttons.html +83 -0
  144. package/plugins/design/templates/design-system-inspiration/core/preview/components-cards.html +79 -0
  145. package/plugins/design/templates/design-system-inspiration/core/preview/components-inputs.html +82 -0
  146. package/plugins/design/templates/design-system-inspiration/core/preview/motion.html +66 -0
  147. package/plugins/design/templates/design-system-inspiration/core/preview/spacing-scale.html +53 -0
  148. package/plugins/design/templates/design-system-inspiration/core/preview/type-scale.html +62 -0
  149. package/plugins/design/templates/design-system-inspiration/foundations/borders.html +39 -0
  150. package/plugins/design/templates/design-system-inspiration/foundations/elevation.html +46 -0
  151. package/plugins/design/templates/design-system-inspiration/foundations/focus.html +48 -0
  152. package/plugins/design/templates/design-system-inspiration/foundations/grid.html +51 -0
  153. package/plugins/design/templates/design-system-inspiration/foundations/iconography.html +73 -0
  154. package/plugins/design/templates/design-system-inspiration/foundations/opacity.html +45 -0
  155. package/plugins/design/templates/design-system-inspiration/foundations/radii.html +40 -0
  156. package/plugins/design/templates/design-system-inspiration/foundations/selection.html +45 -0
  157. package/plugins/design/templates/design-system-inspiration/meta/accessibility.html +62 -0
  158. package/plugins/design/templates/design-system-inspiration/meta/i18n.html +73 -0
  159. package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +71 -0
  160. package/plugins/design/templates/design-system-inspiration/meta/tokens-index.html +80 -0
  161. package/plugins/design/templates/design-system-inspiration/patterns/patterns-auth.html +78 -0
  162. package/plugins/design/templates/design-system-inspiration/patterns/patterns-data-density.html +61 -0
  163. package/plugins/design/templates/design-system-inspiration/patterns/patterns-error-pages.html +70 -0
  164. package/plugins/design/templates/design-system-inspiration/patterns/patterns-form-layouts.html +70 -0
  165. package/plugins/design/templates/design-system-inspiration/patterns/patterns-onboarding.html +71 -0
  166. package/plugins/design/templates/design-system-inspiration/patterns/patterns-pricing.html +83 -0
  167. package/plugins/design/templates/design-system-inspiration/platform-desktop/components-resize-panels.html +63 -0
  168. package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-index.html +55 -0
  169. package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +302 -0
  170. package/plugins/design/templates/design-system-inspiration/platform-mobile/components-bottom-sheet.html +63 -0
  171. package/plugins/design/templates/design-system-inspiration/platform-mobile/components-pull-to-refresh.html +74 -0
  172. package/plugins/design/templates/design-system-inspiration/platform-mobile/components-segmented-control.html +51 -0
  173. package/plugins/design/templates/design-system-inspiration/platform-mobile/components-tab-bar.html +57 -0
  174. package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-index.html +58 -0
  175. package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +237 -0
  176. package/plugins/design/templates/design-system-inspiration/status/colors-status.html +49 -0
  177. package/plugins/design/templates/design-system-inspiration/status/components-status.html +63 -0
  178. package/plugins/design/templates/design-system-inspiration/status/skeletons.html +74 -0
  179. package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +59 -0
  180. package/plugins/design/templates/design-system-inspiration/universal/components-callout.html +74 -0
  181. package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +81 -0
  182. package/plugins/design/templates/design-system-inspiration/universal/components-tables.html +101 -0
  183. package/plugins/design/templates/design-system-inspiration/universal/components-toggles.html +74 -0
  184. package/plugins/design/templates/design-system-inspiration/universal/components-tooltips.html +74 -0
  185. package/plugins/design/templates/design-system-inspiration/universal/empty-state.html.tpl +34 -0
  186. package/plugins/design/templates/design-system-inspiration/universal/logo.html +42 -0
  187. package/plugins/design/templates/ds-specimen.tsx.template +59 -0
  188. package/plugins/flow/.claude-plugin/config.schema.json +398 -0
  189. package/plugins/flow/README.md +45 -0
  190. package/plugins/flow/templates/ai-skeleton/INDEX.md +50 -0
  191. package/plugins/flow/templates/ai-skeleton/README.md +46 -0
  192. package/plugins/flow/templates/ai-skeleton/browser/har/.gitkeep +0 -0
  193. package/plugins/flow/templates/ai-skeleton/browser/snapshots/.gitkeep +0 -0
  194. package/plugins/flow/templates/ai-skeleton/business/README.md +12 -0
  195. package/plugins/flow/templates/ai-skeleton/context/README.md +12 -0
  196. package/plugins/flow/templates/ai-skeleton/decisions/README.md +20 -0
  197. package/plugins/flow/templates/ai-skeleton/design-import/.gitkeep +0 -0
  198. package/plugins/flow/templates/ai-skeleton/dev-logs/.gitkeep +0 -0
  199. package/plugins/flow/templates/ai-skeleton/device/.gitkeep +0 -0
  200. package/plugins/flow/templates/ai-skeleton/docs/README.md +5 -0
  201. package/plugins/flow/templates/ai-skeleton/logs/.gitkeep +0 -0
  202. package/plugins/flow/templates/ai-skeleton/logs/README.md +13 -0
  203. package/plugins/flow/templates/ai-skeleton/plans/README.md +16 -0
  204. package/plugins/flow/templates/ai-skeleton/plans/archive/.gitkeep +0 -0
  205. package/plugins/flow/templates/ai-skeleton/release-guide.md +35 -0
  206. package/plugins/flow/templates/ai-skeleton/reviews/README.md +15 -0
  207. package/plugins/flow/templates/ai-skeleton/scenarios/README.md +26 -0
  208. package/plugins/flow/templates/ai-skeleton/scenarios/_lib/.gitkeep +0 -0
  209. package/plugins/flow/templates/ai-skeleton/state/STATE.md +25 -0
  210. package/plugins/flow/templates/ai-skeleton/templates/HANDOFF.md +32 -0
  211. package/plugins/flow/templates/ai-skeleton/workflows.config.json +74 -0
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env bash
2
+ # smoke.sh — batch-screenshot every UI canvas + preview specimen.
3
+ # Catches "build green ≠ user-visible green" regressions that bypass the
4
+ # per-canvas hooks in /design:edit step 7 / /design:new step 9 — typically
5
+ # dev-server infra changes or bulk multi-canvas migrations. See DDR-021.
6
+ #
7
+ # Usage:
8
+ # smoke.sh [--root <repo>]
9
+ # [--out-dir <dir>]
10
+ # [--include-system 0|1] (default 1)
11
+ # [--timeout <secs>] (default 8)
12
+ # [--engine auto|agent-browser|playwright]
13
+ #
14
+ # Reads:
15
+ # $DESIGN_ROOT/_server.json (must exist — caller runs server-up.sh first)
16
+ # $DESIGN_ROOT/ui/*.tsx (canvases)
17
+ # $DESIGN_ROOT/system/*/preview/*.tsx (specimens, when --include-system 1)
18
+ #
19
+ # Writes:
20
+ # <out-dir>/<slug>.png (one screenshot per canvas)
21
+ # <out-dir>/report.tsv (machine-parseable summary)
22
+ # <out-dir>/report.md (human-readable summary, links every PNG)
23
+ #
24
+ # Stdout: one line per canvas, tab-separated:
25
+ # STATUS \t FILE \t SCREENSHOT \t DETAIL
26
+ # Stderr: diagnostic / progress.
27
+ # Exit: 0 = all green / 3 = at least one blank/error / 1 = missing deps / 2 = bad args.
28
+
29
+ REPO=""
30
+ OUT_DIR=""
31
+ INCLUDE_SYSTEM=1
32
+ TIMEOUT=8
33
+ ENGINE="auto"
34
+
35
+ while [ $# -gt 0 ]; do
36
+ case "$1" in
37
+ --root) REPO="$2"; shift 2 ;;
38
+ --out-dir) OUT_DIR="$2"; shift 2 ;;
39
+ --include-system) INCLUDE_SYSTEM="$2"; shift 2 ;;
40
+ --timeout) TIMEOUT="$2"; shift 2 ;;
41
+ --engine) ENGINE="$2"; shift 2 ;;
42
+ --help|-h)
43
+ sed -n '2,28p' "$0" | sed 's/^# \?//'
44
+ exit 0
45
+ ;;
46
+ *)
47
+ echo "smoke.sh: unknown arg '$1' (try --help)" >&2
48
+ exit 2
49
+ ;;
50
+ esac
51
+ done
52
+
53
+ # ---------- resolve repo + design root ----------
54
+ if [ -z "$REPO" ]; then
55
+ REPO="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
56
+ fi
57
+ DESIGN_ROOT="$REPO/.design"
58
+ [ -d "$DESIGN_ROOT" ] || { echo "smoke.sh: no .design/ under $REPO" >&2; exit 1; }
59
+
60
+ STATE="$DESIGN_ROOT/_server.json"
61
+ [ -f "$STATE" ] || { echo "smoke.sh: $STATE missing — run server-up.sh first" >&2; exit 1; }
62
+
63
+ if command -v jq >/dev/null 2>&1; then
64
+ PORT=$(jq -r .port "$STATE" 2>/dev/null)
65
+ else
66
+ PORT=$(sed -nE 's/.*"port"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/p' "$STATE" | head -n1)
67
+ fi
68
+ [ -n "$PORT" ] || { echo "smoke.sh: no port in $STATE" >&2; exit 1; }
69
+
70
+ # ---------- resolve out-dir ----------
71
+ if [ -z "$OUT_DIR" ]; then
72
+ TS=$(date +%Y%m%d-%H%M%S)
73
+ OUT_DIR="$DESIGN_ROOT/_history/_smoke/$TS"
74
+ fi
75
+ mkdir -p "$OUT_DIR"
76
+
77
+ # ---------- engine resolution ----------
78
+ if [ "$ENGINE" = "auto" ]; then
79
+ if command -v agent-browser >/dev/null 2>&1; then
80
+ ENGINE="agent-browser"
81
+ else
82
+ ENGINE="playwright"
83
+ fi
84
+ fi
85
+ echo "→ smoke engine: $ENGINE | port: $PORT | out: $OUT_DIR" >&2
86
+
87
+ # ---------- collect canvases ----------
88
+ CANVASES=""
89
+ if [ -d "$DESIGN_ROOT/ui" ]; then
90
+ while IFS= read -r f; do
91
+ base=$(basename "$f")
92
+ case "$base" in _*) continue ;; esac
93
+ CANVASES="$CANVASES$f"$'\n'
94
+ done < <(find "$DESIGN_ROOT/ui" -maxdepth 1 -type f -name '*.tsx' 2>/dev/null | sort)
95
+ fi
96
+
97
+ if [ "$INCLUDE_SYSTEM" = "1" ] && [ -d "$DESIGN_ROOT/system" ]; then
98
+ while IFS= read -r f; do
99
+ base=$(basename "$f")
100
+ case "$base" in _*) continue ;; esac
101
+ CANVASES="$CANVASES$f"$'\n'
102
+ done < <(find "$DESIGN_ROOT/system" -type f -name '*.tsx' -path '*/preview/*' 2>/dev/null | sort)
103
+ fi
104
+
105
+ CANVASES=$(printf '%s' "$CANVASES" | sed '/^$/d')
106
+ COUNT=$(printf '%s' "$CANVASES" | grep -c .)
107
+ [ "$COUNT" -gt 0 ] || { echo "smoke.sh: no canvases found (ui/*.tsx or system/*/preview/*.tsx)" >&2; exit 1; }
108
+ echo "→ found $COUNT canvases" >&2
109
+
110
+ # ---------- helpers ----------
111
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
112
+ SLUG_HELPER="$SCRIPT_DIR/slug.sh"
113
+
114
+ urlencode_path() {
115
+ # Encode spaces only — leaves /, ., etc alone. Server resolves these.
116
+ printf '%s' "$1" | sed 's/ /%20/g'
117
+ }
118
+
119
+ # Probe a canvas via agent-browser. Returns three lines on stdout:
120
+ # <status> one of OK / BLANK / ERROR
121
+ # <detail> short summary string
122
+ # <screenshot-path-or-empty>
123
+ probe_agent_browser() {
124
+ local url="$1"
125
+ local out_png="$2"
126
+
127
+ agent-browser open "$url" >/dev/null 2>&1 || { echo "ERROR"; echo "open-failed"; echo ""; return; }
128
+
129
+ # Poll for any DC marker — [data-dc-screen], [data-dc-slot], [data-cd-id].
130
+ # Specimens may not have any of these, so we also accept body innerText > 0.
131
+ local poll=0
132
+ local mounted=0
133
+ while [ $poll -lt "$TIMEOUT" ]; do
134
+ sleep 1
135
+ poll=$((poll + 1))
136
+ local raw
137
+ raw=$(agent-browser eval "document.querySelectorAll('[data-dc-screen],[data-dc-slot],[data-cd-id]').length + (document.body && document.body.innerText.trim().length > 0 ? 1000 : 0)" 2>/dev/null)
138
+ raw=$(printf '%s' "$raw" | tr -d '[:space:]')
139
+ case "$raw" in
140
+ ''|*[!0-9]*) continue ;;
141
+ 0) continue ;;
142
+ *) mounted=1; break ;;
143
+ esac
144
+ done
145
+
146
+ # Capture screenshot regardless — the PNG is evidence even on failure.
147
+ agent-browser screenshot --full "$out_png" >/dev/null 2>&1 || true
148
+
149
+ if [ $mounted -eq 0 ]; then
150
+ echo "BLANK"
151
+ echo "no-dc-markers-no-text-after-${TIMEOUT}s"
152
+ echo "$out_png"
153
+ return
154
+ fi
155
+
156
+ # Probe for visible error overlays (react-error-overlay, common patterns).
157
+ local err
158
+ err=$(agent-browser eval "(() => {
159
+ const sel = ['#__react-error-overlay', '.react-error-overlay', '[data-error-overlay]', 'pre.error-stack'];
160
+ for (const s of sel) { const el = document.querySelector(s); if (el && el.innerText) return el.innerText.slice(0, 120); }
161
+ const body = document.body && document.body.innerText || '';
162
+ if (body.startsWith('Error:') || body.startsWith('SyntaxError:') || body.startsWith('ReferenceError:')) return body.slice(0, 120);
163
+ return '';
164
+ })()" 2>/dev/null)
165
+ # Strip wrapping quotes that agent-browser eval adds for strings.
166
+ err=$(printf '%s' "$err" | sed 's/^"//; s/"$//; s/^\\n//; s/\\n$//')
167
+
168
+ if [ -n "$err" ] && [ "$err" != "null" ] && [ "$err" != "undefined" ]; then
169
+ echo "ERROR"
170
+ echo "$err"
171
+ echo "$out_png"
172
+ return
173
+ fi
174
+
175
+ # PNG size sanity — < 2 KB usually means blank background only.
176
+ if [ -s "$out_png" ]; then
177
+ local size
178
+ size=$(wc -c < "$out_png" 2>/dev/null | tr -d ' ')
179
+ if [ -n "$size" ] && [ "$size" -lt 2048 ]; then
180
+ echo "BLANK"
181
+ echo "png-${size}B"
182
+ echo "$out_png"
183
+ return
184
+ fi
185
+ fi
186
+
187
+ echo "OK"
188
+ echo "ok"
189
+ echo "$out_png"
190
+ }
191
+
192
+ # ---------- iterate ----------
193
+ TSV="$OUT_DIR/report.tsv"
194
+ MD="$OUT_DIR/report.md"
195
+ printf 'status\tfile\tscreenshot\tdetail\n' > "$TSV"
196
+ {
197
+ echo "# Smoke report — $(date '+%Y-%m-%d %H:%M:%S')"
198
+ echo
199
+ echo "- repo: \`$REPO\`"
200
+ echo "- port: $PORT"
201
+ echo "- canvases: $COUNT"
202
+ echo
203
+ echo "| status | file | screenshot | detail |"
204
+ echo "|---|---|---|---|"
205
+ } > "$MD"
206
+
207
+ FAILED=0
208
+ N=0
209
+ while IFS= read -r CANVAS; do
210
+ [ -z "$CANVAS" ] && continue
211
+ N=$((N + 1))
212
+ REL="${CANVAS#$DESIGN_ROOT/}"
213
+ REL_ENC=$(urlencode_path "$REL")
214
+ URL="http://localhost:$PORT/$REL_ENC"
215
+ SLUG=$(bash "$SLUG_HELPER" "$REL" 2>/dev/null || printf '%s' "$REL" | tr '/ ' '__' | tr '[:upper:]' '[:lower:]')
216
+ OUT_PNG="$OUT_DIR/$SLUG.png"
217
+
218
+ printf ' [%d/%d] %s … ' "$N" "$COUNT" "$REL" >&2
219
+
220
+ if [ "$ENGINE" = "agent-browser" ]; then
221
+ RESULT=$(probe_agent_browser "$URL" "$OUT_PNG")
222
+ else
223
+ # Playwright fallback — coarser: just screenshot, accept any PNG > 2 KB as OK.
224
+ # See _screenshot-playwright.mjs for the underlying tool.
225
+ bash "$SCRIPT_DIR/screenshot.sh" --url "$URL" --full --out "$OUT_PNG" --engine playwright --timeout "$TIMEOUT" >/dev/null 2>&1
226
+ if [ -s "$OUT_PNG" ]; then
227
+ SIZE=$(wc -c < "$OUT_PNG" | tr -d ' ')
228
+ if [ "$SIZE" -lt 2048 ]; then
229
+ RESULT=$'BLANK\npng-'"$SIZE"$'B\n'"$OUT_PNG"
230
+ else
231
+ RESULT=$'OK\nok\n'"$OUT_PNG"
232
+ fi
233
+ else
234
+ RESULT=$'ERROR\nno-png\n'"$OUT_PNG"
235
+ fi
236
+ fi
237
+
238
+ STATUS=$(printf '%s\n' "$RESULT" | sed -n '1p')
239
+ DETAIL=$(printf '%s\n' "$RESULT" | sed -n '2p')
240
+ SHOT=$(printf '%s\n' "$RESULT" | sed -n '3p')
241
+
242
+ case "$STATUS" in
243
+ OK) SYM="✓"; echo "$SYM" >&2 ;;
244
+ BLANK) SYM="✗"; FAILED=$((FAILED + 1)); echo "$SYM blank ($DETAIL)" >&2 ;;
245
+ ERROR) SYM="⚠"; FAILED=$((FAILED + 1)); echo "$SYM error ($DETAIL)" >&2 ;;
246
+ *) SYM="?"; FAILED=$((FAILED + 1)); echo "? unknown ($STATUS)" >&2 ;;
247
+ esac
248
+
249
+ printf '%s\t%s\t%s\t%s\n' "$STATUS" "$REL" "$SHOT" "$DETAIL" >> "$TSV"
250
+ printf '| %s %s | `%s` | [`%s`](%s) | %s |\n' "$SYM" "$STATUS" "$REL" "$(basename "$SHOT")" "$(basename "$SHOT")" "$DETAIL" >> "$MD"
251
+
252
+ printf '%s\t%s\t%s\t%s\n' "$STATUS" "$REL" "$SHOT" "$DETAIL"
253
+ done <<< "$CANVASES"
254
+
255
+ {
256
+ echo
257
+ if [ $FAILED -eq 0 ]; then
258
+ echo "**Result:** ✓ all $COUNT canvases rendered."
259
+ else
260
+ echo "**Result:** ✗ $FAILED / $COUNT canvases failed."
261
+ fi
262
+ } >> "$MD"
263
+
264
+ echo "→ report: $MD" >&2
265
+ echo "→ tsv: $TSV" >&2
266
+
267
+ if [ $FAILED -gt 0 ]; then
268
+ echo "✗ smoke: $FAILED / $COUNT canvases failed" >&2
269
+ exit 3
270
+ fi
271
+ echo "✓ smoke: all $COUNT canvases rendered" >&2
272
+ exit 0
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env bun
2
+ // Build orchestrator for the dev-server.
3
+ //
4
+ // Three steps (run on demand):
5
+ // (a) Client JSX bundle: client/app.jsx + React 19 -> dist/client.bundle.js (IIFE, tree-shaken).
6
+ // (b) CSS bundle: client/styles/_index.css (Lightning CSS, @layer, OKLCH fallback).
7
+ // (c) Server binary: server.ts -> dist/maude-<platform> (bun build --compile, per-platform).
8
+ //
9
+ // Modes:
10
+ // bun run build.ts -> dev build for current platform (no compile, no minify)
11
+ // bun run build.ts --release -> release build for all platforms in the matrix
12
+ // bun run build.ts --release --target=bun-darwin-arm64
13
+ // bun run build.ts --watch -> watch client + CSS; broadcast over HMR socket
14
+ // bun run build.ts --dry-run -> exit 0 without writing files (smoke for CI / Task 2 validation)
15
+ //
16
+ // Per DDR-009 (Bun runtime authoritative) + DDR-012 (React 19 unified) + DDR-014 (Lightning CSS).
17
+
18
+ import { existsSync, mkdirSync } from 'node:fs';
19
+ import { dirname, join, resolve } from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+
22
+ import { browserslistToTargets, bundle as lcssBundle } from 'lightningcss';
23
+
24
+ const ROOT = dirname(fileURLToPath(import.meta.url));
25
+ const DIST = join(ROOT, 'dist');
26
+
27
+ const ARGS = new Set(process.argv.slice(2));
28
+ const FLAG_TARGET = process.argv.find((a) => a.startsWith('--target='))?.slice('--target='.length);
29
+ const MODE: 'dev' | 'release' | 'dry' = ARGS.has('--dry-run')
30
+ ? 'dry'
31
+ : ARGS.has('--release')
32
+ ? 'release'
33
+ : 'dev';
34
+ const WATCH = ARGS.has('--watch');
35
+
36
+ const PLATFORM_MATRIX = [
37
+ 'bun-darwin-arm64',
38
+ 'bun-darwin-x64',
39
+ 'bun-linux-x64',
40
+ 'bun-linux-arm64',
41
+ 'bun-linux-x64-musl',
42
+ 'bun-linux-arm64-musl',
43
+ 'bun-windows-x64',
44
+ ] as const;
45
+
46
+ type PlatformTarget = (typeof PLATFORM_MATRIX)[number];
47
+
48
+ function platformSlug(target: PlatformTarget): string {
49
+ // bun calls Windows "windows"; npm and process.platform call it "win32".
50
+ // Sub-package directories + the build-binaries.yml matrix slug use win32-x64.
51
+ const s = target.replace(/^bun-/, '');
52
+ return s === 'windows-x64' ? 'win32-x64' : s;
53
+ }
54
+
55
+ function currentTarget(): PlatformTarget {
56
+ const p = process.platform;
57
+ const a = process.arch;
58
+ if (p === 'darwin') return a === 'arm64' ? 'bun-darwin-arm64' : 'bun-darwin-x64';
59
+ if (p === 'linux') return a === 'arm64' ? 'bun-linux-arm64' : 'bun-linux-x64';
60
+ if (p === 'win32') return 'bun-windows-x64';
61
+ throw new Error(`Unsupported host platform: ${p}-${a}`);
62
+ }
63
+
64
+ function ensureDist() {
65
+ if (!existsSync(DIST)) mkdirSync(DIST, { recursive: true });
66
+ }
67
+
68
+ // ---------- (a) Client JSX bundle ----------
69
+
70
+ async function buildClient(): Promise<{ outBytes: number; outPath: string }> {
71
+ ensureDist();
72
+ const outPath = join(DIST, 'client.bundle.js');
73
+ // Read package.json version at build time for the wordmark sub-line.
74
+ const pkg = JSON.parse(await Bun.file(join(ROOT, '..', '..', '..', 'package.json')).text());
75
+ const result = await Bun.build({
76
+ entrypoints: [join(ROOT, 'client/app.jsx')],
77
+ outdir: DIST,
78
+ target: 'browser',
79
+ // ESM — not IIFE. Bun.build's IIFE + full minify + React 19 hits a TDZ
80
+ // bug (`Cannot access 'VZ' before initialization`) because top-level
81
+ // `const`s get rotated past their declaration. ESM has stable hoisting
82
+ // semantics the minifier respects. Cost: `<script type="module">` in
83
+ // index.html (already set).
84
+ format: 'esm',
85
+ naming: 'client.bundle.js',
86
+ minify: MODE === 'release',
87
+ sourcemap: MODE === 'dev' ? 'inline' : 'none',
88
+ define: {
89
+ 'process.env.NODE_ENV': JSON.stringify(MODE === 'release' ? 'production' : 'development'),
90
+ __MDCC_VERSION__: JSON.stringify(pkg.version),
91
+ },
92
+ });
93
+ if (!result.success) {
94
+ const messages = result.logs.map((l) => l.message ?? String(l)).join('\n');
95
+ throw new Error(`Client build failed:\n${messages}`);
96
+ }
97
+ const out = Bun.file(outPath);
98
+ return { outBytes: out.size, outPath };
99
+ }
100
+
101
+ // ---------- (b) CSS bundle (Lightning CSS) ----------
102
+
103
+ async function buildCss(): Promise<{ outBytes: number; outPath: string }> {
104
+ ensureDist();
105
+ const inputPath = join(ROOT, 'client/styles/_index.css');
106
+ const outPath = join(DIST, 'styles.css');
107
+ const { code } = lcssBundle({
108
+ filename: inputPath,
109
+ minify: MODE === 'release',
110
+ sourceMap: false,
111
+ targets: browserslistToTargets([
112
+ 'Chrome >= 110',
113
+ 'Safari >= 16',
114
+ 'Firefox >= 110',
115
+ 'Edge >= 110',
116
+ ]),
117
+ drafts: { customMedia: true },
118
+ });
119
+ await Bun.write(outPath, code);
120
+ return { outBytes: code.byteLength, outPath };
121
+ }
122
+
123
+ // ---------- (c) Server binary (bun build --compile, per-platform) ----------
124
+
125
+ async function buildServerBinary(target: PlatformTarget): Promise<{ outPath: string }> {
126
+ ensureDist();
127
+ const slug = platformSlug(target);
128
+ const ext = slug.startsWith('win32') ? '.exe' : '';
129
+ const outPath = join(DIST, `maude-${slug}${ext}`);
130
+ const entry = join(ROOT, 'server.ts');
131
+ if (!existsSync(entry)) {
132
+ // T7 not landed yet — fall back to the .mjs entry so this script remains runnable mid-migration.
133
+ const legacy = join(ROOT, 'server.mjs');
134
+ if (!existsSync(legacy)) throw new Error(`Neither server.ts nor server.mjs exists in ${ROOT}`);
135
+ const proc = Bun.spawn(
136
+ [
137
+ 'bun',
138
+ 'build',
139
+ '--compile',
140
+ `--target=${target}`,
141
+ '--minify',
142
+ '--sourcemap',
143
+ `--outfile=${outPath}`,
144
+ legacy,
145
+ ],
146
+ { cwd: ROOT, stdout: 'inherit', stderr: 'inherit' }
147
+ );
148
+ const code = await proc.exited;
149
+ if (code !== 0)
150
+ throw new Error(`bun build --compile (legacy) failed for ${target} (exit ${code})`);
151
+ return { outPath };
152
+ }
153
+ const proc = Bun.spawn(
154
+ [
155
+ 'bun',
156
+ 'build',
157
+ '--compile',
158
+ `--target=${target}`,
159
+ '--minify',
160
+ '--sourcemap',
161
+ '--smol',
162
+ `--outfile=${outPath}`,
163
+ entry,
164
+ ],
165
+ { cwd: ROOT, stdout: 'inherit', stderr: 'inherit' }
166
+ );
167
+ const code = await proc.exited;
168
+ if (code !== 0) throw new Error(`bun build --compile failed for ${target} (exit ${code})`);
169
+ return { outPath };
170
+ }
171
+
172
+ // ---------- Watch mode ----------
173
+
174
+ async function watch() {
175
+ await buildClient();
176
+ await buildCss();
177
+ console.log('[build:watch] initial build complete; watching client/ + server source...');
178
+
179
+ // Bun fs.watch is recursive on darwin/linux/win32.
180
+ const fs = await import('node:fs');
181
+ const seen = new Map<string, number>();
182
+ const debounce = 50;
183
+
184
+ const trigger = async (path: string) => {
185
+ const now = Date.now();
186
+ const last = seen.get(path) ?? 0;
187
+ if (now - last < debounce) return;
188
+ seen.set(path, now);
189
+ try {
190
+ if (path.endsWith('.css')) {
191
+ const r = await buildCss();
192
+ console.log(`[build:watch] CSS rebuilt (${r.outBytes} B)`);
193
+ broadcastHmr({ type: 'css-update', path: '/_client/styles.css', hash: now });
194
+ } else if (/\.(jsx|tsx|ts|mjs)$/.test(path)) {
195
+ const r = await buildClient();
196
+ console.log(`[build:watch] client rebuilt (${r.outBytes} B)`);
197
+ broadcastHmr({ type: 'module-update', path: '/_client/client.bundle.js', hash: now });
198
+ }
199
+ } catch (err) {
200
+ console.error('[build:watch] rebuild failed:', err);
201
+ }
202
+ };
203
+
204
+ fs.watch(join(ROOT, 'client'), { recursive: true }, (_, filename) => {
205
+ if (filename) void trigger(filename);
206
+ });
207
+ }
208
+
209
+ // HMR notifications: the dev-server (server.ts) owns the WS endpoint; here we just POST a hint.
210
+ async function broadcastHmr(payload: object) {
211
+ try {
212
+ const port = Bun.env.MDCC_DEV_PORT ?? '4399';
213
+ await fetch(`http://localhost:${port}/_hmr`, {
214
+ method: 'POST',
215
+ headers: { 'content-type': 'application/json' },
216
+ body: JSON.stringify(payload),
217
+ });
218
+ } catch {
219
+ // server may not be running yet; ignore.
220
+ }
221
+ }
222
+
223
+ // ---------- Orchestrate ----------
224
+
225
+ async function main() {
226
+ if (MODE === 'dry') {
227
+ console.log(`[build] --dry-run OK (mode=dry, target=${FLAG_TARGET ?? currentTarget()})`);
228
+ return;
229
+ }
230
+
231
+ if (WATCH) {
232
+ await watch();
233
+ // Keep alive — fs.watch holds the event loop.
234
+ await new Promise(() => {});
235
+ return;
236
+ }
237
+
238
+ console.log(`[build] mode=${MODE}`);
239
+
240
+ const t0 = performance.now();
241
+ const client = await buildClient();
242
+ const t1 = performance.now();
243
+ console.log(
244
+ `[build] client.bundle.js ${client.outBytes.toLocaleString()} B (${(t1 - t0).toFixed(0)} ms)`
245
+ );
246
+
247
+ const css = await buildCss();
248
+ const t2 = performance.now();
249
+ console.log(
250
+ `[build] styles.css ${css.outBytes.toLocaleString()} B (${(t2 - t1).toFixed(0)} ms)`
251
+ );
252
+
253
+ if (MODE === 'release') {
254
+ const targets: PlatformTarget[] = FLAG_TARGET
255
+ ? [FLAG_TARGET as PlatformTarget]
256
+ : [currentTarget()];
257
+ for (const target of targets) {
258
+ const t = performance.now();
259
+ const bin = await buildServerBinary(target);
260
+ console.log(`[build] ${bin.outPath} (${(performance.now() - t).toFixed(0)} ms)`);
261
+ }
262
+ }
263
+ }
264
+
265
+ await main();
266
+
267
+ export { buildClient, buildCss, buildServerBinary, PLATFORM_MATRIX, type PlatformTarget };