minimal-agent 0.8.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
@@ -0,0 +1,81 @@
1
+ import { mkdir, readFile, rm, stat, utimes, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ const RETRY_MS = 200;
4
+ const STALE_MS = 2 * 60_000;
5
+ function sleep(ms, signal) {
6
+ return new Promise((resolve, reject) => {
7
+ // acquireContextLock 每 200ms 重试一次都会调用 sleep;listener 必须在正常
8
+ // 到点后摘掉,否则长等待会在同一个 AbortSignal 上线性堆积 listener。
9
+ const onAbort = () => {
10
+ clearTimeout(timer);
11
+ reject(new Error('aborted while waiting for workspace context lock'));
12
+ };
13
+ const timer = setTimeout(() => {
14
+ signal?.removeEventListener('abort', onAbort);
15
+ resolve();
16
+ }, ms);
17
+ signal?.addEventListener('abort', onAbort, { once: true });
18
+ });
19
+ }
20
+ function pidAlive(pid) {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ }
25
+ catch (e) {
26
+ // EPERM = 进程存在但不属于当前用户 → 算活(与 sessionRegistry/configFile 的
27
+ // processIsAlive 语义对齐);判死会在共享机器上误抢别的用户的活锁。
28
+ return e.code === 'EPERM';
29
+ }
30
+ }
31
+ async function stale(lockDir) {
32
+ try {
33
+ const info = await stat(lockDir);
34
+ if (Date.now() - info.mtimeMs <= STALE_MS)
35
+ return false;
36
+ const raw = JSON.parse(await readFile(`${lockDir}/owner.json`, 'utf8'));
37
+ return typeof raw.pid !== 'number' || !pidAlive(raw.pid);
38
+ }
39
+ catch {
40
+ return true;
41
+ }
42
+ }
43
+ /**
44
+ * Cross-process exclusive lock for one canonical context file. A heartbeat keeps
45
+ * long agent turns from looking stale; dead processes are recovered by PID.
46
+ */
47
+ export async function acquireContextLock(sessionFile, signal) {
48
+ const lockDir = `${sessionFile}.lock`;
49
+ // 父目录(~/.minimal-agent/sessions/)通常早已存在(任何一个 cwd 聊过一轮就会由
50
+ // saveContext 递归建出来);只有全新 HOME 的第一次交互恰好落在这条锁路径上时才会
51
+ // 缺失。用普通 mkdir(lockDir)(不带 recursive)是刻意的——EEXIST 是这把互斥锁的
52
+ // 判活信号(下面 catch 靠它识别"别人正持锁"),recursive:true 会让已存在的锁目录
53
+ // 也悄悄"创建成功"、互斥失效。故只把父目录单独递归建好,叶子目录仍走非 recursive
54
+ // 的 mkdir 以保留 EEXIST 语义。
55
+ while (true) {
56
+ signal?.throwIfAborted();
57
+ try {
58
+ await mkdir(dirname(lockDir), { recursive: true });
59
+ await mkdir(lockDir);
60
+ await writeFile(`${lockDir}/owner.json`, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), 'utf8');
61
+ const heartbeat = setInterval(() => {
62
+ const now = new Date();
63
+ void utimes(lockDir, now, now).catch(() => { });
64
+ }, 30_000);
65
+ return async () => {
66
+ clearInterval(heartbeat);
67
+ await rm(lockDir, { recursive: true, force: true });
68
+ };
69
+ }
70
+ catch (error) {
71
+ const code = error.code;
72
+ if (code !== 'EEXIST')
73
+ throw error;
74
+ if (await stale(lockDir)) {
75
+ await rm(lockDir, { recursive: true, force: true });
76
+ continue;
77
+ }
78
+ await sleep(RETRY_MS, signal);
79
+ }
80
+ }
81
+ }
@@ -1,14 +1,20 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/context/persistContext.ts —— 单上下文持久化(无会话管理)
3
+ * src/context/persistContext.ts —— 单个会话文件的持久化读写
4
4
  * ------------------------------------------------------------
