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
@@ -0,0 +1,144 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/download/download.ts —— Download 工具(URL → 文件)
4
+ * ------------------------------------------------------------
5
+ * 通用基础能力:把一个 URL 的字节流下载落盘。填补 WebFetch(HTML→markdown,
6
+ * 读文本用)与 Bash(curl/Invoke-WebRequest,跨平台脆)之间的空白——纯 YAML
7
+ * 工作流要「把图片/资产存盘」时用它(如 illustrate 配图工作流逐图下载候选)。
8
+ *
9
+ * 输入:
10
+ * url 必填,http(s) 资源地址
11
+ * path 必填,落盘路径(相对 getWorkingDir 锚定;绝对路径原样)
12
+ * headers 可选,自定义请求头(如 Referer/User-Agent,绕部分防盗链)
13
+ * timeout 可选,毫秒,默认 60000(无新字节判死由 fetch/AbortSignal 控)
14
+ * max_bytes 可选,体积上限(默认 25MB;超限不写、报错,防失控)
15
+ *
16
+ * 输出:成功 = { ok, content: "DOWNLOADED: <bytes> → <path> (<content-type>)" };
17
+ * 失败 = { ok:false, error } —— 不抛异常,error 喂回 LLM(同全项目工具约定)。
18
+ *
19
+ * 可测:fetch 经 deps 注入(不连真网);落盘走真实 fs(测试用 tmp 目录)。
20
+ * ============================================================
21
+ */
22
+ import { mkdir, writeFile } from 'node:fs/promises';
23
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
24
+ import { z } from 'zod';
25
+ import { getWorkingDir } from '../../bootstrap/workingDir.js';
26
+ import { toToolParameters } from '../../utils/zodToJson.js';
27
+ const DEFAULT_TIMEOUT_MS = 60_000;
28
+ const DEFAULT_MAX_BYTES = 25 * 1024 * 1024;
29
+ // ---------------- 1. Zod 输入 schema ----------------
30
+ const inputSchema = z.object({
31
+ url: z.string().url().describe('http(s) resource URL to download'),
32
+ path: z
33
+ .string()
34
+ .describe('Destination file path (relative paths anchor to the working dir)'),
35
+ headers: z
36
+ .record(z.string())
37
+ .optional()
38
+ .describe('Optional request headers (e.g. Referer/User-Agent to bypass hotlink guards)'),
39
+ timeout: z
40
+ .number()
41
+ .int()
42
+ .positive()
43
+ .optional()
44
+ .describe('Timeout in milliseconds (default: 60000)'),
45
+ max_bytes: z
46
+ .number()
47
+ .int()
48
+ .positive()
49
+ .optional()
50
+ .describe('Max size in bytes; over this the download is rejected (default: 25MB)'),
51
+ });
52
+ const parameters = toToolParameters(inputSchema);
53
+ const description = `Download a URL to a local file (raw bytes).
54
+
55
+ When to use Download vs WebFetch:
56
+ - WebFetch: read a web page as markdown text (for the model to consume).
57
+ - Download: save the raw bytes of a resource (image / pdf / asset) to disk.
58
+
59
+ Input parameters:
60
+ - url (required): http(s) URL.
61
+ - path (required): destination file path (relative paths anchor to the working dir; parent dirs are created).
62
+ - headers (optional): request headers (e.g. Referer/User-Agent to bypass hotlink protection).
63
+ - timeout (optional): milliseconds (default 60000).
64
+ - max_bytes (optional): reject downloads larger than this (default 25MB).
65
+
66
+ Notes:
67
+ - Returns an error (not an exception) on HTTP failure / timeout / oversize.
68
+ - Overwrites an existing file at path.`;
69
+ const defaultDeps = {
70
+ fetchImpl: globalThis.fetch,
71
+ };
72
+ // ---------------- 3. call() ----------------
73
+ async function call(input, signal, deps = defaultDeps) {
74
+ const { url, path } = input;
75
+ const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS;
76
+ const maxBytes = input.max_bytes ?? DEFAULT_MAX_BYTES;
77
+ const abs = isAbsolute(path) ? path : resolve(join(getWorkingDir(), path));
78
+ // 合并外部 signal 与超时
79
+ const ctrl = new AbortController();
80
+ const timer = setTimeout(() => ctrl.abort(), timeout);
81
+ if (signal) {
82
+ if (signal.aborted)
83
+ ctrl.abort();
84
+ else
85
+ signal.addEventListener('abort', () => ctrl.abort(), { once: true });
86
+ }
87
+ try {
88
+ let res;
89
+ try {
90
+ res = await deps.fetchImpl(url, { headers: input.headers, signal: ctrl.signal });
91
+ }
92
+ catch (e) {
93
+ const msg = ctrl.signal.aborted
94
+ ? `下载超时(${timeout}ms)或被中断`
95
+ : e instanceof Error
96
+ ? e.message
97
+ : String(e);
98
+ return { ok: false, error: `[Download] 请求失败:${msg}(${url})` };
99
+ }
100
+ if (!res.ok) {
101
+ return {
102
+ ok: false,
103
+ error: `[Download] HTTP ${res.status} ${res.statusText}(${url})`,
104
+ };
105
+ }
106
+ // content-length 预检(有则提前拒绝超大文件,省下载)
107
+ const declared = Number(res.headers.get('content-length') ?? '');
108
+ if (Number.isFinite(declared) && declared > maxBytes) {
109
+ return {
110
+ ok: false,
111
+ error: `[Download] 体积 ${declared} 字节超过上限 ${maxBytes}(${url})`,
112
+ };
113
+ }
114
+ const buf = Buffer.from(await res.arrayBuffer());
115
+ if (buf.byteLength > maxBytes) {
116
+ return {
117
+ ok: false,
118
+ error: `[Download] 下载 ${buf.byteLength} 字节超过上限 ${maxBytes}(${url})`,
119
+ };
120
+ }
121
+ await mkdir(dirname(abs), { recursive: true });
122
+ await writeFile(abs, buf);
123
+ const ctype = res.headers.get('content-type') ?? 'unknown';
124
+ return {
125
+ ok: true,
126
+ content: `DOWNLOADED: ${buf.byteLength} 字节 → ${abs}(${ctype})`,
127
+ };
128
+ }
129
+ finally {
130
+ clearTimeout(timer);
131
+ }
132
+ }
133
+ // ---------------- 4. 导出 ----------------
134
+ export const downloadTool = {
135
+ name: 'Download',
136
+ description,
137
+ inputSchema,
138
+ parameters,
139
+ isReadOnly: false,
140
+ isConcurrencySafe: true,
141
+ call,
142
+ };
143
+ // 供单测:直接调 call 注入 deps。
144
+ export const _downloadCall = call;
@@ -276,5 +276,6 @@ export const editTool = {
276
276
  isReadOnly: false,
277
277
  isConcurrencySafe: false,
278
278
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
279
+ interruptBehavior: () => 'block',
279
280
  call,
280
281
  };
