@bastani/atomic 0.8.13 โ†’ 0.8.14

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 +23 -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
@@ -1,42 +1,32 @@
1
1
  /**
2
2
  * Renderer for the inline chat-history workflow form.
3
3
  *
4
- * Identity mirrors the orchestrator panel:
5
- * - 3-row mantle chrome band with an outlined accent pill on the left,
6
- * workflow name beside it, and a `<i> / <n>` counter on the right.
7
- * - One bordered "node card" per field with the field name centred inside
8
- * the top border (matches the DAG node-card title slot in node-card.ts).
9
- * - Caption row beneath each field card: `<type> ยท <required|optional>
10
- * ยท <description>` in dim.
11
- * - 3-row mantle chrome footer with the `EDIT` mode pill and hints
12
- * anchored at the bottom of the widget.
4
+ * Identity mirrors the multi ask_user_question dialog:
5
+ * - Top/bottom dynamic border rules wrap the live form.
6
+ * - A compact tab row shows each input (`โ– ` valid / `โ–ก` missing) plus Submit,
7
+ * matching the multi-question tab bar affordance.
8
+ * - Every declared input is rendered on one page as a question block.
9
+ * - The focused field owns the ask-style pointer/caret.
10
+ * - Footer hints sit below the bottom rule, like ask_user_question hints.
13
11
  *
14
- * โ•ญ WORKFLOW โ•ฎ ralph 1 / 4
15
- * โ”‚ WORKFLOW โ”‚
16
- * โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
12
+ * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
+ * โ† โ–ก prompt โ–  iters โœ“ Submit โ†’
17
14
  *
18
- * โ•ญโ”€โ”€โ”€โ”€โ”€ prompt โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
19
- * โ”‚ build me a TUI for arg-pickers โ”‚
20
- * โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
21
- * text ยท required ยท task prompt
15
+ * task prompt
22
16
  *
23
- * โ•ญโ”€โ”€โ”€โ”€โ”€ iters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
24
- * โ”‚ 5 โ”‚
25
- * โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
26
- * integer ยท optional ยท loop count
17
+ * โฏ 1. build me a TUI for arg-pickers
27
18
  *
28
- * โ•ญ EDIT โ•ฎ tab next ยท shift+tab prev ยท ctrl+x run ยท esc cancel
29
- * โ”‚ EDIT โ”‚
30
- * โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
19
+ * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
+ * Enter to select ยท โ†‘/โ†“ to navigate ยท Tab to switch input fields ยท Esc to cancel
31
21
  *
32
- * Frozen states drop all chrome โ€” submitted and cancelled forms are
33
- * single-line ledger entries in the scrollback.
22
+ * Submitted forms become a single-line ledger entry in scrollback. Cancelled
23
+ * forms render no rows so cancellation leaves no chat artefact.
34
24
  *
35
25
  * The card never owns keystrokes โ€” keystrokes are routed by the editor.
36
26
  * `renderInlineCard` is a pure function of `state + theme + width`.
37
27
  *
38
28
  * cross-ref:
39
- * - src/tui/header.ts (renderOutlinePill โ€” shared pill primitive)
29
+ * - packages/coding-agent/src/core/tools/ask-user-question/view/dialog-builder.ts
40
30
  * - src/tui/node-card.ts (centred title-in-border pattern)
41
31
  * - src/tui/graph-view.ts (statusline + chrome band composition)
42
32
  */
@@ -44,10 +34,18 @@
44
34
  import type { InlineFormState } from "./inline-form-store.js";
45
35
  import type { WorkflowInputEntry } from "../extension/render-result.js";
46
36
  import type { GraphTheme } from "./graph-theme.js";
47
- import { invalidForField } from "./inputs-picker.js";
48
- import { renderOutlinePill } from "./header.js";
49
- import { BOLD, RESET, hexBg, hexToAnsi, paint } from "./color-utils.js";
50
- import { truncateToWidth, visibleWidth } from "./text-helpers.js";
37
+ import { computeInvalid } from "./inputs-picker.js";
38
+ import { paint } from "./color-utils.js";
39
+ import { renderCompactBandHeader } from "./header.js";
40
+ import {
41
+ truncateToWidth,
42
+ visibleWidth,
43
+ wrapPlainText,
44
+ } from "./text-helpers.js";
45
+ import {
46
+ renderAskChoiceRows,
47
+ renderSubmitControls,
48
+ } from "./submit-pane.js";
51
49
 