5
5
  * 做的事:
6
- * 把"上一次关闭时的对话历史"落盘到一个固定文件。下次启动(TUI 或 -p)
7
- * 自动加载,等于无缝续上。整个项目就一个上下文,不存在 session id /
8
- * list / resume 之类的概念。
6
+ * 把"上一次关闭时的对话历史"落盘到一个文件。下次启动(TUI 或 -p)
7
+ * 自动加载,等于无缝续上。
8
+ *
9
+ * ⚠️ 本文件本身不管"同一 cwd 下有哪些 session、怎么列出/切换/新建"——
10
+ * 那是 context/sessionRegistry.ts 的职责(已支持完整多 session
11
+ * CRUD + TUI 的 /session /sessions /resume + CLI 的 --session)。
12
+ * 这里只负责"给定一个具体文件路径,读/写/清空它",调用方(TUI 的
13
+ * useChat.ts、CLI 的 main.tsx/print.ts/streamJson.ts)决定要读写哪个
14
+ * session 对应的文件——不传 file 时缺省走 `default` session(向后兼容)。
9
15
  *
10
16
  * 文件路径:
11
- * ~/.minimal-agent/sessions/<目录哈希>.json
17
+ * ~/.minimal-agent/sessions/<目录哈希>[--<sessionId>].json
12
18
  * (按当前工作目录隔离;可用 MINIMAL_AGENT_CONTEXT_FILE 覆盖,给测试用)
13
19
  *
14
20
  * 存储格式:
@@ -17,12 +23,13 @@
17
23
  * 抉择:
18
24
  * - load 出来若为空或损坏 → 当作"没历史"返回 null,调用方 fallback 到
19
25
  * 只含 system prompt 的 fresh history。永不抛错。
20
- * - save 直接覆写整个文件,不做 atomic(教学项目可接受;进程崩溃丢的
21
- * 就是当前这一轮)。
26
+ * - save 使用唯一临时文件 + atomic rename,避免崩溃留下半截 JSON;
27
+ * 同一 Session 的跨进程互斥由 contextLock 负责。
22
28
  * - 不做 system prompt 替换:调用方拿到 messages 后想替/不替自己决定。
23
29
  * ============================================================
24
30
  */
25
- import { mkdir, readFile, readdir, rmdir, unlink, writeFile } from 'node:fs/promises';
31
+ import { randomUUID } from 'node:crypto';
32
+ import { mkdir, readFile, readdir, rename, rmdir, unlink, writeFile } from 'node:fs/promises';
26
33
  import { dirname, join } from 'node:path';
27
34
  import { getWorkingDir } from '../bootstrap/workingDir.js';
28
35
  import { archiveCurrent } from './archive.js';
