@bastani/atomic 0.8.13 → 0.8.14-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 (355) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/host-html-template.ts +1 -1
  4. package/dist/builtin/mcp/init.ts +15 -2
  5. package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
  6. package/dist/builtin/mcp/package.json +1 -1
  7. package/dist/builtin/mcp/ui-session.ts +9 -6
  8. package/dist/builtin/subagents/CHANGELOG.md +8 -1
  9. package/dist/builtin/subagents/README.md +39 -32
  10. package/dist/builtin/subagents/package.json +1 -1
  11. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  12. package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
  13. package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
  14. package/dist/builtin/subagents/src/agents/agents.ts +44 -19
  15. package/dist/builtin/subagents/src/extension/config.ts +16 -0
  16. package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
  17. package/dist/builtin/subagents/src/extension/index.ts +466 -603
  18. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
  19. package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
  21. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
  22. package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
  24. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
  25. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
  26. package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
  27. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
  28. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
  30. package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
  31. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
  32. package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
  33. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
  34. package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
  35. package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
  36. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
  37. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
  38. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
  39. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
  40. package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
  41. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
  42. package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
  43. package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
  44. package/dist/builtin/subagents/src/shared/types.ts +96 -1
  45. package/dist/builtin/subagents/src/shared/utils.ts +10 -2
  46. package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
  47. package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
  48. package/dist/builtin/web-access/package.json +1 -1
  49. package/dist/builtin/workflows/CHANGELOG.md +24 -0
  50. package/dist/builtin/workflows/README.md +28 -11
  51. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
  52. package/dist/builtin/workflows/builtin/ralph.ts +362 -176
  53. package/dist/builtin/workflows/package.json +2 -5
  54. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  55. package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
  56. package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
  57. package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
  58. package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
  59. package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
  60. package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
  61. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  62. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  63. package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
  64. package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
  65. package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  66. package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
  67. package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
  68. package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
  69. package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
  70. package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
  71. package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
  72. package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
  73. package/dist/builtin/workflows/src/extension/index.ts +869 -93
  74. package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
  75. package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
  76. package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
  77. package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
  78. package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
  79. package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
  80. package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
  81. package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
  82. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
  83. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
  84. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
  85. package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
  86. package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
  87. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
  88. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
  89. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
  90. package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
  91. package/dist/builtin/workflows/src/shared/store.ts +145 -17
  92. package/dist/builtin/workflows/src/shared/timing.ts +6 -2
  93. package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
  94. package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
  95. package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
  96. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
  97. package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
  98. package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
  99. package/dist/builtin/workflows/src/tui/header.ts +38 -0
  100. package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
  101. package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
  102. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
  103. package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
  104. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
  105. package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
  106. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
  107. package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
  108. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
  109. package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
  110. package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
  111. package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
  112. package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
  113. package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
  114. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
  115. package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
  116. package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
  117. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
  118. package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
  119. package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
  120. package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
  121. package/dist/builtin/workflows/src/tui/widget.ts +90 -68
  122. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
  123. package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
  124. package/dist/cli/file-processor.d.ts.map +1 -1
  125. package/dist/cli/file-processor.js +2 -3
  126. package/dist/cli/file-processor.js.map +1 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +3 -10
  129. package/dist/config.js.map +1 -1
  130. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  131. package/dist/core/agent-session-runtime.js +2 -1
  132. package/dist/core/agent-session-runtime.js.map +1 -1
  133. package/dist/core/agent-session-services.d.ts.map +1 -1
  134. package/dist/core/agent-session-services.js +3 -2
  135. package/dist/core/agent-session-services.js.map +1 -1
  136. package/dist/core/agent-session.d.ts +6 -0
  137. package/dist/core/agent-session.d.ts.map +1 -1
  138. package/dist/core/agent-session.js +16 -2
  139. package/dist/core/agent-session.js.map +1 -1
  140. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  141. package/dist/core/atomic-guide-command.js +8 -9
  142. package/dist/core/atomic-guide-command.js.map +1 -1
  143. package/dist/core/auth-storage.d.ts.map +1 -1
  144. package/dist/core/auth-storage.js +3 -2
  145. package/dist/core/auth-storage.js.map +1 -1
  146. package/dist/core/bash-executor.d.ts.map +1 -1
  147. package/dist/core/bash-executor.js +2 -1
  148. package/dist/core/bash-executor.js.map +1 -1
  149. package/dist/core/export-html/index.d.ts.map +1 -1
  150. package/dist/core/export-html/index.js +8 -6
  151. package/dist/core/export-html/index.js.map +1 -1
  152. package/dist/core/export-html/template.js +6 -3
  153. package/dist/core/extensions/loader.d.ts.map +1 -1
  154. package/dist/core/extensions/loader.js +12 -29
  155. package/dist/core/extensions/loader.js.map +1 -1
  156. package/dist/core/model-registry.d.ts.map +1 -1
  157. package/dist/core/model-registry.js +5 -1
  158. package/dist/core/model-registry.js.map +1 -1
  159. package/dist/core/package-manager.d.ts +8 -0
  160. package/dist/core/package-manager.d.ts.map +1 -1
  161. package/dist/core/package-manager.js +145 -58
  162. package/dist/core/package-manager.js.map +1 -1
  163. package/dist/core/prompt-templates.d.ts.map +1 -1
  164. package/dist/core/prompt-templates.js +6 -20
  165. package/dist/core/prompt-templates.js.map +1 -1
  166. package/dist/core/resource-loader.d.ts.map +1 -1
  167. package/dist/core/resource-loader.js +38 -31
  168. package/dist/core/resource-loader.js.map +1 -1
  169. package/dist/core/sdk.d.ts.map +1 -1
  170. package/dist/core/sdk.js +9 -4
  171. package/dist/core/sdk.js.map +1 -1
  172. package/dist/core/session-manager.d.ts.map +1 -1
  173. package/dist/core/session-manager.js +32 -24
  174. package/dist/core/session-manager.js.map +1 -1
  175. package/dist/core/settings-manager.d.ts.map +1 -1
  176. package/dist/core/settings-manager.js +8 -15
  177. package/dist/core/settings-manager.js.map +1 -1
  178. package/dist/core/skills.d.ts.map +1 -1
  179. package/dist/core/skills.js +8 -22
  180. package/dist/core/skills.js.map +1 -1
  181. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
  182. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  183. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
  184. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  185. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
  186. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  187. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
  188. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  189. package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
  190. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
  191. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
  192. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  193. package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
  194. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  195. package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
  196. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  197. package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
  198. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
  199. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  200. package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
  201. package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
  202. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
  203. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  204. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
  205. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  206. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  207. package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
  208. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  209. package/dist/core/tools/bash.d.ts.map +1 -1
  210. package/dist/core/tools/bash.js +56 -53
  211. package/dist/core/tools/bash.js.map +1 -1
  212. package/dist/core/tools/edit-diff.d.ts +3 -1
  213. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  214. package/dist/core/tools/edit-diff.js +8 -1
  215. package/dist/core/tools/edit-diff.js.map +1 -1
  216. package/dist/core/tools/edit.d.ts +3 -1
  217. package/dist/core/tools/edit.d.ts.map +1 -1
  218. package/dist/core/tools/edit.js +44 -81
  219. package/dist/core/tools/edit.js.map +1 -1
  220. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  221. package/dist/core/tools/file-mutation-queue.js +27 -12
  222. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  223. package/dist/core/tools/find.d.ts.map +1 -1
  224. package/dist/core/tools/find.js +2 -3
  225. package/dist/core/tools/find.js.map +1 -1
  226. package/dist/core/tools/grep.d.ts.map +1 -1
  227. package/dist/core/tools/grep.js +3 -3
  228. package/dist/core/tools/grep.js.map +1 -1
  229. package/dist/core/tools/ls.d.ts.map +1 -1
  230. package/dist/core/tools/ls.js +5 -5
  231. package/dist/core/tools/ls.js.map +1 -1
  232. package/dist/core/tools/output-accumulator.d.ts +2 -0
  233. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  234. package/dist/core/tools/output-accumulator.js +11 -4
  235. package/dist/core/tools/output-accumulator.js.map +1 -1
  236. package/dist/core/tools/path-utils.d.ts +2 -0
  237. package/dist/core/tools/path-utils.d.ts.map +1 -1
  238. package/dist/core/tools/path-utils.js +39 -21
  239. package/dist/core/tools/path-utils.js.map +1 -1
  240. package/dist/core/tools/read.d.ts.map +1 -1
  241. package/dist/core/tools/read.js +9 -8
  242. package/dist/core/tools/read.js.map +1 -1
  243. package/dist/core/tools/truncate.d.ts.map +1 -1
  244. package/dist/core/tools/truncate.js +12 -2
  245. package/dist/core/tools/truncate.js.map +1 -1
  246. package/dist/core/tools/write.d.ts.map +1 -1
  247. package/dist/core/tools/write.js +20 -35
  248. package/dist/core/tools/write.js.map +1 -1
  249. package/dist/index.d.ts +2 -1
  250. package/dist/index.d.ts.map +1 -1
  251. package/dist/index.js +4 -1
  252. package/dist/index.js.map +1 -1
  253. package/dist/main.d.ts.map +1 -1
  254. package/dist/main.js +5 -6
  255. package/dist/main.js.map +1 -1
  256. package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
  257. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
  258. package/dist/modes/interactive/chat-input-actions.js +179 -0
  259. package/dist/modes/interactive/chat-input-actions.js.map +1 -0
  260. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  261. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
  263. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  264. package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
  265. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
  266. package/dist/modes/interactive/components/chat-session-host.js +1007 -0
  267. package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
  268. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/config-selector.js +1 -1
  270. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  271. package/dist/modes/interactive/components/footer.d.ts +1 -0
  272. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/footer.js +14 -5
  274. package/dist/modes/interactive/components/footer.js.map +1 -1
  275. package/dist/modes/interactive/components/index.d.ts +1 -0
  276. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/index.js +1 -0
  278. package/dist/modes/interactive/components/index.js.map +1 -1
  279. package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
  280. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/login-dialog.js +29 -4
  282. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  283. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  284. package/dist/modes/interactive/interactive-mode.js +18 -67
  285. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  286. package/dist/utils/child-process.d.ts +1 -0
  287. package/dist/utils/child-process.d.ts.map +1 -1
  288. package/dist/utils/child-process.js +8 -0
  289. package/dist/utils/child-process.js.map +1 -1
  290. package/dist/utils/clipboard-native.d.ts +3 -1
  291. package/dist/utils/clipboard-native.d.ts.map +1 -1
  292. package/dist/utils/clipboard-native.js +14 -8
  293. package/dist/utils/clipboard-native.js.map +1 -1
  294. package/dist/utils/image-resize-core.d.ts +30 -0
  295. package/dist/utils/image-resize-core.d.ts.map +1 -0
  296. package/dist/utils/image-resize-core.js +124 -0
  297. package/dist/utils/image-resize-core.js.map +1 -0
  298. package/dist/utils/image-resize-worker.d.ts +2 -0
  299. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  300. package/dist/utils/image-resize-worker.js +31 -0
  301. package/dist/utils/image-resize-worker.js.map +1 -0
  302. package/dist/utils/image-resize.d.ts +7 -27
  303. package/dist/utils/image-resize.d.ts.map +1 -1
  304. package/dist/utils/image-resize.js +75 -115
  305. package/dist/utils/image-resize.js.map +1 -1
  306. package/dist/utils/paths.d.ts +16 -1
  307. package/dist/utils/paths.d.ts.map +1 -1
  308. package/dist/utils/paths.js +49 -7
  309. package/dist/utils/paths.js.map +1 -1
  310. package/docs/changelog.mdx +29 -0
  311. package/docs/compaction.md +1 -1
  312. package/docs/custom-provider.md +2 -2
  313. package/docs/development.md +1 -1
  314. package/docs/docs.json +98 -143
  315. package/docs/extensions.md +29 -16
  316. package/docs/favicon.svg +29 -0
  317. package/docs/images/interactive-mode.png +0 -0
  318. package/docs/images/tree-view.png +0 -0
  319. package/docs/images/workflow-command.png +0 -0
  320. package/docs/images/workflow-graph.png +0 -0
  321. package/docs/images/workflow-input-picker.png +0 -0
  322. package/docs/images/workflow-list.png +0 -0
  323. package/docs/index.md +10 -1
  324. package/docs/logo.svg +59 -0
  325. package/docs/packages.md +3 -3
  326. package/docs/providers.md +1 -1
  327. package/docs/quickstart.md +98 -2
  328. package/docs/rpc.md +8 -8
  329. package/docs/sdk.md +23 -12
  330. package/docs/sessions.md +1 -1
  331. package/docs/skills.md +15 -1
  332. package/docs/termux.md +11 -1
  333. package/docs/themes.md +6 -6
  334. package/docs/tui.md +18 -18
  335. package/docs/usage.md +1 -1
  336. package/docs/workflows.md +172 -2
  337. package/examples/extensions/subagent/index.ts +2 -1
  338. package/package.json +6 -6
  339. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
  340. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
  341. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
  342. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
  343. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
  344. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
  345. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
  346. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
  347. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
  348. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
  349. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
  350. /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
  351. /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
  352. /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
  353. /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
  354. /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
  355. /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
