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,581 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ source_usage_check.py —— 确定性硬闸门:校验「用户提供的图片是否都用进了 deck.html」。
5
+
6
+ 为什么需要
7
+ ==========
8
+ 用户红线:客户给的图**一定要用进 PPT,不能给了不用**。靠 prompt 要求弱模型「别冷落客户图」不可靠,
9
+ 故收归确定性闸门(同 image_plan_lint / deck_pages_check 哲学):读 material.json 的客户图清单 + deck.html,
10
+ 逐张核对是否被真实资源引用。未用尽 → 报 UNUSED + 退出 1,让构建步必须补上。
11
+
12
+ 判定
13
+ ====
14
+ - material.json 无 / 无客户图 → `SOURCE_USAGE_OK: 无客户图`,退出 0(纯 topic / 纯文档无图,无需检查)。
15
+ - 每张 images[].file(src_K.png)必须出现在真实资源引用里才算「已用」:`img/src`、`source/srcset`、
16
+ SVG `image href`、内联 style 或 `<style>` 里的 `url(...)` 等。正文、注释、脚本字符串里出现文件名不算。
17
+ - 图生图增强派生图是默认首选使用:默认识别 `enhanced_src_K.png` / `src_K_enhanced.png`,也支持 material
18
+ 中显式声明 `derived_files` / `enhanced_file`。原图直接使用只在 `image_generation_report.json` 记录对应增强图失败时允许。
19
+ 导出前可加 `--require-existing-derived`,要求增强图必须真实存在于 build/assets;若失败报告存在,则原图 fallback 可通过。
20
+ - 低清原图不得做全幅背景:宽 <1280 或像素 <0.8MP 的原始 `src_K` 若出现在 CSS `background-image`
21
+ / `data-bg` / `data-background` 中,判 `LOW_RES_BACKGROUND`。这类图应走 `source-enhanced` 或小图/侧图/画廊。
22
+ - 全部用上 → `SOURCE_USAGE_OK: N 张客户图全部用上`,退出 0。
23
+ - 有未用 → `UNUSED: src_3.png, src_5.png(共 K/N 张客户图未用进 deck)`,退出 1。
24
+ - deck.html 不存在 → `NO_DECK: …`,退出 2(无法核验;构建未产出 deck)。
25
+
26
+ 用法
27
+ ====
28
+ python source_usage_check.py [--build ./build] [--require-existing-derived]
29
+ python source_usage_check.py --material ./build/material.json --deck ./build/deck.html
30
+ python source_usage_check.py --build ./build --design-only # 仅章节错位预警(WARNING,恒 0)
31
+ python source_usage_check.py --build ./build --layout-intent-only # 仅版式违规(ERROR,exit 1;可修档)
32
+ """
33
+
34
+ import argparse
35
+ from html.parser import HTMLParser
36
+ import json
37
+ import os
38
+ import re
39
+ import sys
40
+ from urllib.parse import unquote, urlparse
41
+
42
+
43
+ URL_RE = re.compile(r"url\(\s*(['\"]?)(.*?)\1\s*\)", re.IGNORECASE | re.DOTALL)
44
+ LOW_RES_BG_MIN_WIDTH = 1280
45
+ LOW_RES_BG_MIN_MEGAPIXELS = 0.8
46
+ RESOURCE_ATTRS = {
47
+ "src",
48
+ "poster",
49
+ "data-src",
50
+ }
51
+ BACKGROUND_RESOURCE_ATTRS = {"data-bg", "data-background"}
52
+ HREF_RESOURCE_TAGS = {"image", "feimage"}
53
+ SRCSET_ATTRS = {"srcset", "data-srcset"}
54
+
55
+
56
+ def _normalize_resource(value):
57
+ """从 URL/路径里取 basename;query/hash 不影响匹配。"""
58
+ value = (value or "").strip().strip("\"'")
59
+ if not value:
60
+ return ""
61
+ parsed = urlparse(value)
62
+ path = parsed.path or value.split("?", 1)[0].split("#", 1)[0]
63
+ return os.path.basename(unquote(path))
64
+
65
+
66
+ def _srcset_urls(value):
67
+ for item in (value or "").split(","):
68
+ first = item.strip().split()
69
+ if first:
70
+ yield first[0]
71
+
72
+
73
+ def _css_urls(value):
74
+ for m in URL_RE.finditer(value or ""):
75
+ yield m.group(2)
76
+
77
+
78
+ class ResourceRefParser(HTMLParser):
79
+ """只收集 HTML/CSS 资源引用,不把正文、注释、script 字符串当作图片使用。
80
+
81
+ 另单独收集「整页背景用法」证据(bg_img_uses,供 --layout-intent-only 版式违规检查):
82
+ `<img class="bg-img">` 的 src 与 data-source-file——**不并入 refs/background_refs**,
83
+ 以免改变 hard 档(未用尽/低清背景)的既有判定语义。"""
84
+
85
+ def __init__(self):
86
+ super().__init__(convert_charrefs=True)
87
+ self.refs = set()
88
+ self.background_refs = set()
89
+ # [(slide_no, basename)];slide_no 从 1 起(按 class 含 slide token 的容器计数),供报错定位页。
90
+ self.bg_img_uses = []
91
+ self._slide_no = 0
92
+ self._style_depth = 0
93
+ self._script_depth = 0
94
+
95
+ def handle_starttag(self, tag, attrs):
96
+ tag = tag.lower()
97
+ attr_map = {str(k).lower(): (v or "") for k, v in attrs}
98
+ if tag == "style":
99
+ self._style_depth += 1
100
+ elif tag == "script":
101
+ self._script_depth += 1
102
+
103
+ class_tokens = set((attr_map.get("class") or "").split())
104
+ if "slide" in class_tokens:
105
+ self._slide_no += 1
106
+ if tag == "img" and "bg-img" in class_tokens:
107
+ for key in ("src", "data-source-file"):
108
+ base = _normalize_resource(attr_map.get(key, ""))
109
+ if base:
110
+ self.bg_img_uses.append((self._slide_no, base))
111
+
112
+ for raw_name, raw_value in attrs:
113
+ name = (raw_name or "").lower()
114
+ value = raw_value or ""
115
+ if name in RESOURCE_ATTRS:
116
+ self._add(value)
117
+ elif name in BACKGROUND_RESOURCE_ATTRS:
118
+ self._add_background(value)
119
+ elif name in {"href", "xlink:href"} and self._is_resource_href(tag, attr_map):
120
+ self._add(value)
121
+ elif name in SRCSET_ATTRS:
122
+ for url in _srcset_urls(value):
123
+ self._add(url)
124
+ elif name == "style":
125
+ for url in _css_urls(value):
126
+ self._add_background(url)
127
+
128
+ def handle_endtag(self, tag):
129
+ tag = tag.lower()
130
+ if tag == "style" and self._style_depth:
131
+ self._style_depth -= 1
132
+ elif tag == "script" and self._script_depth:
133
+ self._script_depth -= 1
134
+
135
+ def handle_data(self, data):
136
+ if self._style_depth and not self._script_depth:
137
+ for url in _css_urls(data):
138
+ self._add_background(url)
139
+
140
+ def _add(self, value):
141
+ base = _normalize_resource(value)
142
+ if base:
143
+ self.refs.add(base)
144
+
145
+ def _add_background(self, value):
146
+ base = _normalize_resource(value)
147
+ if base:
148
+ self.refs.add(base)
149
+ self.background_refs.add(base)
150
+
151
+ def _is_resource_href(self, tag, attrs):
152
+ if tag in HREF_RESOURCE_TAGS:
153
+ return True
154
+ if tag == "link":
155
+ rel = attrs.get("rel", "").lower()
156
+ as_type = attrs.get("as", "").lower()
157
+ return "preload" in rel and as_type == "image"
158
+ return False
159
+
160
+
161
+ def extract_resource_filenames(html):
162
+ parser = ResourceRefParser()
163
+ parser.feed(html)
164
+ parser.close()
165
+ return parser.refs
166
+
167
+
168
+ def extract_resource_usage(html):
169
+ parser = ResourceRefParser()
170
+ parser.feed(html)
171
+ parser.close()
172
+ return parser.refs, parser.background_refs
173
+
174
+
175
+ def extract_layout_usage(html):
176
+ """供版式违规检查:([(slide_no, basename)] 的 .bg-img 用法, CSS background basename 集合)。"""
177
+ parser = ResourceRefParser()
178
+ parser.feed(html)
179
+ parser.close()
180
+ return parser.bg_img_uses, parser.background_refs
181
+
182
+
183
+ def _derived_candidates(image):
184
+ if not isinstance(image, dict):
185
+ return []
186
+ fn = image.get("file")
187
+ out = []
188
+ for key in ("enhanced_file", "derived_file"):
189
+ v = image.get(key)
190
+ if isinstance(v, str) and v:
191
+ out.append(os.path.basename(v))
192
+ for key in ("derived_files", "enhanced_files"):
193
+ values = image.get(key)
194
+ if isinstance(values, list):
195
+ out.extend(os.path.basename(v) for v in values if isinstance(v, str) and v)
196
+ if isinstance(fn, str) and fn:
197
+ stem, ext = os.path.splitext(fn)
198
+ out.append(f"enhanced_{fn}")
199
+ out.append(f"{stem}_enhanced{ext or '.png'}")
200
+ # 去重保序
201
+ seen = set()
202
+ result = []
203
+ for item in out:
204
+ if item and item not in seen:
205
+ seen.add(item)
206
+ result.append(item)
207
+ return result
208
+
209
+
210
+ def _asset_exists(deck_path, filename):
211
+ """增强派生图只有真实落盘才算用上;普通原图由抽取阶段保证存在。"""
212
+ if not filename:
213
+ return False
214
+ deck_dir = os.path.dirname(os.path.abspath(deck_path))
215
+ candidates = [
216
+ os.path.join(deck_dir, "assets", filename),
217
+ os.path.join(os.path.dirname(deck_dir), "assets", filename),
218
+ ]
219
+ return any(os.path.isfile(p) for p in candidates)
220
+
221
+
222
+ def _is_low_res_for_background(image):
223
+ try:
224
+ w = int(image.get("w") or 0)
225
+ h = int(image.get("h") or 0)
226
+ except Exception:
227
+ return False
228
+ if w <= 0 or h <= 0:
229
+ return False
230
+ return w < LOW_RES_BG_MIN_WIDTH or (w * h / 1_000_000) < LOW_RES_BG_MIN_MEGAPIXELS
231
+
232
+
233
+ def _load_generation_report(deck_path):
234
+ deck_dir = os.path.dirname(os.path.abspath(deck_path))
235
+ candidates = [
236
+ os.path.join(deck_dir, "image_generation_report.json"),
237
+ os.path.join(os.path.dirname(deck_dir), "image_generation_report.json"),
238
+ ]
239
+ for path in candidates:
240
+ try:
241
+ with open(path, encoding="utf-8") as f:
242
+ data = json.load(f)
243
+ if isinstance(data, dict):
244
+ return data
245
+ except Exception:
246
+ continue
247
+ return {}
248
+
249
+
250
+ def _failed_enhanced_files(report):
251
+ out = set()
252
+ for item in (report.get("degraded") or []):
253
+ if not isinstance(item, dict):
254
+ continue
255
+ if item.get("mode") == "image-to-image" and item.get("file"):
256
+ out.add(os.path.basename(item["file"]))
257
+ return out
258
+
259
+
260
+ CJK_RE = re.compile(r"[一-鿿]+")
261
+ WORD_RE = re.compile(r"[A-Za-z0-9]{2,}")
262
+ # 无锚定意义的 section 形态:pdf/pptx 按页号命名("page 3" / "第3页")——不是真实章节标题。
263
+ PAGELIKE_SECTION_RE = re.compile(r"#?\s*(?:page|slide|p)\s*\d+|第\s*\d+\s*[页张]", re.IGNORECASE)
264
+
265
+
266
+ def _tokens(text):
267
+ """CJK 双字 bigram + ASCII 词(≥2 字符小写)——供「章节+邻近文案 ↔ 页主题」词元交集判定。"""
268
+ out = set()
269
+ for seg in CJK_RE.findall(text or ""):
270
+ for i in range(len(seg) - 1):
271
+ out.add(seg[i : i + 2])
272
+ for w in WORD_RE.findall(text or ""):
273
+ out.add(w.lower())
274
+ return out
275
+
276
+
277
+ def _section_meaningful(section, file=""):
278
+ """章节字段是否可作配页锚点:空 / 等于文件名(独立散图)/ 纯页号(pdf/pptx 锚)都不算。"""
279
+ sec = (section or "").strip()
280
+ if not sec:
281
+ return False
282
+ base = os.path.basename(file or "")
283
+ if sec in (base, os.path.splitext(base)[0]):
284
+ return False
285
+ if PAGELIKE_SECTION_RE.fullmatch(sec):
286
+ return False
287
+ return True
288
+
289
+
290
+ def _load_json_or_none(path):
291
+ try:
292
+ with open(path, encoding="utf-8") as f:
293
+ return json.load(f)
294
+ except Exception:
295
+ return None
296
+
297
+
298
+ def section_mismatch_warnings(design_path, plan_path):
299
+ """【图片章节错位】(治张冠李戴,WARNING 级不阻断):客户图来自原文特定章节(plan.section,
300
+ 由 extract→material→plan 透传),却被设计步配到词元零交集的页。返回 (warnings, checked)。
301
+
302
+ 诚实边界:词元交集(CJK bigram + ASCII 词)是模糊判定,只报 WARNING + 精确定位;
303
+ 真正防错在设计 prompt 的「章节锚定优先」硬规则,本检查是它的确定性兜底。
304
+ 豁免:纯背景氛围(source_enhanced_bg)与弃用图(discard)不做章节锚定;
305
+ section 无意义(空/文件名/页号)跳过。design/plan 缺失或非法 → 返回空(不添乱)。"""
306
+ design = _load_json_or_none(design_path)
307
+ plan = _load_json_or_none(plan_path)
308
+ if not isinstance(design, dict) or not isinstance(plan, dict):
309
+ return [], 0
310
+ plan_map = {
311
+ str(item.get("file")): item
312
+ for item in (plan.get("images") or [])
313
+ if isinstance(item, dict) and item.get("file")
314
+ }
315
+ warnings = []
316
+ checked = 0
317
+ for p in design.get("pages") or []:
318
+ if not isinstance(p, dict):
319
+ continue
320
+ visual = p.get("visual") if isinstance(p.get("visual"), dict) else {}
321
+ src = visual.get("source_file")
322
+ if not isinstance(src, str) or not src:
323
+ continue
324
+ src = os.path.basename(src)
325
+ entry = plan_map.get(src) or {}
326
+ if str(entry.get("usage_intent") or "") in ("source_enhanced_bg", "discard"):
327
+ continue
328
+ section = entry.get("section")
329
+ if not _section_meaningful(section, src):
330
+ continue
331
+ checked += 1
332
+ img_tokens = _tokens(str(section)) | _tokens(str(entry.get("near_text") or ""))
333
+ page_text = " ".join(
334
+ str(p.get(k) or "") for k in ("title", "key_point", "one_sentence_takeaway")
335
+ )
336
+ if _tokens(page_text) & img_tokens:
337
+ continue
338
+ warnings.append(
339
+ f"【图片章节错位】页 {p.get('page', '?')} · 「{src}」来自章节『{section}』"
340
+ f"· 页主题『{str(p.get('title') or '')[:24]}』——两者词元零交集"
341
+ f" |建议移到覆盖该章节内容的页;确属跨章节氛围素材可忽略"
342
+ )
343
+ return warnings, checked
344
+
345
+
346
+ # 规划为「框内展示」的意图:被用作整页背景即版式违规(2026-07-03 真机第4页事故:src_6 识图明说
347
+ # "绝对不能用作整页背景或叠字"、规划判 direct_contain,build 仍铺成 .bg-img 整页背景+叠打同款标题)。
348
+ # source_enhanced_bg(本就是背景)/ source_enhanced_hero(编辑式融合 hero 允许出血)/ discard 豁免。
349
+ CONTAIN_INTENTS = ("direct_contain", "gallery", "small_evidence")
350
+
351
+
352
+ def _stem(name):
353
+ return os.path.splitext(os.path.basename(name or ""))[0]
354
+
355
+
356
+ def layout_intent_violations(material_path, deck_path):
357
+ """【版式违规】(Edit 可修,供视觉门可修档驱动修复轮,不进 hard 档):
358
+ 规划 usage_intent ∈ CONTAIN_INTENTS 的客户图(含其 enhanced 派生图),却在 deck.html 中
359
+ 被用作整页背景(`<img class="bg-img">` 或 CSS background url())。
360
+
361
+ 比对按**文件名 stem**(掉扩展名):真机 deck 里 data-source-file="src_6.jpg" 而 material
362
+ 记 src_6.png,扩展名会漂移。返回 (errors, checked);material/plan/deck 缺失 → ([], 0)。"""
363
+ material = _load_json_or_none(material_path)
364
+ if not isinstance(material, dict):
365
+ return [], 0
366
+ image_items = [
367
+ im for im in (material.get("images") or [])
368
+ if isinstance(im, dict) and im.get("file")
369
+ ]
370
+ if not image_items:
371
+ return [], 0
372
+ plan_map = _load_plan_map(material_path, deck_path)
373
+ if not plan_map:
374
+ return [], 0
375
+ try:
376
+ with open(deck_path, encoding="utf-8") as f:
377
+ html = f.read()
378
+ except Exception:
379
+ return [], 0
380
+
381
+ bg_img_uses, css_bg_refs = extract_layout_usage(html)
382
+ bg_stem_pages = {}
383
+ for slide_no, base in bg_img_uses:
384
+ bg_stem_pages.setdefault(_stem(base), set()).add(slide_no)
385
+ for base in css_bg_refs:
386
+ bg_stem_pages.setdefault(_stem(base), set())
387
+
388
+ errors = []
389
+ checked = 0
390
+ for im in image_items:
391
+ fn = im.get("file")
392
+ plan = plan_map.get(fn) if isinstance(plan_map.get(fn), dict) else {}
393
+ intent = str(plan.get("usage_intent") or "")
394
+ if intent not in CONTAIN_INTENTS:
395
+ continue
396
+ checked += 1
397
+ cand_stems = {_stem(fn)} | {_stem(c) for c in _derived_candidates(im)}
398
+ hit_pages = set()
399
+ hit = False
400
+ for stem in cand_stems:
401
+ if stem in bg_stem_pages:
402
+ hit = True
403
+ hit_pages |= bg_stem_pages[stem]
404
+ if not hit:
405
+ continue
406
+ pages = "/".join(str(p) for p in sorted(hit_pages) if p) or "?"
407
+ errors.append(
408
+ f"ERROR: 【版式违规】页 {pages} · 「{fn}」规划为 {intent}(框内展示),"
409
+ f"却被用作整页背景(.bg-img / CSS background)"
410
+ f" |必须改为 contain 框图/画廊格:换成 <img> 放进图槽卡片,背景另配氛围图或纯色;"
411
+ f"带自有文字的截图类素材当背景会与顶层文字互相打架(识图/规划早已禁止)"
412
+ )
413
+ return errors, checked
414
+
415
+
416
+ def _load_plan_map(material_path, deck_path):
417
+ """source_layout_plan.json 的 {file: entry}(与 material 同目录探测;缺则空 = 行为不变)。"""
418
+ for base in (os.path.dirname(os.path.abspath(material_path)),
419
+ os.path.dirname(os.path.abspath(deck_path))):
420
+ path = os.path.join(base, "source_layout_plan.json")
421
+ try:
422
+ with open(path, encoding="utf-8") as f:
423
+ data = json.load(f)
424
+ except Exception:
425
+ continue
426
+ out = {}
427
+ for item in (data.get("images") or []) if isinstance(data, dict) else []:
428
+ if isinstance(item, dict) and item.get("file"):
429
+ out[str(item["file"])] = item
430
+ return out
431
+ return {}
432
+
433
+
434
+ def check(material_path, deck_path, require_existing_derived=False):
435
+ """返回 (code, message)。code: 0 ok / 1 有未用 / 2 无 deck。"""
436
+ try:
437
+ with open(material_path, encoding="utf-8") as f:
438
+ material = json.load(f)
439
+ except Exception:
440
+ return 0, "SOURCE_USAGE_OK: 无 material.json(无客户图,跳过)"
441
+
442
+ images = material.get("images") or []
443
+ image_items = [im for im in images if isinstance(im, dict) and im.get("file")]
444
+ files = [im.get("file") for im in image_items]
445
+ if not files:
446
+ return 0, "SOURCE_USAGE_OK: 无客户图"
447
+
448
+ if not os.path.isfile(deck_path):
449
+ return 2, f"NO_DECK: {deck_path} 不存在,无法核验客户图是否用进 deck"
450
+
451
+ with open(deck_path, encoding="utf-8") as f:
452
+ html = f.read()
453
+
454
+ used, background_used = extract_resource_usage(html)
455
+ # 2026-07-05 真机复盘:material.json 没记 enhanced_file 时,_derived_candidates 只能按
456
+ # 「原文件同扩展名」猜(enhanced_src_2.jpg),但生图实际落盘常是 .png——deck.html 引用
457
+ # enhanced_src_2.png,逐字符串匹配对不上,客户图被误判 UNUSED 直接 hard halt(真机连续两次
458
+ # 事故)。用 stem(掉扩展名)建索引兜底,与 layout_intent_violations 的扩展名漂移处理同口径。
459
+ used_stems = {}
460
+ for u in used:
461
+ used_stems.setdefault(_stem(u), u)
462
+ failed_enhanced = _failed_enhanced_files(_load_generation_report(deck_path))
463
+ plan_map = _load_plan_map(material_path, deck_path)
464
+ unused = []
465
+ direct_without_failed_enhance = []
466
+ low_res_background = []
467
+ for im in image_items:
468
+ fn = im.get("file")
469
+ plan = plan_map.get(fn) if isinstance(plan_map.get(fn), dict) else {}
470
+ # 弃用图(规划判定与主题不相关/负面,2026-07-02 坠机图事故):豁免「必须用上」。
471
+ # 「弃用图反被用进 deck」不在此判——那是 Edit 可修缺陷,归 deck_visual_style_check ERROR
472
+ # 驱动修复轮;本检查在 hard 档(失败即 halt、先于修复步),只留「未用尽」这类构建级失败。
473
+ if str(plan.get("usage_intent") or "") == "discard":
474
+ continue
475
+ direct_used = fn in used
476
+ candidates = _derived_candidates(im)
477
+ derived_used = []
478
+ missing_derived = []
479
+ for c in candidates:
480
+ actual = c if c in used else used_stems.get(_stem(c))
481
+ if not actual:
482
+ continue
483
+ if require_existing_derived and not _asset_exists(deck_path, actual):
484
+ missing_derived.append(actual)
485
+ continue
486
+ derived_used.append(actual)
487
+ has_failed_enhance = any(c in failed_enhanced for c in candidates)
488
+ if direct_used and not derived_used and not has_failed_enhance:
489
+ direct_without_failed_enhance.append(fn)
490
+ if not direct_used and not derived_used:
491
+ unused.append(fn)
492
+ if fn in background_used and _is_low_res_for_background(im):
493
+ low_res_background.append(
494
+ f"{fn}({im.get('w', '?')}×{im.get('h', '?')})"
495
+ )
496
+ if low_res_background:
497
+ return 1, (
498
+ "LOW_RES_BACKGROUND: "
499
+ + ", ".join(low_res_background)
500
+ + " 被当作背景使用;低清客户原图必须先 source-enhanced 图生图扩图/重绘,"
501
+ + "或改成小图/侧图/画廊/证据图。"
502
+ )
503
+ if direct_without_failed_enhance:
504
+ return 1, (
505
+ "SOURCE_DIRECT_WITHOUT_ENHANCE_FAILURE: "
506
+ + ", ".join(direct_without_failed_enhance)
507
+ + " 直接使用了客户原图;当前策略要求先使用 enhanced_src_K 图生图派生图,"
508
+ + "只有 image_generation_report.json 记录对应 image-to-image 失败时才允许原图 fallback。"
509
+ )
510
+ if unused:
511
+ return 1, (
512
+ f"UNUSED: {', '.join(unused)}"
513
+ f"(共 {len(unused)}/{len(files)} 张客户图未用进 deck —— 必须全部用上)"
514
+ )
515
+ n_discard = sum(
516
+ 1 for im in image_items
517
+ if str((plan_map.get(im.get("file")) or {}).get("usage_intent") or "") == "discard"
518
+ )
519
+ if n_discard:
520
+ return 0, (
521
+ f"SOURCE_USAGE_OK: {len(files) - n_discard}/{len(files)} 张客户图用上"
522
+ f"({n_discard} 张规划弃用,豁免)"
523
+ )
524
+ return 0, f"SOURCE_USAGE_OK: {len(files)} 张客户图全部用上"
525
+
526
+
527
+ def main() -> int:
528
+ parser = argparse.ArgumentParser(description="校验客户图是否都用进 deck.html(确定性硬闸门)")
529
+ parser.add_argument("--build", default="./build", help="产物目录(默认 ./build)")
530
+ parser.add_argument("--material", default=None, help="material.json 路径(覆盖 --build 推断)")
531
+ parser.add_argument("--deck", default=None, help="deck.html 路径(覆盖 --build 推断)")
532
+ parser.add_argument(
533
+ "--require-existing-derived",
534
+ action="store_true",
535
+ help="导出前使用:增强派生图必须已真实落盘,否则不算客户图使用",
536
+ )
537
+ parser.add_argument("--design", default=None, help="design.json 路径(覆盖 --build 推断;供章节错位检查)")
538
+ parser.add_argument(
539
+ "--design-only",
540
+ action="store_true",
541
+ help="只跑【图片章节错位】检查(不需要 deck.html;WARNING 级恒 exit 0,供生图前预警)",
542
+ )
543
+ parser.add_argument(
544
+ "--layout-intent-only",
545
+ action="store_true",
546
+ help="只跑【版式违规】检查(contain 类客户图被当整页背景;ERROR 级 exit 1,供视觉门可修档驱动修复)",
547
+ )
548
+ args = parser.parse_args()
549
+
550
+ material_path = args.material or os.path.join(args.build, "material.json")
551
+ deck_path = args.deck or os.path.join(args.build, "deck.html")
552
+ design_path = args.design or os.path.join(args.build, "design.json")
553
+ plan_path = os.path.join(os.path.dirname(os.path.abspath(material_path)), "source_layout_plan.json")
554
+
555
+ if args.layout_intent_only:
556
+ errors, checked = layout_intent_violations(material_path, deck_path)
557
+ for e in errors:
558
+ print(e)
559
+ if errors:
560
+ print(f"LAYOUT_INTENT_FAILED: {len(errors)}/{checked} 张 contain 类客户图被违规用作整页背景")
561
+ return 1
562
+ print(f"LAYOUT_INTENT_OK: 核对 {checked} 张 contain 类客户图,无整页背景违规")
563
+ return 0
564
+
565
+ sec_warnings, sec_checked = section_mismatch_warnings(design_path, plan_path)
566
+ for w in sec_warnings:
567
+ print("WARNING: " + w)
568
+ if args.design_only:
569
+ print(
570
+ f"SECTION_MATCH_DONE: 核对 {sec_checked} 张章节锚定图,"
571
+ f"{len(sec_warnings)} 条错位提示(WARNING 级,不阻断)"
572
+ )
573
+ return 0
574
+
575
+ code, msg = check(material_path, deck_path, args.require_existing_derived)
576
+ print(msg)
577
+ return code
578
+
579
+
580
+ if __name__ == "__main__":
581
+ sys.exit(main())