minimal-agent 0.9.0 → 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 (467) hide show
  1. package/README.md +11 -8
  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 +2 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +172 -9
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +150 -13
  23. package/plugins/workflow-runner/src/index.js +39 -8
  24. package/plugins/workflow-runner/src/loader.js +216 -64
  25. package/plugins/workflow-runner/src/multimodal.js +37 -0
  26. package/plugins/workflow-runner/src/recovery.js +26 -24
  27. package/plugins/workflow-runner/src/runner.js +693 -287
  28. package/plugins/workflow-runner/src/stepExecutors/agent.js +126 -37
  29. package/plugins/workflow-runner/src/stepExecutors/assert.js +3 -2
  30. package/plugins/workflow-runner/src/stepExecutors/llm.js +38 -30
  31. package/plugins/workflow-runner/src/stepExecutors/skill.js +27 -38
  32. package/plugins/workflow-runner/src/stepExecutors/tool.js +56 -3
  33. package/plugins/workflow-runner/src/stepExecutors/verify.js +19 -10
  34. package/plugins/workflow-runner/src/taskIdentity.js +185 -30
  35. package/plugins/workflow-runner/src/types.js +18 -7
  36. package/plugins/workflow-runner/src/workflowState.js +355 -55
  37. package/skills/ai-image/SKILL.md +89 -0
  38. package/skills/ai-image/scripts/generate.mjs +497 -0
  39. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  40. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  41. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  42. package/skills/browser-automation/SKILL.md +47 -0
  43. package/skills/docx/SKILL.md +25 -3
  44. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  45. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  46. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/opc_check.py +253 -0
  53. package/skills/image-gen-openrouter/SKILL.md +10 -2
  54. package/skills/image-stats/SKILL.md +65 -0
  55. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  56. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  57. package/skills/image-stats/scripts/image_stats.py +368 -0
  58. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  59. package/skills/pptx/SKILL.md +123 -95
  60. package/skills/pptx/design.md +82 -0
  61. package/skills/pptx/examples/README.md +29 -0
  62. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  63. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  64. package/skills/pptx/pptxgenjs.md +41 -0
  65. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  66. package/skills/pptx/scripts/layout_lint.py +293 -0
  67. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  68. package/skills/pptx/scripts/opc_check.py +339 -0
  69. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  70. package/skills/pptx/svg.md +67 -0
  71. package/skills/tts-zh/SKILL.md +19 -2
  72. package/skills/ui-design/SKILL.md +158 -0
  73. package/skills/ui-design/claude_design.md +309 -0
  74. package/skills/ui-design/evals/evals.json +254 -0
  75. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  76. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  77. package/skills/ui-design/references/design-context.md +53 -0
  78. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  79. package/skills/ui-design/references/export-pptx.md +124 -0
  80. package/skills/ui-design/references/layout-composition.md +654 -0
  81. package/skills/ui-design/references/preview-feedback.md +37 -0
  82. package/skills/ui-design/references/source-image-layout.md +189 -0
  83. package/skills/ui-design/references/starter-components.md +78 -0
  84. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  85. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  86. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  87. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  88. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  89. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  90. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  91. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  92. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  93. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  94. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  95. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  96. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  97. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  98. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  99. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  100. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/assemble_material.py +217 -0
  140. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  141. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  142. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  143. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  144. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  145. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  146. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  147. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  148. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  149. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  150. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  151. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  152. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  153. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  154. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  155. package/skills/ui-design/scripts/design_style_check.py +397 -0
  156. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  157. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  158. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  159. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  160. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  161. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  162. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  163. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  164. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  165. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  166. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  167. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  168. package/skills/ui-design/scripts/layout_balance.py +881 -0
  169. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  170. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  171. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  172. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  173. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  174. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  175. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  176. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  177. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  178. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  179. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  180. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  181. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  182. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  183. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  184. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  185. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  186. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  187. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  188. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  195. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  196. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  205. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  206. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  221. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  222. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  225. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  226. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  239. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  240. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  248. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  249. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  261. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  262. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  275. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  292. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  293. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  316. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  324. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  325. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  328. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  329. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  337. package/skills/video-compose/SKILL.md +8 -1
  338. package/skills/web-crawler/SKILL.md +70 -0
  339. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  340. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  341. package/skills/xlsx/SKILL.md +12 -0
  342. package/skills/xlsx/scripts/opc_check.py +253 -0
  343. package/src/advisorState.js +22 -0
  344. package/src/askBridge.js +87 -3
  345. package/src/bootstrap/workingDir.js +12 -1
  346. package/src/cli/args.js +17 -1
  347. package/src/cli/configMenu.js +236 -0
  348. package/src/cli/configWizard.js +7 -5
  349. package/src/cli/print.js +37 -4
  350. package/src/cli/streamJson.js +77 -24
  351. package/src/cli/wizards/advisorWizard.js +147 -0
  352. package/src/cli/wizards/fieldForm.js +149 -0
  353. package/src/cli/wizards/imageWizard.js +60 -0
  354. package/src/cli/wizards/openRouterWizard.js +23 -0
  355. package/src/cli/wizards/providerPresets.js +95 -0
  356. package/src/cli/wizards/providerWizard.js +219 -0
  357. package/src/cli/wizards/searchWizard.js +23 -0
  358. package/src/cli/wizards/ttsWizard.js +34 -0
  359. package/src/config/configFile.js +441 -83
  360. package/src/config/configService.js +446 -0
  361. package/src/config.js +24 -64
  362. package/src/context/agentHome.js +9 -0
  363. package/src/context/archive.js +3 -2
  364. package/src/context/compact.js +8 -5
  365. package/src/context/contextLock.js +81 -0
  366. package/src/context/persistContext.js +30 -11
  367. package/src/context/reactiveCompact.js +4 -2
  368. package/src/context/recentDirs.js +21 -61
  369. package/src/context/sessionPath.js +97 -23
  370. package/src/context/sessionRegistry.js +303 -24
  371. package/src/context/tokenCounter.js +18 -0
  372. package/src/context/workspaceIndex.js +731 -0
  373. package/src/infra/verification.js +3 -2
  374. package/src/llm/client.js +293 -206
  375. package/src/llm/providers/anthropicFormat.js +241 -0
  376. package/src/llm/providers/openaiFormat.js +190 -0
  377. package/src/loop.js +211 -27
  378. package/src/main.js +61 -11
  379. package/src/messageQueue.js +97 -0
  380. package/src/multimodal.js +203 -0
  381. package/src/plugin-sdk.js +15 -0
  382. package/src/plugins/commandRouter.js +3 -0
  383. package/src/plugins/hookEngine.js +5 -1
  384. package/src/plugins/pluginLifecycle.js +28 -0
  385. package/src/plugins/pluginRunner.js +40 -0
  386. package/src/plugins/transcript.js +4 -3
  387. package/src/prompts/pluginList.js +2 -2
  388. package/src/prompts/projectInstructions.js +3 -3
  389. package/src/prompts/promptAssembly.js +133 -109
  390. package/src/prompts/skillList.js +12 -22
  391. package/src/session/runAgentTurn.js +70 -0
  392. package/src/session/runTurn.js +41 -6
  393. package/src/toolGate.js +17 -0
  394. package/src/tools/advisor/advisor.js +379 -0
  395. package/src/tools/agent/agent.js +555 -0
  396. package/src/tools/agent/agentTypes.js +59 -0
  397. package/src/tools/agent/coordinator.js +93 -0
  398. package/src/tools/agent/worktree.js +270 -0
  399. package/src/tools/ask/ask.js +7 -2
  400. package/src/tools/bash/bash.js +112 -18
  401. package/src/tools/download/download.js +144 -0
  402. package/src/tools/edit/edit.js +1 -0
  403. package/src/tools/edit/multi-edit.js +1 -0
  404. package/src/tools/genimage/genimage.js +242 -0
  405. package/src/tools/genspeech/genspeech.js +67 -0
  406. package/src/tools/glob/glob.js +1 -0
  407. package/src/tools/grep/grep.js +3 -0
  408. package/src/tools/grep/rgPath.js +2 -0
  409. package/src/tools/index.js +25 -2
  410. package/src/tools/read/read.js +1 -0
  411. package/src/tools/shared/killTree.js +52 -0
  412. package/src/tools/toolProgressBridge.js +48 -0
  413. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  414. package/src/tools/webbrowser/browser.js +59 -42
  415. package/src/tools/webbrowser/browserControl.js +118 -0
  416. package/src/tools/webbrowser/browserDaemon.js +97 -0
  417. package/src/tools/webbrowser/browserService.js +27 -0
  418. package/src/tools/webbrowser/browserStream.js +119 -0
  419. package/src/tools/webbrowser/webbrowser.js +699 -130
  420. package/src/tools/webfetch/webfetch.js +1 -0
  421. package/src/tools/websearch/websearch.js +134 -51
  422. package/src/tools/write/write.js +1 -0
  423. package/src/ui/App.js +249 -22
  424. package/src/ui/AskQuestion.js +19 -17
  425. package/src/ui/InputBox.js +34 -9
  426. package/src/ui/LiveArea.js +112 -19
  427. package/src/ui/MessageList.js +100 -14
  428. package/src/ui/ModelPicker.js +56 -0
  429. package/src/ui/QueuedMessagesBanner.js +14 -0
  430. package/src/ui/Root.js +18 -10
  431. package/src/ui/SessionPicker.js +102 -0
  432. package/src/ui/StatusLine.js +13 -4
  433. package/src/ui/WorkspacePicker.js +55 -0
  434. package/src/ui/figures.js +44 -0
  435. package/src/ui/hooks/askSlot.js +82 -0
  436. package/src/ui/hooks/useChat.js +345 -64
  437. package/src/ui/hooks/usePasteHandler.js +11 -1
  438. package/src/ui/hooks/useTextBuffer.js +1 -3
  439. package/src/ui/liveViewport.js +5 -1
  440. package/src/ui/markdown.js +206 -0
  441. package/src/ui/textBuffer.js +0 -7
  442. package/src/ui/theme.js +32 -0
  443. package/src/utils/debugLog.js +0 -4
  444. package/src/utils/resourcePaths.js +2 -2
  445. package/src/utils/stripThink.js +50 -0
  446. package/src/web/browserBridge.js +447 -0
  447. package/src/web/protocol.js +19 -0
  448. package/workflows/adaptive-research.yaml +9 -2
  449. package/workflows/book-review-short.yaml +31 -7
  450. package/workflows/e2e-write-greet.yaml +3 -0
  451. package/workflows/illustrate.yaml +227 -0
  452. package/workflows/plan.yaml +437 -207
  453. package/workflows/ppt.yaml +1659 -0
  454. package/workflows/ralph-loop.yaml +142 -112
  455. package/workflows/schema.json +81 -23
  456. package/workflows/webppt.yaml +496 -0
  457. package/workflows/word.yaml +710 -0
  458. package/workflows/youtube-shorts.yaml +38 -10
  459. package/MINIMAL-TRACE.md +0 -77
  460. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  461. package/skills/config/SKILL.md +0 -123
  462. package/skills/frontend-design/LICENSE.txt +0 -177
  463. package/skills/frontend-design/SKILL.md +0 -42
  464. package/skills/pptx/scripts/office/__pycache__/soffice.cpython-314.pyc +0 -0
  465. package/src/ui/ProgressPanel.js +0 -54
  466. package/src/ui/StreamingBlock.js +0 -11
  467. package/src/ui/ToolStatus.js +0 -11