@@ -3,683 +3,526 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import type { ExtensionAPI, ExtensionContext } from "@bastani/atomic";
5
5
  import { Key, matchesKey } from "@earendil-works/pi-tui";
6
- import {
7
- discoverAgents,
8
- discoverAgentsAll,
9
- type ChainConfig,
10
- } from "../agents/agents.ts";
6
+ import { discoverAgents, discoverAgentsAll, type ChainConfig } from "../agents/agents.ts";
11
7
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
12
8
  import { isParallelStep, type ChainStep } from "../shared/settings.ts";
13
- import type {
14
- SlashSubagentResponse,
15
- SlashSubagentUpdate,
16
- } from "./slash-bridge.ts";
9
+ import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
17
10
  import {
18
- applySlashUpdate,
19
- buildSlashInitialResult,
20
- failSlashResult,
21
- finalizeSlashResult,
11
+ applySlashUpdate,
12
+ buildSlashInitialResult,
13
+ failSlashResult,
14
+ finalizeSlashResult,
22
15
  } from "./slash-live-state.ts";
23
16
  import {
24
- SLASH_RESULT_TYPE,
25
- SLASH_SUBAGENT_CANCEL_EVENT,
26
- SLASH_SUBAGENT_REQUEST_EVENT,
27
- SLASH_SUBAGENT_RESPONSE_EVENT,
28
- SLASH_SUBAGENT_STARTED_EVENT,
29
- SLASH_SUBAGENT_UPDATE_EVENT,
30
- type SingleResult,
31
- type SubagentState,
17
+ SLASH_RESULT_TYPE,
18
+ SLASH_SUBAGENT_CANCEL_EVENT,
19
+ SLASH_SUBAGENT_REQUEST_EVENT,
20
+ SLASH_SUBAGENT_RESPONSE_EVENT,
21
+ SLASH_SUBAGENT_STARTED_EVENT,
22
+ SLASH_SUBAGENT_UPDATE_EVENT,
23
+ type SingleResult,
24
+ type SubagentState,
32
25
  } from "../shared/types.ts";