@@ -175,5 +175,6 @@ export const multiEditTool = {
175
175
  isReadOnly: false,
176
176
  isConcurrencySafe: false,
177
177
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
178
+ interruptBehavior: () => 'block',
178
179
  call,
179
180
  };
@@ -0,0 +1,242 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/genimage/genimage.ts —— GenerateImage 工具
4
+ * ------------------------------------------------------------
5
+ * 专用「图像生成」工具:按 img_plan.json 批量生成 PPT 配图(文生图 / 图生图)。
6
+ *
7
+ * 为什么要专用工具、而不是用 Bash 跑脚本(用户拍板·Claude Code 同款):
8
+ * ─────────────────────────────────────────────────────────
9
+ * 图像生成是**专用的长时操作**(单张 Seedream ~50s,一批 5 张 ~250s)。
10
+ * 通用 Bash 工具默认超时 120s、硬上限 600s(src/tools/bash/bash.ts),
11
+ * 用它跑生图必然把整批**在中途 SIGKILL**(实测两次都杀在第 3 张)。
12
+ * Bash 该是「最后兜底」,专用长操作应封装专用工具——本工具**无 120s 帽**,
13
+ * 仅凭 `ctx.signal`(Ctrl+C/ESC)中断,配合脚本自身的「总时长自限 + 降级」,
14
+ * 让生图永不被误杀、AI 配图失败也优雅退化、绝不阻断工作流。
15
+ *
16
+ * 薄封装:HTTP 双协议 / 重试 / 降级写占位逻辑仍住 `skills/ai-image`
17
+ * (generate_from_plan.mjs + generate.mjs),本工具只负责「无帽地把它跑完、
18
+ * 收口成 ToolResult」,不重写生图逻辑(守减法 + 三层解耦)。
19
+ * ============================================================
20
+ */
21
+ import { spawn } from 'node:child_process';
22
+ import { existsSync } from 'node:fs';
23
+ import { dirname, join, resolve } from 'node:path';
24
+ import { z } from 'zod';
25
+ import { trustedToolEnv } from '../../config/configService.js';
26
+ import { getResourceSearchPaths } from '../../utils/resourcePaths.js';
27
+ import { killTree } from '../shared/killTree.js';
28
+ import { toToolParameters } from '../../utils/zodToJson.js';
29
+ // ---------------- 1. Zod 输入 schema ----------------
30
+ const inputSchema = z.object({
31
+ plan: z
32
+ .string()
33
+ .optional()
34
+ .describe('img_plan.json 路径(每条 {file,mode,prompt,...});默认 ./build/img_plan.json'),
35
+ assets: z
36
+ .string()
37
+ .optional()
38
+ .describe('图片输出目录(客户原图也在这);默认 <plan 同级>/assets 或 ./build/assets'),
39
+ size: z.string().optional().describe('生成尺寸档(透传 generate.mjs,默认 1K)'),
40
+ timeout_ms: z
41
+ .number()
42
+ .int()
43
+ .positive()
44
+ .optional()
45
+ .describe('单张生成超时(毫秒,透传 generate_from_plan 的 --timeout-ms,默认 600000=10min)'),
46
+ wall_budget_ms: z
47
+ .number()
48
+ .int()
49
+ .positive()
50
+ .optional()
51
+ // 成本不计、最大努力生图:预算放宽到 60min,防尾部图撞预算被误降级。
52
+ .describe('整批总时长预算(毫秒);超了把剩余图全部走降级占位、主动收尾。默认 3600000=60min'),
53
+ retry_degraded_report: z
54
+ .string()
55
+ .optional()
56
+ .describe('只重试某次 image_generation_report.json 里的 degraded 条目'),
57
+ concurrency: z
58
+ .number()
59
+ .int()
60
+ .positive()
61
+ .optional()
62
+ .describe('批内并发生成张数(透传 generate_from_plan 的 --concurrency,默认 1=顺序)'),
63
+ max_rounds: z
64
+ .number()
65
+ .int()
66
+ .positive()
67
+ .optional()
68
+ .describe('重试轮数;配合 retry_degraded_report 使用时默认 3'),
69
+ });
70
+ const parameters = toToolParameters(inputSchema);
71
+ const description = `按 img_plan.json 批量生成 PPT 配图(文生图 / 图生图),专为长时图像生成设计。
72
+ - 无 120s 超时帽(区别于 Bash),仅响应 Ctrl+C/ESC 中断;适合一批多张图(每张可达分钟级)。
73
+ - 内建优雅退化:单张失败(敏感内容/超时/网络/空输出)会写降级占位资产(图生图退回原图、文生图写渐变背景),**绝不让 deck 坏图、绝不阻断工作流**。
74
+ - 输入 plan(默认 ./build/img_plan.json)+ assets(默认同级 assets)。空 plan / 无 plan 直接跳过。`;
75
+ /** 在 skills 搜索路径里定位 ai-image 的生图编排脚本。 */
76
+ function locateScript() {
77
+ for (const base of getResourceSearchPaths('skills', import.meta.url)) {
78
+ const p = join(base, 'ai-image', 'scripts', 'generate_from_plan.mjs');
79
+ if (existsSync(p))
80
+ return p;
81
+ }
82
+ return null;
83
+ }
84
+ const TLS_WARNING_RE = /Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0'[\s\S]*?\(Use `node --trace-warnings \.\.\.`[\s\S]*?\)\n?/g;
85
+ function stripKnownNoise(text) {
86
+ return text.replace(TLS_WARNING_RE, '').trim();
87
+ }
88
+ function collectProgressSummary(stderr) {
89
+ const lines = stripKnownNoise(stderr)
90
+ .split('\n')
91
+ .map((line) => line.trim())
92
+ .filter((line) => line.startsWith('[ai-image-plan]'));
93
+ if (lines.length === 0)
94
+ return '';
95
+ const uniqueTail = [...new Set(lines)].slice(-6);
96
+ return uniqueTail.join('\n');
97
+ }
98
+ /**
99
+ * 步内实时进度:把子进程 stderr 流按行切分(chunk 可能把一行切成两半,必须缓冲),
100
+ * 只把 `[ai-image-plan] …` 进度行交给 onLine。导出供单测直接喂「切断的 chunk 流」。
101
+ * 治「31 张生图 ~13 分钟 TUI 全程静默被误判死机」(2026-07-02 事故)——
102
+ * generate_from_plan.mjs 本就逐张打进度到 stderr,此前只在结束后汇总,运行中无人消费。
103
+ */
104
+ export function createProgressLineFeed(onLine) {
105
+ let buf = '';
106
+ return (chunk) => {
107
+ buf += chunk;
108
+ let idx = buf.indexOf('\n');
109
+ while (idx >= 0) {
110
+ const line = buf.slice(0, idx).trim();
111
+ buf = buf.slice(idx + 1);
112
+ if (line.startsWith('[ai-image-plan]'))
113
+ onLine(line);
114
+ idx = buf.indexOf('\n');
115
+ }
116
+ };
117
+ }
118
+ // killTree(中断杀整棵进程树)已升为共享 util —— 见 ../shared/killTree.js
119
+ // (两次规则:genimage + bash 长命令都需要,2026-07-02 生图事故的同类问题在 Bash 复现)。
120
+ // ---------------- call() ----------------
121
+ async function call(input, signal, ctx) {
122
+ const planPath = resolve(input.plan ?? './build/img_plan.json');
123
+ const assetsDir = resolve(input.assets ?? join(dirname(planPath), 'assets'));
124
+ if (!existsSync(planPath)) {
125
+ return { ok: true, content: `PLAN_EMPTY: ${planPath} 不存在,跳过生图。` };
126
+ }
127
+ const script = locateScript();
128
+ if (!script) {
129
+ // 找不到脚本也不抛、不 halt——工作流靠 deck 已写的 enhanced/原图引用降级(onError:continue)。
130
+ return {
131
+ ok: false,
132
+ error: 'GenerateImage: 未找到 skills/ai-image/scripts/generate_from_plan.mjs(已跳过,不阻断)。',
133
+ };
134
+ }
135
+ const cmd = [
136
+ script,
137
+ '--plan',
138
+ planPath,
139
+ '--assets',
140
+ assetsDir,
141
+ '--timeout-ms',
142
+ String(input.timeout_ms ?? 600_000),
143
+ '--wall-budget-ms',
144
+ // 成本不计、最大努力生图:默认 60min(原 25min 偏紧,尾部图常撞预算被降级)。
145
+ String(input.wall_budget_ms ?? 3_600_000),
146
+ '--size',
147
+ input.size ?? '1K',
148
+ ];
149
+ if (input.retry_degraded_report) {
150
+ cmd.push('--retry-degraded-report', resolve(input.retry_degraded_report));
151
+ }
152
+ if (input.max_rounds) {
153
+ cmd.push('--max-rounds', String(input.max_rounds));
154
+ }
155
+ if (input.concurrency) {
156
+ cmd.push('--concurrency', String(input.concurrency));
157
+ }
158
+ const trustedEnv = await trustedToolEnv('imageGeneration');
159
+ return await new Promise((resolvePromise) => {
160
+ let child;
161
+ try {
162
+ // 用 `node` 跑 .mjs(与既有 Bash 步一致;绿色版同样依赖 node 在 PATH)。
163
+ // POSIX 下 detached 自成进程组,中断时 kill(-pid) 才能掀到孙进程(见 killTree);
164
+ // win32 走 taskkill /T,不需要(也不该)detached。
165
+ child = spawn('node', cmd, {
166
+ stdio: ['ignore', 'pipe', 'pipe'],
167
+ env: { ...trustedEnv, MINIMAL_AGENT_CAPTURED_STDIO: '1' },
168
+ detached: process.platform !== 'win32',
169
+ });
170
+ }
171
+ catch (e) {
172
+ resolvePromise({ ok: false, error: `GenerateImage 启动失败:${e.message}(不阻断)` });
173
+ return;
174
+ }
175
+ let out = '';
176
+ let errOut = '';
177
+ // taskkill /F 杀掉的子进程在 win32 上 close 时 signal=null、exit code 非 0,
178
+ // 单看 sig 判不出「被用户中断」——用本地 flag 记账,close 时据此正确报「被中断」。
179
+ let killedByAbort = false;
180
+ const onAbort = () => {
181
+ killedByAbort = true;
182
+ killTree(child);
183
+ };
184
+ if (signal) {
185
+ if (signal.aborted)
186
+ onAbort();
187
+ else
188
+ signal.addEventListener('abort', onAbort, { once: true });
189
+ }
190
+ // 步内实时进度通过调用级上下文上报,精确关联到当前 toolCallId。
191
+ const feedProgress = createProgressLineFeed((line) => ctx?.reportProgress?.(line));
192
+ child.stdout?.on('data', (d) => {
193
+ out += d.toString();
194
+ });
195
+ child.stderr?.on('data', (d) => {
196
+ const text = d.toString();
197
+ errOut += text;
198
+ feedProgress(text);
199
+ });
200
+ child.on('error', (e) => {
201
+ if (signal)
202
+ signal.removeEventListener('abort', onAbort);
203
+ resolvePromise({ ok: false, error: `GenerateImage 子进程错误:${e.message}(不阻断)` });
204
+ });
205
+ child.on('close', (code, sig) => {
206
+ if (signal)
207
+ signal.removeEventListener('abort', onAbort);
208
+ // 末行结局:脚本降级时仍 exit 0(IMAGE_PLAN_OK/DEGRADED);exit≠0 仅结构/安全 bug。
209
+ const tail = (out + '\n' + errOut)
210
+ .split('\n')
211
+ .filter((l) => /IMAGE_PLAN_(OK|DEGRADED|FAILED|EMPTY|ERROR)/.test(l))
212
+ .join('\n')
213
+ .trim();
214
+ const progress = collectProgressSummary(errOut);
215
+ const summaryParts = [
216
+ tail || `生图结束(exit ${code ?? 'null'}${sig ? ` ${sig}` : ''})`,
217
+ progress ? `进度摘要:\n${progress}` : '',
218
+ ].filter(Boolean);
219
+ const summary = summaryParts.join('\n');
220
+ if (killedByAbort || sig === 'SIGTERM') {
221
+ resolvePromise({ ok: false, error: `GenerateImage 被中断(${sig ?? `exit ${code ?? '?'}`})。${summary}` });
222
+ }
223
+ else if ((code ?? 1) === 0) {
224
+ resolvePromise({ ok: true, content: summary });
225
+ }
226
+ else {
227
+ // 结构/安全校验失败(计划 bug)——报错但不抛;工作流 onError:continue 兜底。
228
+ resolvePromise({ ok: false, error: summary });
229
+ }
230
+ });
231
+ });
232
+ }
233
+ export const genImageTool = {
234
+ name: 'GenerateImage',
235
+ description,
236
+ inputSchema,
237
+ parameters,
238
+ isReadOnly: false,
239
+ isConcurrencySafe: false,
240
+ call: (input, signal) => call(input, signal),
241
+ callWithContext: call,
242
+ };
@@ -0,0 +1,67 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { configService } from '../../config/configService.js';
5
+ import { resolveToolPath } from '../shared/fileUtils.js';
6
+ import { filePathField } from '../shared/schemas.js';
7
+ import { toToolParameters } from '../../utils/zodToJson.js';
8
+ const inputSchema = z.object({
9
+ text: z.string().min(1).max(10_000).describe('要合成的文本'),
10
+ output: filePathField('写入').describe('工作目录内的 MP3 输出路径'),
11
+ voice: z.string().optional().describe('MiniMax voice_id,默认 female-shaonv'),
12
+ speed: z.number().min(0.5).max(2).optional().describe('语速,0.5 到 2.0,默认 1'),
13
+ });
14
+ async function call(input, signal) {
15
+ return callWith(input, signal, async () => configService.tool('textToSpeech'), fetch);
16
+ }
17
+ async function callWith(input, signal, getConfig, fetchImpl) {
18
+ const config = await getConfig();
19
+ if (!config)
20
+ return { ok: false, error: '尚未配置 MiniMax 语音。请在 KakadeAI 设置 → 工具中配置。' };
21
+ const output = resolveToolPath(input.output);
22
+ if (!output.ok)
23
+ return output;
24
+ try {
25
+ const url = new URL('/v1/t2a_v2', 'https://api.minimax.chat');
26
+ url.searchParams.set('GroupId', config.groupId);
27
+ const response = await fetchImpl(url, {
28
+ method: 'POST',
29
+ signal,
30
+ headers: { Authorization: `Bearer ${config.apiKey}`, 'Content-Type': 'application/json' },
31
+ body: JSON.stringify({
32
+ model: 'speech-01',
33
+ text: input.text,
34
+ voice_setting: { voice_id: input.voice ?? 'female-shaonv', speed: input.speed ?? 1 },
35
+ audio_setting: { format: 'mp3' },
36
+ }),
37
+ });
38
+ const payload = await response.json().catch(() => null);
39
+ const hex = payload?.data?.audio?.replace(/\s+/g, '');
40
+ if (!response.ok || !hex || !/^[0-9a-f]+$/i.test(hex) || hex.length % 2 !== 0) {
41
+ const detail = payload?.base_resp?.status_msg || `HTTP ${response.status}`;
42
+ return { ok: false, error: `MiniMax 语音生成失败:${detail}` };
43
+ }
44
+ const audio = Buffer.from(hex, 'hex');
45
+ if (audio.length < 128)
46
+ return { ok: false, error: 'MiniMax 返回的音频数据为空或不完整' };
47
+ await mkdir(dirname(output.resolvedPath), { recursive: true });
48
+ await writeFile(output.resolvedPath, audio, { mode: 0o600 });
49
+ return { ok: true, content: `saved: ${input.output} (provider=minimax, size=${audio.length})` };
50
+ }
51
+ catch (error) {
52
+ if (signal?.aborted)
53
+ return { ok: false, error: '语音生成已取消' };
54
+ return { ok: false, error: `MiniMax 语音生成失败:${error instanceof Error ? error.message : String(error)}` };
55
+ }
56
+ }
57
+ export function createGenerateSpeechTool(getConfig = async () => configService.tool('textToSpeech'), fetchImpl = fetch) {
58
+ return {
59
+ name: 'GenerateSpeech',
60
+ description: '使用已配置的 MiniMax TTS 安全生成 MP3。凭据由受控工具读取,不会暴露给 Bash、日志或模型。',
61
+ inputSchema,
62
+ parameters: toToolParameters(inputSchema),
63
+ isReadOnly: false,
64
+ call: (input, signal) => callWith(input, signal, getConfig, fetchImpl),
65
+ };
66
+ }
67
+ export const generateSpeechTool = createGenerateSpeechTool();
@@ -93,5 +93,6 @@ export const globTool = {
93
93
  isReadOnly: true,
94
94
  isConcurrencySafe: true,
95
95
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
96
+ interruptBehavior: () => 'cancel',
96
97
  call,
97
98
  };