@@ -7,71 +7,261 @@
7
7
  * 核心变更:
8
8
  * - init() 不再无条件清空,而是先读旧状态 → 判定任务身份 → 分发 4 种模式
9
9
  * - 新增 checkpoints/ 目录记录每步完成状态和 capture 变量
10
- * - 新增 artifacts/ 目录管理产出物
10
+ * - 产物生命周期:current.json.artifacts 记录工作流【声明】的产物路径(相对 cwd,
11
+ * 来自 yaml 的 def.artifacts / step.artifacts);new-task 时据此清旧产物(如旧 PLAN.md),
12
+ * completed 时据此报实际存在的产物。不再用从不写入的 artifacts/ 子目录。
11
13
  * - resume 模式时回放已完成步骤的 capture 到 vars 中
14
+ * - completed 检测:成功后 archive() 把目录改名 .done,故 init() 在活动目录缺失时
15
+ * 回退读 .done/current.json(status=done + taskId 匹配 → completed),修活「completed 死代码」。
12
16
  *
13
- * 目录结构:
14
- * <cwd>/.minimal-agent-workflow/
15
- * ├── current.json # { name, taskId, status, startedAt, lastFailedStep?, lastError? }
16
- * ├── inputs.json # 用户传入的 inputs 快照
17
- * ├── vars.json # 最后一次成功 step 后的 VarStack.snapshot()
18
- * ├── progress.md # 时间戳追加的 step 完成日志
19
- * ├── checkpoints/ # 每步一个 JSON:{ stepId, completedAt, output, captures, varsSnapshot }
20
- * └── artifacts/ # 产出物文件(PLAN.md 等)
17
+ * 目录结构(E6:按 workflow 名隔离,跨工作流互不可见/互不干扰):
18
+ * <cwd>/.minimal-agent-workflow/<name>/ (成功后改名为 <name>.done/)
19
+ * ├── current.json # { name, taskId, status, startedAt, artifacts?, lastFailedStep?, lastError? }
20
+ * ├── inputs.json # 用户传入的 inputs 快照
21
+ * ├── vars.json # 最后一次成功 step 后的 VarStack.snapshot()
22
+ * ├── progress.md # 时间戳追加的 step 完成日志
23
+ * ├── supplements.json # 贯穿补充要求(pause 反馈/澄清结论,resume 恢复进锚定)
24
+ * └── checkpoints/ # 每步一个 JSON:{ stepId, completedAt, output, captures, varsSnapshot }
25
+ * # 顶层步用原名;顶层 loop 内步用复合名 loopId@@迭代号@@stepId
26
+ * # (resume 可恢复到迭代中间,见 scanLoopResume)
27
+ * /new 按 .minimal-agent 前缀清理父目录,行为不变。
21
28
  * ============================================================