33
26
 
34
27
  interface InlineConfig {
35
- output?: string | false;
36
- outputMode?: "inline" | "file-only";
37
- reads?: string[] | false;
38
- model?: string;
39
- skill?: string[] | false;
40
- progress?: boolean;
28
+ output?: string | false;
29
+ outputMode?: "inline" | "file-only";
30
+ reads?: string[] | false;
31
+ model?: string;
32
+ skill?: string[] | false;
33
+ progress?: boolean;
41
34
  }
42
35
 
43
36
  const parseInlineConfig = (raw: string): InlineConfig => {
44
- const config: InlineConfig = {};
45
- for (const part of raw.split(",")) {
46
- const trimmed = part.trim();
47
- if (!trimmed) continue;
48
- const eq = trimmed.indexOf("=");
49
- if (eq === -1) {
50
- if (trimmed === "progress") config.progress = true;
51
- continue;
52
- }
53
- const key = trimmed.slice(0, eq).trim();
54
- const val = trimmed.slice(eq + 1).trim();
55
- switch (key) {
56
- case "output":
57
- config.output = val === "false" ? false : val;
58
- break;
59
- case "outputMode":
60
- if (val === "inline" || val === "file-only") config.outputMode = val;
61
- break;
62
- case "reads":
63
- config.reads = val === "false" ? false : val.split("+").filter(Boolean);
64
- break;
65
- case "model":
66
- config.model = val || undefined;
67
- break;
68
- case "skill":
69
- case "skills":
70
- config.skill = val === "false" ? false : val.split("+").filter(Boolean);
71
- break;
72
- case "progress":
73
- config.progress = val !== "false";
74
- break;
75
- }
76
- }
77
- return config;
37
+ const config: InlineConfig = {};
38
+ for (const part of raw.split(",")) {
39
+ const trimmed = part.trim();
40
+ if (!trimmed) continue;
41
+ const eq = trimmed.indexOf("=");
42
+ if (eq === -1) {
43
+ if (trimmed === "progress") config.progress = true;
44
+ continue;
45
+ }
46
+ const key = trimmed.slice(0, eq).trim();
47
+ const val = trimmed.slice(eq + 1).trim();
48
+ switch (key) {
49
+ case "output": config.output = val === "false" ? false : val; break;
50
+ case "outputMode": if (val === "inline" || val === "file-only") config.outputMode = val; break;
51
+ case "reads": config.reads = val === "false" ? false : val.split("+").filter(Boolean); break;
52
+ case "model": config.model = val || undefined; break;
53
+ case "skill": case "skills": config.skill = val === "false" ? false : val.split("+").filter(Boolean); break;
54
+ case "progress": config.progress = val !== "false"; break;
55
+ }
56
+ }
57
+ return config;
78
58
  };
79
59
 
80
- const parseAgentToken = (
81
- token: string,
82
- ): { name: string; config: InlineConfig } => {
83
- const bracket = token.indexOf("[");
84
- if (bracket === -1) return { name: token, config: {} };
85
- const end = token.lastIndexOf("]");
86
- return {
87
- name: token.slice(0, bracket),
88
- config: parseInlineConfig(
89
- token.slice(bracket + 1, end !== -1 ? end : undefined),
90
- ),
91
- };
60
+ const parseAgentToken = (token: string): { name: string; config: InlineConfig } => {
61
+ const bracket = token.indexOf("[");
62
+ if (bracket === -1) return { name: token, config: {} };
63
+ const end = token.lastIndexOf("]");
64
+ return { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : undefined)) };
92
65
  };
93
66
 
94
- const extractExecutionFlags = (
95
- rawArgs: string,
96
- ): { args: string; bg: boolean; fork: boolean } => {
97
- let args = rawArgs.trim();
98
- let bg = false;
99
- let fork = false;
100
-
101
- while (true) {
102
- if (args.endsWith(" --bg") || args === "--bg") {
103
- bg = true;
104
- args = args === "--bg" ? "" : args.slice(0, -5).trim();
105
- continue;
106
- }
107
- if (args.endsWith(" --fork") || args === "--fork") {
108
- fork = true;
109
- args = args === "--fork" ? "" : args.slice(0, -7).trim();
110
- continue;
111
- }
112
- break;
113
- }
114
-
115
- return { args, bg, fork };
67
+ const extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean; fork: boolean } => {
68
+ let args = rawArgs.trim();
69
+ let bg = false;
70
+ let fork = false;
71
+
72
+ while (true) {
73
+ if (args.endsWith(" --bg") || args === "--bg") {
74
+ bg = true;
75
+ args = args === "--bg" ? "" : args.slice(0, -5).trim();
76
+ continue;
77
+ }
78
+ if (args.endsWith(" --fork") || args === "--fork") {
79
+ fork = true;
80
+ args = args === "--fork" ? "" : args.slice(0, -7).trim();
81
+ continue;
82
+ }
83
+ break;
84
+ }
85
+
86
+ return { args, bg, fork };
116
87
  };
117
88
 
118
- const makeAgentCompletions =
119
- (state: SubagentState, multiAgent: boolean) => (prefix: string) => {
120
- if (!state.baseCwd) return null;
121
- const agents = discoverAgents(state.baseCwd, "both").agents;
122
- if (!multiAgent) {
123
- if (prefix.includes(" ")) return null;
124
- return agents
125
- .filter((a) => a.name.startsWith(prefix))
126
- .map((a) => ({ value: a.name, label: a.name }));
127
- }
128
-
129
- const lastArrow = prefix.lastIndexOf(" -> ");
130
- const segment = lastArrow !== -1 ? prefix.slice(lastArrow + 4) : prefix;
131
- if (
132
- segment.includes(" -- ") ||
133
- segment.includes('"') ||
134
- segment.includes("'")
135
- )
136
- return null;
137
-
138
- const lastWord = (prefix.match(/(\S*)$/) || ["", ""])[1];
139
- const beforeLastWord = prefix.slice(0, prefix.length - lastWord.length);
140
-
141
- if (lastWord === "->") {
142
- return agents.map((a) => ({
143
- value: `${prefix} ${a.name}`,
144
- label: a.name,
145
- }));
146
- }
147
-
148
- return agents
149
- .filter((a) => a.name.startsWith(lastWord))
150
- .map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
151
- };
89
+ const makeAgentCompletions = (state: SubagentState, multiAgent: boolean) => (prefix: string) => {
90
+ if (!state.baseCwd) return null;
91
+ const agents = discoverAgents(state.baseCwd, "both").agents;
92
+ if (!multiAgent) {
93
+ if (prefix.includes(" ")) return null;
94
+ return agents.filter((a) => a.name.startsWith(prefix)).map((a) => ({ value: a.name, label: a.name }));
95
+ }
96
+
97
+ const lastArrow = prefix.lastIndexOf(" -> ");
98
+ const segment = lastArrow !== -1 ? prefix.slice(lastArrow + 4) : prefix;
99
+ if (segment.includes(" -- ") || segment.includes('"') || segment.includes("'")) return null;
100
+
101
+ const lastWord = (prefix.match(/(\S*)$/) || ["", ""])[1];
102
+ const beforeLastWord = prefix.slice(0, prefix.length - lastWord.length);
103
+
104
+ if (lastWord === "->") {
105
+ return agents.map((a) => ({ value: `${prefix} ${a.name}`, label: a.name }));
106
+ }
107
+
108
+ return agents.filter((a) => a.name.startsWith(lastWord)).map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
109
+ };
152
110
 
