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,427 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ source_usage_check_test.py —— source_usage_check.py 单测。
5
+
6
+ 锁定:
7
+ - 全部客户图被 deck.html 引用 → code 0 / SOURCE_USAGE_OK。
8
+ - 有客户图未被引用 → code 1 / UNUSED 列出缺的。
9
+ - background-image:url() 引用也算「已用」。
10
+ - 无客户图 / 无 material.json → code 0(跳过)。
11
+ - deck.html 不存在 → code 2 / NO_DECK。
12
+ 依赖:标准库。
13
+ """
14
+
15
+ import json
16
+ import os
17
+ import sys
18
+ import tempfile
19
+ import unittest
20
+
21
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
22
+ import source_usage_check as su # noqa: E402
23
+
24
+
25
+ def _write(path, obj):
26
+ with open(path, "w", encoding="utf-8") as f:
27
+ if isinstance(obj, str):
28
+ f.write(obj)
29
+ else:
30
+ json.dump(obj, f, ensure_ascii=False)
31
+
32
+
33
+ class TestUsageCheck(unittest.TestCase):
34
+ def setUp(self):
35
+ self.d = tempfile.mkdtemp(prefix="usage-")
36
+ self.material = os.path.join(self.d, "material.json")
37
+ self.deck = os.path.join(self.d, "deck.html")
38
+
39
+ def _material(self, files):
40
+ _write(self.material, {"has_source": True, "images": [{"file": f} for f in files]})
41
+
42
+ def _asset(self, name):
43
+ assets = os.path.join(self.d, "assets")
44
+ os.makedirs(assets, exist_ok=True)
45
+ path = os.path.join(assets, name)
46
+ with open(path, "wb") as f:
47
+ f.write(b"x")
48
+ return path
49
+
50
+ def _failure_report(self, *items):
51
+ _write(
52
+ os.path.join(self.d, "image_generation_report.json"),
53
+ {"degraded": [{"file": file, "source_file": source, "mode": "image-to-image"} for file, source in items]},
54
+ )
55
+
56
+ def test_all_used(self):
57
+ self._material(["src_1.png", "src_2.png"])
58
+ _write(
59
+ self.deck,
60
+ '<img src="assets/enhanced_src_1.png"><div style="background-image:url(assets/enhanced_src_2.png)"></div>',
61
+ )
62
+ code, msg = su.check(self.material, self.deck)
63
+ self.assertEqual(code, 0)
64
+ self.assertIn("SOURCE_USAGE_OK", msg)
65
+
66
+ def test_style_tag_srcset_and_query_are_real_usage(self):
67
+ self._material(["src_1.png", "src_2.png", "src_3.png"])
68
+ _write(
69
+ self.deck,
70
+ """
71
+ <style>.hero{background-image:url("./assets/enhanced_src_1.png?v=2")}</style>
72
+ <picture><source srcset="assets/enhanced_src_2.png 1x, assets/other.png 2x"></picture>
73
+ <svg><image href="assets/enhanced_src_3.png#crop"></image></svg>
74
+ """,
75
+ )
76
+ code, msg = su.check(self.material, self.deck)
77
+ self.assertEqual(code, 0)
78
+ self.assertIn("SOURCE_USAGE_OK", msg)
79
+
80
+ def test_enhanced_source_image_counts_as_original_usage(self):
81
+ _write(
82
+ self.material,
83
+ {
84
+ "has_source": True,
85
+ "images": [
86
+ {"file": "src_1.png"},
87
+ {"file": "src_2.png", "enhanced_file": "custom_enhanced_2.png"},
88
+ ],
89
+ },
90
+ )
91
+ self._asset("enhanced_src_1.png")
92
+ self._asset("custom_enhanced_2.png")
93
+ _write(
94
+ self.deck,
95
+ '<img src="assets/enhanced_src_1.png"><img src="assets/custom_enhanced_2.png">',
96
+ )
97
+ code, msg = su.check(self.material, self.deck)
98
+ self.assertEqual(code, 0)
99
+ self.assertIn("SOURCE_USAGE_OK", msg)
100
+
101
+ def test_planned_enhanced_file_counts_before_generation(self):
102
+ _write(
103
+ self.material,
104
+ {"has_source": True, "images": [{"file": "src_1.png", "enhanced_file": "enhanced_src_1.png"}]},
105
+ )
106
+ _write(self.deck, '<img src="assets/enhanced_src_1.png">')
107
+ code, msg = su.check(self.material, self.deck)
108
+ self.assertEqual(code, 0)
109
+ self.assertIn("SOURCE_USAGE_OK", msg)
110
+
111
+ def test_missing_enhanced_file_does_not_count_at_export_gate(self):
112
+ _write(
113
+ self.material,
114
+ {"has_source": True, "images": [{"file": "src_1.png", "enhanced_file": "enhanced_src_1.png"}]},
115
+ )
116
+ _write(self.deck, '<img src="assets/enhanced_src_1.png">')
117
+ code, msg = su.check(self.material, self.deck, require_existing_derived=True)
118
+ self.assertEqual(code, 1)
119
+ self.assertIn("UNUSED", msg)
120
+ self.assertIn("src_1.png", msg)
121
+
122
+ def test_low_resolution_original_image_cannot_be_background(self):
123
+ _write(
124
+ self.material,
125
+ {"has_source": True, "images": [{"file": "src_1.png", "w": 640, "h": 412}]},
126
+ )
127
+ _write(self.deck, '<section style="background-image:url(assets/src_1.png)"></section>')
128
+ code, msg = su.check(self.material, self.deck)
129
+ self.assertEqual(code, 1)
130
+ self.assertIn("LOW_RES_BACKGROUND", msg)
131
+
132
+ def test_low_resolution_original_image_can_be_inline_evidence(self):
133
+ _write(
134
+ self.material,
135
+ {"has_source": True, "images": [{"file": "src_1.png", "w": 640, "h": 412}]},
136
+ )
137
+ self._failure_report(("enhanced_src_1.png", "src_1.png"))
138
+ _write(self.deck, '<figure class="evidence"><img src="assets/src_1.png"></figure>')
139
+ code, msg = su.check(self.material, self.deck)
140
+ self.assertEqual(code, 0)
141
+ self.assertIn("SOURCE_USAGE_OK", msg)
142
+
143
+ def test_original_image_without_enhance_failure_is_rejected(self):
144
+ _write(
145
+ self.material,
146
+ {"has_source": True, "images": [{"file": "src_1.png", "w": 1600, "h": 900}]},
147
+ )
148
+ _write(self.deck, '<figure class="evidence"><img src="assets/src_1.png"></figure>')
149
+ code, msg = su.check(self.material, self.deck)
150
+ self.assertEqual(code, 1)
151
+ self.assertIn("SOURCE_DIRECT_WITHOUT_ENHANCE_FAILURE", msg)
152
+
153
+ def test_text_comment_and_script_mentions_do_not_count_as_usage(self):
154
+ self._material(["src_1.png", "src_2.png", "src_3.png"])
155
+ _write(
156
+ self.deck,
157
+ """
158
+ <p>素材 src_1.png 应该放在这里</p>
159
+ <!-- src_2.png TODO -->
160
+ <script>const asset = "src_3.png";</script>
161
+ """,
162
+ )
163
+ code, msg = su.check(self.material, self.deck)
164
+ self.assertEqual(code, 1)
165
+ self.assertIn("UNUSED", msg)
166
+ self.assertIn("src_1.png", msg)
167
+ self.assertIn("src_2.png", msg)
168
+ self.assertIn("src_3.png", msg)
169
+
170
+ def test_plain_anchor_href_does_not_count_but_image_preload_does(self):
171
+ self._material(["src_1.png", "src_2.png"])
172
+ _write(
173
+ self.deck,
174
+ """
175
+ <a href="assets/src_1.png">下载素材</a>
176
+ <link rel="preload" as="image" href="assets/enhanced_src_2.png">
177
+ """,
178
+ )
179
+ code, msg = su.check(self.material, self.deck)
180
+ self.assertEqual(code, 1)
181
+ self.assertIn("src_1.png", msg)
182
+ self.assertNotIn("src_2.png", msg)
183
+
184
+ def test_some_unused(self):
185
+ self._material(["src_1.png", "src_2.png", "src_3.png"])
186
+ _write(self.deck, '<img src="assets/enhanced_src_1.png">')
187
+ code, msg = su.check(self.material, self.deck)
188
+ self.assertEqual(code, 1)
189
+ self.assertIn("UNUSED", msg)
190
+ self.assertIn("src_2.png", msg)
191
+ self.assertIn("src_3.png", msg)
192
+ self.assertNotIn("src_1.png", msg)
193
+
194
+ def test_derived_extension_drift_still_counts_as_used(self):
195
+ # 2026-07-05 真机复盘:material.json 无 enhanced_file 字段时按「原扩展名」猜
196
+ # enhanced_src_2.jpg,但实际生图落盘是 .png;deck.html 用 .bg-img 引用 .png 版,
197
+ # 逐字符串比对对不上,误判 UNUSED 直接 hard halt(真机连续两次真实事故)。
198
+ self._material(["src_2.jpg"])
199
+ self._asset("enhanced_src_2.png")
200
+ _write(
201
+ self.deck,
202
+ '<img class="bg-img" src="assets/enhanced_src_2.png" data-source-file="src_2.jpg">',
203
+ )
204
+ code, msg = su.check(self.material, self.deck, require_existing_derived=True)
205
+ self.assertEqual(code, 0)
206
+ self.assertIn("SOURCE_USAGE_OK", msg)
207
+
208
+ def test_no_images(self):
209
+ self._material([])
210
+ _write(self.deck, "<html></html>")
211
+ code, msg = su.check(self.material, self.deck)
212
+ self.assertEqual(code, 0)
213
+ self.assertIn("无客户图", msg)
214
+
215
+ def test_no_material(self):
216
+ _write(self.deck, "<html></html>")
217
+ code, msg = su.check(os.path.join(self.d, "nope.json"), self.deck)
218
+ self.assertEqual(code, 0)
219
+
220
+ def test_no_deck(self):
221
+ self._material(["src_1.png"])
222
+ code, msg = su.check(self.material, os.path.join(self.d, "nope.html"))
223
+ self.assertEqual(code, 2)
224
+ self.assertIn("NO_DECK", msg)
225
+
226
+
227
+ class TestDiscardExemption(unittest.TestCase):
228
+ """discard 豁免(2026-07-02 坠机图事故):规划判弃用的图不再被「必须全部用上」硬塞进 deck。"""
229
+
230
+ def setUp(self):
231
+ self.d = tempfile.mkdtemp(prefix="usage-discard-")
232
+ self.material = os.path.join(self.d, "material.json")
233
+ self.deck = os.path.join(self.d, "deck.html")
234
+
235
+ def _plan(self, intents):
236
+ _write(os.path.join(self.d, "source_layout_plan.json"),
237
+ {"images": [{"file": f, "usage_intent": i} for f, i in intents.items()]})
238
+
239
+ def test_discarded_unused_is_ok(self):
240
+ _write(self.material, {"has_source": True,
241
+ "images": [{"file": "src_1.jpg"}, {"file": "src_2.png"}]})
242
+ self._plan({"src_1.jpg": "discard", "src_2.png": "source_enhanced_bg"})
243
+ _write(self.deck, "<img src='assets/enhanced_src_2.png'>")
244
+ code, msg = su.check(self.material, self.deck)
245
+ self.assertEqual(code, 0, msg)
246
+ self.assertIn("豁免", msg)
247
+
248
+ def test_non_discarded_unused_still_fails(self):
249
+ _write(self.material, {"has_source": True,
250
+ "images": [{"file": "src_1.jpg"}, {"file": "src_2.png"}]})
251
+ self._plan({"src_1.jpg": "discard", "src_2.png": "source_enhanced_bg"})
252
+ _write(self.deck, "<p>无图</p>")
253
+ code, msg = su.check(self.material, self.deck)
254
+ self.assertEqual(code, 1, msg)
255
+ self.assertIn("src_2.png", msg)
256
+ self.assertNotIn("src_1.jpg", msg)
257
+
258
+ def test_no_plan_file_behavior_unchanged(self):
259
+ # 无 source_layout_plan.json(旧产物/webppt 场景)→ 行为与改前一致:全要求用上。
260
+ _write(self.material, {"has_source": True, "images": [{"file": "src_1.jpg"}]})
261
+ _write(self.deck, "<p>无图</p>")
262
+ code, msg = su.check(self.material, self.deck)
263
+ self.assertEqual(code, 1, msg)
264
+ self.assertIn("src_1.jpg", msg)
265
+
266
+
267
+ class TestSectionMismatch(unittest.TestCase):
268
+ """【图片章节错位】(2026-07-02 张冠李戴裁决,WARNING 级):客户图来自原文特定章节,
269
+ 却被设计步配到词元零交集的页。模糊判定只提示不阻断;防错主力在设计 prompt 章节锚定。"""
270
+
271
+ def setUp(self):
272
+ self.d = tempfile.mkdtemp(prefix="usage-section-")
273
+ self.design = os.path.join(self.d, "design.json")
274
+ self.plan = os.path.join(self.d, "source_layout_plan.json")
275
+
276
+ def _write_plan(self, images):
277
+ _write(self.plan, {"images": images})
278
+
279
+ def _write_design(self, pages):
280
+ _write(self.design, {"pages": pages})
281
+
282
+ def _page(self, page, title, key_point="", src=None, takeaway=""):
283
+ p = {"page": page, "title": title, "key_point": key_point,
284
+ "one_sentence_takeaway": takeaway}
285
+ if src:
286
+ p["visual"] = {"route": "source-enhanced", "source_file": src}
287
+ return p
288
+
289
+ def test_mismatch_reports_warning(self):
290
+ self._write_plan([{"file": "src_1.jpg", "usage_intent": "source_enhanced_hero",
291
+ "section": "三、现场作业场景", "near_text": "巡检人员在高空平台开展检修作业"}])
292
+ self._write_design([self._page(5, "成本效益分析", "投入产出比测算", src="src_1.jpg")])
293
+ warnings, checked = su.section_mismatch_warnings(self.design, self.plan)
294
+ self.assertEqual(checked, 1)
295
+ self.assertEqual(len(warnings), 1, warnings)
296
+ self.assertIn("【图片章节错位】页 5", warnings[0])
297
+ self.assertIn("src_1.jpg", warnings[0])
298
+ self.assertIn("三、现场作业场景", warnings[0])
299
+
300
+ def test_section_token_match_passes(self):
301
+ # 章节「系统平台建设」与页题「平台总体架构」共享 bigram「平台」→ 视为锚定成立。
302
+ self._write_plan([{"file": "src_1.jpg", "usage_intent": "source_enhanced_hero",
303
+ "section": "二、系统平台建设"}])
304
+ self._write_design([self._page(3, "平台总体架构", src="src_1.jpg")])
305
+ warnings, checked = su.section_mismatch_warnings(self.design, self.plan)
306
+ self.assertEqual((checked, warnings), (1, []))
307
+
308
+ def test_near_text_match_passes(self):
309
+ # 章节词不合、但邻近文案与页 key_point 有交集 → 也算锚定成立(near_text 扩大匹配面)。
310
+ self._write_plan([{"file": "src_1.jpg", "usage_intent": "direct_contain",
311
+ "section": "五、附录", "near_text": "巡检效率提升与人力成本对比数据"}])
312
+ self._write_design([self._page(5, "成本效益", "巡检效率提升三倍", src="src_1.jpg")])
313
+ warnings, _ = su.section_mismatch_warnings(self.design, self.plan)
314
+ self.assertEqual(warnings, [])
315
+
316
+ def test_background_and_discard_exempt(self):
317
+ self._write_plan([
318
+ {"file": "src_1.jpg", "usage_intent": "source_enhanced_bg", "section": "三、现场作业场景"},
319
+ {"file": "src_2.jpg", "usage_intent": "discard", "section": "三、现场作业场景"},
320
+ ])
321
+ self._write_design([self._page(5, "成本效益分析", src="src_1.jpg"),
322
+ self._page(6, "总结展望", src="src_2.jpg")])
323
+ warnings, checked = su.section_mismatch_warnings(self.design, self.plan)
324
+ self.assertEqual((checked, warnings), (0, []))
325
+
326
+ def test_meaningless_section_skipped(self):
327
+ # 页号型(pdf/pptx 锚)与文件名型(独立散图)section 无锚定意义 → 跳过不查。
328
+ self._write_plan([
329
+ {"file": "src_1.jpg", "usage_intent": "source_enhanced_hero", "section": "page 3"},
330
+ {"file": "src_2.jpg", "usage_intent": "source_enhanced_hero", "section": "第3页"},
331
+ {"file": "src_3.jpg", "usage_intent": "source_enhanced_hero", "section": "src_3"},
332
+ ])
333
+ self._write_design([self._page(2, "成本效益", src="src_1.jpg"),
334
+ self._page(3, "范式变化", src="src_2.jpg"),
335
+ self._page(4, "总结展望", src="src_3.jpg")])
336
+ warnings, checked = su.section_mismatch_warnings(self.design, self.plan)
337
+ self.assertEqual((checked, warnings), (0, []))
338
+
339
+ def test_missing_files_silent(self):
340
+ warnings, checked = su.section_mismatch_warnings(
341
+ os.path.join(self.d, "nope.json"), os.path.join(self.d, "nope2.json"))
342
+ self.assertEqual((checked, warnings), (0, []))
343
+
344
+
345
+ class TestLayoutIntentViolations(unittest.TestCase):
346
+ """【版式违规】(2026-07-03 真机第4页事故,ERROR 级可修档):规划为 contain 类
347
+ (direct_contain/gallery/small_evidence)的客户图被 build 违规用作整页背景
348
+ (<img class="bg-img"> 或 CSS background url())。豁免 source_enhanced_bg/hero/discard。"""
349
+
350
+ def setUp(self):
351
+ self.d = tempfile.mkdtemp(prefix="usage-intent-")
352
+ self.material = os.path.join(self.d, "material.json")
353
+ self.deck = os.path.join(self.d, "deck.html")
354
+
355
+ def _setup(self, intents, deck_html):
356
+ _write(self.material, {"has_source": True,
357
+ "images": [{"file": f} for f in intents]})
358
+ _write(os.path.join(self.d, "source_layout_plan.json"),
359
+ {"images": [{"file": f, "usage_intent": i} for f, i in intents.items()]})
360
+ _write(self.deck, deck_html)
361
+
362
+ def test_contain_image_as_bg_img_reports_error_with_page(self):
363
+ # 真机第4页原型:enhanced 派生图铺成 .bg-img,data-source-file 扩展名漂移(.jpg vs .png)。
364
+ self._setup(
365
+ {"src_6.png": "direct_contain"},
366
+ '<section class="slide"><p>p1</p></section>'
367
+ '<section class="slide"><img class="bg-img" src="assets/enhanced_src_6.png"'
368
+ ' data-source-file="src_6.jpg"></section>',
369
+ )
370
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
371
+ self.assertEqual(checked, 1)
372
+ self.assertEqual(len(errors), 1, errors)
373
+ self.assertIn("【版式违规】页 2", errors[0])
374
+ self.assertIn("src_6.png", errors[0])
375
+ self.assertIn("direct_contain", errors[0])
376
+
377
+ def test_contain_image_as_css_background_reports_error(self):
378
+ self._setup(
379
+ {"src_1.png": "gallery"},
380
+ '<section class="slide" style="background-image:url(assets/enhanced_src_1.png)"></section>',
381
+ )
382
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
383
+ self.assertEqual(checked, 1)
384
+ self.assertEqual(len(errors), 1, errors)
385
+ self.assertIn("src_1.png", errors[0])
386
+
387
+ def test_contain_image_in_contain_slot_passes(self):
388
+ self._setup(
389
+ {"src_1.png": "direct_contain"},
390
+ '<section class="slide"><figure><img src="assets/enhanced_src_1.png"></figure></section>',
391
+ )
392
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
393
+ self.assertEqual((checked, errors), (1, []))
394
+
395
+ def test_enhanced_bg_and_hero_intents_exempt(self):
396
+ self._setup(
397
+ {"src_1.png": "source_enhanced_bg", "src_2.png": "source_enhanced_hero"},
398
+ '<img class="bg-img" src="assets/enhanced_src_1.png">'
399
+ '<img class="bg-img" src="assets/enhanced_src_2.png">',
400
+ )
401
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
402
+ self.assertEqual((checked, errors), (0, []))
403
+
404
+ def test_unrelated_bg_img_does_not_flag_contain_image(self):
405
+ # AI 生成背景(ai_p3.png)与 contain 客户图无关 → 不误报。
406
+ self._setup(
407
+ {"src_1.png": "small_evidence"},
408
+ '<section class="slide"><img class="bg-img" src="assets/ai_p3.png">'
409
+ '<img src="assets/enhanced_src_1.png"></section>',
410
+ )
411
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
412
+ self.assertEqual((checked, errors), (1, []))
413
+
414
+ def test_missing_plan_or_material_silent(self):
415
+ _write(self.material, {"has_source": True, "images": [{"file": "src_1.png"}]})
416
+ _write(self.deck, '<img class="bg-img" src="assets/enhanced_src_1.png">')
417
+ # 无 source_layout_plan.json → 不添乱(webppt/旧产物场景)。
418
+ errors, checked = su.layout_intent_violations(self.material, self.deck)
419
+ self.assertEqual((checked, errors), (0, []))
420
+ # 无 material.json → 同样静默。
421
+ errors, checked = su.layout_intent_violations(
422
+ os.path.join(self.d, "nope.json"), self.deck)
423
+ self.assertEqual((checked, errors), (0, []))
424
+
425
+
426
+ if __name__ == "__main__":
427
+ unittest.main(verbosity=2)