@@ -74,14 +81,26 @@ export function filterEphemeralMessages(messages) {
74
81
  */
75
82
  export async function saveContext(messages, file) {
76
83
  const target = file ?? getContextPath();
84
+ const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
77
85
  try {
78
86
  await mkdir(dirname(target), { recursive: true });
79
87
  const persistable = filterEphemeralMessages(messages);
80
88
  const data = { updatedAt: Date.now(), messages: persistable };
81
- await writeFile(target, JSON.stringify(data), 'utf8');
89
+ await writeFile(temp, JSON.stringify(data), { encoding: 'utf8', mode: 0o600 });
90
+ await rename(temp, target);
82
91
  }
83
- catch {
84
- // 静默吞:持久化失败不该影响对话本身
92
+ catch (e) {
93
+ // 不抛(持久化失败不该影响对话本身),但要出声——磁盘满/权限问题时
94
+ // 用户整段会话会静默丢失;与 clearContext 的 archive 失败处理风格对齐。
95
+ try {
96
+ process.stderr.write(`[minimal-agent] 会话落盘失败(本轮继续):${e.message}\n`);
97
+ }
98
+ catch {
99
+ /* stderr 不可用也不阻断 */
100
+ }
101
+ }
102
+ finally {
103
+ await unlink(temp).catch(() => { });
85
104
  }
86
105
  }
87
106
  /**
@@ -87,8 +87,9 @@ function errorMessage(error) {
87
87
  * @param provider 当前 provider(用于 LLM 压缩)
88
88
  * @param error 刚刚抛出的错误
89
89
  * @param state 可选的独立状态(默认使用进程级单例)
90
+ * @param signal 用户中断信号(透传给内部的 forceCompact → chat(),让"压缩中"这个阶段也能响应 ESC/Ctrl+C)
90
91
  */
91
- export async function reactiveCompactIfApplicable(messages, provider, error, state = defaultState) {
92
+ export async function reactiveCompactIfApplicable(messages, provider, error, state = defaultState, signal, dependencies = {}) {
92
93
  // 非 prompt_too_long 错误:直接短路,不消耗计数
93
94
  if (!isPromptTooLongError(error)) {
94
95
  return { recovered: false, messages, reason: 'not a prompt-too-long error' };
@@ -103,7 +104,8 @@ export async function reactiveCompactIfApplicable(messages, provider, error, sta
103
104
  }
104
105
  // Step 1: 先试 LLM 全量压缩
105
106
  try {
106
- const r = await forceCompact(messages, provider);
107
+ const compact = dependencies.forceCompact ?? forceCompact;
108
+ const r = await compact(messages, provider, signal);
107
109
  // 救活成功 → 清零计数,让后续继续可用
108
110
  state.consecutiveFailures = 0;
109
111
  return {
@@ -1,66 +1,26 @@
1
- /**
2
- * ============================================================
3
- * src/context/recentDirs.ts —— 最近工作目录注册表(Web 侧栏用)
4
- * ------------------------------------------------------------
5
- * 做的事:
6
- * 维护 ~/.minimal-agent/recent-dirs.json —— 一个"用过哪些工作目录"的列表。
7
- * 每个工作目录 = 一个会话(项目"一 cwd 一 session"约束),Web 前端侧栏据此
8
- * 列出会话;每条记录顺带存该目录的会话文件路径,让前端不必复刻哈希命名规则
9
- * 即可定位历史。
10
- *
11
- * 谁写:stream-json 模式每轮开始时 recordRecentDir(cwd, 首条用户消息)。
12
- * 谁读:webchat 服务端直接 JSON.parse 本文件(不 import 后端业务模块)。
13
- *
14
- * 抉择:读失败返回 [];写失败静默 —— 注册表只是 UI 便利,绝不该影响对话本身。
15
- * ============================================================
16
- */
17
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
18
- import { homedir } from 'node:os';
19
- import { basename, dirname, join, resolve } from 'node:path';
20
- import { sessionFileFor } from './sessionPath.js';
21
- /** 注册表上限:只留最近 N 个,防文件无限增长。 */
22
- const MAX_ENTRIES = 50;
23
- function registryPath() {
24
- return join(homedir(), '.minimal-agent', 'recent-dirs.json');
1
+ /** Compatibility facade over the authoritative Workspace Index. */
2
+ import { workspaceIndexService } from './workspaceIndex.js';
3
+ function asRecent(workspace) {
4
+ const latest = [...workspace.sessions].sort((a, b) => b.updatedAt - a.updatedAt)[0];
5
+ return {
6
+ path: workspace.displayPath,
7
+ name: workspace.displayName,
8
+ lastUsedAt: workspace.lastActiveAt,
9
+ ...(latest?.title ? { title: latest.title } : {}),
10
+ available: workspace.available,
11
+ workspaceId: workspace.id,
12
+ };
25
13
  }
26
- /** 读注册表,按最近使用倒序;任何异常返回 []。 */
27
14
  export async function listRecentDirs() {
28
- try {
29
- const raw = await readFile(registryPath(), 'utf8');
30
- const data = JSON.parse(raw);
31
- if (!Array.isArray(data))
32
- return [];
33
- return data
34
- .filter((e) => e && typeof e.path === 'string' && typeof e.sessionFile === 'string')
35
- .sort((a, b) => b.lastUsedAt - a.lastUsedAt);
36
- }
37
- catch {
38
- return [];
39
- }
15
+ return (await workspaceIndexService.list()).map(asRecent);
40
16
  }
41
- /**
42
- * 记录/更新一个工作目录。upsert by path:已存在则更新 lastUsedAt(title 给了才覆盖,
43
- * 否则保留旧 title),不存在则插入。写失败静默。
44
- */
17
+ export async function upsertRecentDir(cwd, title) {
18
+ return asRecent(await workspaceIndexService.register(cwd, title));
19
+ }
20
+ /** Best-effort CLI hook: index failure must not fail an Agent turn. */
45
21
  export async function recordRecentDir(cwd, title) {
46
- try {
47
- const abs = resolve(cwd);
48
- const list = await listRecentDirs();
49
- const prev = list.find((e) => e.path === abs);
50
- const rest = list.filter((e) => e.path !== abs);
51
- const entry = {
52
- path: abs,
53
- name: basename(abs) || abs,
54
- lastUsedAt: Date.now(),
55
- title: title ? title.slice(0, 80) : prev?.title,
56
- sessionFile: sessionFileFor(abs),
57
- };
58
- const next = [entry, ...rest].slice(0, MAX_ENTRIES);
59
- const file = registryPath();
60
- await mkdir(dirname(file), { recursive: true });
61
- await writeFile(file, JSON.stringify(next, null, 2), 'utf8');
62
- }
63
- catch {
64
- // 静默:注册失败不影响对话
65
- }
22
+ await workspaceIndexService.register(cwd, title).then(() => undefined, () => undefined);
23
+ }
24
+ export async function removeRecentDir(cwd) {
25
+ await workspaceIndexService.removeWorkspace(cwd);
66
26
  }
@@ -3,19 +3,18 @@
3
3
  * src/context/sessionPath.ts —— 按目录隔离的会话文件路径
4
4
  * ------------------------------------------------------------
5
5
  * 做的事:
6
- * 把当前工作目录编码为 ~/.minimal-agent/sessions/<encoded>.json
7
- * 的文件名,让不同项目目录的对话历史互不覆盖。
6
+ * 新会话使用 Workspace Index 的稳定 workspaceId,写入
7
+ * ~/.minimal-agent/sessions/workspace-<id>--<sessionId>.json。
8
+ * 因此外接盘挂载名、盘符或 symlink 表达变化时仍指向同一份历史。
8
9
  *
9
- * 命名规则(v2 加 sessionId 后):
10
+ * 旧命名规则(路径索引时代,仍用于兼容迁移):
10
11
  * <sanitized-path>-<6-hex-hash>--<sessionId>.json
11
12
  * sanitized:路径归一化后只保留 [a-z0-9-],最长 80 字符
12
13
  * hash :原始路径 sha1 前 6 位,防 sanitize 后碰撞
13
14
  * sessionId:v2 多 session 支持;默认 'default'
14
15
  *
15
- * 向后兼容:sessionId='default' 时,新文件名 `<sanitized>-<hash>--default.json`
16
- * 与旧文件名 `<sanitized>-<hash>.json` 都可能存在。resolveSessionFile()
17
- * 优先返新路径,新路径不存在但旧路径存在时回退到旧路径,保证老用户的
18
- * last-context 无缝继承。
16
+ * resolveSessionFile() 会验证当前路径及 Workspace Index 中的历史别名,
17
+ * 首次打开旧文件时复制到稳定文件名;旧文件保留作为恢复依据。
19
18
  *
20
19
  * 迁移:
21
20
  * 旧版 ~/.minimal-agent/last-context.json 启动时一次性 rename 到
@@ -23,10 +22,64 @@
23
22
  * ============================================================
24
23
  */
25
24
  import { createHash } from 'node:crypto';
26
- import { mkdir, rename, stat } from 'node:fs/promises';
27
- import { homedir } from 'node:os';
25
+ import { constants, readFileSync, realpathSync } from 'node:fs';
26
+ import { chmod, copyFile, mkdir, rename, stat } from 'node:fs/promises';
28
27
  import { dirname, join, resolve } from 'node:path';
28
+ import { agentStateDir } from './agentHome.js';
29
29
  const DEFAULT_SESSION_ID = 'default';
30
+ const SESSION_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/;
31
+ function comparablePath(path) {
32
+ try {
33
+ return realpathSync(resolve(path)).replace(/\\/g, '/').toLowerCase();
34
+ }
35
+ catch {
36
+ return resolve(path).replace(/\\/g, '/').toLowerCase();
37
+ }
38
+ }
39
+ function mountStablePath(path) {
40
+ return comparablePath(path).replace(/^\/volumes\/([^/]+?) \d+(\/|$)/, '/volumes/$1$2');
41
+ }
42
+ function indexedWorkspaceFor(cwd) {
43
+ try {
44
+ const parsed = JSON.parse(readFileSync(join(agentStateDir(), 'workspace-index.json'), 'utf8'));
45
+ const target = comparablePath(cwd);
46
+ const mountTarget = mountStablePath(cwd);
47
+ return parsed.workspaces?.find((workspace) => [
48
+ workspace.canonicalPath,
49
+ workspace.displayPath,
50
+ ...(workspace.pathAliases ?? []),
51
+ ].some((path) => typeof path === 'string'
52
+ && (comparablePath(path) === target || mountStablePath(path) === mountTarget))) ?? null;
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }
58
+ function stableSessionFileFor(workspaceId, sessionId) {
59
+ const safeWorkspaceId = workspaceId.replace(/[^a-zA-Z0-9._-]/g, '');
60
+ if (!safeWorkspaceId)
61
+ throw new Error('workspace id 无效');
62
+ return join(agentStateDir(), 'sessions', `workspace-${safeWorkspaceId}--${validateSessionId(sessionId)}.json`);
63
+ }
64
+ /** 仅用于旧历史识别和迁移;新调用应使用 sessionFileFor。 */
65
+ export function legacyPathSessionFileFor(cwd, sessionId = DEFAULT_SESSION_ID) {
66
+ const safeSessionId = validateSessionId(sessionId);
67
+ const normalized = resolve(cwd).replace(/\\/g, '/').toLowerCase();
68
+ const sanitized = normalized
69
+ .replace(/:/g, '')
70
+ .replace(/[^a-z0-9]+/g, '-')
71
+ .replace(/^-+|-+$/g, '')
72
+ .slice(0, 80);
73
+ const hash = createHash('sha1').update(normalized).digest('hex').slice(0, 6);
74
+ return join(agentStateDir(), 'sessions', `${sanitized}-${hash}--${safeSessionId}.json`);
75
+ }
76
+ export function validateSessionId(sessionId) {
77
+ const value = sessionId.trim();
78
+ if (!SESSION_ID_RE.test(value) || value === '.' || value === '..' || value.includes('..')) {
79
+ throw new Error('session id 只能包含字母、数字、点、下划线和短横线,且长度不超过 80');
80
+ }
81
+ return value;
82
+ }
30
83
  /**
31
84
  * 把 cwd + sessionId 编码成会话文件绝对路径。
32
85
  *
@@ -38,14 +91,11 @@ const DEFAULT_SESSION_ID = 'default';
38
91
  * 拿到实际存在的路径用于 load。
39
92
  */
40
93
  export function sessionFileFor(cwd, sessionId = DEFAULT_SESSION_ID) {
41
- const normalized = resolve(cwd).replace(/\\/g, '/').toLowerCase();
42
- const sanitized = normalized
43
- .replace(/:/g, '')
44
- .replace(/[^a-z0-9]+/g, '-')
45
- .replace(/^-+|-+$/g, '')
46
- .slice(0, 80);
47
- const hash = createHash('sha1').update(normalized).digest('hex').slice(0, 6);
48
- return join(homedir(), '.minimal-agent', 'sessions', `${sanitized}-${hash}--${sessionId}.json`);
94
+ const safeSessionId = validateSessionId(sessionId);
95
+ const workspace = indexedWorkspaceFor(cwd);
96
+ return workspace
97
+ ? stableSessionFileFor(workspace.id, safeSessionId)
98
+ : legacyPathSessionFileFor(cwd, safeSessionId);
49
99
  }
50
100
  /** 旧格式(v1,不带 `--<id>` 后缀)—— 仅 'default' session 可能存在 */
51
101
  function legacySessionFileFor(cwd) {
@@ -56,7 +106,7 @@ function legacySessionFileFor(cwd) {
56
106
  .replace(/^-+|-+$/g, '')
57
107
  .slice(0, 80);
58
108
  const hash = createHash('sha1').update(normalized).digest('hex').slice(0, 6);
59
- return join(homedir(), '.minimal-agent', 'sessions', `${sanitized}-${hash}.json`);
109
+ return join(agentStateDir(), 'sessions', `${sanitized}-${hash}.json`);
60
110
  }
61
111
  /**
62
112
  * 解析 cwd + sessionId 对应的实际存在的会话文件路径。
@@ -74,15 +124,39 @@ export async function resolveSessionFile(cwd, sessionId = DEFAULT_SESSION_ID) {
74
124
  catch {
75
125
  // 新路径不存在
76
126
  }
77
- if (sessionId === DEFAULT_SESSION_ID) {
78
- const legacy = legacySessionFileFor(cwd);
127
+ const workspace = indexedWorkspaceFor(cwd);
128
+ const aliases = workspace
129
+ ? [cwd, workspace.canonicalPath, workspace.displayPath, ...(workspace.pathAliases ?? [])]
130
+ : [cwd];
131
+ const candidates = [...new Set(aliases.filter((value) => typeof value === 'string'))]
132
+ .flatMap((path) => [
133
+ legacyPathSessionFileFor(path, sessionId),
134
+ ...(sessionId === DEFAULT_SESSION_ID ? [legacySessionFileFor(path)] : []),
135
+ ]);
136
+ for (const legacy of candidates) {
79
137
  try {
80
138
  await stat(legacy);
81
- return legacy;
82
139
  }
83
140
  catch {
84
- // 旧路径也不存在
141
+ continue;
142
+ }
143
+ if (legacy !== fresh && workspace) {
144
+ try {
145
+ await mkdir(dirname(fresh), { recursive: true });
146
+ await copyFile(legacy, fresh, constants.COPYFILE_EXCL).catch(async (error) => {
147
+ if (error.code !== 'EEXIST')
148
+ throw error;
149
+ });
150
+ await chmod(fresh, 0o600).catch(() => { });
151
+ return fresh;
152
+ }
153
+ catch {
154
+ // Migration is an optimization. The verified legacy history remains
155
+ // authoritative when the stable destination cannot be written.
156
+ return legacy;
157
+ }
85
158
  }
159
+ return legacy;
86
160
  }
87
161
  return fresh;
88
162
  }
@@ -95,7 +169,7 @@ export async function resolveSessionFile(cwd, sessionId = DEFAULT_SESSION_ID) {
95
169
  * - rename 失败(目标已存在 / 权限等)→ 忽略,旧文件原位等下次启动
96
170
  */
97
171
  export async function migrateLegacyContext(cwd) {
98
- const legacy = join(homedir(), '.minimal-agent', 'last-context.json');
172
+ const legacy = join(agentStateDir(), 'last-context.json');
99
173
  try {
100
174
  await stat(legacy);
101
175
  }