153
111
  const discoverSavedChains = (cwd: string): ChainConfig[] => {
154
- const chainsByName = new Map<string, ChainConfig>();
155
- for (const chain of discoverAgentsAll(cwd).chains) {
156
- chainsByName.set(chain.name, chain);
157
- }
158
- return Array.from(chainsByName.values());
112
+ const chainsByName = new Map<string, ChainConfig>();
113
+ for (const chain of discoverAgentsAll(cwd).chains) {
114
+ chainsByName.set(chain.name, chain);
115
+ }
116
+ return Array.from(chainsByName.values());
159
117
  };
160
118
 
161
119
  const makeChainCompletions = (state: SubagentState) => (prefix: string) => {
162
- if (prefix.includes(" ") || !state.baseCwd) return null;
163
- return discoverSavedChains(state.baseCwd)
164
- .filter((chain) => chain.name.startsWith(prefix))
165
- .map((chain) => ({ value: chain.name, label: chain.name }));
120
+ if (prefix.includes(" ") || !state.baseCwd) return null;
121
+ return discoverSavedChains(state.baseCwd)
122
+ .filter((chain) => chain.name.startsWith(prefix))
123
+ .map((chain) => ({ value: chain.name, label: chain.name }));
166
124
  };
167
125
 
168
- const mapSavedChainSteps = (
169
- chain: ChainConfig,
170
- worktree = false,
171
- ): ChainStep[] => {
172
- return (chain.steps as Array<ChainStep & { skills?: string[] | false }>).map(
173
- (step) => {
174
- if (isParallelStep(step))
175
- return worktree ? { ...step, worktree: true } : { ...step };
176
- return {
177
- agent: step.agent,
178
- task: step.task || undefined,
179
- output: step.output,
180
- outputMode: step.outputMode,
181
- reads: step.reads,
182
- progress: step.progress,
183
- skill: step.skill ?? step.skills,
184
- model: step.model,
185
- };
186
- },
187
- );
126
+ const mapSavedChainSteps = (chain: ChainConfig, worktree = false): ChainStep[] => {
127
+ return (chain.steps as Array<ChainStep & { skills?: string[] | false }>).map((step) => {
128
+ if (isParallelStep(step)) return worktree ? { ...step, worktree: true } : { ...step };
129
+ return {
130
+ agent: step.agent,
131
+ task: step.task || undefined,
132
+ output: step.output,
133
+ outputMode: step.outputMode,
134
+ reads: step.reads,
135
+ progress: step.progress,
136
+ skill: step.skill ?? step.skills,
137
+ model: step.model,
138
+ };
139
+ });
188
140
  };
189
141
 
190
142
  async function requestSlashRun(
191
- pi: ExtensionAPI,
192
- ctx: ExtensionContext,
193
- requestId: string,
194
- params: SubagentParamsLike,
143
+ pi: ExtensionAPI,
144
+ ctx: ExtensionContext,
145
+ requestId: string,
146
+ params: SubagentParamsLike,
195
147
  ): Promise<SlashSubagentResponse> {
196
- return new Promise((resolve, reject) => {
197
- let done = false;
198
- let started = false;
199
-
200
- const startTimeoutMs = 15_000;
201
- const startTimeout = setTimeout(() => {
202
- finish(() =>
203
- reject(
204
- new Error(
205
- "Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.",
206
- ),
207
- ),
208
- );
209
- }, startTimeoutMs);
210
-
211
- const onStarted = (data: unknown) => {
212
- if (done || !data || typeof data !== "object") return;
213
- if ((data as { requestId?: unknown }).requestId !== requestId) return;
214
- started = true;
215
- clearTimeout(startTimeout);
216
- if (ctx.hasUI) ctx.ui.setStatus("subagent-slash", "running...");
217
- };
218
-
219
- const onResponse = (data: unknown) => {
220
- if (done || !data || typeof data !== "object") return;
221
- const response = data as Partial<SlashSubagentResponse>;
222
- if (response.requestId !== requestId) return;
223
- clearTimeout(startTimeout);
224
- finish(() => resolve(response as SlashSubagentResponse));
225
- };
226
-
227
- const onUpdate = (data: unknown) => {
228
- if (done || !data || typeof data !== "object") return;
229
- const update = data as SlashSubagentUpdate;
230
- if (update.requestId !== requestId) return;
231
- applySlashUpdate(requestId, update);
232
- if (!ctx.hasUI) return;
233
- const tool = update.currentTool ? ` ${update.currentTool}` : "";
234
- const count = update.toolCount ?? 0;
235
- ctx.ui.setStatus(
236
- "subagent-slash",
237
- `${count} tools${tool} | ctrl+o live detail`,
238
- );
239
- };
240
-
241
- const onTerminalInput = ctx.hasUI
242
- ? ctx.ui.onTerminalInput((input) => {
243
- if (!matchesKey(input, Key.escape)) return undefined;
244
- pi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });
245
- finish(() => reject(new Error("Cancelled")));
246
- return { consume: true };
247
- })
248
- : undefined;
249
-
250
- const unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);
251
- const unsubResponse = pi.events.on(
252
- SLASH_SUBAGENT_RESPONSE_EVENT,
253
- onResponse,
254
- );
255
- const unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);
256
-
257
- const finish = (next: () => void) => {
258
- if (done) return;
259
- done = true;
260
- clearTimeout(startTimeout);
261
- unsubStarted();
262
- unsubResponse();
263
- unsubUpdate();
264
- onTerminalInput?.();
265
- next();
266
- };
267
-
268
- pi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params });
269
-
270
- // Bridge emits STARTED synchronously during REQUEST emit.
271
- // If not started, no bridge received the request.
272
- if (!started && done) return;
273
- if (!started) {
274
- finish(() =>
275
- reject(
276
- new Error(
277
- "No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.",
278
- ),
279
- ),
280
- );
281
- }
282
- });
148
+ return new Promise((resolve, reject) => {
149
+ let done = false;
150
+ let started = false;
151
+
152
+ const startTimeoutMs = 15_000;
153
+ const startTimeout = setTimeout(() => {
154
+ finish(() => reject(new Error(
155
+ "Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.",
156
+ )));
157
+ }, startTimeoutMs);
158
+
159
+ const onStarted = (data: unknown) => {
160
+ if (done || !data || typeof data !== "object") return;
161
+ if ((data as { requestId?: unknown }).requestId !== requestId) return;
162
+ started = true;
163
+ clearTimeout(startTimeout);
164
+ if (ctx.hasUI) ctx.ui.setStatus("subagent-slash", "running...");
165
+ };
166
+
167
+ const onResponse = (data: unknown) => {
168
+ if (done || !data || typeof data !== "object") return;
169
+ const response = data as Partial<SlashSubagentResponse>;
170
+ if (response.requestId !== requestId) return;
171
+ clearTimeout(startTimeout);
172
+ finish(() => resolve(response as SlashSubagentResponse));
173
+ };
174
+
175
+ const onUpdate = (data: unknown) => {
176
+ if (done || !data || typeof data !== "object") return;
177
+ const update = data as SlashSubagentUpdate;
178
+ if (update.requestId !== requestId) return;
179
+ applySlashUpdate(requestId, update);
180
+ if (!ctx.hasUI) return;
181
+ const tool = update.currentTool ? ` ${update.currentTool}` : "";
182
+ const count = update.toolCount ?? 0;
183
+ ctx.ui.setStatus("subagent-slash", `${count} tools${tool} | ctrl+o live detail`);
184
+ };
185
+
186
+ const onTerminalInput = ctx.hasUI
187
+ ? ctx.ui.onTerminalInput((input) => {
188
+ if (!matchesKey(input, Key.escape)) return undefined;
189
+ pi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });
190
+ finish(() => reject(new Error("Cancelled")));
191
+ return { consume: true };
192
+ })
193
+ : undefined;
194
+
195
+ const unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);
196
+ const unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);
197
+ const unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);
198
+
199
+ const finish = (next: () => void) => {
200
+ if (done) return;
201
+ done = true;
202
+ clearTimeout(startTimeout);
203
+ unsubStarted();
204
+ unsubResponse();
205
+ unsubUpdate();
206
+ onTerminalInput?.();
207
+ next();
208
+ };
209
+
210
+ pi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params });
211
+
212
+ // Bridge emits STARTED synchronously during REQUEST emit.
213
+ // If not started, no bridge received the request.
214
+ if (!started && done) return;
215
+ if (!started) {
216
+ finish(() => reject(new Error(
217
+ "No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.",
218
+ )));
219
+ }
220
+ });
283
221
  }
