@bastani/atomic 0.8.13-0 → 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 +24 -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
@@ -34,7 +34,8 @@ export type StageControlStatus =
34
34
  | "paused"
35
35
  | "blocked"
36
36
  | "completed"
37
- | "failed";
37
+ | "failed"
38
+ | "skipped";
38
39
 
39
40
  export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
40
41
 
@@ -53,6 +54,8 @@ export interface StageControlHandle {
53
54
  readonly sessionId: string | undefined;
54
55
  readonly sessionFile: string | undefined;
55
56
  readonly isStreaming: boolean;
57
+ /** True after the executor has released the live SDK session behind this handle. */
58
+ readonly isDisposed?: boolean;
56
59
  readonly messages: AgentSession["messages"];
57
60
  /** Live coding-agent session when available, used by embedded chat/footer UI. */
58
61
  readonly agentSession?: AgentSession;
@@ -190,7 +193,7 @@ export function createStageControlRegistry(): StageControlRegistry {
190
193
  );
191
194
  for (const handle of targets) {
192
195
  if (handle.status === "paused") continue;
193
- if (handle.status === "completed" || handle.status === "failed") continue;
196
+ if (handle.status === "completed" || handle.status === "failed" || handle.status === "skipped") continue;
194
197
  await handle.pause();
195
198
  }
196
199
  return controlledEntries(runId)
@@ -45,6 +45,8 @@ export interface StageSessionRuntime {
45
45
  readonly thinkingLevel: AgentSession["thinkingLevel"];
46
46
  readonly messages: AgentSession["messages"];
47
47
  readonly isStreaming: AgentSession["isStreaming"];
48
+ /** Number of SDK-level queued steering/follow-up messages, when supported. */
49
+ readonly pendingMessageCount?: number;
48
50
  navigateTree: AgentSession["navigateTree"];
49
51
  compact: AgentSession["compact"];
50
52
  abortCompaction(): void;
@@ -114,6 +116,8 @@ export interface InternalStageContext extends StageContext {
114
116
  __sessionMeta(): { sessionId: string | undefined; sessionFile: string | undefined };
115
117
  /** Internal: live coding-agent session when the adapter returned one. */
116
118
  __agentSession(): AgentSession | undefined;
119
+ /** Internal: SDK queued steering/follow-up message count, when available. */
120
+ __pendingMessageCount(): number;
117
121
  /** Internal: selected/effective model and fallback attempt metadata. */
118
122
  __modelFallbackMeta(): StageModelFallbackMeta;
119
123
  /**
@@ -711,6 +715,10 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
711
715
  return asAgentSession(session);
712
716
  },
713
717
 
718
+ __pendingMessageCount() {
719
+ return typeof session?.pendingMessageCount === "number" ? session.pendingMessageCount : 0;
720
+ },
721
+
714
722
  __modelFallbackMeta() {
715
723
  const attemptedModels = modelAttempts.map((attempt) => attempt.model);
716
724
  const model = selectedModel ?? workflowModelId(session?.model);
@@ -214,13 +214,15 @@ export async function validateWorkflowModels(input: {
214
214
  };
215
215
 
216
216
  const failures: ModelResolutionFailure[] = [];
217
- const availableModels = input.catalog === undefined ? undefined : await input.catalog.listModels().catch(() => undefined);
218
- if (input.catalog !== undefined && availableModels === undefined) {
219
- if (input.catalog.currentModel !== undefined) {
217
+ let availableModels: readonly WorkflowModelInfo[] | undefined;
218
+ if (input.catalog !== undefined) {
219
+ try {
220
+ availableModels = await input.catalog.listModels();
221
+ } catch (err) {
222
+ if (input.catalog.currentModel === undefined) throw err;
220
223
  recordWarning(catalogUnavailableWarning());
221
224
  return warnings;
222
225
  }
223
- throw new WorkflowModelValidationError([{ input: "model catalog", reason: "unavailable and no current model is configured" }]);
224
226
  }
225
227
 
226
228
  for (const request of relevant) {
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
+ import { APP_NAME } from "@bastani/atomic";
5
6
 
6
7
  export interface WorktreeSetup {
7
8
  cwd: string;
@@ -149,11 +150,11 @@ function safePatchAgentName(agent: string): string {
149
150
  }
150
151
 
151
152
  function buildWorktreeBranch(runId: string, index: number): string {
152
- return `pi-parallel-${runId}-${index}`;
153
+ return `${APP_NAME}-parallel-${runId}-${index}`;
153
154
  }
154
155
 
155
156
  function buildWorktreePath(runId: string, index: number): string {
156
- return path.join(os.tmpdir(), `pi-worktree-${runId}-${index}`);
157
+ return path.join(os.tmpdir(), `${APP_NAME}-worktree-${runId}-${index}`);
157
158
  }
158
159
 
159
160
  function resolveRepoCwdRelative(cwd: string): string {
@@ -7,7 +7,8 @@
7
7
  */
8
8
 
9
9
  import type { Store } from "./store.js";
10
- import type { RunSnapshot, StageSnapshot } from "./store-types.js";
10
+ import type { RunSnapshot, StageSnapshot, StageStatus } from "./store-types.js";
11
+ import { isWorkflowFailureKind } from "./workflow-failures.js";
11
12
 
12
13
  // ---------------------------------------------------------------------------
13
14
  // Config option
@@ -161,10 +162,15 @@ export function restoreOnSessionStart(
161
162
  if (typeof getEntries !== "function") return;
162
163
 
163
164
  const entries = getEntries.call(sessionManager);
164
- const inFlight = scanInFlightRuns(entries as readonly SessionEntry[]);
165
+ const sessionEntries = entries as readonly SessionEntry[];
166
+ restoreEndedFailedRuns(sessionEntries, store);
167
+ const inFlight = scanInFlightRuns(sessionEntries);
165
168
  if (inFlight.length === 0) return;
166
169
 
167
170
  for (const run of inFlight) {
171
+ const runMeta = findRunStartMetadata(sessionEntries, run.runId);
172
+ const stages = _buildStageSnapshots(sessionEntries, run.runId);
173
+
168
174
  if (config.resumeInFlight === "auto") {
169
175
  // Re-hydrate the run into the store as "running"
170
176
  const runSnapshot: RunSnapshot = {
@@ -172,8 +178,10 @@ export function restoreOnSessionStart(
172
178
  name: run.name,
173
179
  inputs: run.inputs,
174
180
  status: "running",
175
- stages: _buildStageSnapshots(entries as readonly SessionEntry[], run.runId),
181
+ stages,
176
182
  startedAt: run.startTs,
183
+ ...(runMeta.resumedFromRunId !== undefined ? { resumedFromRunId: runMeta.resumedFromRunId } : {}),
184
+ ...(runMeta.resumeFromStageId !== undefined ? { resumeFromStageId: runMeta.resumeFromStageId } : {}),
177
185
  };
178
186
  store.recordRunStart(runSnapshot);
179
187
  callbacks.onResume?.(run);
@@ -184,10 +192,14 @@ export function restoreOnSessionStart(
184
192
  name: run.name,
185
193
  inputs: run.inputs,
186
194
  status: "failed",
187
- stages: _buildStageSnapshots(entries as readonly SessionEntry[], run.runId),
195
+ stages,
188
196
  startedAt: run.startTs,
189
197
  endedAt: Date.now(),
190
198
  error: "Run did not complete — process was interrupted.",
199
+ failureKind: "unknown",
200
+ resumable: false,
201
+ ...(runMeta.resumedFromRunId !== undefined ? { resumedFromRunId: runMeta.resumedFromRunId } : {}),
202
+ ...(runMeta.resumeFromStageId !== undefined ? { resumeFromStageId: runMeta.resumeFromStageId } : {}),
191
203
  };
192
204
  store.recordRunStart(runSnapshot);
193
205
  store.recordRunEnd(run.runId, "failed");
@@ -224,6 +236,7 @@ function _buildStageSnapshots(
224
236
  status: "running",
225
237
  parentIds: Array.isArray(parentIds) ? (parentIds as string[]) : [],
226
238
  startedAt: typeof ts === "number" ? ts : undefined,
239
+ ...replayMetadata(entry.payload),
227
240
  toolEvents: [],
228
241
  });
229
242
  }
@@ -234,13 +247,22 @@ function _buildStageSnapshots(
234
247
  const status = entry.payload["status"];
235
248
  const durationMs = entry.payload["durationMs"];
236
249
  const summary = entry.payload["summary"];
250
+ const error = entry.payload["error"];
251
+ const failureKind = entry.payload["failureKind"];
252
+ const failureMessage = entry.payload["failureMessage"];
253
+ const skippedReason = entry.payload["skippedReason"];
237
254
  if (typeof stageId !== "string") continue;
238
255
  endedStages.add(stageId);
239
256
  const snap = stageMap.get(stageId);
240
257
  if (snap) {
241
- snap.status = (status === "completed" || status === "failed") ? status : "failed";
258
+ snap.status = restoreStageStatus(status);
242
259
  if (typeof durationMs === "number") snap.durationMs = durationMs;
243
260
  if (typeof summary === "string") snap.result = summary;
261
+ if (typeof error === "string") snap.error = error;
262
+ if (typeof failureKind === "string" && isWorkflowFailureKind(failureKind)) snap.failureKind = failureKind;
263
+ if (typeof failureMessage === "string") snap.failureMessage = failureMessage;
264
+ if (typeof skippedReason === "string") snap.skippedReason = skippedReason;
265
+ Object.assign(snap, replayMetadata(entry.payload));
244
266
  }
245
267
  }
246
268
  }
@@ -255,3 +277,114 @@ function _buildStageSnapshots(
255
277
 
256
278
  return [...stageMap.values()];
257
279
  }
280
+
281
+ function replayMetadata(payload: Record<string, unknown>): Pick<StageSnapshot, "replayedFromStageId" | "replayed"> {
282
+ const replayedFromStageId = payload["replayedFromStageId"];
283
+ const replayed = payload["replayed"];
284
+ return {
285
+ ...(typeof replayedFromStageId === "string" ? { replayedFromStageId } : {}),
286
+ ...(typeof replayed === "boolean" ? { replayed } : {}),
287
+ };
288
+ }
289
+
290
+ function restoreStageStatus(status: unknown): StageStatus {
291
+ switch (status) {
292
+ case "completed":
293
+ case "failed":
294
+ case "skipped":
295
+ return status;
296
+ default:
297
+ return "failed";
298
+ }
299
+ }
300
+
301
+ function restoreEndedFailedRuns(entries: readonly SessionEntry[], store: Store): void {
302
+ const started = new Map<string, { readonly name: string; readonly inputs: Readonly<Record<string, unknown>>; readonly startTs: number }>();
303
+ const ended = new Map<string, Record<string, unknown>>();
304
+
305
+ for (const entry of entries) {
306
+ if (entry.type === "workflow.run.start") {
307
+ const runId = entry.payload["runId"];
308
+ const name = entry.payload["name"];
309
+ const inputs = entry.payload["inputs"];
310
+ const ts = entry.payload["ts"];
311
+ if (typeof runId === "string" && typeof name === "string" && typeof ts === "number") {
312
+ started.set(runId, {
313
+ name,
314
+ inputs: (inputs !== null && typeof inputs === "object" && !Array.isArray(inputs))
315
+ ? (inputs as Record<string, unknown>)
316
+ : {},
317
+ startTs: ts,
318
+ });
319
+ }
320
+ }
321
+ if (entry.type === "workflow.run.end") {
322
+ const runId = entry.payload["runId"];
323
+ if (typeof runId === "string") ended.set(runId, entry.payload);
324
+ }
325
+ }
326
+
327
+ for (const [runId, start] of started) {
328
+ if (store.runs().some((run) => run.id === runId)) continue;
329
+ const end = ended.get(runId);
330
+ const status = restoreTerminalRunStatus(end?.["status"]);
331
+ if (end === undefined || status === undefined) continue;
332
+
333
+ const runMeta = findRunStartMetadata(entries, runId);
334
+ const stages = _buildStageSnapshots(entries, runId);
335
+ store.recordRunStart({
336
+ id: runId,
337
+ name: start.name,
338
+ inputs: start.inputs,
339
+ status: "running",
340
+ stages,
341
+ startedAt: start.startTs,
342
+ ...(runMeta.resumedFromRunId !== undefined ? { resumedFromRunId: runMeta.resumedFromRunId } : {}),
343
+ ...(runMeta.resumeFromStageId !== undefined ? { resumeFromStageId: runMeta.resumeFromStageId } : {}),
344
+ });
345
+
346
+ const error = end["error"];
347
+ const failureKind = end["failureKind"];
348
+ const failureMessage = end["failureMessage"];
349
+ const failedStageId = end["failedStageId"];
350
+ const resumable = end["resumable"];
351
+ store.recordRunEnd(
352
+ runId,
353
+ status,
354
+ undefined,
355
+ typeof error === "string" ? error : undefined,
356
+ {
357
+ ...(typeof failureKind === "string" && isWorkflowFailureKind(failureKind) ? { failureKind } : {}),
358
+ ...(typeof failureMessage === "string" ? { failureMessage } : {}),
359
+ ...(typeof failedStageId === "string" ? { failedStageId } : {}),
360
+ ...(typeof resumable === "boolean" ? { resumable } : {}),
361
+ },
362
+ );
363
+ }
364
+ }
365
+
366
+ function restoreTerminalRunStatus(status: unknown): "failed" | "killed" | undefined {
367
+ switch (status) {
368
+ case "failed":
369
+ case "killed":
370
+ return status;
371
+ default:
372
+ return undefined;
373
+ }
374
+ }
375
+
376
+ function findRunStartMetadata(
377
+ entries: readonly SessionEntry[],
378
+ runId: string,
379
+ ): { readonly resumedFromRunId?: string; readonly resumeFromStageId?: string } {
380
+ for (const entry of entries) {
381
+ if (entry.type !== "workflow.run.start" || entry.payload["runId"] !== runId) continue;
382
+ const resumedFromRunId = entry.payload["resumedFromRunId"];
383
+ const resumeFromStageId = entry.payload["resumeFromStageId"];
384
+ return {
385
+ ...(typeof resumedFromRunId === "string" ? { resumedFromRunId } : {}),
386
+ ...(typeof resumeFromStageId === "string" ? { resumeFromStageId } : {}),
387
+ };
388
+ }
389
+ return {};
390
+ }
@@ -31,6 +31,8 @@ export interface RunStartPayload {
31
31
  readonly runId: string;
32
32
  readonly name: string;
33
33
  readonly inputs: Readonly<Record<string, unknown>>;
34
+ readonly resumedFromRunId?: string;
35
+ readonly resumeFromStageId?: string;
34
36
  readonly ts: number;
35
37
  }
36
38
 
@@ -40,6 +42,8 @@ export interface StageStartPayload {
40
42
  readonly name: string;
41
43
  readonly parentIds: readonly string[];
42
44
  readonly model?: string;
45
+ readonly replayedFromStageId?: string;
46
+ readonly replayed?: boolean;
43
47
  readonly ts: number;
44
48
  }
45
49
 
@@ -56,12 +60,23 @@ export interface StageEndPayload {
56
60
  readonly status: string;
57
61
  readonly durationMs?: number;
58
62
  readonly summary?: string;
63
+ readonly error?: string;
64
+ readonly failureKind?: string;
65
+ readonly failureMessage?: string;
66
+ readonly skippedReason?: string;
67
+ readonly replayedFromStageId?: string;
68
+ readonly replayed?: boolean;
59
69
  }
60
70
 
61
71
  export interface RunEndPayload {
62
72
  readonly runId: string;
63
73
  readonly status: string;
64
74
  readonly result?: unknown;
75
+ readonly error?: string;
76
+ readonly failureKind?: string;
77
+ readonly failureMessage?: string;
78
+ readonly failedStageId?: string;
79
+ readonly resumable?: boolean;
65
80
  readonly ts: number;
66
81
  }
67
82
 
@@ -79,6 +94,8 @@ export function appendRunStart(api: PersistenceAPI, payload: RunStartPayload): v
79
94
  runId: payload.runId,
80
95
  name: payload.name,
81
96
  inputs: payload.inputs,
97
+ ...(payload.resumedFromRunId !== undefined ? { resumedFromRunId: payload.resumedFromRunId } : {}),
98
+ ...(payload.resumeFromStageId !== undefined ? { resumeFromStageId: payload.resumeFromStageId } : {}),
82
99
  ts: payload.ts,
83
100
  });
84
101
  if (entryId && typeof api.setLabel === "function") {
@@ -96,6 +113,8 @@ export function appendStageStart(api: PersistenceAPI, payload: StageStartPayload
96
113
  name: payload.name,
97
114
  parentIds: [...payload.parentIds],
98
115
  ...(payload.model !== undefined ? { model: payload.model } : {}),
116
+ ...(payload.replayedFromStageId !== undefined ? { replayedFromStageId: payload.replayedFromStageId } : {}),
117
+ ...(payload.replayed !== undefined ? { replayed: payload.replayed } : {}),
99
118
  ts: payload.ts,
100
119
  });
101
120
  }
@@ -124,6 +143,12 @@ export function appendStageEnd(
124
143
  status: payload.status,
125
144
  ...(payload.durationMs !== undefined ? { durationMs: payload.durationMs } : {}),
126
145
  ...(payload.summary !== undefined ? { summary: payload.summary } : {}),
146
+ ...(payload.error !== undefined ? { error: payload.error } : {}),
147
+ ...(payload.failureKind !== undefined ? { failureKind: payload.failureKind } : {}),
148
+ ...(payload.failureMessage !== undefined ? { failureMessage: payload.failureMessage } : {}),
149
+ ...(payload.skippedReason !== undefined ? { skippedReason: payload.skippedReason } : {}),
150
+ ...(payload.replayedFromStageId !== undefined ? { replayedFromStageId: payload.replayedFromStageId } : {}),
151
+ ...(payload.replayed !== undefined ? { replayed: payload.replayed } : {}),
127
152
  });