22
29
  */
30
+ import { createHash } from 'node:crypto';
23
31
  import { mkdir, writeFile, appendFile, rm, rename, readFile, readdir } from 'node:fs/promises';
32
+ import { existsSync } from 'node:fs';
24
33
  import { join } from 'node:path';
25
- import { computeTaskId } from './taskIdentity.js';
26
- const STATE_DIR = '.minimal-agent-workflow';
34
+ import { computeTaskId, extractGoal, workflowStateDir, } from './taskIdentity.js';
27
35
  const DONE_SUFFIX = '.done';
28
36
  const CHECKPOINTS_DIR = 'checkpoints';
29
- const ARTIFACTS_DIR = 'artifacts';
37
+ const SUPPLEMENTS_FILE = 'supplements.json';
38
+ /**
39
+ * 顶层 loop 内步的复合 checkpoint 文件名分隔符。
40
+ * 选 '@@':yaml step id 习惯用 [a-z0-9_],不会撞;且对文件系统安全。
41
+ */
42
+ export const LOOP_CP_SEP = '@@';
43
+ /** 复合 checkpoint 名:<loopId>@@<迭代号>@@<stepId>(runner 的 CpScope 与本模块扫描共用)。 */
44
+ export function loopCheckpointName(loopId, iteration, stepId) {
45
+ return `${loopId}${LOOP_CP_SEP}${iteration}${LOOP_CP_SEP}${stepId}`;
46
+ }
47
+ /** 解析复合 checkpoint 文件名;非复合名(普通顶层步)返回 null。 */
48
+ function parseLoopCheckpointName(name) {
49
+ const parts = name.split(LOOP_CP_SEP);
50
+ if (parts.length !== 3)
51
+ return null;
52
+ const iteration = Number(parts[1]);
53
+ if (!Number.isInteger(iteration) || iteration < 0)
54
+ return null;
55
+ return { loopId: parts[0], iteration, stepId: parts[2] };
56
+ }
57
+ /**
58
+ * 工作流定义的**结构指纹**:步骤 id + 执行形态 + 嵌套块(then/else/do)骨架的 hash。
59
+ * prompt 文案、args、condition 等内容字段不参与——改提示词/参数不应作废断点;
60
+ * 改结构(增删步/换 id/换类型/调嵌套)才作废:resume 的恢复点是**索引**、循环断点是
61
+ * 复合名(loopId@@迭代@@stepId),结构一变旧 checkpoint 就对不上,按旧断点续跑会错跳步
62
+ * (曾真实发生:plan.yaml v3→v4 重构后同 goal 重跑提示「从第 2 步续跑」,跳过的步与
63
+ * 新结构毫无对应关系)。current.json 记录运行时的指纹,init 同任务重跑时比对。
64
+ */
65
+ export function computeDefFingerprint(steps) {
66
+ const skeleton = (list) => (list ?? []).map((s) => ({
67
+ id: s.id,
68
+ kind: s.type ??
69
+ (s.tool ? 'tool' : s.skill ? 'skill' : s.agent ? 'agent' : s.llm ? 'llm' : '?'),
70
+ then: s.then ? skeleton(s.then) : undefined,
71
+ else: s.else ? skeleton(s.else) : undefined,
72
+ do: s.do ? skeleton(s.do) : undefined,
73
+ }));
74
+ return createHash('sha1')
75
+ .update(JSON.stringify(skeleton(steps)))
76
+ .digest('hex')
77
+ .slice(0, 16);
78
+ }
30
79
  export class WorkflowState {
31
80
  dir;
32
- constructor(workingDir) {
33
- this.dir = join(workingDir, STATE_DIR);
81
+ workingDir;
82
+ constructor(workingDir, workflowName) {
83
+ this.workingDir = workingDir;
84
+ // 按 workflow 名隔离(与 taskIdentity.computeTaskId 读的是同一权威路径)
85
+ this.dir = workflowStateDir(workingDir, workflowName);
34
86
  }
35
87
  /**
36
88
  * 初始化工作流状态 —— 核心入口,返回恢复信息。
37
89
  *
38
90
  * 4 种模式:
39
91
  * 1. fresh — 首次运行此工作流(无旧状态)
40
- * 2. new-task — 不同任务(hash 或时间窗口不匹配)→ 清理旧产物后全新开始
92
+ * 2. new-task — 不同任务 → 清理旧产物后全新开始
41
93
  * 3. completed — 同一任务已完成 → 返回产物列表
42
94
  * 4. resume — 同一任务未完成 → 扫描 checkpoint 决定恢复点
95
+ *
96
+ * hash 相等 = 同任务(权威信号,与时间无关)。hash 不等且旧任务【未完成】时
97
+ * 不再一票否决 new-task:若提供了 arbitrate(语义仲裁器,见 taskIdentity.ts),
98
+ * 由它判定新输入是「旧任务的延续指令(如"继续")→ resume」还是「全新任务 → new-task」。
43
99
  */
44
- async init(name, inputs, steps) {
45
- // 1. 计算当前任务的 identity
46
- const workingDir = this.dir.replace(`/${STATE_DIR}`, '') || process.cwd();
47
- const identity = computeTaskId(name, inputs, workingDir);
48
- // 2. 尝试读取旧 current.json
100
+ async init(name, inputs, steps,
101
+ // 跨次重跑回退步数(= WorkflowDef.self_heal.backoff),runWorkflow 注入;缺省 1。
102
+ selfHealBackoff = 1,
103
+ // 工作流【声明】的产物路径(相对 cwd,= def.artifacts + step.artifacts),runWorkflow 注入。
104
+ declaredArtifacts = [],
105
+ // 语义仲裁器(hash 不等 + 旧任务未完成时的二审),runWorkflow 注入;缺省直接 new-task。
106
+ arbitrate) {
107
+ // 1. 计算当前任务的 identity(goal 内容 hash 是唯一权威信号)
108
+ // 与本次定义的结构指纹(resume 兼容性信号:yaml 结构变了旧断点即作废)
109
+ const identity = computeTaskId(name, inputs, this.workingDir);
110
+ const fp = computeDefFingerprint(steps);
111
+ // 2. 尝试读取活动目录的 current.json
49
112
  const oldState = await this.readCurrentState();
50
113
  if (!oldState) {
51
- // 模式 1: fresh —— 无旧状态,首次运行
52
- await this.cleanSlate(name, inputs, identity.hash);
53
- return { mode: 'fresh', startFrom: 0 };
114
+ // 活动目录无状态 —— 可能是「上次成功已归档到 .done」。读归档判定 completed / 不同 goal 新任务。
115
+ const archived = await this.readArchivedState();
116
+ if (archived && archived.name === name) {
117
+ if (archived.taskId === identity.hash) {
118
+ // 模式 3: completed —— 同任务此前已成功(报实际存在的声明产物)
119
+ return {
120
+ mode: 'completed',
121
+ artifacts: this.existingArtifacts(archived.artifacts ?? declaredArtifacts),
122
+ };
123
+ }
124
+ // 同名 workflow 但 goal 不同 → 全新任务:清旧归档声明产物后 fresh
125
+ // (legacy 归档无 artifacts 字段时用本次声明清单兜底——同名 workflow 的声明产物
126
+ // 语义稳定,旧任务的产物路径与本次一致的概率极高,残留比误删危害大)
127
+ const cleaned = await this.cleanupDeclaredArtifacts(archived.artifacts ?? declaredArtifacts);
128
+ await this.cleanSlate(name, inputs, identity.hash, declaredArtifacts, fp);
129
+ return {
130
+ mode: 'new-task',
131
+ startFrom: 0,
132
+ cleanedArtifacts: [
133
+ ...cleaned,
134
+ ...(await this.cleanupStaleDeclared(declaredArtifacts)),
135
+ ],
136
+ };
137
+ }
138
+ // 模式 1: fresh —— 活动 + 归档都没有,首次运行。
139
+ // 声明产物若已存在于磁盘 = 残留孤儿(如 /new 删状态目录后的旧 PLAN.md,
140
+ // artifacts 清单随 current.json 一起被删,引擎无从知道它属于谁)。
141
+ // 用户约定:全新任务开始时清理残留与中间文件——删除范围**仅限本工作流
142
+ // 声明的产物**(与运行自身对它们的覆盖写权限一致,不碰用户其他文件)。
143
+ await this.cleanSlate(name, inputs, identity.hash, declaredArtifacts, fp);
144
+ return {
145
+ mode: 'fresh',
146
+ startFrom: 0,
147
+ cleanedArtifacts: await this.cleanupStaleDeclared(declaredArtifacts),
148
+ };
54
149
  }
55
- // 3. 用 computeTaskId 的 isSameTask 结果判断
150
+ // 3. 有活动状态(上次中断 / 失败 / 进行中,未归档)—— 用 hash 判同/异任务
56
151
  if (!identity.isSameTask) {
57
- // 模式 2: new-task —— 不同任务,清理旧产物后全新开始
58
- const artifacts = await this.listArtifacts();
59
- await this.cleanupArtifacts(artifacts);
60
- await this.cleanSlate(name, inputs, identity.hash);
61
- return { mode: 'new-task', cleanedArtifacts: artifacts, startFrom: 0 };
152
+ // hash 不等 + 同名 workflow + 旧任务未完成 → 语义仲裁二审:
153
+ // 新输入可能是「继续」这类延续指令(goal 文本必然与旧任务不同,按旧规则
154
+ // 会被误判 new-task 把没做完的产物清掉重开)。不同名 workflow / 旧任务已
155
+ // 完成 / 未提供仲裁器 → 维持 new-task。仲裁器自身抛错也兜底 new-task。
156
+ if (arbitrate &&
157
+ identity.reason === 'hash_diff' &&
158
+ oldState.name === name &&
159
+ oldState.status !== 'done' &&
160
+ // 定义结构已变(或 legacy 无指纹)→ 旧断点不可续,仲裁 resume 无意义,
161
+ // 跳过仲裁直接走 new-task
162
+ oldState.defFingerprint === fp) {
163
+ const prevGoal = extractGoal((await this.readInputs()) ?? {});
164
+ const newGoal = extractGoal(inputs);
165
+ let verdict = 'new';
166
+ try {
167
+ verdict = await arbitrate({
168
+ workflowName: name,
169
+ prevGoal,
170
+ newGoal,
171
+ completedStepIds: await this.scanCheckpoints(steps),
172
+ totalSteps: steps.length,
173
+ artifacts: oldState.artifacts ?? [],
174
+ });
175
+ }
176
+ catch {
177
+ verdict = 'new';
178
+ }
179
+ if (verdict === 'resume') {
180
+ // 延续旧任务:状态目录原样保留(taskId 仍是旧 goal 的 hash——
181
+ // 下次再输「继续」依然 hash 不等 → 再次仲裁快路径命中,语义自洽)。
182
+ const info = await this.buildResumeInfo(oldState, steps, selfHealBackoff);
183
+ info.prevGoal = prevGoal;
184
+ info.supplementaryGoal = newGoal;
185
+ return info;
186
+ }
187
+ }
188
+ // 模式 2: new-task —— 不同任务,清旧声明产物 + 旧状态后全新开始。
189
+ // legacy 状态文件无 artifacts 字段时:仅当同名 workflow 用本次声明清单兜底
190
+ //(不同名 workflow 的产物不是我们的,不能殃及)。
191
+ const cleaned = await this.cleanupDeclaredArtifacts(oldState.artifacts ?? (oldState.name === name ? declaredArtifacts : []));
192
+ await this.cleanSlate(name, inputs, identity.hash, declaredArtifacts, fp);
193
+ return {
194
+ mode: 'new-task',
195
+ startFrom: 0,
196
+ cleanedArtifacts: [
197
+ ...cleaned,
198
+ ...(await this.cleanupStaleDeclared(declaredArtifacts)),
199
+ ],
200
+ };
62
201
  }
63
- // 4. 同一任务 —— 检查完成状态
202
+ // 4. 同一任务 —— 检查完成状态(兜底:正常成功会 archive,故活动目录 done 少见)
64
203
  if (oldState.status === 'done') {
65
- // 模式 3: completed —— 同一任务已完成
66
- const artifacts = await this.listArtifacts();
67
- return { mode: 'completed', artifacts };
204
+ return {
205
+ mode: 'completed',
206
+ artifacts: this.existingArtifacts(oldState.artifacts ?? declaredArtifacts),
207
+ };
68
208
  }
69
- // 5. 模式 4: resume —— 同一任务未完成,扫描 checkpoint 决定恢复点
209
+ // 4.5 定义结构变更守卫:同任务但 yaml 结构改过(增删步/换 id/调嵌套)——
210
+ // resume 恢复点是索引、循环断点是复合名,结构一变旧 checkpoint 全对不上,
211
+ // 按旧断点续跑会错跳步(曾真实发生:plan v3→v4 重构后同 goal 重跑
212
+ // 「从第 2 步续跑」,用户困惑是 bug 还是残留)。旧现场作废、全新开始,
213
+ // runner 经 defChanged 旗标向用户明示。legacy 状态(无 defFingerprint
214
+ // 字段)无从验证兼容性,同样按变更处理(一次性升级成本,安全优先)。
215
+ if (oldState.defFingerprint !== fp) {
216
+ const cleaned = await this.cleanupDeclaredArtifacts(oldState.artifacts ?? declaredArtifacts);
217
+ await this.cleanSlate(name, inputs, identity.hash, declaredArtifacts, fp);
218
+ return {
219
+ mode: 'new-task',
220
+ startFrom: 0,
221
+ defChanged: true,
222
+ cleanedArtifacts: [
223
+ ...cleaned,
224
+ ...(await this.cleanupStaleDeclared(declaredArtifacts)),
225
+ ],
226
+ };
227
+ }
228
+ // 5. 模式 4: resume —— 同一任务未完成,扫描 checkpoint 决定恢复点。
229
+ return this.buildResumeInfo(oldState, steps, selfHealBackoff);
230
+ }
231
+ /**
232
+ * 构建 resume 信息:扫描 checkpoint 决定恢复点 + 加载 capture 回放数据。
233
+ * 回退步数由 WorkflowDef.self_heal.backoff 经 runWorkflow 传入(缺省 1)。
234
+ * 同 goal resume(hash 相等)与仲裁延续 resume(hash 不等但判定延续)共用。
235
+ */
236
+ async buildResumeInfo(oldState, steps, selfHealBackoff) {
70
237
  const completedSteps = await this.scanCheckpoints(steps);
71
- const selfHealBackoff = 1; // 默认回退 1 步,runner 可通过 WorkflowDef.self_heal.backoff 覆盖
72
- const recoveryStart = Math.max(0, completedSteps.length - selfHealBackoff);
238
+ // 恢复点 = 最后一个已完成步在 steps[] 中的【索引】+1 - backoff。
239
+ // 不能用 checkpoint【计数】当索引:when=false 跳过步 / pause 步不记 checkpoint,
240
+ // 计数会低估位置 → resume 重复执行已跑过的步(对非幂等 tool 步有实害)。
241
+ // 已完成步是连续前缀时索引法与计数法等价(向后兼容)。
242
+ const lastCompletedId = completedSteps[completedSteps.length - 1];
243
+ const lastIdx = lastCompletedId !== undefined
244
+ ? steps.findIndex((s) => s.id === lastCompletedId)
245
+ : -1;
246
+ const recoveryStart = Math.max(0, lastIdx + 1 - selfHealBackoff);
73
247
  // 加载 capturedVars 用于回放
74
248
  const capturedVars = await this.loadAllCaptures(completedSteps);
249
+ // 循环级恢复:中断发生在某顶层 loop 的迭代中间(该 loop 自身无完成 checkpoint、
250
+ // 但有复合迭代 checkpoint)→ 顶层直接定位到该 loop、迭代内续跑,
251
+ // 不再整条循环从头重来(plan.yaml 95% 的工作都在一个 loop 里,此前粒度形同虚设)。
252
+ const loopResume = await this.scanLoopResume(steps, completedSteps, selfHealBackoff);
253
+ if (loopResume) {
254
+ return {
255
+ mode: 'resume',
256
+ startFrom: loopResume.loopIndex,
257
+ completedStepIds: completedSteps,
258
+ failedStep: oldState.lastFailedStep,
259
+ recoveryStart: loopResume.loopIndex,
260
+ lastError: oldState.lastError,
261
+ capturedVars,
262
+ loopResume,
263
+ };
264
+ }
75
265
  return {
76
266
  mode: 'resume',
77
267
  startFrom: recoveryStart,
@@ -82,6 +272,97 @@ export class WorkflowState {
82
272
  capturedVars,
83
273
  };
84
274
  }
275
+ /**
276
+ * 扫描复合迭代 checkpoint(loopId@@iter@@stepId),产出进行中循环的恢复信息。
277
+ * 候选条件:顶层 type=loop 步 + 自身无完成 checkpoint(completedTopIds 不含它)。
278
+ * 多个候选(罕见,前一循环中断后又跑到后一循环)取顶层索引最大者(最远进度)。
279
+ */
280
+ async scanLoopResume(steps, completedTopIds, selfHealBackoff) {
281
+ const cpDir = join(this.dir, CHECKPOINTS_DIR);
282
+ let files;
283
+ try {
284
+ files = await readdir(cpDir);
285
+ }
286
+ catch {
287
+ return null;
288
+ }
289
+ const completedTop = new Set(completedTopIds);
290
+ // loopId → iteration → stepId 集合
291
+ const byLoop = new Map();
292
+ for (const f of files) {
293
+ if (!f.endsWith('.json'))
294
+ continue;
295
+ const parsed = parseLoopCheckpointName(f.replace(/\.json$/, ''));
296
+ if (!parsed)
297
+ continue;
298
+ let iters = byLoop.get(parsed.loopId);
299
+ if (!iters)
300
+ byLoop.set(parsed.loopId, (iters = new Map()));
301
+ let ids = iters.get(parsed.iteration);
302
+ if (!ids)
303
+ iters.set(parsed.iteration, (ids = new Set()));
304
+ ids.add(parsed.stepId);
305
+ }
306
+ if (byLoop.size === 0)
307
+ return null;
308
+ let best = null;
309
+ for (const [loopId, iters] of byLoop) {
310
+ const loopIndex = steps.findIndex((s) => s.id === loopId && s.type === 'loop');
311
+ if (loopIndex === -1)
312
+ continue; // yaml 改动后的孤儿 checkpoint,忽略
313
+ if (completedTop.has(loopId))
314
+ continue; // 该循环已整体完成
315
+ const loopDef = steps[loopIndex];
316
+ const doSteps = loopDef.do ?? [];
317
+ const validInner = new Set(doSteps.map((s) => s.id));
318
+ const iteration = Math.max(...iters.keys());
319
+ const innerIds = [...(iters.get(iteration) ?? [])]
320
+ .filter((id) => validInner.has(id))
321
+ .sort((a, b) => doSteps.findIndex((s) => s.id === a) - doSteps.findIndex((s) => s.id === b));
322
+ if (innerIds.length === 0)
323
+ continue;
324
+ const lastInnerIdx = doSteps.findIndex((s) => s.id === innerIds[innerIds.length - 1]);
325
+ const innerStartFrom = Math.max(0, lastInnerIdx + 1 - selfHealBackoff);
326
+ // 中断时刻最近的 vars 快照(迭代内最后一个完成步的 checkpoint)
327
+ let lastVarsSnapshot;
328
+ try {
329
+ const raw = await readFile(join(cpDir, `${loopCheckpointName(loopId, iteration, innerIds[innerIds.length - 1])}.json`), 'utf-8');
330
+ lastVarsSnapshot = JSON.parse(raw).varsSnapshot;
331
+ }
332
+ catch {
333
+ // 快照读失败不阻断:仍可定位迭代,变量靠顶层 capture 回放兜底
334
+ }
335
+ const candidate = {
336
+ loopId,
337
+ loopIndex,
338
+ iteration,
339
+ innerStartFrom,
340
+ completedInnerIds: innerIds,
341
+ lastVarsSnapshot,
342
+ };
343
+ if (!best || candidate.loopIndex > best.loopIndex)
344
+ best = candidate;
345
+ }
346
+ return best;
347
+ }
348
+ // ---- 公共方法:贯穿补充要求持久化(goalSupplements,runner 写/读) ----
349
+ /** 追加一条用户补充要求到 supplements.json(resume 跨进程不丢)。 */
350
+ async appendSupplement(text) {
351
+ const list = await this.readSupplements();
352
+ list.push(text);
353
+ await writeFile(join(this.dir, SUPPLEMENTS_FILE), JSON.stringify(list, null, 2), 'utf-8').catch(() => { });
354
+ }
355
+ /** 读取已累积的补充要求列表(无文件 / 解析失败返回 [])。 */
356
+ async readSupplements() {
357
+ try {
358
+ const raw = await readFile(join(this.dir, SUPPLEMENTS_FILE), 'utf-8');
359
+ const parsed = JSON.parse(raw);
360
+ return Array.isArray(parsed) ? parsed.filter((x) => typeof x === 'string') : [];
361
+ }
362
+ catch {
363
+ return [];
364
+ }
365
+ }
85
366
  // ---- 公共方法:checkpoint / 失败 / 完成 ----
86
367
  /** 记录单个步骤成功完成(含 capture 变量和 vars 快照) */
87
368
  async recordCheckpoint(stepId, output, captures, varsSnapshot) {
@@ -180,24 +461,22 @@ export class WorkflowState {
180
461
  }
181
462
  return result;
182
463
  }
183
- // ---- 公共方法:产物管理 ----
184
- /** 列出 artifacts/ 目录中的产物文件路径 */
185
- async listArtifacts() {
186
- const artDir = join(this.dir, ARTIFACTS_DIR);
187
- try {
188
- const files = await readdir(artDir);
189
- return files.filter((f) => f !== '.gitkeep');
190
- }
191
- catch {
192
- return [];
193
- }
194
- }
195
- /** 清理指定的产物文件 */
196
- async cleanupArtifacts(paths) {
197
- const artDir = join(this.dir, ARTIFACTS_DIR);
464
+ // ---- 私有方法:声明式产物生命周期 ----
465
+ /** 删除工作流【声明】的产物文件(相对 cwd),用于 new-task 清理旧任务残留(如旧 PLAN.md)。
466
+ * 仅删工作流自己声明的产物、仅 force-rm 文件,scoped 安全。返回清理过的路径列表。 */
467
+ async cleanupDeclaredArtifacts(paths) {
198
468
  for (const p of paths) {
199
- await rm(join(artDir, p), { force: true }).catch(() => { });
469
+ await rm(join(this.workingDir, p), { force: true }).catch(() => { });
200
470
  }
471
+ return paths;
472
+ }
473
+ /** 从声明产物路径里过滤出 cwd 下【实际存在】的文件(completed 报真实产物用)。 */
474
+ existingArtifacts(paths) {
475
+ return paths.filter((p) => existsSync(join(this.workingDir, p)));
476
+ }
477
+ /** 清理【实际存在】的本次声明产物(fresh/new-task 启动时清残留孤儿),返回真正删掉的路径。 */
478
+ async cleanupStaleDeclared(declaredArtifacts) {
479
+ return this.cleanupDeclaredArtifacts(this.existingArtifacts(declaredArtifacts));
201
480
  }
202
481
  // ---- 公共方法:归档与清理(保持原有签名兼容) ----
203
482
  /**
@@ -225,21 +504,32 @@ export class WorkflowState {
225
504
  await rm(this.dir + DONE_SUFFIX, { recursive: true, force: true }).catch(() => { });
226
505
  }
227
506
  // ---- 私有方法 ----
228
- /** 创建全新的空白状态目录(含 checkpoints/ 和 artifacts/ 子目录) */
229
- async cleanSlate(name, inputs, taskId) {
507
+ /** 创建全新的空白状态目录(含 checkpoints/ 子目录),并记录声明产物清单。 */
508
+ async cleanSlate(name, inputs, taskId, declaredArtifacts = [], defFingerprint) {
230
509
  await this.cleanup();
231
510
  await mkdir(this.dir, { recursive: true });
232
511
  await mkdir(join(this.dir, CHECKPOINTS_DIR), { recursive: true });
233
- await mkdir(join(this.dir, ARTIFACTS_DIR), { recursive: true });
234
512
  await this.writeCurrent({
235
513
  name,
236
514
  taskId,
237
515
  status: 'running',
238
516
  startedAt: new Date().toISOString(),
517
+ artifacts: declaredArtifacts,
518
+ defFingerprint,
239
519
  });
240
520
  await writeFile(join(this.dir, 'inputs.json'), JSON.stringify(inputs, null, 2), 'utf-8');
241
521
  await writeFile(join(this.dir, 'progress.md'), `# ${name}\n\n`, 'utf-8');
242
522
  }
523
+ /** 读取旧任务的 inputs.json(仲裁时取旧 goal 用),失败返回 null */
524
+ async readInputs() {
525
+ try {
526
+ const raw = await readFile(join(this.dir, 'inputs.json'), 'utf-8');
527
+ return JSON.parse(raw);
528
+ }
529
+ catch {
530
+ return null;
531
+ }
532
+ }
243
533
  /** 读取 current.json,解析失败或文件不存在返回 null */
244
534
  async readCurrentState() {
245
535
  try {
@@ -250,6 +540,16 @@ export class WorkflowState {
250
540
  return null;
251
541
  }
252
542
  }
543
+ /** 读取归档目录(.done)的 current.json,用于 completed 检测(成功后 dir 被 archive 改名)。 */
544
+ async readArchivedState() {
545
+ try {
546
+ const raw = await readFile(join(this.dir + DONE_SUFFIX, 'current.json'), 'utf-8');
547
+ return JSON.parse(raw);
548
+ }
549
+ catch {
550
+ return null;
551
+ }
552
+ }
253
553
  /** 写入 current.json */
254
554
  async writeCurrent(data) {
255
555
  await writeFile(join(this.dir, 'current.json'), JSON.stringify(data, null, 2), 'utf-8');
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: ai-image
3
+ description: 当用户说"生成图片"、"画一张图"、"配图"、"插画/插图"、"AI 生图"、"图生图"、"参考这张图改图/扩图/增强"或者你判断出客户实际上的要求等价于用AI生成或编辑图片时使用(OPENAI_IMAGE_* 配置那一路)。支持文生图和带输入图的图生图/参考图生成,支持指定宽高比。与 image-gen-openrouter(OPENROUTER_API_KEY)是两套独立机制,按用户配置选用,别混用。
4
+ ---
5
+
6
+ # ai-image —— 文生图 / 图生图
7
+
8
+ > 生图能力住在本技能的脚本 `scripts/generate.mjs`(零依赖、裸 fetch、自己落盘),
9
+ > 走 `OPENAI_IMAGE_*` 配置。工作流批量生图优先用内核 `GenerateImage` 工具(无 Bash 120s 帽),
10
+ > 手动调试或单张生成时用 Bash 调脚本即可。
11
+ > 如果用户明确点名 OpenRouter / nano banana 且用 `OPENROUTER_API_KEY`,那是另一个技能
12
+ > `image-gen-openrouter`(curl 版),别混用。
13
+
14
+ ## 怎么干
15
+
16
+ 用 **Bash 工具**跑本技能目录下的脚本(**用 node 跑,不是 bun**):
17
+
18
+ ```bash
19
+ node "<本技能目录>/scripts/generate.mjs" \
20
+ --prompt "A serene mountain lake at sunrise, soft mist, photorealistic, wide cinematic shot" \
21
+ --out ./assets/lake.png \
22
+ --aspect 16:9
23
+ ```
24
+
25
+ 图生图 / 参考图增强:
26
+
27
+ ```bash
28
+ node "<本技能目录>/scripts/generate.mjs" \
29
+ --prompt "Extend this product photo into a clean 16:9 hero background, preserve the main product, premium soft light, no text, no logos" \
30
+ --input-image ./assets/source.png \
31
+ --out ./assets/source_enhanced.png \
32
+ --aspect 16:9
33
+ ```
34
+
35
+ 批量 PPT 配图不要让 agent 循环手写 Bash;在 workflow 中优先调用 `GenerateImage` 工具。手动调试时,
36
+ 让计划脚本读取 `img_plan.json` 确定性执行:
37
+
38
+ ```bash
39
+ node "<本技能目录>/scripts/generate_from_plan.mjs" \
40
+ --plan ./build/img_plan.json \
41
+ --assets ./build/assets \
42
+ --timeout-ms 600000 \
43
+ --size 1K
44
+ ```
45
+
46
+ - `<本技能目录>` = 系统提示「可用技能」列表里本技能(ai-image)后面给出的**绝对目录**,直接拼 `/scripts/generate.mjs` 或 `/scripts/generate_from_plan.mjs`。
47
+ - 路径含空格时给整条路径加引号。
48
+ - **调 Bash 时务必把 `timeout` 设成 `600000`(10 分钟)**:图像模型很慢(一张常要 1 分钟、重模型几分钟),
49
+ Bash 默认只给 120s,会把还没出图的进程当超时掐断。脚本运行时会往 stderr 打 `[ai-image] attempt k/N …` 进度,
50
+ 看到它就说明在正常等待、不是卡死。
51
+
52
+ ### 参数
53
+
54
+ | 参数 | 必填 | 说明 |
55
+ |------|------|------|
56
+ | `--prompt` | ✅ | 图片提示词。**英文通常效果更好**;用户给的是中文就保留,不要主动翻译。越具体(主体/风格/光线/构图/负空间)越好。 |
57
+ | `--out` | ✅ | 落盘路径。绝对或相对(相对当前工作目录)。**建议 `.png` 后缀**。父目录自动创建。 |
58
+ | `--aspect` | ⬜ | 宽高比,如 `16:9` / `9:16` / `4:5` / `1:1`。脚本会同时①发 provider 原生 `image_config.aspect_ratio`、②在提示词里追加构图提示,双保险。 |
59
+ | `--size` | ⬜ | 分辨率档,如 `1K` / `2K` / `4K`(部分模型/端点支持;不支持则忽略)。 |
60
+ | `--model` | ⬜ | 临时覆盖 `OPENAI_IMAGE_MODEL`(想换更快/更便宜的模型试一张时用)。 |
61
+ | `--input-image` | ⬜ | 输入参考图,本地路径 / URL / data URL 均可。本地图片会转成 `data:image/...;base64,...`。用于图生图、扩图、换背景、比例修复、质感增强。 |
62
+
63
+ 成功后脚本在 stdout 打**一行**:`saved: <path> (model=<m>, bytes=<n>, aspect=<a>)`;失败信息走 stderr,退出码非 0。
64
+
65
+ ## 注意事项
66
+
67
+ - **先确认配置**:脚本需要 `.env`/环境里有 `OPENAI_IMAGE_BASE_URL` / `OPENAI_IMAGE_API_KEY` / `OPENAI_IMAGE_MODEL`。
68
+ 缺了脚本以**退出码 3** + 明确文案报错——如实转告用户补好配置再试,别瞎重试。
69
+ - **协议自动选(无需关心)**:脚本支持两套生图协议并按 provider 自动切——OpenRouter 的多模态图模型走
70
+ `chat/completions`(图在 `message.images[]`,输入图作为 `image_url` 内容块);OpenAI / Azure / **火山方舟 Seedream** 走官方 Images API
71
+ `images/generations`(图在 `data[0].b64_json`,输入图通过 `image` 扩展参数传入)。`OPENAI_IMAGE_PROVIDER ∈ {volces,volces-plan,ark,doubao,seedream,openai,azure}`
72
+ → images,其余 → chat;可用 `OPENAI_IMAGE_PROTOCOL=chat|images` 显式覆盖。用户只换 `.env` 即可切 provider,
73
+ 调脚本的命令行参数完全不变。
74
+ - **火山 Seedream 注意 base URL**:图片生成/图生图用 `https://ark.cn-beijing.volces.com/api/v3`;不要用聊天/识图的
75
+ `https://ark.cn-beijing.volces.com/api/coding/v3`。若开通了「流量包」套餐(`OPENAI_IMAGE_PROVIDER=volces-plan`),
76
+ base URL 改用 `https://ark.cn-beijing.volces.com/api/plan/v3`(按套餐计费,模型 ID 需与套餐支持列表一致,别混用 v3 端点否则产生套餐外费用)。
77
+ - **图生图用途**:客户图比例不适合、主体太小、背景杂乱、需要扩成 16:9 hero、需要统一光影或增强清晰度时使用。不要把图生图当作结构图生成器;流程、架构、表格、模块图仍用 HTML/SVG/图表。
78
+ - **速度取决于模型**:flash 类(如 `google/gemini-3.1-flash-image-preview`)约数十秒/张;重模型(如 `openai/gpt-5.4-image-2`)可达 ~3 分钟/张。生成期间耐心等其返回再发下一张;批量会成倍变慢,按需克制数量。
79
+ - **鉴权失败**(401/403,退出码 1):脚本返回「API key 可能失效/无权」诊断;转告用户检查 key,别重试烧钱。
80
+ - **不要回显 API key**:脚本本身绝不打印 key;你转述错误时也别把 `OPENAI_IMAGE_API_KEY` 带出来。
81
+ - **绿色版**:脚本随 `skills/` 分发到 exe 同级,但执行需机器装有 **Node**(与 ui-design 的 PPTX 导出脚本同样要求)。
82
+ - **校验产物**:生成后可用 `Read`(offset=0 limit=1)触发二进制嗅探,或 `Bash` 看文件 size > 10KB 确认是真图。
83
+ - 一次一张;多张就多次调用、每次换 `--out` 路径。
84
+
85
+ ## 关于宽高比(重要)
86
+
87
+ `--aspect` 决定**生成图的构图**(主体摆放、负空间留给谁)。当生图是为了放进 PPT/网页的固定槽位时,
88
+ **aspect 应贴合槽位的宽高比**——这样 CSS `object-fit:cover` 裁切最少、构图意图(如给文字留的净空)能保住。
89
+ 怎么按版面选 aspect 见 ui-design 的 `references/layout-composition.md`(图文混排方法论)。