284
222
 
285
- function extractSlashMessageText(
286
- content: string | Array<{ type?: string; text?: string }>,
287
- ): string {
288
- if (typeof content === "string") return content;
289
- if (!Array.isArray(content)) return "";
290
- return content
291
- .filter(
292
- (part): part is { type: "text"; text: string } =>
293
- part?.type === "text" && typeof part.text === "string",
294
- )
295
- .map((part) => part.text)
296
- .join("\n");
223
+ function extractSlashMessageText(content: string | Array<{ type?: string; text?: string }>): string {
224
+ if (typeof content === "string") return content;
225
+ if (!Array.isArray(content)) return "";
226
+ return content
227
+ .filter((part): part is { type: "text"; text: string } => part?.type === "text" && typeof part.text === "string")
228
+ .map((part) => part.text)
229
+ .join("\n");
297
230
  }
298
231
 
299
232
  function formatExportPathList(paths: string[]): string {
300
- return paths.map((file) => `- \`${file}\``).join("\n");
233
+ return paths.map((file) => `- \`${file}\``).join("\n");
301
234
  }
302
235
 
303
- function collectResultPaths(
304
- results: SingleResult[],
305
- getPath: (result: SingleResult) => string | undefined,
306
- ): string[] {
307
- return results
308
- .map(getPath)
309
- .filter(
310
- (file): file is string => typeof file === "string" && file.length > 0,
311
- );
236
+ function collectResultPaths(results: SingleResult[], getPath: (result: SingleResult) => string | undefined): string[] {
237
+ return results
238
+ .map(getPath)
239
+ .filter((file): file is string => typeof file === "string" && file.length > 0);
312
240
  }
313
241
 
314
242
  function buildSlashExportText(response: SlashSubagentResponse): string {
315
- const output =
316
- extractSlashMessageText(response.result.content) ||
317
- response.errorText ||
318
- "(no output)";
319
- const results = response.result.details?.results ?? [];
320
- const sessionFiles = collectResultPaths(
321
- results,
322
- (result) => result.sessionFile,
323
- );
324
- const savedOutputs = collectResultPaths(
325
- results,
326
- (result) => result.savedOutputPath,
327
- );
328
- const artifactOutputs = collectResultPaths(
329
- results,
330
- (result) => result.artifactPaths?.outputPath,
331
- );
332
- const sections = ["## Subagent result", output];
333
- if (sessionFiles.length > 0)
334
- sections.push(
335
- "## Child session exports",
336
- formatExportPathList(sessionFiles),
337
- );
338
- if (savedOutputs.length > 0)
339
- sections.push("## Saved outputs", formatExportPathList(savedOutputs));
340
- if (artifactOutputs.length > 0)
341
- sections.push("## Artifact outputs", formatExportPathList(artifactOutputs));
342
- return sections.join("\n\n");
243
+ const output = extractSlashMessageText(response.result.content) || response.errorText || "(no output)";
244
+ const results = response.result.details?.results ?? [];
245
+ const sessionFiles = collectResultPaths(results, (result) => result.sessionFile);
246
+ const savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);
247
+ const artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);
248
+ const sections = ["## Subagent result", output];
249
+ if (sessionFiles.length > 0) sections.push("## Child session exports", formatExportPathList(sessionFiles));
250
+ if (savedOutputs.length > 0) sections.push("## Saved outputs", formatExportPathList(savedOutputs));
251
+ if (artifactOutputs.length > 0) sections.push("## Artifact outputs", formatExportPathList(artifactOutputs));
252
+ return sections.join("\n\n");
343
253
  }
344
254
 
345
255
  function persistSlashSessionSnapshot(ctx: ExtensionContext): void {
346
- try {
347
- if (!ctx.sessionManager) return;
348
- const sessionManager = ctx.sessionManager as typeof ctx.sessionManager & {
349
- _rewriteFile?: () => void;
350
- flushed?: boolean;
351
- };
352
- const sessionFile = sessionManager.getSessionFile();
353
- if (!sessionFile || typeof sessionManager._rewriteFile !== "function")
354
- return;
355
- fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
356
- sessionManager._rewriteFile();
357
- sessionManager.flushed = true;
358
- } catch (error) {
359
- console.error(
360
- "Failed to persist slash session snapshot for export:",
361
- error,
362
- );
363
- }
256
+ try {
257
+ if (!ctx.sessionManager) return;
258
+ const sessionManager = ctx.sessionManager as typeof ctx.sessionManager & {
259
+ _rewriteFile?: () => void;
260
+ flushed?: boolean;
261
+ };
262
+ const sessionFile = sessionManager.getSessionFile();
263
+ if (!sessionFile || typeof sessionManager._rewriteFile !== "function") return;
264
+ fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
265
+ sessionManager._rewriteFile();
266
+ sessionManager.flushed = true;
267
+ } catch (error) {
268
+ console.error("Failed to persist slash session snapshot for export:", error);
269
+ }
364
270
  }
365
271
 