128
153
  if (opts?.emitMessage === true && payload.summary && typeof api.appendCustomMessageEntry === "function") {
129
154
  api.appendCustomMessageEntry(
@@ -140,6 +165,11 @@ export function appendRunEnd(api: PersistenceAPI, payload: RunEndPayload): void
140
165
  runId: payload.runId,
141
166
  status: payload.status,
142
167
  ...(payload.result !== undefined ? { result: payload.result as Record<string, unknown> } : {}),
168
+ ...(payload.error !== undefined ? { error: payload.error } : {}),
169
+ ...(payload.failureKind !== undefined ? { failureKind: payload.failureKind } : {}),
170
+ ...(payload.failureMessage !== undefined ? { failureMessage: payload.failureMessage } : {}),
171
+ ...(payload.failedStageId !== undefined ? { failedStageId: payload.failedStageId } : {}),
172
+ ...(payload.resumable !== undefined ? { resumable: payload.resumable } : {}),
143
173
  ts: payload.ts,
144
174
  });
145
175
  }
@@ -11,7 +11,7 @@
11
11
  * The renderer has two output modes:
12
12
  *
13
13
  * - **pretty** (default when a GraphTheme is supplied) — emits ANSI-coloured
14
- * blocks that match the `▎ INPUTS` aesthetic established in
14
+ * blocks that match the indented `INPUTS` aesthetic established in
15
15
  * `src/tui/session-list.ts`. Drawn for any interactive surface where the