52
50
  export interface InlineCardOpts {
53
51
  width: number;
@@ -111,7 +109,7 @@ function tailToWidth(text: string, width: number): string {
111
109
  export function renderInlineCard(opts: InlineCardOpts): string[] {
112
110
  const { state, theme, width } = opts;
113
111
  if (state.status === "submitted") return [fitLine(renderSubmittedLine(state, theme), width)];
114
- if (state.status === "cancelled") return [fitLine(renderCancelledLine(state, theme), width)];
112
+ if (state.status === "cancelled") return [];
115
113
  return renderEditingCard(opts).map((line) => fitLine(line, width));
116
114
  }
117
115
 
@@ -119,24 +117,17 @@ function renderEditingCard(opts: InlineCardOpts): string[] {
119
117
  const { state, theme, width } = opts;
120
118
  const lines: string[] = [];
121
119
 
122
- lines.push(...renderHeaderBand(state, theme, width));
123
- if (state.description) {
124
- lines.push(" " + paint(state.description, theme.textMuted));
125
- }
120
+ lines.push(...renderWorkflowHeader(state.workflowName, state.fields.length, state.focusedIdx, theme, width));
126
121
  lines.push("");
127
122
 
128
- for (let i = 0; i < state.fields.length; i++) {
129
- const f = state.fields[i]!;
130
- const raw = state.rawText[f.name] ?? "";
131
- const focused = i === state.focusedIdx;
132
- // Don't paint a focused field as invalid โ€” the caret is already on it,
133
- // the user is fixing it now.
134
- const invalid = focused ? null : invalidForField(f, raw, i);
135
- lines.push(...renderFieldCard(f, raw, focused, invalid, theme, width, focused ? state.caret : undefined));
123
+ for (let i = 0; i < state.fields.length; i += 1) {
124
+ const field = state.fields[i]!;
125
+ const raw = state.rawText[field.name] ?? "";
126
+ lines.push(...renderField(field, raw, state.caret, state.focusedIdx === i, theme, width));
136
127
  lines.push("");
137
128
  }
138
129
 
139
- lines.push(...renderFooterBand(theme, width));
130
+ lines.push(...renderFooterBand(state, theme, width));
140
131
  return lines;
141
132
  }
142
133
 
@@ -144,215 +135,148 @@ function renderEditingCard(opts: InlineCardOpts): string[] {
144
135
  // Header / footer chrome bands
145
136
  // ---------------------------------------------------------------------------
146
137
 
147
- const HEADER_PILL_LABEL = "WORKFLOW";
148
- const FOOTER_PILL_LABEL = "EDIT";
149
-
150
- function renderHeaderBand(state: InlineFormState, theme: GraphTheme, width: number): string[] {
151
- const chromeBg = hexBg(theme.backgroundPanel);
152
- const muted = hexToAnsi(theme.textMuted);
153
- const dim = hexToAnsi(theme.dim);
154
-
155
- const { top, mid, bot, visibleWidth: pillW } = renderOutlinePill(
156
- HEADER_PILL_LABEL,
157
- theme.accent,
158
- chromeBg,
159
- );
160
-
161
- const nameVisible = ` ${state.workflowName}`;
162
- const focusTargetCount = state.fields.length;
163
- const counter = `${Math.min(state.focusedIdx + 1, focusTargetCount)} / ${focusTargetCount}`;
164
- const counterVisible = counter;
165
-
166
- const leftEdgePad = 1;
167
- const rightEdgePad = 2;
168
- const fillerVisible = Math.max(
169
- 1,
170
- width - leftEdgePad - pillW - nameVisible.length - counterVisible.length - rightEdgePad,
171
- );
172
- const blankAcross = " ".repeat(nameVisible.length + fillerVisible + counterVisible.length + rightEdgePad);
173
-
174
- return [
175
- `${chromeBg} ${RESET}${top}${chromeBg}${blankAcross}${RESET}`,
176
- `${chromeBg} ${RESET}${mid}${chromeBg} ${muted}${state.workflowName}${RESET}${chromeBg}${" ".repeat(fillerVisible)}${dim}${counter}${RESET}${chromeBg}${" ".repeat(rightEdgePad)}${RESET}`,
177
- `${chromeBg} ${RESET}${bot}${chromeBg}${blankAcross}${RESET}`,
178
- ];
138
+ function renderWorkflowHeader(
139
+ workflowName: string,
140
+ fieldCount: number,
141
+ focusedIdx: number,
142
+ theme: GraphTheme,
143
+ width: number,
144
+ ): string[] {
145
+ const current = Math.min(fieldCount, Math.max(1, focusedIdx + 1));
146
+ return renderCompactBandHeader({
147
+ label: "WORKFLOW",
148
+ subtitle: workflowName,
149
+ badges: fieldCount > 0 ? [{ text: `${current} / ${fieldCount}`, fg: theme.dim }] : [],
150
+ width,
151
+ theme,
152
+ });
179
153
  }
180
154
 
181
- function renderFooterBand(theme: GraphTheme, width: number): string[] {
182
- const chromeBg = hexBg(theme.backgroundPanel);
183
- const dim = hexToAnsi(theme.dim);
184
-
185
- const { top, mid, bot, visibleWidth: pillW } = renderOutlinePill(
186
- FOOTER_PILL_LABEL,
187
- theme.accent,
188
- chromeBg,
189
- );
155
+ function renderFooterBand(state: InlineFormState, theme: GraphTheme, width: number): string[] {
156
+ return renderInlineSubmitControls(state, theme, width);
157
+ }
190
158
 
191
- const text = hexToAnsi(theme.text);
192
- const muted = hexToAnsi(theme.textMuted);
193
- const hints: Array<{ key: string; label: string }> = [
194
- { key: "tab", label: "Next" },
195
- { key: "shift+tab", label: "Prev" },
196
- { key: "ctrl+x", label: "Run" },
197
- { key: "esc", label: "Cancel" },
198
- ];
199
- const sep = `${chromeBg} ${dim}ยท${RESET}${chromeBg} `;
200
- const segments = hints.map(
201
- ({ key, label }) =>
202
- `${text}${BOLD}${key}${RESET}${chromeBg} ${muted}${label}${RESET}${chromeBg}`,
203
- );
204
- const hintsStyled = segments.join(sep);
205
- const hintsVisible =
206
- hints.reduce((sum, h) => sum + h.key.length + 1 + h.label.length, 0) +
207
- (hints.length - 1) * 5;
208
-
209
- const leftEdgePad = 1;
210
- const leadGap = 2; // gap between pill and hints, matching graph statusline
211
- const rightEdgePad = 2;
212
- const tailFiller = Math.max(
213
- 0,
214
- width - leftEdgePad - pillW - leadGap - hintsVisible - rightEdgePad,
215
- );
216
- const blankAcross = " ".repeat(leadGap + hintsVisible + tailFiller + rightEdgePad);
159
+ // ---------------------------------------------------------------------------
160
+ // Field body (ask_user_question-style list/input rows)
161
+ // ---------------------------------------------------------------------------
217
162
 
163
+ function renderField(
164
+ field: WorkflowInputEntry,
165
+ raw: string,
166
+ caret: number,
167
+ focused: boolean,
168
+ theme: GraphTheme,
169
+ width: number,
170
+ ): string[] {
171
+ const boxWidth = Math.max(4, width);
172
+ const contentWidth = Math.max(1, boxWidth - 2);
173
+ const borderColor = focused ? theme.accent : theme.borderDim;
174
+ const rows = renderAskStyleFieldBody(field, raw, focused ? caret : raw.length, focused, theme, contentWidth);
218
175
  return [
219
- `${chromeBg} ${RESET}${top}${chromeBg}${blankAcross}${RESET}`,
220
- `${chromeBg} ${RESET}${mid}${chromeBg}${" ".repeat(leadGap)}${hintsStyled}${chromeBg}${" ".repeat(tailFiller + rightEdgePad)}${RESET}`,
221
- `${chromeBg} ${RESET}${bot}${chromeBg}${blankAcross}${RESET}`,
176
+ renderFieldTop(field.name, boxWidth, borderColor, focused, theme),
177
+ ...rows.map((row) => renderFieldRow(row, contentWidth, borderColor)),
178
+ renderFieldBottom(boxWidth, borderColor),
179
+ ...renderFieldMeta(field, theme, width),
222
180
  ];
223
181
  }
224
182
 
225
- // ---------------------------------------------------------------------------
226
- // Field card (orchestrator node-card identity: centred title in top border)
227
- // ---------------------------------------------------------------------------
228
-
229
- function renderFieldCard(
183
+ function renderAskStyleFieldBody(
230
184
  field: WorkflowInputEntry,
231
185
  raw: string,
186
+ caret: number,
232
187
  focused: boolean,
233
- invalid: string | null,
234
188
  theme: GraphTheme,
235
189
  width: number,
236
- caret?: number,
237
190
  ): string[] {
238
- const borderHex = invalid
239
- ? theme.error
240
- : focused
241
- ? theme.accent
242
- : theme.borderDim;
243
- const titleHex = borderHex;
244
- const bc = hexToAnsi(borderHex);
245
- const inner = Math.max(20, width - 2); // 1 col border on each side
246
- const usable = inner - 2; // 1 col content padding on each side
247
-
248
- // Centred title: โ•ญโ”€โ”€โ”€โ”€โ”€ prompt โ”€โ”€โ”€โ”€โ”€โ•ฎ. Title text is bold, in border color.
249
- const titleRaw = ` ${field.name} `;
250
- const titleStart = Math.max(1, Math.floor((inner - titleRaw.length) / 2));
251
- const leadDashes = "โ”€".repeat(titleStart);
252
- const tailDashes = "โ”€".repeat(Math.max(0, inner - titleStart - titleRaw.length));
253
- const top =
254
- `${bc}โ•ญ${leadDashes}` +
255
- `${BOLD}${hexToAnsi(titleHex)}${titleRaw}${RESET}${bc}` +
256
- `${tailDashes}โ•ฎ${RESET}`;
257
- const bottom = `${bc}โ•ฐ${"โ”€".repeat(inner)}โ•ฏ${RESET}`;
258
-
259
- const contentLines = renderFieldContent(field, raw, focused, usable, theme, caret).map(
260
- (row) => `${bc}โ”‚${RESET} ${row}${" ".repeat(Math.max(0, usable - visibleWidth(row)))} ${bc}โ”‚${RESET}`,
261
- );
191
+ if (field.type === "select" && field.choices && field.choices.length > 0) {
192
+ const selected = Math.max(0, field.choices.indexOf(raw));
193
+ return field.choices.flatMap((choice, i) =>
194
+ renderAskChoiceRows(i + 1, focused || i !== selected ? choice : `โœ“ ${choice}`, focused && i === selected, theme, width),
195
+ );
196
+ }
197
+
198
+ if (field.type === "boolean") {
199
+ const normalized = raw.trim().toLowerCase();
200
+ const hasValue = normalized.length > 0;
201
+ const on = normalized === "true" || normalized === "1";
202
+ return [
203
+ ...renderAskChoiceRows(1, focused || !hasValue || !on ? "on" : "โœ“ on", focused && hasValue && on, theme, width),
204
+ ...renderAskChoiceRows(2, focused || !hasValue || on ? "off" : "โœ“ off", focused && hasValue && !on, theme, width),
205
+ ];
206
+ }
262
207
 
263
- // Caption: type ยท required|optional ยท description
264
- const caption = renderCaption(field, invalid, theme);
208
+ return renderAskInputRows(raw, caret, focused, field.placeholder, theme, width);
209
+ }
265
210
 
266
- return [top, ...contentLines, bottom, caption];
211
+ function renderInlineSubmitControls(state: InlineFormState, theme: GraphTheme, width: number): string[] {
212
+ const invalid = computeInvalid(state.fields, state.rawText);
213
+ return renderSubmitControls({
214
+ invalidFieldNames: invalid.map((i) => state.fields[i]!.name),
215
+ submitFocused: state.focusedIdx === state.fields.length,
216
+ theme,
217
+ width,
218
+ });
267
219
  }
268
220
 
269
- function renderCaption(
270
- field: WorkflowInputEntry,
271
- invalid: string | null,
221
+ function renderFieldTop(
222
+ title: string,
223
+ width: number,
224
+ borderColor: string,
225
+ focused: boolean,
272
226
  theme: GraphTheme,
273
227
  ): string {
274
- const sep = paint(" ยท ", theme.dim);
275
- const tagColor = invalid
276
- ? theme.error
277
- : field.required
278
- ? theme.warning
279
- : theme.dim;
280
- const tagLabel = invalid ?? (field.required ? "required" : "optional");
281
- const desc = field.description
282
- ? sep + paint(field.description, theme.dim)
283
- : "";
284
- return (
285
- " " +
286
- paint(field.type, theme.dim) +
287
- sep +
288
- paint(tagLabel, tagColor) +
289
- desc
290
- );
228
+ const label = ` ${title} `;
229
+ const labelText = paint(label, focused ? theme.accent : theme.textMuted, { bold: focused });
230
+ const fill = Math.max(0, width - visibleWidth(label) - 2);
231
+ return paint("โ•ญ", borderColor) + labelText + paint("โ”€".repeat(fill) + "โ•ฎ", borderColor);
291
232
  }
292
233
 
293
- function renderFieldContent(
294
- field: WorkflowInputEntry,
234
+ function renderFieldRow(row: string, contentWidth: number, borderColor: string): string {
235
+ const clipped = truncateToWidth(row, contentWidth, "", true);
236
+ const padded = clipped + " ".repeat(Math.max(0, contentWidth - visibleWidth(clipped)));
237
+ return paint("โ”‚", borderColor) + padded + paint("โ”‚", borderColor);
238
+ }
239
+
240
+ function renderFieldBottom(width: number, borderColor: string): string {
241
+ return paint("โ•ฐ" + "โ”€".repeat(Math.max(0, width - 2)) + "โ•ฏ", borderColor);
242
+ }
243
+
244
+ function renderFieldMeta(field: WorkflowInputEntry, theme: GraphTheme, width: number): string[] {
245
+ const required = field.required ? "required" : "optional";
246
+ const text = field.description && field.description.length > 0
247
+ ? `${field.type} ยท ${required} ยท ${field.description}`
248
+ : `${field.type} ยท ${required}`;
249
+ return wrapPlainText(text, width).map((line) => paintRequiredMetaLine(line, field.required === true, theme));
250
+ }
251
+
252
+ function paintRequiredMetaLine(line: string, required: boolean, theme: GraphTheme): string {
253
+ if (!required) return paint(line, theme.textMuted);
254
+ return line
255
+ .split(/(\brequired\b)/g)
256
+ .map((part) => part === "required" ? paint(part, theme.warning) : paint(part, theme.textMuted))
257
+ .join("");
258
+ }
259
+
260
+ function renderAskInputRows(
295
261
  raw: string,
262
+ caret: number,
296
263
  focused: boolean,
297
- usable: number,
264
+ placeholder: string | undefined,
298
265
  theme: GraphTheme,
299
- caret?: number,
266
+ width: number,
300
267
  ): string[] {
301
- if (field.type === "select" && field.choices && field.choices.length > 0) {
302
- const cells = field.choices.map((c) => {
303
- const sel = c === raw;
304
- const dot = sel
305
- ? paint("โ—", focused ? theme.accent : theme.success)
306
- : paint("โ—‹", theme.dim);
307
- const lbl = sel
308
- ? paint(c, focused ? theme.text : theme.textMuted)
309
- : paint(c, theme.dim);
310
- return dot + " " + lbl;
311
- });
312
- return [clip(cells.join(" "), usable)];
313
- }
314
- if (field.type === "boolean") {
315
- const on = raw === "true";
316
- const onCell =
317
- paint(on ? "โ—" : "โ—‹", on ? theme.accent : theme.dim) +
318
- " " +
319
- paint("on", on ? theme.text : theme.dim);
320
- const offCell =
321
- paint(!on ? "โ—" : "โ—‹", !on ? theme.accent : theme.dim) +
322
- " " +
323
- paint("off", !on ? theme.text : theme.dim);
324
- return [clip(onCell + " " + offCell, usable)];
325
- }
326
- // string / number / integer โ€” single-line scalar input.
327
- if (field.type !== "text") {
328
- if (raw === "") {
329
- if (focused) return [paint("โ–‹", theme.accent)];
330
- return [paint(field.placeholder ?? "", theme.dim)];
331
- }
332
- if (focused) {
333
- return [renderCaretLine(raw, caret ?? raw.length, usable, theme, theme.text)];
334
- }
335
- return [clip(paint(raw, theme.textMuted), usable)];
336
- }
337
- // text โ€” multi-line prompt-box input. Newlines render as actual visual
338
- // line breaks (no more `โŽ` glyph) and long single lines wrap at the
339
- // field's usable width. The box height grows to fit every visual row
340
- // so the user sees their whole prompt; the surrounding card already
341
- // lives in chat scrollback so vertical space is not at a premium.
268
+ const usable = Math.max(1, width);
342
269
  if (raw === "") {
343
- if (focused) return [paint("โ–‹", theme.accent)];
344
- return [paint(field.placeholder ?? "", theme.dim)];
270
+ const value = placeholder && placeholder.length > 0
271
+ ? paint(placeholder, theme.dim) + (focused ? cursorBlock() : "")
272
+ : focused ? cursorBlock() : "";
273
+ return [truncateToWidth(value, usable, "โ€ฆ", true)];
345
274
  }
346
- const layout = layoutTextField(raw, usable, focused ? caret ?? raw.length : 0);
347
- if (!focused) {
348
- return layout.lines.map((line) => paint(line, theme.textMuted));
349
- }
350
- return layout.lines.map((line, row) => {
351
- if (row !== layout.cursorRow) {
352
- return paint(line, theme.text);
353
- }
354
- return renderCaretLine(line, layout.cursorOffset ?? line.length, usable, theme, theme.text);
355
- });
275
+
276
+ const layout = layoutTextField(raw, usable, caret);
277
+ return layout.lines.map((line, row) => focused && row === layout.cursorRow
278
+ ? renderCaretLine(line, layout.cursorOffset ?? line.length, usable, theme, theme.text)
279
+ : truncateToWidth(paint(line, theme.text), usable, "โ€ฆ", true));
356
280
  }
357
281
 
358
282
  function renderCaretLine(
@@ -365,14 +289,17 @@ function renderCaretLine(
365
289
  const safe = clampGraphemeOffset(raw, caret);
366
290
  const beforeFull = raw.slice(0, safe);
367
291
  const afterFull = raw.slice(safe);
368
- const cursorWidth = 1;
292
+ const [at = ""] = graphemes(afterFull);
293
+ const afterRest = at === "" ? "" : afterFull.slice(at.length);
294
+ const cursorPlain = at !== "" ? at : " ";
295
+ const cursorWidth = Math.max(1, visibleWidth(cursorPlain));
369
296
  let before = beforeFull;
370
- let after = afterFull;
371
- if (visibleWidth(beforeFull) + cursorWidth + visibleWidth(afterFull) > usable) {
297
+ let after = afterRest;
298
+ if (visibleWidth(beforeFull) + cursorWidth + visibleWidth(afterRest) > usable) {
372
299
  before = tailToWidth(beforeFull, Math.max(0, usable - cursorWidth));
373
- after = headToWidth(afterFull, Math.max(0, usable - visibleWidth(before) - cursorWidth));
300
+ after = headToWidth(afterRest, Math.max(0, usable - visibleWidth(before) - cursorWidth));
374
301
  }
375
- return clip(paint(before, color) + paint("โ–‹", theme.accent) + paint(after, color), usable);
302
+ return clip(paint(before, color) + cursorBlock(cursorPlain) + paint(after, color), usable);
376
303
  }
377
304
 
378
305
  // ---------------------------------------------------------------------------
@@ -387,14 +314,6 @@ function renderSubmittedLine(state: InlineFormState, theme: GraphTheme): string
387
314
  );
388
315
  }
389
316
 
390
- function renderCancelledLine(state: InlineFormState, theme: GraphTheme): string {
391
- return (
392
- paint("โœ— cancelled", theme.dim) +
393
- paint(" ยท ", theme.dim) +
394
- paint(state.workflowName, theme.textMuted)
395
- );
396
- }
397
-
398
317
  function composeCommand(state: InlineFormState): string {
399
318
  const parts: string[] = [`/workflow ${state.workflowName}`];
400
319
  for (const f of state.fields) {
@@ -418,6 +337,10 @@ function clip(ansi: string, budget: number): string {
418
337
  return truncateToWidth(ansi, Math.max(0, budget), "โ€ฆ", true);
419
338
  }
420
339
 
340
+ function cursorBlock(text = " "): string {
341
+ return `\x1b[7m${text}\x1b[0m`;
342
+ }
343
+
421
344
  /**
422
345
  * Lay out a multi-line text field into visual rows while tracking where the
423
346
  * caret should appear on screen. Newlines (`\n`) always start a new visual