minimal-agent 0.8.2 → 0.10.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 (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Bounded visual quality gate for PPT HTML decks.
4
+
5
+ Hard engineering checks still fail. Product-detail visual issues are repairable
6
+ for at most N rounds; after that this script writes a residual report and lets
7
+ the workflow continue, so the workflow does not loop forever during peak model
8
+ traffic.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import json
15
+ import subprocess
16
+ from pathlib import Path
17
+
18
+
19
+ def _run(cmd: list[str], cwd: Path) -> tuple[int, str]:
20
+ # 显式 UTF-8 解码子进程输出:中文 Windows 上 text=True 默认走 locale=GBK,
21
+ # 解码子脚本的 UTF-8 中文日志会崩(UnicodeDecodeError)。errors="replace" 再兜一层,
22
+ # 保证直接 CLI / 测试 / 非经 bash.ts 的宿主也不炸(bash.ts 已注入 PYTHONUTF8 兜底)。
23
+ proc = subprocess.run(
24
+ cmd,
25
+ cwd=str(cwd),
26
+ text=True,
27
+ encoding="utf-8",
28
+ errors="replace",
29
+ stdout=subprocess.PIPE,
30
+ stderr=subprocess.STDOUT,
31
+ )
32
+ return proc.returncode, proc.stdout
33
+
34
+
35
+ def _load_json(path: Path, default):
36
+ if not path.exists():
37
+ return default
38
+ try:
39
+ return json.loads(path.read_text(encoding="utf-8"))
40
+ except Exception:
41
+ return default
42
+
43
+
44
+ def _errors_first(text: str, limit: int = 12000) -> str:
45
+ """截断前先把 ERROR/FAILED/【…】缺陷行提到段首(各组内保持原顺序)。
46
+
47
+ 治真机事故(2026-07-02):layout_balance 输出超 12000 字符时旧版 `text[-12000:]` 尾截,
48
+ **前几页的 ERROR 被裁掉**、修复 agent 只看得到后半段 WARNING → 第 9 页标题遮挡/越界没人修。
49
+ 现在缺陷行永远在头部,截断只牺牲低价值 WARNING 尾巴。
50
+ """
51
+ if not text:
52
+ return ""
53
+ hot, cold = [], []
54
+ for line in text.splitlines():
55
+ is_hot = line.lstrip().startswith(("ERROR", "- 【", "【")) or "FAILED" in line
56
+ (hot if is_hot else cold).append(line)
57
+ return "\n".join(hot + cold)[:limit]
58
+
59
+
60
+ def _write_report(build: Path, title: str, sections: list[tuple[str, str]]) -> None:
61
+ lines = [f"# {title}", ""]
62
+ for name, text in sections:
63
+ lines.append(f"## {name}")
64
+ lines.append("")
65
+ lines.append("```text")
66
+ lines.append(_errors_first(text))
67
+ lines.append("```")
68
+ lines.append("")
69
+ (build / "visual_quality_report.md").write_text("\n".join(lines), encoding="utf-8")
70
+
71
+
72
+ def _generated_review_log(build: Path) -> tuple[bool, str]:
73
+ report = _load_json(build / "generated_image_review.json", {})
74
+ if not isinstance(report, dict) or not report:
75
+ return True, (
76
+ "generated_image_review.json 不存在或为空;跳过生成图语义硬门"
77
+ "(阶段7c 已按 2026-07-02 裁决屏蔽 LLM 图像复核,此为预期状态、非 bug;"
78
+ "生成图带字风险由 image_prompt_rewrite 背景层去内容化 + image_plan_lint 确定性拦截)。"
79
+ )
80
+ ok = bool(report.get("ok", False))
81
+ failed = report.get("failed") if isinstance(report.get("failed"), list) else []
82
+ reviews = report.get("reviews") if isinstance(report.get("reviews"), list) else []
83
+ lines = [
84
+ f"ok={ok}",
85
+ f"reviewed={report.get('reviewed', len(reviews))}",
86
+ f"failed_count={report.get('failed_count', len(failed))}",
87
+ ]
88
+ for item in failed[:20]:
89
+ if isinstance(item, dict):
90
+ lines.append(f"- {item.get('file')}: {item.get('reason')}")
91
+ for item in reviews[:20]:
92
+ if isinstance(item, dict) and item.get("hard_error"):
93
+ lines.append(f"- {item.get('file')}: {item.get('hard_error')} {item.get('reason')}")
94
+ ok = False
95
+ return ok, "\n".join(lines)
96
+
97
+
98
+ def main() -> int:
99
+ parser = argparse.ArgumentParser(description="Run bounded visual quality checks.")
100
+ parser.add_argument("--build", default="./build")
101
+ parser.add_argument("--max-rounds", type=int, default=3)
102
+ parser.add_argument("--strict-product-detail", action="store_true")
103
+ args = parser.parse_args()
104
+
105
+ root = Path.cwd()
106
+ build = Path(args.build)
107
+ build.mkdir(parents=True, exist_ok=True)
108
+ state_path = build / "visual_quality_state.json"
109
+ state = _load_json(state_path, {"round": 0})
110
+ current_round = int(state.get("round") or 0) + 1
111
+ state["round"] = current_round
112
+ state_path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")
113
+
114
+ ok_path = build / "visual_quality_ok"
115
+ residual_path = build / "visual_quality_residual.md"
116
+ ok_path.unlink(missing_ok=True)
117
+
118
+ # lint 只扫 img_plan prompt(不带 --outline):ppt 链路里 t2i prompt 已由
119
+ # image_prompt_rewrite.py 确定性净化(共享 lint 词表),design.visual.note 到不了生图模型,
120
+ # 扫 note 只剩误伤面(页 note/标题含「架构/对比图」即 halt 的假阳事故)。
121
+ # 若未来移除 image_prompt_rewrite,需恢复 --outline ./build/design.json。
122
+ lint_cmd = [
123
+ "python3",
124
+ "skills/ui-design/scripts/image_plan_lint.py",
125
+ str(build / "img_plan.json"),
126
+ "-v",
127
+ ]
128
+ source_cmd = [
129
+ "python3",
130
+ "skills/ui-design/scripts/source_usage_check.py",
131
+ "--build",
132
+ str(build),
133
+ "--require-existing-derived",
134
+ ]
135
+ style_cmd = [
136
+ "python3",
137
+ "skills/ui-design/scripts/deck_visual_style_check.py",
138
+ str(build / "deck.html"),
139
+ "--design",
140
+ str(build / "design.json"),
141
+ "--image-report",
142
+ str(build / "image_generation_report.json"),
143
+ "--source-plan",
144
+ str(build / "source_layout_plan.json"),
145
+ "-v",
146
+ ]
147
+ if args.strict_product_detail:
148
+ style_cmd.append("--strict-product-detail")
149
+
150
+ # 版式规划执行检查(2026-07-03 真机第4页事故):contain 类客户图被 build 违规铺成整页背景。
151
+ # Edit 可修 → 归**可修档**驱动修复轮(source_usage_check 主模式在 hard 档,违规不能混进去 halt)。
152
+ intent_cmd = [
153
+ "python3",
154
+ "skills/ui-design/scripts/source_usage_check.py",
155
+ "--build",
156
+ str(build),
157
+ "--layout-intent-only",
158
+ ]
159
+
160
+ lint_status, lint_log = _run(lint_cmd, root)
161
+ source_status, source_log = _run(source_cmd, root)
162
+ intent_status, intent_log = _run(intent_cmd, root)
163
+ style_status, style_log = _run(style_cmd, root)
164
+ generated_review_ok, generated_review_log = _generated_review_log(build)
165
+
166
+ # 几何构图门(2026-07-02 接线升级:catastrophic-only → 全量 + --ppt-strict):
167
+ # 旧口径只判「内容不可见灾难」——第 9 页标题被卡遮挡 / 内容超出下边缘这类**测到了却不驱动修复**
168
+ # (全量只在导出阶段 || true 报告态),41 个缺陷带残留放行是真机事故的直接原因。
169
+ # 现在全量跑 + --ppt-strict(重叠去噪防追鬼 / 越界升 ERROR / 大空卡升 ERROR,见 layout_balance.py),
170
+ # 归入**可修档**(visual_failed)驱动 repair;「内容不可见」零容忍仍由导出阶段 catastrophic 硬门兜底。
171
+ export_dir = build / "export"
172
+ geometry_status = 0
173
+ if (export_dir / "layout-report.json").exists() or (export_dir / "export-manifest.json").exists():
174
+ geometry_cmd = [
175
+ "python3",
176
+ "skills/ui-design/scripts/layout_balance.py",
177
+ str(export_dir),
178
+ "--ppt-strict",
179
+ "--no-occupancy",
180
+ ]
181
+ # source_layout_plan.json 只在有客户图版式规划时才存在(webppt 无此文件)——传入路径即可,
182
+ # layout_balance.py 自行判空跳过,额外反查 is_dense_info 客户图渲染宽度是否达标(WARNING)。
183
+ source_layout_plan = build / "source_layout_plan.json"
184
+ if source_layout_plan.exists():
185
+ geometry_cmd += ["--source-layout-plan", str(source_layout_plan)]
186
+ geometry_status, geometry_log = _run(geometry_cmd, root)
187
+ else:
188
+ geometry_log = (
189
+ "layout-report.json 不存在(应由本阶段起头的 measure-only 产出);跳过几何复核"
190
+ "——不阻断,但意味着本轮未做几何检查(如 chromium 缺失导致 measure 失败)。"
191
+ )
192
+
193
+ # 像素级文字可读性门(2026-07-02 新增):CSS 对比度在「文字压背景图」页物理上测不准
194
+ # (resolveBg 只拿得到页面底色)——用 measure-only --with-bg-shots 落的背景截图采真实像素。
195
+ # 缺 PIL/缺截图时脚本响亮 SKIP 且 exit 0(fail-open),不阻断。归可修档。
196
+ readability_status, readability_log = _run(
197
+ [
198
+ "python3",
199
+ "skills/ui-design/scripts/text_readability_pixel.py",
200
+ str(export_dir),
201
+ ],
202
+ root,
203
+ )
204
+
205
+ # SVG 图表一致性门(2026-07-02 新增):柱高与百分比标签颠倒 / 标签装不下(slide11/19 事故)。
206
+ # 复杂图 fail-open 跳过。归可修档。
207
+ chart_status, chart_log = _run(
208
+ [
209
+ "python3",
210
+ "skills/ui-design/scripts/chart_sanity_check.py",
211
+ str(build / "deck.html"),
212
+ ],
213
+ root,
214
+ )
215
+
216
+ # 用户裁决(2026-07-02):构图/视觉验证门**只吃确定性脚本信号**,LLM 图像识别裁决屏蔽——
217
+ # generated_image_review(多模态语义复核)不再进 hard_failed(ppt.yaml 阶段14-16 已同步 when:"false")。
218
+ # 复核结果仍记录进报告(信息性,供人工参考/未来恢复);恢复 = 把 `or not generated_review_ok` 加回。
219
+ hard_failed = (
220
+ lint_status != 0
221
+ or source_status != 0
222
+ )
223
+ visual_failed = (
224
+ style_status != 0
225
+ or intent_status != 0
226
+ or geometry_status != 0
227
+ or readability_status != 0
228
+ or chart_status != 0
229
+ )
230
+
231
+ report = {
232
+ "round": current_round,
233
+ "max_rounds": args.max_rounds,
234
+ "hard_ok": not hard_failed,
235
+ "visual_ok": not visual_failed,
236
+ "ok": not hard_failed and not visual_failed,
237
+ "accepted_with_residual": False,
238
+ "lint_status": lint_status,
239
+ "source_status": source_status,
240
+ "intent_status": intent_status,
241
+ "style_status": style_status,
242
+ "geometry_status": geometry_status,
243
+ "readability_status": readability_status,
244
+ "chart_status": chart_status,
245
+ "generated_image_review_ok": generated_review_ok,
246
+ }
247
+ (build / "visual_quality_report.json").write_text(
248
+ json.dumps(report, ensure_ascii=False, indent=2),
249
+ encoding="utf-8",
250
+ )
251
+ _write_report(
252
+ build,
253
+ f"视觉质量门第 {current_round}/{args.max_rounds} 轮",
254
+ [
255
+ ("image_plan_lint", lint_log),
256
+ ("generated_image_review", generated_review_log),
257
+ ("source_usage_check", source_log),
258
+ ("source_layout_intent (版式规划执行)", intent_log),
259
+ ("deck_visual_style_check", style_log),
260
+ ("layout_balance (几何·全量 --ppt-strict)", geometry_log),
261
+ ("text_readability_pixel (像素级文字压图可读性)", readability_log),
262
+ ("chart_sanity_check (SVG 图表一致性)", chart_log),
263
+ ],
264
+ )
265
+
266
+ if hard_failed:
267
+ print("VISUAL_QUALITY_HARD_FAILED")
268
+ return 1
269
+ if not visual_failed:
270
+ ok_path.touch()
271
+ residual_path.unlink(missing_ok=True)
272
+ print("VISUAL_QUALITY_OK")
273
+ return 0
274
+ if current_round >= args.max_rounds:
275
+ # 达轮数**带残留放行**(落实 docstring「lets the workflow continue」与「逐页有界修复后放行」裁决):
276
+ # 视觉档(style/geometry)问题记录进 residual 后 touch ok 放行导出——fast(max_rounds=1)没有
277
+ # 修复步,不放行 = 无人能修的确定性死端;best 修满轮数后同理不再空转。
278
+ # 硬档(lint/source/生成图复核)不走这里(上面已 exit 1);「内容不可见」灾难仍由
279
+ # stage_export 的 layout_balance --catastrophic-only 硬门兜底,绝不因放行而出货不可见 deck。
280
+ report["accepted_with_residual"] = True
281
+ (build / "visual_quality_report.json").write_text(
282
+ json.dumps(report, ensure_ascii=False, indent=2),
283
+ encoding="utf-8",
284
+ )
285
+ residual_path.write_text(
286
+ "# 视觉质量残留问题(带残留放行)\n\n"
287
+ f"已达到最多 {args.max_rounds} 轮视觉修复,带残留放行导出。以下问题供人工复核参考;\n"
288
+ "「内容不可见」灾难仍由导出阶段的 catastrophic 几何硬门兜底拦截。\n\n"
289
+ + (build / "visual_quality_report.md").read_text(encoding="utf-8"),
290
+ encoding="utf-8",
291
+ )
292
+ ok_path.touch()
293
+ print("VISUAL_QUALITY_ACCEPTED_WITH_RESIDUAL")
294
+ return 0
295
+
296
+ print("VISUAL_QUALITY_NEEDS_REPAIR")
297
+ return 0
298
+
299
+
300
+ if __name__ == "__main__":
301
+ raise SystemExit(main())
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import json
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import tempfile
9
+ import unittest
10
+ from pathlib import Path
11
+
12
+
13
+ HERE = Path(__file__).resolve().parent
14
+ SCRIPT = HERE / "visual_quality_gate.py"
15
+ ROOT = HERE.parents[2]
16
+
17
+
18
+ def _write(path: Path, text: str) -> None:
19
+ path.parent.mkdir(parents=True, exist_ok=True)
20
+ path.write_text(text, encoding="utf-8")
21
+
22
+
23
+ class VisualQualityGateTest(unittest.TestCase):
24
+ def test_visual_residual_accepted_after_max_rounds(self):
25
+ # 达 --max-rounds 且仅视觉档失败 → **带残留放行**:ok 与 residual 并存、
26
+ # accepted_with_residual=true、打印 ACCEPTED_WITH_RESIDUAL。
27
+ # (原断言「残留不产 ok」已知情反转:fast 无修复步,阻断=无人能修的确定性死端;
28
+ # docstring 与「有界修复后放行」裁决都是放行语义。硬档 halt 见下一测试,不受影响。)
29
+ with tempfile.TemporaryDirectory() as d:
30
+ build = Path(d)
31
+ _write(
32
+ build / "deck.html",
33
+ """
34
+ <html><body>
35
+ <section class="slide fullbleed" data-screen-label="01 测试">
36
+ <div class="edit-bg"><img class="bg-img" src="assets/ai_p1.png"></div>
37
+ <div class="edit-content"><h1>只有文字</h1></div>
38
+ </section>
39
+ </body></html>
40
+ """,
41
+ )
42
+ _write(
43
+ build / "design.json",
44
+ json.dumps(
45
+ {
46
+ "pages": [
47
+ {
48
+ "page": 1,
49
+ "composition_style": "scenario-fullbleed",
50
+ "page_grammar": "hero-scene",
51
+ "max_visible_words": 80,
52
+ "supporting_visual_layer": "前景产品特写",
53
+ }
54
+ ]
55
+ },
56
+ ensure_ascii=False,
57
+ ),
58
+ )
59
+ _write(build / "img_plan.json", "[]")
60
+ _write(build / "source_assets.json", json.dumps({"images": []}, ensure_ascii=False))
61
+ _write(build / "source_layout_plan.json", json.dumps({"enhance_entries": []}, ensure_ascii=False))
62
+ _write(build / "image_generation_report.json", json.dumps({"degraded": []}, ensure_ascii=False))
63
+
64
+ res = subprocess.run(
65
+ [sys.executable, str(SCRIPT), "--build", str(build), "--max-rounds", "1", "--strict-product-detail"],
66
+ cwd=str(ROOT),
67
+ text=True,
68
+ stdout=subprocess.PIPE,
69
+ stderr=subprocess.STDOUT,
70
+ check=False,
71
+ )
72
+ self.assertEqual(res.returncode, 0, res.stdout)
73
+ self.assertIn("VISUAL_QUALITY_ACCEPTED_WITH_RESIDUAL", res.stdout)
74
+ self.assertTrue((build / "visual_quality_residual.md").exists())
75
+ self.assertTrue((build / "visual_quality_ok").exists())
76
+ report = json.loads((build / "visual_quality_report.json").read_text(encoding="utf-8"))
77
+ self.assertTrue(report.get("accepted_with_residual"), report)
78
+
79
+ def test_generated_image_review_failure_is_informational_only(self):
80
+ # 知情反转(2026-07-02 用户裁决):构图/视觉验证门只吃**确定性脚本**信号,
81
+ # LLM 图像识别复核(generated_image_review.json)已屏蔽出 hard_failed——
82
+ # 即便复核 ok=false 也**不**硬 halt,只记录进报告(generated_image_review_ok=false)。
83
+ # 原断言「复核 fail → VISUAL_QUALITY_HARD_FAILED」是被反转的旧行为。
84
+ with tempfile.TemporaryDirectory() as d:
85
+ build = Path(d)
86
+ _write(
87
+ build / "deck.html",
88
+ """
89
+ <html><body>
90
+ <section class="slide fullbleed" data-screen-label="01 测试">
91
+ <div class="edit-bg"><img class="bg-img" src="assets/ai_p1.png"></div>
92
+ <div class="edit-content"><h1>全过程可追溯</h1><p>一眼看见关键链路。</p><img class="product-visual" src="assets/hero.png"></div>
93
+ </section>
94
+ </body></html>
95
+ """,
96
+ )
97
+ _write(
98
+ build / "design.json",
99
+ json.dumps(
100
+ {
101
+ "pages": [
102
+ {
103
+ "page": 1,
104
+ "composition_style": "scenario-fullbleed",
105
+ "page_grammar": "hero-scene",
106
+ "max_visible_words": 80,
107
+ "supporting_visual_layer": "前景产品特写",
108
+ }
109
+ ]
110
+ },
111
+ ensure_ascii=False,
112
+ ),
113
+ )
114
+ _write(build / "img_plan.json", "[]")
115
+ _write(build / "source_assets.json", json.dumps({"images": []}, ensure_ascii=False))
116
+ _write(build / "source_layout_plan.json", json.dumps({"enhance_entries": []}, ensure_ascii=False))
117
+ _write(build / "image_generation_report.json", json.dumps({"degraded": []}, ensure_ascii=False))
118
+ _write(
119
+ build / "generated_image_review.json",
120
+ json.dumps(
121
+ {
122
+ "ok": False,
123
+ "failed_count": 1,
124
+ "failed": [{"file": "ai_p1.png", "reason": "背景含数字"}],
125
+ },
126
+ ensure_ascii=False,
127
+ ),
128
+ )
129
+
130
+ res = subprocess.run(
131
+ [sys.executable, str(SCRIPT), "--build", str(build), "--max-rounds", "1", "--strict-product-detail"],
132
+ cwd=str(ROOT),
133
+ text=True,
134
+ stdout=subprocess.PIPE,
135
+ stderr=subprocess.STDOUT,
136
+ check=False,
137
+ )
138
+ self.assertEqual(res.returncode, 0, res.stdout)
139
+ self.assertNotIn("VISUAL_QUALITY_HARD_FAILED", res.stdout)
140
+ report = json.loads((build / "visual_quality_report.json").read_text(encoding="utf-8"))
141
+ self.assertTrue(report.get("hard_ok"), report) # 硬档只由确定性脚本决定
142
+ self.assertFalse(report.get("generated_image_review_ok"), report) # 复核结果仍被记录(信息性)
143
+ self.assertTrue((build / "visual_quality_ok").exists()) # 放行(直接 OK 或带残留放行皆产 ok)
144
+
145
+ def test_catastrophic_geometry_is_repairable_not_hard(self):
146
+ # F2 回归锁:有 layout-report.json 且几何 catastrophic(空页无视觉)时——
147
+ # 几何走**可修档**(visual_failed)而非 hard_failed:returncode==0(非 HARD_FAILED)、
148
+ # report.geometry_status!=0。改前(几何算 hard)此用例会 return 1。
149
+ # 达轮数后带残留放行(ok 会存在)——「内容不可见不出货」由 stage_export 的
150
+ # layout_balance --catastrophic-only 硬门保证(该门行为不动),不靠此处阻断。
151
+ with tempfile.TemporaryDirectory() as d:
152
+ build = Path(d)
153
+ _write(
154
+ build / "deck.html",
155
+ """
156
+ <html><body>
157
+ <section class="slide" data-screen-label="01 空页"><div class="edit-content"></div></section>
158
+ </body></html>
159
+ """,
160
+ )
161
+ _write(
162
+ build / "design.json",
163
+ json.dumps({"pages": [{"page": 1, "composition_style": "scenario-fullbleed",
164
+ "page_grammar": "hero-scene", "max_visible_words": 80}]},
165
+ ensure_ascii=False),
166
+ )
167
+ _write(build / "img_plan.json", "[]")
168
+ _write(build / "source_assets.json", json.dumps({"images": []}, ensure_ascii=False))
169
+ _write(build / "source_layout_plan.json", json.dumps({"enhance_entries": []}, ensure_ascii=False))
170
+ _write(build / "image_generation_report.json", json.dumps({"degraded": []}, ensure_ascii=False))
171
+ # 灾难级几何:content_boxes==0 且无任何真实视觉(无底图/无前景切图)→ catastrophic ERROR。
172
+ empty_balance = {
173
+ "grid": {"cols": 16, "rows": 8}, "contentBoxes": 0,
174
+ "hasBgImage": False, "hasForegroundVisual": False, "coverage": 0.0,
175
+ "halfOccupancy": {"top": 0, "bottom": 0, "left": 0, "right": 0},
176
+ "bbox": {"x": 0, "y": 0, "w": 1920, "h": 1080}, "centroid": {"x": 960, "y": 540},
177
+ "overlaps": [], "cards": [], "overflowRuns": [], "smallText": [],
178
+ "bakedText": [], "offCanvas": [], "lowContrast": [],
179
+ }
180
+ _write(
181
+ build / "export" / "layout-report.json",
182
+ json.dumps({"mode": "hybrid", "width": 1920, "height": 1080,
183
+ "slides": [{"index": 1, "label": "01 空页", "balance": empty_balance}]},
184
+ ensure_ascii=False),
185
+ )
186
+
187
+ res = subprocess.run(
188
+ [sys.executable, str(SCRIPT), "--build", str(build), "--max-rounds", "1", "--strict-product-detail"],
189
+ cwd=str(ROOT),
190
+ text=True,
191
+ encoding="utf-8",
192
+ stdout=subprocess.PIPE,
193
+ stderr=subprocess.STDOUT,
194
+ check=False,
195
+ )
196
+ self.assertEqual(res.returncode, 0, res.stdout) # 几何不再 hard-fail
197
+ self.assertNotIn("VISUAL_QUALITY_HARD_FAILED", res.stdout)
198
+ report = json.loads((build / "visual_quality_report.json").read_text(encoding="utf-8"))
199
+ self.assertNotEqual(report.get("geometry_status"), 0, report) # 几何确被检出
200
+ # max-rounds=1 → 本轮即达轮数:带残留放行(ok+residual 并存,出货兜底在导出 catastrophic 硬门)
201
+ self.assertTrue(report.get("accepted_with_residual"), report)
202
+ self.assertTrue((build / "visual_quality_ok").exists())
203
+ self.assertTrue((build / "visual_quality_residual.md").exists())
204
+
205
+
206
+ class ErrorsFirstTest(unittest.TestCase):
207
+ """报告截断前 ERROR 行提头(2026-07-02:layout_balance 超 12000 字符尾截把第 9 页 ERROR 裁掉的事故)。"""
208
+
209
+ def test_errors_moved_to_head_before_truncation(self):
210
+ sys.path.insert(0, str(HERE))
211
+ from visual_quality_gate import _errors_first
212
+ text = chr(10).join(
213
+ [f"WARNING: 噪声 {i}" for i in range(3)]
214
+ + [" - 【内容相互叠加】09 页 · 标题被卡压", "ERROR: 硬缺陷一条"]
215
+ + [f"WARNING: 噪声尾 {i}" for i in range(3)]
216
+ )
217
+ out = _errors_first(text)
218
+ lines = out.splitlines()
219
+ self.assertIn("【内容相互叠加】", lines[0])
220
+ self.assertTrue(lines[1].startswith("ERROR"))
221
+ # WARNING 全保留(无截断场景不丢行)
222
+ self.assertEqual(len(lines), 8)
223
+
224
+ def test_truncation_sacrifices_tail_warnings_not_errors(self):
225
+ from visual_quality_gate import _errors_first
226
+ text = chr(10).join(["ERROR: 关键缺陷"] + ["WARNING: " + "x" * 100 for _ in range(300)])
227
+ out = _errors_first(text, limit=2000)
228
+ self.assertTrue(out.startswith("ERROR: 关键缺陷"))
229
+ self.assertLessEqual(len(out), 2000)
230
+
231
+
232
+ if __name__ == "__main__":
233
+ unittest.main(verbosity=2)
@@ -0,0 +1,19 @@
1
+ # Deck 端到端测试题
2
+
3
+ 使用 `ui-design` 技能,为 **舒适视觉 第三季度港口韧性简报** 创建一个 5 页 HTML deck。
4
+
5
+ 受众:港口管理层和计划负责人。
6
+
7
+ 设计约束:
8
+ - 视觉上像可编辑的 PPT 风格管理层简报,但实现方式是 HTML。
9
+ - 使用固定 16:9 舞台,并实现 viewport scaling。
10
+ - slide 必须使用 1 起始 `data-screen-label`,例如 `01 标题`。
11
+ - 使用 `localStorage` 持久化当前 slide。
12
+ - 提供上一页/下一页控件和键盘导航。
13
+ - 视觉系统要与 Web 页面测试相关,但节奏要更适合演示文稿。
14
+ - 避免密集段落页、填充结论、通用渐变背景和素材库感装饰。
15
+ - 交付一个能通过 `scripts/check_ui_artifact.py` 和 `scripts/check_language_policy.py` 的单文件 HTML。
16
+ - 所有用户可见文案必须使用简体中文,品牌名 舒适视觉 可以保留英文。
17
+
18
+ 期望输出路径:
19
+ `minimal-agent/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html`
@@ -0,0 +1,17 @@
1
+ # Web 页面端到端测试题
2
+
3
+ 使用 `ui-design` 技能,为 **舒适视觉** 创建一个高保真 Web 页面。舒适视觉 是面向区域港口管理机构的气候风险运营产品。
4
+
5
+ 受众:运营总监。他们需要在每周计划会之前理解泊位中断风险、排放暴露和天气导致的容量变化。
6
+
7
+ 设计约束:
8
+ - 首屏必须像真实运营产品,而不是泛泛 SaaS 落地页。
9
+ - 使用克制但有辨识度的视觉系统:海事中性色、信号色、精确字体、高密度但可读的信息,以及一个令人记住的视觉母题。
10
+ - 内容要真实可信:船舶队列、天气窗口、泊位状态、排放暴露和计划动作。
11
+ - 至少包含一个使用 `ui-design` edit-mode 协议的可调设计维度。
12
+ - 避免填充指标、紫色渐变、通用图标网格、表情符号和装饰性卡片堆叠。
13
+ - 交付一个能通过 `scripts/check_ui_artifact.py` 和 `scripts/check_language_policy.py` 的单文件 HTML。
14
+ - 所有用户可见文案必须使用简体中文,品牌名 舒适视觉 可以保留英文。
15
+
16
+ 期望输出路径:
17
+ `minimal-agent/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html`
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import { resolve } from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ import { chromium } from 'playwright-core';
6
+
7
+ const skillRoot = resolve(fileURLToPath(new URL('../..', import.meta.url)));
8
+ const runRoot = resolve(skillRoot, 'tests/e2e/runs/iteration-1');
9
+
10
+ const targets = {
11
+ webpage: {
12
+ html: resolve(runRoot, 'webpage/outputs/index.html'),
13
+ shots: resolve(runRoot, 'webpage/screenshots'),
14
+ checks: resolve(runRoot, 'webpage/checks/browser-check.json'),
15
+ },
16
+ deck: {
17
+ html: resolve(runRoot, 'deck/outputs/deck.html'),
18
+ shots: resolve(runRoot, 'deck/screenshots'),
19
+ checks: resolve(runRoot, 'deck/checks/browser-check.json'),
20
+ },
21
+ };
22
+
23
+ async function inspectPage(page) {
24
+ return page.evaluate(() => ({
25
+ title: document.title,
26
+ screenLabels: Array.from(document.querySelectorAll('[data-screen-label]')).map((node) =>
27
+ node.getAttribute('data-screen-label'),
28
+ ),
29
+ editModeBlocks: (document.documentElement.outerHTML.match(/EDITMODE-BEGIN/g) || []).length,
30
+ hasEditModeAvailable: document.documentElement.outerHTML.includes('__edit_mode_available'),
31
+ bodyOverflowX: getComputedStyle(document.body).overflowX,
32
+ documentWidth: document.documentElement.scrollWidth,
33
+ viewportWidth: window.innerWidth,
34
+ }));
35
+ }
36
+
37
+ async function main() {
38
+ const browser = await chromium.launch({ headless: true });
39
+ const results = {};
40
+
41
+ try {
42
+ const page = await browser.newPage({ viewport: { width: 1440, height: 980 } });
43
+ const webpageConsole = [];
44
+ page.on('console', (msg) => webpageConsole.push({ type: msg.type(), text: msg.text() }));
45
+ page.on('pageerror', (error) => webpageConsole.push({ type: 'pageerror', text: error.message }));
46
+ await page.goto(pathToFileURL(targets.webpage.html).href, { waitUntil: 'load' });
47
+ await mkdir(targets.webpage.shots, { recursive: true });
48
+ await page.screenshot({ path: resolve(targets.webpage.shots, 'desktop.png'), fullPage: true });
49
+ const webpageDesktop = await inspectPage(page);
50
+ await page.setViewportSize({ width: 390, height: 844 });
51
+ await page.reload({ waitUntil: 'load' });
52
+ await page.screenshot({ path: resolve(targets.webpage.shots, 'mobile.png'), fullPage: true });
53
+ const webpageMobile = await inspectPage(page);
54
+ results.webpage = {
55
+ console: webpageConsole,
56
+ desktop: webpageDesktop,
57
+ mobile: webpageMobile,
58
+ pass: webpageConsole.filter((item) => item.type === 'error' || item.type === 'pageerror').length === 0,
59
+ };
60
+
61
+ const deckPage = await browser.newPage({ viewport: { width: 1440, height: 900 } });
62
+ const deckConsole = [];
63
+ deckPage.on('console', (msg) => deckConsole.push({ type: msg.type(), text: msg.text() }));
64
+ deckPage.on('pageerror', (error) => deckConsole.push({ type: 'pageerror', text: error.message }));
65
+ await deckPage.goto(pathToFileURL(targets.deck.html).href, { waitUntil: 'load' });
66
+ await mkdir(targets.deck.shots, { recursive: true });
67
+ await deckPage.screenshot({ path: resolve(targets.deck.shots, 'slide-01.png'), fullPage: false });
68
+ const deckSlideOne = await inspectPage(deckPage);
69
+ await deckPage.getByRole('button', { name: '下一页' }).click();
70
+ await deckPage.screenshot({ path: resolve(targets.deck.shots, 'slide-02.png'), fullPage: false });
71
+ const deckSlideTwo = await deckPage.evaluate(() => ({
72
+ activeSlide: localStorage.getItem('comfort-vision-active-slide'),
73
+ counter: document.getElementById('slide-counter')?.textContent,
74
+ visibleLabel: Array.from(document.querySelectorAll('.slide'))
75
+ .find((node) => !node.hidden)
76
+ ?.getAttribute('data-screen-label'),
77
+ }));
78
+ results.deck = {
79
+ console: deckConsole,
80
+ slideOne: deckSlideOne,
81
+ slideTwo: deckSlideTwo,
82
+ pass:
83
+ deckConsole.filter((item) => item.type === 'error' || item.type === 'pageerror').length === 0 &&
84
+ deckSlideTwo.activeSlide === '1' &&
85
+ deckSlideTwo.visibleLabel === '02 运营态势',
86
+ };
87
+ } finally {
88
+ await browser.close();
89
+ }
90
+
91
+ await mkdir(resolve(runRoot, 'webpage/checks'), { recursive: true });
92
+ await mkdir(resolve(runRoot, 'deck/checks'), { recursive: true });
93
+ await writeFile(targets.webpage.checks, JSON.stringify(results.webpage, null, 2));
94
+ await writeFile(targets.deck.checks, JSON.stringify(results.deck, null, 2));
95
+ console.log(JSON.stringify({ webpage: results.webpage.pass, deck: results.deck.pass }, null, 2));
96
+ }
97
+
98
+ main().catch((error) => {
99
+ console.error(error);
100
+ process.exit(1);
101
+ });