366
272
  async function runSlashSubagent(
367
- pi: ExtensionAPI,
368
- ctx: ExtensionContext,
369
- params: SubagentParamsLike,
273
+ pi: ExtensionAPI,
274
+ ctx: ExtensionContext,
275
+ params: SubagentParamsLike,
370
276
  ): Promise<void> {
371
- if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
372
- const requestId = randomUUID();
373
- const initialDetails = buildSlashInitialResult(requestId, params);
374
- const initialText =
375
- extractSlashMessageText(initialDetails.result.content) ||
376
- "Running subagent...";
377
- pi.sendMessage({
378
- customType: SLASH_RESULT_TYPE,
379
- content: initialText,
380
- display: true,
381
- details: initialDetails,
382
- });
383
- persistSlashSessionSnapshot(ctx);
384
-
385
- try {
386
- const response = await requestSlashRun(pi, ctx, requestId, params);
387
- const finalDetails = finalizeSlashResult(response);
388
- pi.sendMessage({
389
- customType: SLASH_RESULT_TYPE,
390
- content: buildSlashExportText(response),
391
- display: true,
392
- details: finalDetails,
393
- });
394
- persistSlashSessionSnapshot(ctx);
395
- if (ctx.hasUI) {
396
- ctx.ui.setStatus("subagent-slash", undefined);
397
- }
398
- if (response.isError && ctx.hasUI) {
399
- ctx.ui.notify(response.errorText || "Subagent failed", "error");
400
- }
401
- } catch (error) {
402
- const message = error instanceof Error ? error.message : String(error);
403
- const failedDetails = failSlashResult(requestId, params, message);
404
- pi.sendMessage({
405
- customType: SLASH_RESULT_TYPE,
406
- content: `## Subagent result\n\n${message}`,
407
- display: true,
408
- details: failedDetails,
409
- });
410
- persistSlashSessionSnapshot(ctx);
411
- if (ctx.hasUI) {
412
- ctx.ui.setStatus("subagent-slash", undefined);
413
- }
414
- if (message === "Cancelled") {
415
- if (ctx.hasUI) ctx.ui.notify("Cancelled", "warning");
416
- return;
417
- }
418
- if (ctx.hasUI) ctx.ui.notify(message, "error");
419
- }
277
+ if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
278
+ const requestId = randomUUID();
279
+ const initialDetails = buildSlashInitialResult(requestId, params);
280
+ const initialText = extractSlashMessageText(initialDetails.result.content) || "Running subagent...";
281
+ pi.sendMessage({
282
+ customType: SLASH_RESULT_TYPE,
283
+ content: initialText,
284
+ display: true,
285
+ details: initialDetails,
286
+ });
287
+ persistSlashSessionSnapshot(ctx);
288
+
289
+ try {
290
+ const response = await requestSlashRun(pi, ctx, requestId, params);
291
+ const finalDetails = finalizeSlashResult(response);
292
+ pi.sendMessage({
293
+ customType: SLASH_RESULT_TYPE,
294
+ content: buildSlashExportText(response),
295
+ display: true,
296
+ details: finalDetails,
297
+ });
298
+ persistSlashSessionSnapshot(ctx);
299
+ if (ctx.hasUI) {
300
+ ctx.ui.setStatus("subagent-slash", undefined);
301
+ }
302
+ if (response.isError && ctx.hasUI) {
303
+ ctx.ui.notify(response.errorText || "Subagent failed", "error");
304
+ }
305
+ } catch (error) {
306
+ const message = error instanceof Error ? error.message : String(error);
307
+ const failedDetails = failSlashResult(requestId, params, message);
308
+ pi.sendMessage({
309
+ customType: SLASH_RESULT_TYPE,
310
+ content: `## Subagent result\n\n${message}`,
311
+ display: true,
312
+ details: failedDetails,
313
+ });
314
+ persistSlashSessionSnapshot(ctx);
315
+ if (ctx.hasUI) {
316
+ ctx.ui.setStatus("subagent-slash", undefined);
317
+ }
318
+ if (message === "Cancelled") {
319
+ if (ctx.hasUI) ctx.ui.notify("Cancelled", "warning");
320
+ return;
321
+ }
322
+ if (ctx.hasUI) ctx.ui.notify(message, "error");
323
+ }
420
324
  }
421
325
 
422
- interface ParsedStep {
423
- name: string;
424
- config: InlineConfig;
425
- task?: string;
426
- }
326
+
327
+ interface ParsedStep { name: string; config: InlineConfig; task?: string }
427
328
 
428
329
  const parseAgentArgs = (
429
- state: SubagentState,
430
- args: string,
431
- command: string,
432
- ctx: ExtensionContext,
330
+ state: SubagentState,
331
+ args: string,
332
+ command: string,
333
+ ctx: ExtensionContext,
433
334
  ): { steps: ParsedStep[]; task: string } | null => {
434
- const input = args.trim();
435
- const usage = `Usage: /${command} agent1 "task1" -> agent2 "task2"`;
436
- let steps: ParsedStep[];
437
- let sharedTask: string;
438
- let perStep = false;
439
-
440
- if (input.includes(" -> ")) {
441
- perStep = true;
442
- const segments = input.split(" -> ");
443
- steps = [];
444
- for (const seg of segments) {
445
- const trimmed = seg.trim();
446
- if (!trimmed) continue;
447
- let agentPart: string;
448
- let task: string | undefined;
449
- const qMatch = trimmed.match(
450
- /^(\S+(?:\[[^\]]*\])?)\s+(?:"([^"]*)"|'([^']*)')$/,
451
- );
452
- if (qMatch) {
453
- agentPart = qMatch[1]!;
454
- task = (qMatch[2] ?? qMatch[3]) || undefined;
455
- } else {
456
- const dashIdx = trimmed.indexOf(" -- ");
457
- if (dashIdx !== -1) {
458
- agentPart = trimmed.slice(0, dashIdx).trim();
459
- task = trimmed.slice(dashIdx + 4).trim() || undefined;
460
- } else {
461
- agentPart = trimmed;
462
- }
463
- }
464
- const parsed = parseAgentToken(agentPart);
465
- steps.push({ ...parsed, task });
466
- }
467
- sharedTask = steps.find((s) => s.task)?.task ?? "";
468
- } else {
469
- const delimiterIndex = input.indexOf(" -- ");
470
- if (delimiterIndex === -1) {
471
- ctx.ui.notify(usage, "error");
472
- return null;
473
- }
474
- const agentsPart = input.slice(0, delimiterIndex).trim();
475
- sharedTask = input.slice(delimiterIndex + 4).trim();
476
- if (!agentsPart || !sharedTask) {
477
- ctx.ui.notify(usage, "error");
478
- return null;
479
- }
480
- steps = agentsPart
481
- .split(/\s+/)
482
- .filter(Boolean)
483
- .map((t) => parseAgentToken(t));
484
- }
485
-
486
- if (steps.length === 0) {
487
- ctx.ui.notify(usage, "error");
488
- return null;
489
- }
490
- if (!state.baseCwd) {
491
- ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
492
- return null;
493
- }
494
- const agents = discoverAgents(state.baseCwd, "both").agents;
495
- for (const step of steps) {
496
- if (!agents.find((a) => a.name === step.name)) {
497
- ctx.ui.notify(`Unknown agent: ${step.name}`, "error");
498
- return null;
499
- }
500
- }
501
- if (command === "chain" && !steps[0]?.task && (perStep || !sharedTask)) {
502
- ctx.ui.notify(
503
- `First step must have a task: /chain agent "task" -> agent2`,
504
- "error",
505
- );
506
- return null;
507
- }
508
- if (command === "parallel" && !steps.some((s) => s.task) && !sharedTask) {
509
- ctx.ui.notify("At least one step must have a task", "error");
510
- return null;
511
- }
512
- return { steps, task: sharedTask };
335
+ const input = args.trim();
336
+ const usage = `Usage: /${command} agent1 "task1" -> agent2 "task2"`;
337
+ let steps: ParsedStep[];
338
+ let sharedTask: string;
339
+ let perStep = false;
340
+
341
+ if (input.includes(" -> ")) {
342
+ perStep = true;
343
+ const segments = input.split(" -> ");
344
+ steps = [];
345
+ for (const seg of segments) {
346
+ const trimmed = seg.trim();
347
+ if (!trimmed) continue;
348
+ let agentPart: string;
349
+ let task: string | undefined;
350
+ const qMatch = trimmed.match(/^(\S+(?:\[[^\]]*\])?)\s+(?:"([^"]*)"|'([^']*)')$/);
351
+ if (qMatch) {
352
+ agentPart = qMatch[1]!;
353
+ task = (qMatch[2] ?? qMatch[3]) || undefined;
354
+ } else {
355
+ const dashIdx = trimmed.indexOf(" -- ");
356
+ if (dashIdx !== -1) {
357
+ agentPart = trimmed.slice(0, dashIdx).trim();
358
+ task = trimmed.slice(dashIdx + 4).trim() || undefined;
359
+ } else {
360
+ agentPart = trimmed;
361
+ }
362
+ }
363
+ const parsed = parseAgentToken(agentPart);
364
+ steps.push({ ...parsed, task });
365
+ }
366
+ sharedTask = steps.find((s) => s.task)?.task ?? "";
367
+ } else {
368
+ const delimiterIndex = input.indexOf(" -- ");
369
+ if (delimiterIndex === -1) {
370
+ ctx.ui.notify(usage, "error");
371
+ return null;
372
+ }
373
+ const agentsPart = input.slice(0, delimiterIndex).trim();
374
+ sharedTask = input.slice(delimiterIndex + 4).trim();
375
+ if (!agentsPart || !sharedTask) {
376
+ ctx.ui.notify(usage, "error");
377
+ return null;
378
+ }
379
+ steps = agentsPart.split(/\s+/).filter(Boolean).map((t) => parseAgentToken(t));
380
+ }
381
+
382
+ if (steps.length === 0) {
383
+ ctx.ui.notify(usage, "error");
384
+ return null;
385
+ }
386
+ if (!state.baseCwd) {
387
+ ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
388
+ return null;
389
+ }
390
+ const agents = discoverAgents(state.baseCwd, "both").agents;
391
+ for (const step of steps) {
392
+ if (!agents.find((a) => a.name === step.name)) {
393
+ ctx.ui.notify(`Unknown agent: ${step.name}`, "error");
394
+ return null;
395
+ }
396
+ }
397
+ if (command === "chain" && !steps[0]?.task && (perStep || !sharedTask)) {
398
+ ctx.ui.notify(`First step must have a task: /chain agent "task" -> agent2`, "error");
399
+ return null;
400
+ }
401
+ if (command === "parallel" && !steps.some((s) => s.task) && !sharedTask) {
402
+ ctx.ui.notify("At least one step must have a task", "error");
403
+ return null;
404
+ }
405
+ return { steps, task: sharedTask };
513
406
  };