@@ -18,6 +18,7 @@ import { spawn } from 'node:child_process';
18
18
  import { resolve } from 'node:path';
19
19
  import { z } from 'zod';
20
20
  import { getWorkingDir } from '../../bootstrap/workingDir.js';
21
+ import { sanitizeCredentialEnv } from '../../config/configService.js';
21
22
  import { resolveRgPath } from './rgPath.js';
22
23
  import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
23
24
  import { toToolParameters } from '../../utils/zodToJson.js';
@@ -123,6 +124,7 @@ async function call(input, signal) {
123
124
  await new Promise((resolveP, rejectP) => {
124
125
  const child = spawn(rgPath, args, {
125
126
  cwd: getWorkingDir(),
127
+ env: sanitizeCredentialEnv(),
126
128
  signal,
127
129
  windowsHide: true,
128
130
  });
@@ -181,5 +183,6 @@ export const grepTool = {
181
183
  isReadOnly: true,
182
184
  isConcurrencySafe: true,
183
185
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
186
+ interruptBehavior: () => 'cancel',
184
187
  call,
185
188
  };
@@ -26,6 +26,7 @@
26
26
  import { spawn } from 'node:child_process';
27
27
  import { chmodSync, existsSync } from 'node:fs';
28
28
  import { resolve } from 'node:path';
29
+ import { sanitizeCredentialEnv } from '../../config/configService.js';
29
30
  import { execSiblingDir } from '../../utils/greenRoot.js';
30
31
  import { findPackageRoot } from '../../utils/packageRoot.js';
31
32
  let cached;
@@ -125,6 +126,7 @@ function trySpawn(cmd) {
125
126
  try {
126
127
  const child = spawn(cmd, ['--version'], {
127
128
  stdio: ['ignore', 'pipe', 'ignore'],
129
+ env: sanitizeCredentialEnv(),
128
130
  windowsHide: true,
129
131
  });
130
132
  let stdout = '';
@@ -12,10 +12,18 @@
12
12
  * 这层是"防线":模型给的参数永远不可信,必须 Zod 校验后才进 call()。
13
13
  * ============================================================
14
14
  */
15
+ import { advisorTool } from './advisor/advisor.js';
16
+ // ⚠️ agent.ts 与本模块构成环(tools/index → agent → loop → tools/index):
17
+ // agent.ts 顶层不求值本模块/loop 的任何导出、loop.ts 的工具 token 估算已懒化,
18
+ // 环在运行时是安全的(详见 agent.ts 文件头「循环依赖纪律」)。
19
+ import { agentTool } from './agent/agent.js';
15
20
  import { askUserQuestionTool } from './ask/ask.js';
16
21
  import { bashTool } from './bash/bash.js';
22
+ import { downloadTool } from './download/download.js';
17
23
  import { editTool } from './edit/edit.js';
18
24
  import { multiEditTool } from './edit/multi-edit.js';
25
+ import { genImageTool } from './genimage/genimage.js';
26
+ import { generateSpeechTool } from './genspeech/genspeech.js';
19
27
  import { globTool } from './glob/glob.js';
20
28
  import { grepTool } from './grep/grep.js';
21
29
  import { readTool } from './read/read.js';
@@ -34,8 +42,14 @@ export const ALL_TOOLS = [
34
42
  webSearchTool,
35
43
  webfetchTool,
36
44
  webbrowserTool,
45
+ downloadTool,
46
+ genImageTool,
47
+ generateSpeechTool,
37
48
  bashTool,
38
49
  askUserQuestionTool,
50
+ advisorTool,
51
+ // 追加在末尾:主循环 tools schema 的原有前缀字节不动(提示词缓存友好)
52
+ agentTool,
39
53
  ];
40
54
  /** 按名字查表 */
41
55
  export function getToolByName(name) {
@@ -47,11 +61,12 @@ export function getToolByName(name) {
47
61
  * @param name 工具名(来自 LLM 的 tool_call.function.name)
48
62
  * @param rawArguments LLM 给的参数 —— 注意这是 JSON 字符串,不是对象
49
63
  * @param signal 支持中断
64
+ * @param ctx 可选环境态透传(如 Advisor 需要的当前 history),绝大多数工具忽略
50
65
  *
51
66
  * 返回 ToolResult:失败时不抛异常,把 error 写在结果里(便于把"工具失败"
52
67
  * 当成普通 tool_result 喂回 LLM,让模型自己重试或换思路)。
53
68
  */
54
- export async function executeTool(name, rawArguments, signal) {
69
+ export async function executeTool(name, rawArguments, signal, ctx) {
55
70
  // 1. 找工具
56
71
  const tool = getToolByName(name);
57
72
  if (!tool) {
@@ -81,7 +96,10 @@ export async function executeTool(name, rawArguments, signal) {
81
96
  };
82
97
  }
83
98
  // 4. 调用 call(),捕获意外异常
99
+ //
84
100
  try {
101
+ if (tool.callWithContext)
102
+ return await tool.callWithContext(checked.data, signal, ctx ?? {});
85
103
  return await tool.call(checked.data, signal);
86
104
  }
87
105
  catch (e) {
@@ -92,5 +110,10 @@ export async function executeTool(name, rawArguments, signal) {
92
110
  }
93
111
  }
94
112
  // 导出每个工具,方便测试单独 import
95
- export { readTool, editTool, writeTool, globTool, grepTool, webSearchTool, webfetchTool, webbrowserTool, bashTool, askUserQuestionTool };
113
+ export { readTool, editTool, writeTool, globTool, grepTool, webSearchTool, webfetchTool, webbrowserTool, downloadTool, bashTool, askUserQuestionTool };
96
114
  export { multiEditTool } from './edit/multi-edit.js';
115
+ export { genImageTool } from './genimage/genimage.js';
116
+ export { advisorTool } from './advisor/advisor.js';
117
+ export { agentTool } from './agent/agent.js';
118
+ export { createGenerateSpeechTool, generateSpeechTool } from './genspeech/genspeech.js';
119
+ export { createWebSearchTool } from './websearch/websearch.js';
@@ -212,5 +212,6 @@ export const readTool = {
212
212
  isReadOnly: true,
213
213
  isConcurrencySafe: true,
214
214
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
215
+ interruptBehavior: () => 'cancel',
215
216
  call,
216
217
  };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/shared/killTree.ts —— 中断/超时时杀「整棵进程树」
4
+ * ------------------------------------------------------------
5
+ * win32 的 `child.kill('SIGTERM')` 只 TerminateProcess **直接子进程**:在飞的孙进程
6
+ * (shell 里的实际命令 / 逐张 spawn 的生图子进程 / bun test·构建·服务 …)会变孤儿继续
7
+ * 跑,还占着 stdout 管道让 'close' 永不触发 —— 表现为「点了停止、agent 停了、活儿还在
8
+ * 后台跑」的"中断看起来没生效"(2026-07-02 生图事故;同类问题后来在 Bash 长命令复现)。
9
+ *
10
+ * - win32:`taskkill /pid <pid> /T /F` 杀整棵树;taskkill 自身失败再兜底 kill 直接子进程。
11
+ * - POSIX:调用方须 `spawn({ detached: true })` 让子进程自成进程组,这里 `kill(-pid)` 掀整组;
12
+ * 失败兜底同上。
13
+ *
14
+ * 两个消费者(genimage / bash)都需要,按「两次规则」升为共享 util。
15
+ * ============================================================
16
+ */
17
+ import { spawn } from 'node:child_process';
18
+ export function killTree(child) {
19
+ const pid = child.pid;
20
+ const fallback = () => {
21
+ try {
22
+ child.kill('SIGTERM');
23
+ }
24
+ catch {
25
+ /* ignore */
26
+ }
27
+ };
28
+ if (!pid) {
29
+ fallback();
30
+ return;
31
+ }
32
+ if (process.platform === 'win32') {
33
+ try {
34
+ const killer = spawn('taskkill', ['/pid', String(pid), '/T', '/F'], {
35
+ stdio: 'ignore',
36
+ windowsHide: true,
37
+ });
38
+ killer.on('error', fallback);
39
+ }
40
+ catch {
41
+ fallback();
42
+ }
43
+ }
44
+ else {
45
+ try {
46
+ process.kill(-pid, 'SIGTERM');
47
+ }
48
+ catch {
49
+ fallback();
50
+ }
51
+ }
52
+ }