16
16
  * terminal supports colour.
17
17
  * - **plain** (used when no theme is supplied) — emits the
@@ -22,7 +22,7 @@
22
22
  * The layout in pretty mode mirrors flora131/atomic's `renderInputsText` so a
23
23
  * dev moving between the atomic CLI and the pi extension feels at home:
24
24
  *
25
- *INPUTS FOR ralph
25
+ * INPUTS FOR ralph
26
26
  *
27
27
  * prompt text · required
28
28
  * The high-level task to plan and execute.
@@ -41,11 +41,12 @@
41
41
 
42
42
  import type { GraphTheme } from "../tui/graph-theme.js";
43
43
  import { paint } from "../tui/color-utils.js";
44
+ import { renderRoundedBox, chatWidth } from "../tui/chat-surface.js";
44
45
  import { truncateToWidth } from "../tui/text-helpers.js";
45
46
  import type { WorkflowInputEntry } from "../extension/render-result.js";
46
47
 
47
48
  export interface RenderInputsSchemaOptions {
48
- /** When provided, output uses ANSI colours and the `▎ INPUTS` chrome. */
49
+ /** When provided, output uses ANSI colours and indented `INPUTS` chrome. */
49
50
  theme?: GraphTheme;
50
51
  /** Optional host render width in terminal cells. */
51
52
  width?: number;
@@ -61,49 +62,42 @@ export function renderInputsSchema(
61
62
  inputs: WorkflowInputEntry[],
62
63
  opts: RenderInputsSchemaOptions = {},
63
64
  ): string {
64
- if (inputs.length === 0) {
65
- return fitBlock(`Workflow "${name}" has no declared inputs.`, opts.width);
66
- }
67
- const rendered = opts.theme === undefined
68
- ? renderPlain(name, inputs)
69
- : renderPretty(name, inputs, opts.theme);
70
- return fitBlock(rendered, opts.width);
71
- }
72
-
73
- function fitBlock(block: string, width?: number): string {
74
- if (width === undefined || width <= 0) return block;
75
- return block
76
- .split("\n")
77
- .map((line) => truncateToWidth(line, width, "…"))
78
- .join("\n");
65
+ return opts.theme === undefined
66
+ ? renderPlain(name, inputs, opts.width)
67
+ : renderPretty(name, inputs, opts.theme, opts.width);
79
68
  }
80
69
 
81
70
  // ---------------------------------------------------------------------------
82
- // Plain renderer — historical shape preserved for the LLM tool path and
83
- // non-TTY consumers. Stable byte-for-byte so existing snapshots keep passing.
71
+ // Plain renderer — rounded, marker-free workflow-tool output without ANSI.
84
72
  // ---------------------------------------------------------------------------
85
73
 
86
- function renderPlain(name: string, inputs: WorkflowInputEntry[]): string {
87
- const lines = inputs.map((inp) => {
88
- const req = inp.required ? " (required)" : "";
89
- const def =
90
- inp.default !== undefined ? ` [default: ${JSON.stringify(inp.default)}]` : "";
91
- const choices =
92
- inp.choices && inp.choices.length > 0
93
- ? ` {choices: ${inp.choices.join(", ")}}`
94
- : "";
95
- const desc = inp.description ? ` — ${inp.description}` : "";
96
- return ` ${inp.name}: ${inp.type}${req}${def}${choices}${desc}`;
74
+ function renderPlain(name: string, inputs: WorkflowInputEntry[], width?: number): string {
75
+ const boxWidth = chatWidth(width);
76
+ const body: string[] = [];
77
+
78
+ if (inputs.length === 0) {
79
+ body.push(` Workflow has no declared inputs. Workflow: "${name}". `);
80
+ } else {
81
+ for (let i = 0; i < inputs.length; i++) {
82
+ if (i > 0) body.push("");
83
+ body.push(...renderInputRows(inputs[i]!));
84
+ }
85
+ body.push("");
86
+ body.push(` ${inputsSummary(name, inputs)} `);
87
+ }
88
+
89
+ return renderRoundedBox({
90
+ title: `INPUTS FOR ${name}`,
91
+ bodyLines: body,
92
+ width: boxWidth,
97
93
  });
98
- return `Inputs for "${name}":\n${lines.join("\n")}`;
99
94
  }
100
95
 
101
96
  // ---------------------------------------------------------------------------
102
97
  // Pretty renderer — ANSI-coloured, themed, matches the session-list aesthetic.
103
98
  //
104
- // Section labels lead with `▎ ` in mauve (the same glyph used across the
105
- // extension's TUI surfaces) and the field name is rendered bold-bright so
106
- // the eye scans the form top-to-bottom. The `required` tag is yellow
99
+ // Section labels lead with a two-cell indent and the field name is rendered
100
+ // bold-bright so the eye scans the form top-to-bottom. The `required` tag is yellow
107
101
  // (theme.warning) — yellow draws attention to "you must fill this in"
108
102
  // without being alarming the way red would.
109
103
  // ---------------------------------------------------------------------------
@@ -112,100 +106,64 @@ function renderPretty(
112
106
  name: string,
113
107
  inputs: WorkflowInputEntry[],
114
108
  theme: GraphTheme,
109
+ width?: number,
115
110
  ): string {
116
- const out: string[] = [];
117
-
118
- // Section header — mauve indicator bar + bold caps label, matching
119
- // session-list.ts. Includes the workflow name so the block is
120
- // self-titling when printed inline in a chat surface.
121
- out.push("");
122
- out.push(
123
- `${paint("▎", theme.mauve)} ${paint(
124
- `INPUTS FOR ${name.toUpperCase()}`,
125
- theme.textMuted,
126
- { bold: true },
127
- )}`,
128
- );
129
- out.push("");
130
-
131
- // Compute a stable left column for the name so types/required tags
132
- // line up vertically — the form should *look* like a form, not a
133
- // ragged paragraph. Cap at 24 chars so a stray long input name
134
- // doesn't push everything off the right edge.
135
- const nameWidth = Math.min(
136
- 24,
137
- Math.max(...inputs.map((i) => i.name.length)),
138
- );
139
- const dimDot = paint(" · ", theme.dim);
140
-
141
- for (const field of inputs) {
142
- const namePad = field.name.padEnd(nameWidth);
143
- const tagColour = field.required ? theme.warning : theme.dim;
144
- const tagLabel = field.required ? "required" : "optional";
145
-
146
- // Row 1: name · type · required|optional
147
- out.push(
148
- " " +
149
- paint(namePad, theme.text, { bold: true }) +
150
- " " +
151
- paint(field.type, theme.dim) +
152
- dimDot +
153
- paint(tagLabel, tagColour),
154
- );
155
-
156
- // Row 2: description (when present)
157
- if (field.description) {
158
- out.push(" " + paint(field.description, theme.textMuted));
159
- }
160
-
161
- // Row 3: choices for selects — dot-separator style so the values
162
- // read as a clear closed set.
163
- if (field.choices && field.choices.length > 0) {
164
- const joined = field.choices
165
- .map((v) => paint(v, theme.text))
166
- .join(dimDot);
167
- out.push(" " + paint("values: ", theme.dim) + joined);
168
- }
111
+ const boxWidth = chatWidth(width);
112
+ const body: string[] = [];
169
113
 
170
- // Row 4: default (when present)
171
- if (field.default !== undefined) {
172
- out.push(
173
- " " +
174
- paint("default: ", theme.dim) +
175
- paint(String(field.default), theme.text),
176
- );
114
+ if (inputs.length === 0) {
115
+ body.push(` ${paint(`Workflow has no declared inputs. Workflow: "${name}".`, theme.dim)} `);
116
+ } else {
117
+ for (let i = 0; i < inputs.length; i++) {
118
+ if (i > 0) body.push("");
119
+ body.push(...renderInputRows(inputs[i]!, theme));
177
120
  }
121
+ body.push("");
122
+ body.push(` ${paint(inputsSummary(name, inputs), theme.dim)} `);
123
+ }
178
124
 
179
- // Row 5: placeholder hint (optional, mostly used by the picker)
180
- if (field.placeholder) {
181
- out.push(
182
- " " +
183
- paint("placeholder: ", theme.dim) +
184
- paint(field.placeholder, theme.textMuted),
185
- );
186
- }
125
+ return renderRoundedBox({
126
+ title: `INPUTS FOR ${name.toUpperCase()}`,
127
+ bodyLines: body,
128
+ accent: theme.mauve,
129
+ theme,
130
+ width: boxWidth,
131
+ });
132
+ }
187
133
 
188
- out.push(""); // spacer between fields
134
+ function renderInputRows(
135
+ field: WorkflowInputEntry,
136
+ theme?: GraphTheme,
137
+ ): string[] {
138
+ const tagLabel = field.required ? "required" : "optional";
139
+ const heading = theme
140
+ ? ` ${paint(field.name, theme.text, { bold: true })} ${paint(field.type, theme.dim)} · ${paint(tagLabel, field.required ? theme.warning : theme.dim)} `
141
+ : ` ${field.name} ${field.type} · ${tagLabel} `;
142
+ const lines: string[] = [heading];
143
+
144
+ if (field.description) {
145
+ lines.push(` ${theme ? paint(field.description, theme.textMuted) : field.description} `);
146
+ }
147
+ if (field.choices && field.choices.length > 0) {
148
+ const values = field.choices.join(" · ");
149
+ lines.push(` ${theme ? paint("values: ", theme.dim) + paint(values, theme.text) : `values: ${values}`} `);
150
+ }
151
+ if (field.default !== undefined) {
152
+ const value = JSON.stringify(field.default);
153
+ lines.push(` ${theme ? paint("default: ", theme.dim) + paint(value, theme.text) : `default: ${value}`} `);
154
+ }
155
+ if (field.placeholder) {
156
+ lines.push(` ${theme ? paint("placeholder: ", theme.dim) + paint(field.placeholder, theme.textMuted) : `placeholder: ${field.placeholder}`} `);
189
157
  }
190
158
 
191
- // Summary footer — total/required count + the two-path hint that
192
- // teaches the slash key=value form and the picker fallback in one line.
159
+ return lines;
160
+ }
161
+
162
+ function inputsSummary(name: string, inputs: readonly WorkflowInputEntry[]): string {
193
163
  const required = inputs.filter((i) => i.required).length;
194
164
  const total = inputs.length;
195
165
  const totalLabel = `${total} input${total === 1 ? "" : "s"}`;
196
166
  const reqLabel = `${required} required`;
197
- out.push(
198
- " " +
199
- paint(totalLabel, theme.dim) +
200
- dimDot +
201
- paint(reqLabel, theme.dim) +
202
- dimDot +
203
- paint(
204
- `pass via key=value or run \`/workflow ${name}\` with no args for picker`,
205
- theme.dim,
206
- ),
207
- );
208
- out.push("");
209
-
210
- return out.join("\n");
167
+ return `${totalLabel} · ${reqLabel} · pass via key=value or run \`/workflow ${name}\` with no args for picker`;
211
168
  }
169
+