514
407
 
515
408
  export function registerSlashCommands(
516
- pi: ExtensionAPI,
517
- state: SubagentState,
409
+ pi: ExtensionAPI,
410
+ state: SubagentState,
518
411
  ): void {
519
- pi.registerCommand("run", {
520
- description:
521
- "Run a subagent directly: /run agent[output=file] [task] [--bg] [--fork]",
522
- getArgumentCompletions: makeAgentCompletions(state, false),
523
- handler: async (args, ctx) => {
524
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
525
- const input = cleanedArgs.trim();
526
- const firstSpace = input.indexOf(" ");
527
- if (!input) {
528
- ctx.ui.notify("Usage: /run <agent> [task] [--bg] [--fork]", "error");
529
- return;
530
- }
531
- const { name: agentName, config: inline } = parseAgentToken(
532
- firstSpace === -1 ? input : input.slice(0, firstSpace),
533
- );
534
- const task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
535
-
536
- if (!state.baseCwd) {
537
- ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
538
- return;
539
- }
540
- const agents = discoverAgents(state.baseCwd, "both").agents;
541
- if (!agents.find((a) => a.name === agentName)) {
542
- ctx.ui.notify(`Unknown agent: ${agentName}`, "error");
543
- return;
544
- }
545
-
546
- let finalTask = task;
547
- if (
548
- inline.reads &&
549
- Array.isArray(inline.reads) &&
550
- inline.reads.length > 0
551
- ) {
552
- finalTask = `[Read from: ${inline.reads.join(", ")}]\n\n${finalTask}`;
553
- }
554
- const params: SubagentParamsLike = {
555
- agent: agentName,
556
- task: finalTask,
557
- clarify: false,
558
- agentScope: "both",
559
- };
560
- if (inline.output !== undefined) params.output = inline.output;
561
- if (inline.outputMode !== undefined)
562
- params.outputMode = inline.outputMode;
563
- if (inline.skill !== undefined) params.skill = inline.skill;
564
- if (inline.model) params.model = inline.model;
565
- if (bg) params.async = true;
566
- if (fork) params.context = "fork";
567
- await runSlashSubagent(pi, ctx, params);
568
- },
569
- });
570
-
571
- pi.registerCommand("chain", {
572
- description:
573
- 'Run agents in sequence: /chain scout "task" -> planner [--bg] [--fork]',
574
- getArgumentCompletions: makeAgentCompletions(state, true),
575
- handler: async (args, ctx) => {
576
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
577
- const parsed = parseAgentArgs(state, cleanedArgs, "chain", ctx);
578
- if (!parsed) return;
579
- const chain = parsed.steps.map(({ name, config, task: stepTask }, i) => ({
580
- agent: name,
581
- ...(stepTask
582
- ? { task: stepTask }
583
- : i === 0 && parsed.task
584
- ? { task: parsed.task }
585
- : {}),
586
- ...(config.output !== undefined ? { output: config.output } : {}),
587
- ...(config.outputMode !== undefined
588
- ? { outputMode: config.outputMode }
589
- : {}),
590
- ...(config.reads !== undefined ? { reads: config.reads } : {}),
591
- ...(config.model ? { model: config.model } : {}),
592
- ...(config.skill !== undefined ? { skill: config.skill } : {}),
593
- ...(config.progress !== undefined ? { progress: config.progress } : {}),
594
- }));
595
- const params: SubagentParamsLike = {
596
- chain,
597
- task: parsed.task,
598
- clarify: false,
599
- agentScope: "both",
600
- };
601
- if (bg) params.async = true;
602
- if (fork) params.context = "fork";
603
- await runSlashSubagent(pi, ctx, params);
604
- },
605
- });
606
-
607
- pi.registerCommand("run-chain", {
608
- description:
609
- "Run a saved chain: /run-chain chainName -- task [--bg] [--fork]",
610
- getArgumentCompletions: makeChainCompletions(state),
611
- handler: async (args, ctx) => {
612
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
613
- const delimiterIndex = cleanedArgs.indexOf(" -- ");
614
- const usage = "Usage: /run-chain <chainName> -- <task> [--bg] [--fork]";
615
- if (delimiterIndex === -1) {
616
- ctx.ui.notify(usage, "error");
617
- return;
618
- }
619
- const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
620
- const task = cleanedArgs.slice(delimiterIndex + 4).trim();
621
- if (!chainName || !task) {
622
- ctx.ui.notify(usage, "error");
623
- return;
624
- }
625
- if (!state.baseCwd) {
626
- ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
627
- return;
628
- }
629
- const chain = discoverSavedChains(state.baseCwd).find(
630
- (candidate) => candidate.name === chainName,
631
- );
632
- if (!chain) {
633
- ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
634
- return;
635
- }
636
- const params: SubagentParamsLike = {
637
- chain: mapSavedChainSteps(chain),
638
- task,
639
- clarify: false,
640
- agentScope: "both",
641
- };
642
- if (bg) params.async = true;
643
- if (fork) params.context = "fork";
644
- await runSlashSubagent(pi, ctx, params);
645
- },
646
- });
647
-
648
- pi.registerCommand("parallel", {
649
- description:
650
- 'Run agents in parallel: /parallel scout "task1" -> reviewer "task2" [--bg] [--fork]',
651
- getArgumentCompletions: makeAgentCompletions(state, true),
652
- handler: async (args, ctx) => {
653
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
654
- const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
655
- if (!parsed) return;
656
- const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
657
- agent: name,
658
- task: stepTask ?? parsed.task,
659
- ...(config.output !== undefined ? { output: config.output } : {}),
660
- ...(config.outputMode !== undefined
661
- ? { outputMode: config.outputMode }
662
- : {}),
663
- ...(config.reads !== undefined ? { reads: config.reads } : {}),
664
- ...(config.model ? { model: config.model } : {}),
665
- ...(config.skill !== undefined ? { skill: config.skill } : {}),
666
- ...(config.progress !== undefined ? { progress: config.progress } : {}),
667
- }));
668
- const params: SubagentParamsLike = {
669
- tasks,
670
- clarify: false,
671
- agentScope: "both",
672
- };
673
- if (bg) params.async = true;
674
- if (fork) params.context = "fork";
675
- await runSlashSubagent(pi, ctx, params);
676
- },
677
- });
678
-
679
- pi.registerCommand("subagents-doctor", {
680
- description: "Show subagent diagnostics",
681
- handler: async (_args, ctx) => {
682
- await runSlashSubagent(pi, ctx, { action: "doctor" });
683
- },
684
- });
412
+ pi.registerCommand("run", {
413
+ description: "Run a subagent directly: /run agent[output=file] [task] [--bg] [--fork]",
414
+ getArgumentCompletions: makeAgentCompletions(state, false),
415
+ handler: async (args, ctx) => {
416
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
417
+ const input = cleanedArgs.trim();
418
+ const firstSpace = input.indexOf(" ");
419
+ if (!input) { ctx.ui.notify("Usage: /run <agent> [task] [--bg] [--fork]", "error"); return; }
420
+ const { name: agentName, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));
421
+ const task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
422
+
423
+ if (!state.baseCwd) { ctx.ui.notify("Subagent session cwd is not initialized yet", "error"); return; }
424
+ const agents = discoverAgents(state.baseCwd, "both").agents;
425
+ if (!agents.find((a) => a.name === agentName)) { ctx.ui.notify(`Unknown agent: ${agentName}`, "error"); return; }
426
+
427
+ let finalTask = task;
428
+ if (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {
429
+ finalTask = `[Read from: ${inline.reads.join(", ")}]\n\n${finalTask}`;
430
+ }
431
+ const params: SubagentParamsLike = { agent: agentName, task: finalTask, clarify: false, agentScope: "both" };
432
+ if (inline.output !== undefined) params.output = inline.output;
433
+ if (inline.outputMode !== undefined) params.outputMode = inline.outputMode;
434
+ if (inline.skill !== undefined) params.skill = inline.skill;
435
+ if (inline.model) params.model = inline.model;
436
+ if (bg) params.async = true;
437
+ if (fork) params.context = "fork";
438
+ await runSlashSubagent(pi, ctx, params);
439
+ },
440
+ });
441
+
442
+ pi.registerCommand("chain", {
443
+ description: "Run agents in sequence: /chain scout \"task\" -> planner [--bg] [--fork]",
444
+ getArgumentCompletions: makeAgentCompletions(state, true),
445
+ handler: async (args, ctx) => {
446
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
447
+ const parsed = parseAgentArgs(state, cleanedArgs, "chain", ctx);
448
+ if (!parsed) return;
449
+ const chain = parsed.steps.map(({ name, config, task: stepTask }, i) => ({
450
+ agent: name,
451
+ ...(stepTask ? { task: stepTask } : i === 0 && parsed.task ? { task: parsed.task } : {}),
452
+ ...(config.output !== undefined ? { output: config.output } : {}),
453
+ ...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),
454
+ ...(config.reads !== undefined ? { reads: config.reads } : {}),
455
+ ...(config.model ? { model: config.model } : {}),
456
+ ...(config.skill !== undefined ? { skill: config.skill } : {}),
457
+ ...(config.progress !== undefined ? { progress: config.progress } : {}),
458
+ }));
459
+ const params: SubagentParamsLike = { chain, task: parsed.task, clarify: false, agentScope: "both" };
460
+ if (bg) params.async = true;
461
+ if (fork) params.context = "fork";
462
+ await runSlashSubagent(pi, ctx, params);
463
+ },
464
+ });
465
+
466
+ pi.registerCommand("run-chain", {
467
+ description: "Run a saved chain: /run-chain chainName -- task [--bg] [--fork]",
468
+ getArgumentCompletions: makeChainCompletions(state),
469
+ handler: async (args, ctx) => {
470
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
471
+ const delimiterIndex = cleanedArgs.indexOf(" -- ");
472
+ const usage = "Usage: /run-chain <chainName> -- <task> [--bg] [--fork]";
473
+ if (delimiterIndex === -1) {
474
+ ctx.ui.notify(usage, "error");
475
+ return;
476
+ }
477
+ const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
478
+ const task = cleanedArgs.slice(delimiterIndex + 4).trim();
479
+ if (!chainName || !task) {
480
+ ctx.ui.notify(usage, "error");
481
+ return;
482
+ }
483
+ if (!state.baseCwd) { ctx.ui.notify("Subagent session cwd is not initialized yet", "error"); return; }
484
+ const chain = discoverSavedChains(state.baseCwd).find((candidate) => candidate.name === chainName);
485
+ if (!chain) {
486
+ ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
487
+ return;
488
+ }
489
+ const params: SubagentParamsLike = { chain: mapSavedChainSteps(chain), task, clarify: false, agentScope: "both" };
490
+ if (bg) params.async = true;
491
+ if (fork) params.context = "fork";
492
+ await runSlashSubagent(pi, ctx, params);
493
+ },
494
+ });
495
+
496
+ pi.registerCommand("parallel", {
497
+ description: "Run agents in parallel: /parallel scout \"task1\" -> reviewer \"task2\" [--bg] [--fork]",
498
+ getArgumentCompletions: makeAgentCompletions(state, true),
499
+ handler: async (args, ctx) => {
500
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
501
+ const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
502
+ if (!parsed) return;
503
+ const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
504
+ agent: name,
505
+ task: stepTask ?? parsed.task,
506
+ ...(config.output !== undefined ? { output: config.output } : {}),
507
+ ...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),
508
+ ...(config.reads !== undefined ? { reads: config.reads } : {}),
509
+ ...(config.model ? { model: config.model } : {}),
510
+ ...(config.skill !== undefined ? { skill: config.skill } : {}),
511
+ ...(config.progress !== undefined ? { progress: config.progress } : {}),
512
+ }));
513
+ const params: SubagentParamsLike = { tasks, clarify: false, agentScope: "both" };
514
+ if (bg) params.async = true;
515
+ if (fork) params.context = "fork";
516
+ await runSlashSubagent(pi, ctx, params);
517
+ },
518
+ });
519
+
520
+
521
+ pi.registerCommand("subagents-doctor", {
522
+ description: "Show subagent diagnostics",
523
+ handler: async (_args, ctx) => {
524
+ await runSlashSubagent(pi, ctx, { action: "doctor" });
525
+ },
526
+ });
527
+
685
528
  }