@iowarp/clio-coder 0.4.6 → 0.4.8

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 (1142) hide show
  1. package/.claude-plugin/marketplace.json +283 -0
  2. package/CHANGELOG.md +79 -0
  3. package/CONTRIBUTING.md +178 -96
  4. package/NOTICE +8 -0
  5. package/README.md +100 -47
  6. package/dist/acp-JWPQZM2D.js +130 -0
  7. package/dist/agents-4GCDIRVX.js +149 -0
  8. package/dist/assets/codewiki.json +1 -1
  9. package/dist/assets/tui-notices/get-east-asian-width-LICENSE +9 -0
  10. package/dist/assets/tui-notices/marked-LICENSE +44 -0
  11. package/dist/assets/tui-notices/pi-tui-LICENSE +21 -0
  12. package/dist/assets/web-notices/@hono__node-server-LICENSE +21 -0
  13. package/dist/assets/web-notices/hono-LICENSE +21 -0
  14. package/dist/auth-SYAKTDQE.js +551 -0
  15. package/dist/background-OW67WMYR.js +37 -0
  16. package/dist/builtins-TKS63VNV.js +22 -0
  17. package/dist/chunk-2JSCCEKO.js +85 -0
  18. package/dist/chunk-2SXGG2IV.js +391 -0
  19. package/dist/chunk-2U2U5POA.js +59 -0
  20. package/dist/chunk-2ZWLMA3T.js +658 -0
  21. package/dist/chunk-36XSBPXL.js +579 -0
  22. package/dist/chunk-3IQOZRQ6.js +396 -0
  23. package/dist/chunk-3XE4NRLF.js +324 -0
  24. package/dist/chunk-44U2OUSM.js +243 -0
  25. package/dist/chunk-4P6M2TTE.js +119 -0
  26. package/dist/chunk-526ABLHD.js +1219 -0
  27. package/dist/chunk-5C2OQHLR.js +674 -0
  28. package/dist/chunk-5D2FNBMG.js +185 -0
  29. package/dist/chunk-5EQO5NWP.js +243 -0
  30. package/dist/chunk-5HULHKT2.js +32 -0
  31. package/dist/chunk-5UG6EZI3.js +66 -0
  32. package/dist/chunk-5W4FRA5Q.js +22283 -0
  33. package/dist/chunk-6R5MBXBJ.js +386 -0
  34. package/dist/chunk-6TKOFFGE.js +1058 -0
  35. package/dist/chunk-6UJ7RDI7.js +1143 -0
  36. package/dist/chunk-6X7VOCDY.js +44 -0
  37. package/dist/chunk-7QQRPB2E.js +915 -0
  38. package/dist/chunk-ARRUMZ7X.js +1377 -0
  39. package/dist/chunk-ATB3Q7YO.js +663 -0
  40. package/dist/chunk-AUHVBIDW.js +19 -0
  41. package/dist/chunk-BE4I3UJ2.js +668 -0
  42. package/dist/chunk-BFPHMSK7.js +169 -0
  43. package/dist/chunk-BHJMMCXS.js +466 -0
  44. package/dist/chunk-BIY22UUK.js +1322 -0
  45. package/dist/chunk-D55WO5J7.js +110 -0
  46. package/dist/chunk-DVJL77SO.js +162 -0
  47. package/dist/chunk-E2NSPYLC.js +814 -0
  48. package/dist/chunk-EFXMQYI6.js +4484 -0
  49. package/dist/chunk-EPYATQMG.js +2718 -0
  50. package/dist/chunk-EXKUF426.js +1801 -0
  51. package/dist/chunk-F7Y6QTPL.js +689 -0
  52. package/dist/chunk-FB3KTE2J.js +1710 -0
  53. package/dist/chunk-FNVPQQYT.js +117 -0
  54. package/dist/chunk-GEVQNST4.js +101 -0
  55. package/dist/chunk-GEYX2NZ5.js +697 -0
  56. package/dist/chunk-GJBJHJNY.js +2120 -0
  57. package/dist/chunk-GM4TY6VN.js +1233 -0
  58. package/dist/chunk-GVU4CXPJ.js +5191 -0
  59. package/dist/chunk-H5DPSUBB.js +637 -0
  60. package/dist/chunk-H77XBUBM.js +688 -0
  61. package/dist/chunk-HMGAKB7E.js +380 -0
  62. package/dist/chunk-HP7KJ5O2.js +797 -0
  63. package/dist/chunk-HVCAY2FH.js +67 -0
  64. package/dist/chunk-ICYIUNO4.js +35 -0
  65. package/dist/chunk-IILXVWJY.js +223 -0
  66. package/dist/chunk-JCVIL6TY.js +595 -0
  67. package/dist/chunk-JIMWEYWO.js +63 -0
  68. package/dist/chunk-JLBVCROW.js +562 -0
  69. package/dist/chunk-JOBN2YQQ.js +176 -0
  70. package/dist/chunk-JOL2QRLE.js +152 -0
  71. package/dist/chunk-JRA6OBHY.js +974 -0
  72. package/dist/chunk-JV25I7ID.js +1865 -0
  73. package/dist/chunk-JYKUMHCQ.js +705 -0
  74. package/dist/chunk-K3W6BF5C.js +283 -0
  75. package/dist/chunk-K45LHSCI.js +194 -0
  76. package/dist/chunk-K53E2PTW.js +1289 -0
  77. package/dist/chunk-K5UJD6MM.js +39 -0
  78. package/dist/chunk-KGP3UQGJ.js +200 -0
  79. package/dist/chunk-KKRXONXG.js +43 -0
  80. package/dist/chunk-KWJXCL2X.js +103 -0
  81. package/dist/chunk-L75H5B3B.js +446 -0
  82. package/dist/chunk-LLCGGVSW.js +108 -0
  83. package/dist/chunk-LZCCBLRC.js +57 -0
  84. package/dist/chunk-LZDLYSUN.js +197 -0
  85. package/dist/chunk-MATCRH3M.js +667 -0
  86. package/dist/chunk-MEHBW2SP.js +132 -0
  87. package/dist/chunk-MUXNDNMZ.js +659 -0
  88. package/dist/chunk-MWMW53WW.js +7346 -0
  89. package/dist/chunk-MZDPF35R.js +45 -0
  90. package/dist/chunk-MZKYVIWW.js +188 -0
  91. package/dist/chunk-O2QJDRWP.js +362 -0
  92. package/dist/chunk-O3HSB2YL.js +693 -0
  93. package/dist/chunk-OEMFEL26.js +891 -0
  94. package/dist/chunk-OH3YYA3B.js +15 -0
  95. package/dist/chunk-OPC4DQD6.js +634 -0
  96. package/dist/chunk-ORRNGQDV.js +207 -0
  97. package/dist/chunk-P4JS3MH5.js +165 -0
  98. package/dist/chunk-P7GTE4FX.js +308 -0
  99. package/dist/chunk-PAQVETLA.js +962 -0
  100. package/dist/chunk-PFZJMXIY.js +146 -0
  101. package/dist/chunk-PGEFOTJB.js +1974 -0
  102. package/dist/chunk-PJ5H5B7A.js +3151 -0
  103. package/dist/chunk-PMUUAABD.js +604 -0
  104. package/dist/chunk-Q32C6AWI.js +236 -0
  105. package/dist/chunk-QGIC5SX5.js +139 -0
  106. package/dist/chunk-QLAGXTTV.js +482 -0
  107. package/dist/chunk-QLKCCC2J.js +573 -0
  108. package/dist/chunk-QWUSS2QP.js +3630 -0
  109. package/dist/chunk-QYSAZMSH.js +47 -0
  110. package/dist/chunk-R5NRX5N4.js +123 -0
  111. package/dist/chunk-RAUOKUZV.js +1572 -0
  112. package/dist/chunk-RBES4TCZ.js +302 -0
  113. package/dist/chunk-RFCKPS2V.js +26 -0
  114. package/dist/chunk-RFULVPBX.js +318 -0
  115. package/dist/chunk-RKFNU5UA.js +404 -0
  116. package/dist/chunk-RM3XJX7P.js +1148 -0
  117. package/dist/chunk-RNLAPBJL.js +386 -0
  118. package/dist/chunk-RPMNIFRY.js +65 -0
  119. package/dist/chunk-RRZFRIJP.js +42 -0
  120. package/dist/chunk-RXI7K4J5.js +4493 -0
  121. package/dist/chunk-SER7RRAE.js +4271 -0
  122. package/dist/chunk-STC6CWUZ.js +402 -0
  123. package/dist/chunk-TEHVMHOK.js +97 -0
  124. package/dist/chunk-TOPICHGR.js +33 -0
  125. package/dist/chunk-U3TPUXOQ.js +2653 -0
  126. package/dist/chunk-UB33F3YW.js +709 -0
  127. package/dist/chunk-UDAK6GR2.js +175 -0
  128. package/dist/chunk-USBBEKHW.js +177 -0
  129. package/dist/chunk-V4ZKOKRB.js +13433 -0
  130. package/dist/chunk-W5RM7YAH.js +78 -0
  131. package/dist/chunk-W7KNI3F5.js +215 -0
  132. package/dist/chunk-WFTSK7N3.js +637 -0
  133. package/dist/chunk-WJWNZXRM.js +395 -0
  134. package/dist/chunk-WLHX3MM5.js +63 -0
  135. package/dist/chunk-WS4FOGLY.js +686 -0
  136. package/dist/chunk-WTZVSMQZ.js +52 -0
  137. package/dist/chunk-WWRV5RJI.js +165 -0
  138. package/dist/chunk-X6FLVUM5.js +141 -0
  139. package/dist/chunk-XC5POIL4.js +68 -0
  140. package/dist/chunk-XTLJDC2X.js +1013 -0
  141. package/dist/chunk-XW2UZICE.js +372 -0
  142. package/dist/chunk-Y3JU5RPK.js +8 -0
  143. package/dist/chunk-YGIV24MF.js +1018 -0
  144. package/dist/chunk-YJCCMYYT.js +125 -0
  145. package/dist/chunk-YKIIWURD.js +36 -0
  146. package/dist/chunk-YQ3GFEUY.js +341 -0
  147. package/dist/chunk-YXANQGCX.js +179 -0
  148. package/dist/chunk-YXFKLMX2.js +222 -0
  149. package/dist/chunk-ZST3FRCV.js +132 -0
  150. package/dist/cli/index.js +40 -40
  151. package/dist/clio-BPBAPZGA.js +26 -0
  152. package/dist/code-nav-KBQ3RIFK.js +686 -0
  153. package/dist/components-6DKAXWFN.js +674 -0
  154. package/dist/config-FWL5JK2H.js +270 -0
  155. package/dist/config-graph-ACDB26L2.js +156 -0
  156. package/dist/configure-ZIZ4YV62.js +85 -0
  157. package/dist/context-EZZPT2ZV.js +1074 -0
  158. package/dist/context-VIL5MFRH.js +282 -0
  159. package/dist/context-Z3GOBHDK.js +344 -0
  160. package/dist/context-clear-ZPB3L7MN.js +188 -0
  161. package/dist/context-index-W6A4V7OC.js +120 -0
  162. package/dist/context-map-DKL3QT6D.js +555 -0
  163. package/dist/context-working-set-2GLBVF7L.js +1559 -0
  164. package/dist/detail-VSBWYSGZ.js +119 -0
  165. package/dist/dispatch-runner-EEEHGKX4.js +2442 -0
  166. package/dist/docs-VMMWFVHL.js +101 -0
  167. package/dist/doctor-V5L4IQGP.js +812 -0
  168. package/dist/eval-VJYJ2BV3.js +5050 -0
  169. package/dist/eval-inventory-CX24K2JT.js +260 -0
  170. package/dist/evals-J2H5V6DD.js +104 -0
  171. package/dist/evidence-POWAEBEW.js +229 -0
  172. package/dist/evidence-XCCCLS36.js +242 -0
  173. package/dist/evidence-Y35YSXH6.js +388 -0
  174. package/dist/evolve-GPG72VB6.js +613 -0
  175. package/dist/extensions-YIZ7W3DA.js +308 -0
  176. package/dist/fleet-2MDCQ2VK.js +264 -0
  177. package/dist/fleet-W2AF3TOJ.js +919 -0
  178. package/dist/fleet-commands-WS56G26H.js +75 -0
  179. package/dist/fleet-decisions-MYGZ3JW7.js +52 -0
  180. package/dist/fleet-graph-ELTBXBAV.js +136 -0
  181. package/dist/fleet-inspect-45IK73L4.js +308 -0
  182. package/dist/fleet-preflight-T5NCZKHE.js +22 -0
  183. package/dist/fleet-validate-VG4JBLA4.js +91 -0
  184. package/dist/fleet-verify-VTSABAYO.js +206 -0
  185. package/dist/fleet-view-XC7WMKSS.js +123 -0
  186. package/dist/init-VTJNNTIP.js +329 -0
  187. package/dist/install-6PIF6F6W.js +19 -0
  188. package/dist/interop-IU6BNA7C.js +246 -0
  189. package/dist/inventory-QPQUTUUZ.js +123 -0
  190. package/dist/library-HUTAS57T.js +492 -0
  191. package/dist/library-LF7DLU6S.js +306 -0
  192. package/dist/library-Q3NWL5CM.js +75 -0
  193. package/dist/library-import-PPW4YUC5.js +183 -0
  194. package/dist/library-inventory-DIRVFRZU.js +56 -0
  195. package/dist/library-validation-XHUZ3INE.js +42 -0
  196. package/dist/memory-Z2LSKNV6.js +516 -0
  197. package/dist/models-3FTREYPY.js +302 -0
  198. package/dist/monitor-HF2MQRB3.js +781 -0
  199. package/dist/orchestrator-UYPA6W4S.js +41502 -0
  200. package/dist/panes-736XB73K.js +74 -0
  201. package/dist/panes-C4P3PIWT.js +103 -0
  202. package/dist/paths-JQ4QYIYB.js +67 -0
  203. package/dist/preload-XSLRYH3Y.js +120 -0
  204. package/dist/reset-V7QSPZYE.js +345 -0
  205. package/dist/resources-OJGDFHYT.js +181 -0
  206. package/dist/run-T354SMDW.js +892 -0
  207. package/dist/settings-MWZXO6RV.js +72 -0
  208. package/dist/share-Y5ZKJPND.js +293 -0
  209. package/dist/skills-4GLZC4LN.js +100 -0
  210. package/dist/skills-eval-L5T7GODM.js +1074 -0
  211. package/dist/skills-inventory-LASKXEZI.js +138 -0
  212. package/dist/slash-commands-OIMRU36D.js +95 -0
  213. package/dist/steer-KMAM2Y2C.js +77 -0
  214. package/dist/support-AWC3XKPX.js +38 -0
  215. package/dist/system-CD75NYBU.js +116 -0
  216. package/dist/targets-VSZX2J2S.js +1116 -0
  217. package/dist/tasks-AN567LYH.js +36 -0
  218. package/dist/terminal-lease-6KTWNHVY.js +418 -0
  219. package/dist/tools-AGRK3V5J.js +28 -0
  220. package/dist/trace-P4MQZD33.js +524 -0
  221. package/dist/tui-primitives-A6MUW32V.js +18 -0
  222. package/dist/uninstall-BRUTEPIC.js +432 -0
  223. package/dist/upgrade-SBCGHQGJ.js +307 -0
  224. package/dist/usage-BIDVTO6V.js +1061 -0
  225. package/dist/verifiers-LTGCAMXS.js +630 -0
  226. package/dist/verify-OZU4LLNP.js +30 -0
  227. package/dist/web/client/THIRD_PARTY_LICENSES.md +1809 -0
  228. package/dist/web/client/assets/abnfDiagram-VCTEODGH-C5wfoHm3.js +1 -0
  229. package/dist/web/client/assets/arc-oH91sfLl.js +1 -0
  230. package/dist/web/client/assets/architecture-7GRP2DOG-EGozsYEO.js +1 -0
  231. package/dist/web/client/assets/architectureDiagram-5GKGNRK7-z9FokbJl.js +36 -0
  232. package/dist/web/client/assets/array-BifhSqXX.js +1 -0
  233. package/dist/web/client/assets/atkinson-hyperlegible-next-latin-ext-wght-normal-C6vrW8VD.woff2 +0 -0
  234. package/dist/web/client/assets/atkinson-hyperlegible-next-latin-wght-normal-BcXVPD7q.woff2 +0 -0
  235. package/dist/web/client/assets/blockDiagram-I7D4REHJ-wi0_CYHL.js +129 -0
  236. package/dist/web/client/assets/c4Diagram-7LVT6UL2-DV7NYc2W.js +38 -0
  237. package/dist/web/client/assets/channel-CiUWbHN9.js +1 -0
  238. package/dist/web/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  239. package/dist/web/client/assets/chunk-4HAMMTFA-Bhsegh3F.js +62 -0
  240. package/dist/web/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  241. package/dist/web/client/assets/chunk-75Z2AOVW-RQCckcQG.js +2 -0
  242. package/dist/web/client/assets/chunk-DU6HZSFF-C5USiOAy.js +125 -0
  243. package/dist/web/client/assets/chunk-F27PBJKO-BcNqv2x1.js +1 -0
  244. package/dist/web/client/assets/chunk-FOHPRMQF-CoA4358r.js +161 -0
  245. package/dist/web/client/assets/chunk-GMAD6QVW-Bq8JWiZT.js +72 -0
  246. package/dist/web/client/assets/chunk-GVQU2GXP-DbchrAeJ.js +1 -0
  247. package/dist/web/client/assets/chunk-IMKFNOWR-DrpvjocK.js +231 -0
  248. package/dist/web/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  249. package/dist/web/client/assets/chunk-L3NEJ4N5-DHLmTnCe.js +1 -0
  250. package/dist/web/client/assets/chunk-OSK3NFVY-CsRbtN9n.js +10 -0
  251. package/dist/web/client/assets/chunk-P2QGCYS3-aqPbxoMh.js +1 -0
  252. package/dist/web/client/assets/chunk-POPQ4Y6H-Dl5vw0h0.js +1 -0
  253. package/dist/web/client/assets/chunk-PWAF6VOD-D7OBRSdO.js +1 -0
  254. package/dist/web/client/assets/chunk-SHT3W25Y-CdKCIwuB.js +168 -0
  255. package/dist/web/client/assets/chunk-SVP7TREG-B94Nu_1-.js +88 -0
  256. package/dist/web/client/assets/chunk-TICWLB2K-jjRQI_hk.js +206 -0
  257. package/dist/web/client/assets/chunk-XXDRQBXY-azkbKE37.js +1 -0
  258. package/dist/web/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  259. package/dist/web/client/assets/classDiagram-ZZMXUADV-CMODalmo.js +1 -0
  260. package/dist/web/client/assets/classDiagram-v2-VYDZK3BY-CMODalmo.js +1 -0
  261. package/dist/web/client/assets/commit-mono-latin-400-normal-s0S3qwFW.woff +0 -0
  262. package/dist/web/client/assets/commit-mono-latin-400-normal-wzhe4RuD.woff2 +0 -0
  263. package/dist/web/client/assets/cose-bilkent-JH36ORCC-RbQL23qS.js +1 -0
  264. package/dist/web/client/assets/cynefin-OW5HDTMX--i9RDU5f.js +1 -0
  265. package/dist/web/client/assets/cynefinDiagram-5FMLGOSQ-cRcvyBdp.js +62 -0
  266. package/dist/web/client/assets/cytoscape.esm-c2aL46s-.js +321 -0
  267. package/dist/web/client/assets/dagre-C-rnJpZF.js +1 -0
  268. package/dist/web/client/assets/dagre-GXQ25YYZ-C9mZmAPe.js +4 -0
  269. package/dist/web/client/assets/defaultLocale-BFoDCU3G.js +1 -0
  270. package/dist/web/client/assets/diagram-S7CK7UJ4-Oj_zlLDY.js +30 -0
  271. package/dist/web/client/assets/diagram-UQ7AKVKN-BWKbbfcR.js +41 -0
  272. package/dist/web/client/assets/diagram-VSXAHHWV-C3N3f7DO.js +3 -0
  273. package/dist/web/client/assets/diagram-VX7I27RA-CtDKDAWe.js +24 -0
  274. package/dist/web/client/assets/diagram-Z3DM3KII-B8bkBRCz.js +24 -0
  275. package/dist/web/client/assets/dist-D0_q-A2b.js +1 -0
  276. package/dist/web/client/assets/ebnfDiagram-PWID7BFC-CskW-Whx.js +1 -0
  277. package/dist/web/client/assets/erDiagram-RLTQ6QDP-C-83cgCW.js +99 -0
  278. package/dist/web/client/assets/eventmodeling-NTZA5JFV-DjVq8wKU.js +1 -0
  279. package/dist/web/client/assets/flowDiagram-HODETNUW-BUiCBGXt.js +1 -0
  280. package/dist/web/client/assets/ganttDiagram-EL5Y4UJY-B2lb8lvF.js +292 -0
  281. package/dist/web/client/assets/gitGraph-4MIJSDKK-B40cQtBE.js +1 -0
  282. package/dist/web/client/assets/gitGraphDiagram-WWUBYQGX-CH-7P7Di.js +106 -0
  283. package/dist/web/client/assets/graphlib-DS17s2tU.js +1 -0
  284. package/dist/web/client/assets/index-Dsi52apB.css +1 -0
  285. package/dist/web/client/assets/index-FSIC94pi.js +83 -0
  286. package/dist/web/client/assets/info-A6RAGUB7-eCCE9D_Q.js +1 -0
  287. package/dist/web/client/assets/infoDiagram-27XIBGKW-DRnvqMjM.js +2 -0
  288. package/dist/web/client/assets/init-C-OQMol4.js +1 -0
  289. package/dist/web/client/assets/ishikawaDiagram-5VMMS53U-DBA_55wp.js +70 -0
  290. package/dist/web/client/assets/journeyDiagram-3NMN7TZE-C-ewpNl7.js +139 -0
  291. package/dist/web/client/assets/kanban-definition-UXKFOSKX-B1taGsu0.js +89 -0
  292. package/dist/web/client/assets/katex-ZlcWpGUi.js +257 -0
  293. package/dist/web/client/assets/line-lZ0kyxDI.js +1 -0
  294. package/dist/web/client/assets/linear-DIc8FgZj.js +1 -0
  295. package/dist/web/client/assets/mermaid-parser.core-SXolxpXw.js +7 -0
  296. package/dist/web/client/assets/mermaid.core-DXgQtkNg.js +44 -0
  297. package/dist/web/client/assets/mindmap-definition-YA3MSWOX-DXJas9K7.js +96 -0
  298. package/dist/web/client/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
  299. package/dist/web/client/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
  300. package/dist/web/client/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
  301. package/dist/web/client/assets/ordinal-BDEzSJ7C.js +1 -0
  302. package/dist/web/client/assets/packet-AYTQ26CC-OqVl_07o.js +1 -0
  303. package/dist/web/client/assets/path-fybaL0A-.js +1 -0
  304. package/dist/web/client/assets/pegDiagram-XKGWAZYB-CPE_zdfN.js +1 -0
  305. package/dist/web/client/assets/pie-WAS4IAKB-DAxnMBLe.js +1 -0
  306. package/dist/web/client/assets/pieDiagram-E7YTZNPT-Uwjpufxv.js +39 -0
  307. package/dist/web/client/assets/prism-bash-D6zCJ74D.js +1 -0
  308. package/dist/web/client/assets/prism-c-04YixN25.js +1 -0
  309. package/dist/web/client/assets/prism-clike-DapgWyxx.js +1 -0
  310. package/dist/web/client/assets/prism-core-BngLtbUd.js +1 -0
  311. package/dist/web/client/assets/prism-cpp-C-lJbC-6.js +1 -0
  312. package/dist/web/client/assets/prism-css-CPgOzxXp.js +1 -0
  313. package/dist/web/client/assets/prism-diff-BF8m0_mq.js +3 -0
  314. package/dist/web/client/assets/prism-docker-DkT6yOFj.js +1 -0
  315. package/dist/web/client/assets/prism-fortran-Be5x3y1V.js +1 -0
  316. package/dist/web/client/assets/prism-go-C_8qAH5n.js +1 -0
  317. package/dist/web/client/assets/prism-ini-BESK3y0r.js +1 -0
  318. package/dist/web/client/assets/prism-javascript-BnO-swvr.js +1 -0
  319. package/dist/web/client/assets/prism-json-Dp_-W-Hv.js +1 -0
  320. package/dist/web/client/assets/prism-jsx-D-D0NWtC.js +1 -0
  321. package/dist/web/client/assets/prism-julia-DIjOgiAV.js +1 -0
  322. package/dist/web/client/assets/prism-latex-DNMbqcH_.js +1 -0
  323. package/dist/web/client/assets/prism-makefile-CRKWLQ8l.js +1 -0
  324. package/dist/web/client/assets/prism-markdown-C0FPJ1Iz.js +1 -0
  325. package/dist/web/client/assets/prism-markup-Cp04rIa1.js +1 -0
  326. package/dist/web/client/assets/prism-matlab-D-nk08al.js +1 -0
  327. package/dist/web/client/assets/prism-python-CfiSVtM6.js +1 -0
  328. package/dist/web/client/assets/prism-r-QmNGR8LX.js +1 -0
  329. package/dist/web/client/assets/prism-rust-Bu9N8X3N.js +1 -0
  330. package/dist/web/client/assets/prism-shell-session-Be4UuYoM.js +1 -0
  331. package/dist/web/client/assets/prism-sql-CYhVDMEE.js +1 -0
  332. package/dist/web/client/assets/prism-toml-DKb54GAT.js +1 -0
  333. package/dist/web/client/assets/prism-tsx-p6J2Kc_4.js +1 -0
  334. package/dist/web/client/assets/prism-typescript-BMqONzxf.js +1 -0
  335. package/dist/web/client/assets/prism-yaml-C0JHu8gr.js +1 -0
  336. package/dist/web/client/assets/purify.es-ChwZkWde.js +3 -0
  337. package/dist/web/client/assets/quadrantDiagram-AXDQQJYC-Bjs4yjgs.js +7 -0
  338. package/dist/web/client/assets/radar-RG4KPBEZ-CuCKjHM3.js +1 -0
  339. package/dist/web/client/assets/railroad-74A4TZTK-CnQK-Cc1.js +1 -0
  340. package/dist/web/client/assets/railroad-abnf-HS5TGJTU-BcW01NZG.js +1 -0
  341. package/dist/web/client/assets/railroad-ebnf-LZEXJU2U-B7YCEDDd.js +1 -0
  342. package/dist/web/client/assets/railroad-peg-WCYAUIDC-DjNZbSTZ.js +1 -0
  343. package/dist/web/client/assets/railroadDiagram-O6MQD6OU-BDDIPNLL.js +1 -0
  344. package/dist/web/client/assets/requirementDiagram-BXWQKSXE-B5Ydyxc1.js +84 -0
  345. package/dist/web/client/assets/rough.esm-Dy-Kn_BL.js +1 -0
  346. package/dist/web/client/assets/sankeyDiagram-P5KCCOFB-Bw2Qym75.js +40 -0
  347. package/dist/web/client/assets/sequenceDiagram-WJ2MYXX4-BZf36nO0.js +162 -0
  348. package/dist/web/client/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
  349. package/dist/web/client/assets/src-yeptRXpt.js +1 -0
  350. package/dist/web/client/assets/stateDiagram-D77RDMKH-DnH6JRkp.js +1 -0
  351. package/dist/web/client/assets/stateDiagram-v2-MP3YSRHH-C7QtHeK4.js +1 -0
  352. package/dist/web/client/assets/swimlanes-42K2YHIH-D9PZmtZg.js +1 -0
  353. package/dist/web/client/assets/swimlanesDiagram-VR7AAH4N-CBZlPFky.js +8 -0
  354. package/dist/web/client/assets/timeline-definition-24CTP7MA-CrkZZBo7.js +120 -0
  355. package/dist/web/client/assets/treeView-Q6P3EWNA-C9zDShoC.js +1 -0
  356. package/dist/web/client/assets/treemap-WGGIJYW6-B-plj9yK.js +1 -0
  357. package/dist/web/client/assets/vennDiagram-4TSXK5OY-VeOdaphN.js +34 -0
  358. package/dist/web/client/assets/wardley-WFR3VGLG-Wl1P_m6L.js +1 -0
  359. package/dist/web/client/assets/wardleyDiagram-VM6X3IG4-B85-R9t2.js +78 -0
  360. package/dist/web/client/assets/xychartDiagram-S5SC5T6Z-BrD8OW8m.js +7 -0
  361. package/dist/web/client/clio-coder-logo.webp +0 -0
  362. package/dist/web/client/icon-192.png +0 -0
  363. package/dist/web/client/icon-512.png +0 -0
  364. package/dist/web/client/index.html +4 -0
  365. package/dist/web/client/manifest.webmanifest +15 -0
  366. package/dist/web/client/offline.css +55 -0
  367. package/dist/web/client/offline.html +5 -0
  368. package/dist/web/client/offline.js +27 -0
  369. package/dist/web/client/sw.js +34 -0
  370. package/dist/web/ops-worker.js +37 -0
  371. package/dist/web/reads-worker.js +1726 -0
  372. package/dist/web/server.js +8499 -0
  373. package/dist/web-EU6ATJWA.js +13 -0
  374. package/dist/web-fetch-U22Q7HV4.js +788 -0
  375. package/dist/wiki-generate-LZRLFG6I.js +623 -0
  376. package/dist/with-panes-X5CZOGML.js +628 -0
  377. package/dist/worker/entry.js +300 -93
  378. package/docs/README.md +58 -14
  379. package/docs/architecture/acp.md +5 -9
  380. package/docs/architecture/alcf-provider.md +0 -3
  381. package/docs/architecture/architecture.md +8 -10
  382. package/docs/architecture/artifact-placement.md +0 -3
  383. package/docs/architecture/artifact-versions.md +3 -6
  384. package/docs/architecture/capacity-and-scheduling.md +0 -3
  385. package/docs/architecture/context-engine.md +35 -4
  386. package/docs/architecture/context-working-set.md +0 -3
  387. package/docs/architecture/dispatch-architecture-rationale.md +0 -3
  388. package/docs/architecture/dispatch-typed-intent.md +0 -3
  389. package/docs/architecture/evidence-and-memory.md +0 -3
  390. package/docs/architecture/library.md +30 -0
  391. package/docs/architecture/middleware-and-components.md +1 -4
  392. package/docs/architecture/model-catalog.md +21 -4
  393. package/docs/architecture/observability.md +0 -3
  394. package/docs/architecture/pi-boundary.md +23 -8
  395. package/docs/architecture/prompt-envelope-and-tools.md +6 -9
  396. package/docs/architecture/provider-adapter-cookbook.md +1 -4
  397. package/docs/architecture/safety-model.md +21 -5
  398. package/docs/architecture/session-lifecycle.md +12 -4
  399. package/docs/architecture/time-conventions.md +0 -3
  400. package/docs/architecture/trace-store.md +28 -20
  401. package/docs/architecture/tui-design.md +108 -12
  402. package/docs/architecture/worker-context.md +104 -0
  403. package/docs/architecture/worker-dispatch-mechanics.md +1 -4
  404. package/docs/guide/authoring-plugins.md +158 -0
  405. package/docs/guide/built-in-agents.md +5 -8
  406. package/docs/guide/commands-and-modes.md +162 -61
  407. package/docs/guide/configuration-and-targets.md +198 -5
  408. package/docs/guide/configuration-reference.md +36 -38
  409. package/docs/guide/environment-variables.md +2 -5
  410. package/docs/guide/exit-codes-and-output.md +1 -4
  411. package/docs/guide/extensions-and-sharing.md +92 -55
  412. package/docs/guide/fleet-dispatch.md +31 -7
  413. package/docs/guide/glossary.md +1 -4
  414. package/docs/guide/harness-extensions.md +289 -0
  415. package/docs/guide/installation-and-lifecycle.md +10 -13
  416. package/docs/guide/interop.md +241 -0
  417. package/docs/guide/panes-and-files.md +3 -6
  418. package/docs/guide/plugins.md +77 -0
  419. package/docs/guide/proactive-memory.md +0 -3
  420. package/docs/guide/resource-library.md +109 -45
  421. package/docs/guide/skills-marketplace.md +34 -73
  422. package/docs/guide/tool-usage.md +19 -10
  423. package/docs/guide/troubleshooting.md +1 -4
  424. package/docs/history/config-knobs-audit.md +0 -3
  425. package/docs/history/release-cut-checklist.md +0 -3
  426. package/docs/process/development-pipeline.md +14 -17
  427. package/docs/process/documentation-coverage.md +31 -4
  428. package/docs/process/documentation-guide.md +28 -27
  429. package/docs/process/eval-runner.md +8 -6
  430. package/docs/process/evals-internal.md +0 -3
  431. package/docs/process/evolution.md +0 -3
  432. package/docs/process/fleet-demo-runbook.md +0 -3
  433. package/docs/process/git-commit-provenance.md +0 -3
  434. package/docs/process/performance-methodology.md +2 -5
  435. package/docs/process/release-cut-checklist.md +114 -217
  436. package/docs/process/scientific-validation.md +23 -3
  437. package/evals/scalar-decision-machinery.ts +1 -1
  438. package/examples/extensions/README.md +67 -0
  439. package/examples/extensions/lab-status/clio-coder-extension.json +14 -0
  440. package/examples/extensions/lab-status/extension.mjs +68 -0
  441. package/examples/extensions/lab-status/jobs.synthetic.json +9 -0
  442. package/examples/extensions/measurements/clio-coder-extension.json +28 -0
  443. package/examples/extensions/measurements/summarize.cjs +23 -0
  444. package/library/README.md +109 -0
  445. package/library/_authoring/templates/agent/README.md +28 -0
  446. package/library/_authoring/templates/agent/agents/benchmark-scout.md +30 -0
  447. package/library/_authoring/templates/agent/plugin.json +23 -0
  448. package/library/_authoring/templates/fleet/README.md +29 -0
  449. package/library/_authoring/templates/fleet/fleets/pipeline-review.md +31 -0
  450. package/library/_authoring/templates/fleet/plugin.json +23 -0
  451. package/library/_authoring/templates/plugin/README.md +37 -0
  452. package/library/_authoring/templates/plugin/agents/data-curator.md +31 -0
  453. package/library/_authoring/templates/plugin/assets/curation-guide.md +22 -0
  454. package/library/_authoring/templates/plugin/plugin.json +42 -0
  455. package/library/_authoring/templates/plugin/prompts/inspect-dataset.md +15 -0
  456. package/library/_authoring/templates/plugin/skills/dataset-curation/SKILL.md +36 -0
  457. package/library/_authoring/templates/plugin/skills/dataset-curation/evals.md +17 -0
  458. package/library/_authoring/templates/prompt/README.md +28 -0
  459. package/library/_authoring/templates/prompt/plugin.json +23 -0
  460. package/library/_authoring/templates/prompt/prompts/paper-summary.md +26 -0
  461. package/library/_authoring/templates/skill/README.md +24 -0
  462. package/library/_authoring/templates/skill/SKILL.md +39 -0
  463. package/library/_authoring/templates/skill/evals.md +30 -0
  464. package/library/_authoring/templates/skill/plugin.json +23 -0
  465. package/library/agents/README.md +7 -0
  466. package/library/fleets/README.md +7 -0
  467. package/library/plugins/materio/README.md +137 -0
  468. package/library/plugins/materio/ai.iowarp.clio/agents/materio-lab-definer.md +208 -0
  469. package/library/plugins/materio/ai.iowarp.clio/agents/materio-literature-reviewer.md +308 -0
  470. package/library/plugins/materio/ai.iowarp.clio/agents/materio-research-explorer.md +249 -0
  471. package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-executor.md +416 -0
  472. package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-verifier.md +60 -0
  473. package/library/plugins/materio/ai.iowarp.clio/agents/materio-workflow-planner.md +238 -0
  474. package/library/plugins/materio/ai.iowarp.clio/fleets/materio-execute-task.md +50 -0
  475. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/add-task.md +66 -0
  476. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/archive-task.md +65 -0
  477. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/checkpoint.md +26 -0
  478. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-research-tasks.md +230 -0
  479. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-virtual-lab.md +181 -0
  480. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/execute-task.md +292 -0
  481. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/help.md +129 -0
  482. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/identify-research.md +206 -0
  483. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/literature-review.md +201 -0
  484. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/pause-research.md +12 -0
  485. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/progress.md +105 -0
  486. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/remove-task.md +63 -0
  487. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/resume-research.md +64 -0
  488. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/settings.md +16 -0
  489. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/status.md +92 -0
  490. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/upload-data.md +104 -0
  491. package/library/plugins/materio/ai.iowarp.clio/prompts/materio/wtfp.md +145 -0
  492. package/library/plugins/materio/ai.iowarp.clio/references/execution.md +56 -0
  493. package/library/plugins/materio/ai.iowarp.portability/provenance.json +9 -0
  494. package/library/plugins/materio/assets/actions/add-task.md +86 -0
  495. package/library/plugins/materio/assets/actions/archive-task.md +84 -0
  496. package/library/plugins/materio/assets/actions/checkpoint.md +47 -0
  497. package/library/plugins/materio/assets/actions/define-research-tasks.md +228 -0
  498. package/library/plugins/materio/assets/actions/define-virtual-lab.md +179 -0
  499. package/library/plugins/materio/assets/actions/execute-task.md +290 -0
  500. package/library/plugins/materio/assets/actions/help.md +140 -0
  501. package/library/plugins/materio/assets/actions/identify-research.md +204 -0
  502. package/library/plugins/materio/assets/actions/literature-review.md +199 -0
  503. package/library/plugins/materio/assets/actions/pause-research.md +34 -0
  504. package/library/plugins/materio/assets/actions/progress.md +125 -0
  505. package/library/plugins/materio/assets/actions/remove-task.md +82 -0
  506. package/library/plugins/materio/assets/actions/resume-research.md +84 -0
  507. package/library/plugins/materio/assets/actions/settings.md +38 -0
  508. package/library/plugins/materio/assets/actions/status.md +112 -0
  509. package/library/plugins/materio/assets/actions/upload-data.md +123 -0
  510. package/library/plugins/materio/assets/actions/wtfp.md +179 -0
  511. package/library/plugins/materio/assets/references/research-domains.md +134 -0
  512. package/library/plugins/materio/assets/references/research-policy.md +77 -0
  513. package/library/plugins/materio/assets/references/traditional-workflows.md +146 -0
  514. package/library/plugins/materio/assets/scripts/check_physics.py +271 -0
  515. package/library/plugins/materio/assets/scripts/check_scripts.py +248 -0
  516. package/library/plugins/materio/assets/scripts/project_plugin.py +165 -0
  517. package/library/plugins/materio/assets/scripts/research_state.py +400 -0
  518. package/library/plugins/materio/assets/scripts/verify_citations.py +616 -0
  519. package/library/plugins/materio/assets/templates/RESEARCH.md +39 -0
  520. package/library/plugins/materio/assets/templates/VIRTUAL-LAB.md +120 -0
  521. package/library/plugins/materio/assets/templates/WORKFLOW.md +42 -0
  522. package/library/plugins/materio/assets/templates/config.json +5 -0
  523. package/library/plugins/materio/evals/scripts.yaml +29 -0
  524. package/library/plugins/materio/plugin.json +530 -0
  525. package/library/plugins/materio/skills/materio-lab-definer/SKILL.md +74 -0
  526. package/library/plugins/materio/skills/materio-literature-reviewer/SKILL.md +64 -0
  527. package/library/plugins/materio/skills/materio-research-explorer/SKILL.md +74 -0
  528. package/library/plugins/materio/skills/materio-task-executor/SKILL.md +214 -0
  529. package/library/plugins/materio/skills/materio-task-verifier/SKILL.md +46 -0
  530. package/library/plugins/materio/skills/materio-workflow-planner/SKILL.md +79 -0
  531. package/library/plugins/materio/tests/test_check_physics.py +139 -0
  532. package/library/plugins/materio/tests/test_check_scripts.py +140 -0
  533. package/library/plugins/materio/tests/test_research_state.py +291 -0
  534. package/library/plugins/materio/tests/test_verify_citations.py +337 -0
  535. package/library/prompts/README.md +7 -0
  536. package/library/registry.yaml +921 -0
  537. package/library/skills/README.md +120 -0
  538. package/library/skills/coding/ast-grep/SKILL.md +213 -0
  539. package/library/skills/coding/ast-grep/evals.md +82 -0
  540. package/library/skills/coding/ast-grep/plugin.json +23 -0
  541. package/library/skills/coding/coding-standards/SKILL.md +164 -0
  542. package/library/skills/coding/coding-standards/evals.md +57 -0
  543. package/library/skills/coding/coding-standards/plugin.json +23 -0
  544. package/library/skills/coding/prototype/SKILL.md +151 -0
  545. package/library/skills/coding/prototype/evals.md +61 -0
  546. package/library/skills/coding/prototype/plugin.json +23 -0
  547. package/library/skills/coding/tdd/SKILL.md +134 -0
  548. package/library/skills/coding/tdd/evals.md +61 -0
  549. package/library/skills/coding/tdd/plugin.json +23 -0
  550. package/library/skills/context/context-handoff/SKILL.md +176 -0
  551. package/library/skills/context/context-handoff/evals.md +101 -0
  552. package/library/skills/context/context-handoff/plugin.json +23 -0
  553. package/library/skills/context/context-prime/SKILL.md +131 -0
  554. package/library/skills/context/context-prime/evals.md +99 -0
  555. package/library/skills/context/context-prime/plugin.json +23 -0
  556. package/library/skills/git/branch-closeout/SKILL.md +132 -0
  557. package/library/skills/git/branch-closeout/plugin.json +23 -0
  558. package/library/skills/git/file-ticket/SKILL.md +126 -0
  559. package/library/skills/git/file-ticket/plugin.json +23 -0
  560. package/library/skills/git/fix-issue/SKILL.md +136 -0
  561. package/library/skills/git/fix-issue/plugin.json +23 -0
  562. package/library/skills/git/resolve-merge-conflicts/SKILL.md +151 -0
  563. package/library/skills/git/resolve-merge-conflicts/evals.md +85 -0
  564. package/library/skills/git/resolve-merge-conflicts/plugin.json +23 -0
  565. package/library/skills/git/ship/SKILL.md +134 -0
  566. package/library/skills/git/ship/plugin.json +23 -0
  567. package/library/skills/git/worktree-create/SKILL.md +127 -0
  568. package/library/skills/git/worktree-create/plugin.json +23 -0
  569. package/library/skills/git/worktree-merge/SKILL.md +147 -0
  570. package/library/skills/git/worktree-merge/plugin.json +23 -0
  571. package/library/skills/meta/clio-coder-dev/SKILL.md +101 -0
  572. package/library/skills/meta/clio-coder-dev/evals.md +46 -0
  573. package/library/skills/meta/clio-coder-dev/plugin.json +23 -0
  574. package/library/skills/meta/clio-coder-test/SKILL.md +156 -0
  575. package/library/skills/meta/clio-coder-test/evals.md +62 -0
  576. package/library/skills/meta/clio-coder-test/plugin.json +23 -0
  577. package/library/skills/meta/clio-coder-test/references/test-map.md +97 -0
  578. package/library/skills/meta/credentials/SKILL.md +132 -0
  579. package/library/skills/meta/credentials/evals.md +104 -0
  580. package/library/skills/meta/credentials/plugin.json +23 -0
  581. package/library/skills/meta/find-skills/SKILL.md +85 -0
  582. package/library/skills/meta/find-skills/evals.md +65 -0
  583. package/library/skills/meta/find-skills/plugin.json +23 -0
  584. package/library/skills/meta/herdr/SKILL.md +133 -0
  585. package/library/skills/meta/herdr/evals.md +38 -0
  586. package/library/skills/meta/herdr/plugin.json +23 -0
  587. package/library/skills/meta/skill-craft/SKILL.md +139 -0
  588. package/library/skills/meta/skill-craft/evals.md +49 -0
  589. package/library/skills/meta/skill-craft/plugin.json +23 -0
  590. package/library/skills/planning/archify/SKILL.md +196 -0
  591. package/library/skills/planning/archify/plugin.json +23 -0
  592. package/library/skills/planning/architecture/SKILL.md +184 -0
  593. package/library/skills/planning/architecture/evals.md +101 -0
  594. package/library/skills/planning/architecture/plugin.json +23 -0
  595. package/library/skills/planning/backlog/SKILL.md +212 -0
  596. package/library/skills/planning/backlog/evals.md +185 -0
  597. package/library/skills/planning/backlog/plugin.json +23 -0
  598. package/library/skills/planning/prd/SKILL.md +129 -0
  599. package/library/skills/planning/prd/evals.md +103 -0
  600. package/library/skills/planning/prd/plugin.json +23 -0
  601. package/library/skills/planning/product-intent/SKILL.md +173 -0
  602. package/library/skills/planning/product-intent/evals.md +106 -0
  603. package/library/skills/planning/product-intent/plugin.json +23 -0
  604. package/library/skills/planning/tech-spec/SKILL.md +172 -0
  605. package/library/skills/planning/tech-spec/evals.md +120 -0
  606. package/library/skills/planning/tech-spec/plugin.json +23 -0
  607. package/library/skills/registry.yaml +144 -0
  608. package/library/skills/remote.yaml +13 -0
  609. package/library/skills/research/arxiv-literature/SKILL.md +168 -0
  610. package/library/skills/research/arxiv-literature/evals.md +108 -0
  611. package/library/skills/research/arxiv-literature/plugin.json +23 -0
  612. package/library/skills/research/experiment-protocol/SKILL.md +148 -0
  613. package/library/skills/research/experiment-protocol/evals.md +114 -0
  614. package/library/skills/research/experiment-protocol/plugin.json +23 -0
  615. package/library/skills/research/scientific-debugging/SKILL.md +149 -0
  616. package/library/skills/research/scientific-debugging/evals.md +156 -0
  617. package/library/skills/research/scientific-debugging/plugin.json +23 -0
  618. package/library/skills/research/scientific-modernization/SKILL.md +170 -0
  619. package/library/skills/research/scientific-modernization/evals.md +111 -0
  620. package/library/skills/research/scientific-modernization/plugin.json +23 -0
  621. package/library/skills/skill-marketplace.json +507 -0
  622. package/library/skills/workflow/cut-it/SKILL.md +153 -0
  623. package/library/skills/workflow/cut-it/evals.md +143 -0
  624. package/library/skills/workflow/cut-it/plugin.json +23 -0
  625. package/library/skills/workflow/design-council/SKILL.md +233 -0
  626. package/library/skills/workflow/design-council/evals.md +258 -0
  627. package/library/skills/workflow/design-council/plugin.json +23 -0
  628. package/library/skills/workflow/grill-me/SKILL.md +267 -0
  629. package/library/skills/workflow/grill-me/evals.md +231 -0
  630. package/library/skills/workflow/grill-me/plugin.json +23 -0
  631. package/library/skills/workflow/workflow-distiller/SKILL.md +204 -0
  632. package/library/skills/workflow/workflow-distiller/evals.md +233 -0
  633. package/library/skills/workflow/workflow-distiller/plugin.json +23 -0
  634. package/package.json +23 -14
  635. package/src/cli/agents.ts +1 -1
  636. package/src/cli/config-inspect.ts +1 -1
  637. package/src/cli/configure.ts +2 -2
  638. package/src/cli/docs.ts +65 -328
  639. package/src/cli/eval.ts +40 -6
  640. package/src/cli/extensions.ts +74 -8
  641. package/src/cli/fleet-preflight.ts +2 -0
  642. package/src/cli/fleet.ts +2 -0
  643. package/src/cli/index.ts +4 -4
  644. package/src/cli/interop-inspect.ts +61 -33
  645. package/src/cli/interop.ts +79 -9
  646. package/src/cli/library-import.ts +139 -0
  647. package/src/cli/library.ts +418 -120
  648. package/src/cli/run.ts +26 -9
  649. package/src/cli/skills-eval.ts +43 -6
  650. package/src/cli/skills-inventory.ts +6 -2
  651. package/src/cli/skills.ts +44 -491
  652. package/src/cli/trace.ts +4 -65
  653. package/src/cli/uninstall.ts +26 -0
  654. package/src/cli/usage.ts +3 -0
  655. package/src/cli/web.ts +79 -0
  656. package/src/core/bus-events.ts +8 -4
  657. package/src/core/config.ts +71 -0
  658. package/src/core/defaults.ts +1 -1
  659. package/src/core/domain-loader.ts +37 -28
  660. package/src/core/recent-models.ts +2 -2
  661. package/src/core/session-routing.ts +3 -3
  662. package/src/core/state-file-lock.ts +1 -1
  663. package/src/core/termination.ts +13 -9
  664. package/src/core/tool-names.ts +5 -0
  665. package/src/domains/agents/contract.ts +1 -1
  666. package/src/domains/agents/extension.ts +53 -9
  667. package/src/domains/agents/fleet-commands.ts +92 -1
  668. package/src/domains/agents/fleet-contract.ts +87 -19
  669. package/src/domains/agents/recipe.ts +5 -4
  670. package/src/domains/agents/registry.ts +80 -24
  671. package/src/domains/config/extension.ts +5 -4
  672. package/src/domains/config/keybindings.ts +155 -95
  673. package/src/domains/context/worker/contract.ts +91 -0
  674. package/src/domains/context/worker/pressure.ts +120 -0
  675. package/src/domains/context/worker/recall.ts +62 -0
  676. package/src/domains/context/worker/select.ts +164 -0
  677. package/src/domains/context/worker/snapshot.ts +35 -0
  678. package/src/domains/context/worker/store.ts +16 -0
  679. package/src/domains/context/working-set/replay/synthetic.ts +2 -1
  680. package/src/domains/dispatch/active-route-planner.ts +2 -0
  681. package/src/domains/dispatch/backoff.ts +1 -0
  682. package/src/domains/dispatch/code-step.ts +12 -2
  683. package/src/domains/dispatch/contract.ts +3 -0
  684. package/src/domains/dispatch/execution-plan.ts +2 -0
  685. package/src/domains/dispatch/extension.ts +184 -39
  686. package/src/domains/dispatch/fleet-plan.ts +10 -0
  687. package/src/domains/dispatch/fleet-run.ts +1 -0
  688. package/src/domains/dispatch/index.ts +1 -0
  689. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  690. package/src/domains/dispatch/receipt-integrity.ts +7 -0
  691. package/src/domains/dispatch/state.ts +2 -2
  692. package/src/domains/dispatch/types.ts +7 -0
  693. package/src/domains/dispatch/validation.ts +12 -0
  694. package/src/domains/dispatch/workspace-checkpoint.ts +1 -1
  695. package/src/domains/eval/metrics/tracked.ts +22 -2
  696. package/src/domains/evidence/failure-attribution.ts +1 -0
  697. package/src/domains/evidence/trust-status.ts +22 -3
  698. package/src/domains/extensions/command-schema.ts +145 -0
  699. package/src/domains/extensions/command-tools.ts +15 -0
  700. package/src/domains/extensions/compatibility.ts +7 -0
  701. package/src/domains/extensions/contract.ts +0 -4
  702. package/src/domains/extensions/discovery.ts +69 -205
  703. package/src/domains/extensions/extension.ts +0 -4
  704. package/src/domains/extensions/index.ts +1 -5
  705. package/src/domains/extensions/manager.ts +1 -6
  706. package/src/domains/extensions/operator-commands.ts +29 -0
  707. package/src/domains/extensions/operator-runtime.ts +485 -0
  708. package/src/domains/extensions/public-api.ts +53 -0
  709. package/src/domains/extensions/runtime-child.mjs +114 -0
  710. package/src/domains/extensions/runtime-process.ts +306 -0
  711. package/src/domains/extensions/runtime-schema.ts +146 -0
  712. package/src/domains/extensions/snapshot-access.ts +21 -0
  713. package/src/domains/extensions/snapshot.ts +4 -32
  714. package/src/domains/extensions/state.ts +5 -83
  715. package/src/domains/extensions/types.ts +33 -34
  716. package/src/domains/interop/adopt.ts +546 -0
  717. package/src/domains/interop/detect.ts +50 -12
  718. package/src/domains/interop/foreign.ts +554 -0
  719. package/src/domains/interop/import.ts +277 -0
  720. package/src/domains/interop/index.ts +24 -0
  721. package/src/domains/interop/install.ts +6 -0
  722. package/src/domains/interop/inventory.ts +351 -0
  723. package/src/domains/interop/projection.ts +360 -0
  724. package/src/domains/interop/registry.ts +106 -3
  725. package/src/domains/interop/types.ts +51 -0
  726. package/src/domains/lifecycle/migrations/index.ts +0 -2
  727. package/src/domains/lifecycle/naming-resources.ts +1 -1
  728. package/src/domains/middleware/hooks.ts +2 -2
  729. package/src/domains/middleware/marketplace-offer.ts +1 -1
  730. package/src/domains/mux/contract.ts +1 -1
  731. package/src/domains/observability/background-memory-usage.ts +3 -0
  732. package/src/domains/observability/compaction-usage.ts +2 -0
  733. package/src/domains/observability/cost.ts +5 -0
  734. package/src/domains/observability/extension.ts +1 -0
  735. package/src/domains/observability/out-of-turn-usage.ts +2 -0
  736. package/src/domains/observability/trace-store.ts +65 -5
  737. package/src/domains/plugins/catalog.ts +204 -0
  738. package/src/domains/plugins/contract.ts +24 -0
  739. package/src/domains/plugins/discovery.ts +316 -0
  740. package/src/domains/plugins/extension.ts +40 -0
  741. package/src/domains/plugins/index.ts +77 -0
  742. package/src/domains/plugins/integrity.ts +6 -0
  743. package/src/domains/plugins/manifest.ts +3 -0
  744. package/src/domains/plugins/resources.ts +102 -0
  745. package/src/domains/plugins/state.ts +753 -0
  746. package/src/domains/plugins/types.ts +217 -0
  747. package/src/domains/prompts/extension.ts +4 -10
  748. package/src/domains/prompts/fragments/operating/skills.md +10 -10
  749. package/src/domains/providers/cache-deployment.ts +147 -0
  750. package/src/domains/providers/catalog.ts +12 -7
  751. package/src/domains/providers/extension.ts +27 -5
  752. package/src/domains/providers/index.ts +1 -0
  753. package/src/domains/providers/plugins.ts +3 -13
  754. package/src/domains/providers/runtimes/common/local-synth.ts +4 -0
  755. package/src/domains/providers/types/target-descriptor.ts +17 -0
  756. package/src/domains/resources/common-loader.ts +9 -6
  757. package/src/domains/resources/index.ts +67 -0
  758. package/src/domains/resources/library-actions.ts +836 -0
  759. package/src/domains/resources/library-evals.ts +38 -0
  760. package/src/domains/resources/library-inventory.ts +1048 -0
  761. package/src/domains/resources/library-types.ts +50 -0
  762. package/src/domains/resources/library-validation.ts +634 -0
  763. package/src/domains/resources/library.ts +437 -156
  764. package/src/domains/resources/package-references.ts +66 -0
  765. package/src/domains/resources/prompts/loader.ts +75 -41
  766. package/src/domains/resources/skills/install.ts +2 -2
  767. package/src/domains/resources/skills/loader.ts +77 -22
  768. package/src/domains/resources/skills/marketplace.ts +48 -57
  769. package/src/domains/resources/skills/promotion.ts +2 -2
  770. package/src/domains/resources/skills/provenance-pin.ts +17 -4
  771. package/src/domains/safety/action-classifier.ts +2 -2
  772. package/src/domains/safety/default-path-policy.ts +3 -1
  773. package/src/domains/safety/policy-engine.ts +1 -1
  774. package/src/domains/safety/protected-artifacts.ts +21 -5
  775. package/src/domains/safety/skill-authority.ts +5 -2
  776. package/src/domains/session/compaction/compact.ts +4 -1
  777. package/src/domains/session/entries.ts +1 -0
  778. package/src/domains/session/usage.ts +6 -7
  779. package/src/domains/share/archive.ts +1 -1
  780. package/src/engine/acp/server.ts +11 -0
  781. package/src/engine/ai.ts +2 -0
  782. package/src/engine/apis/openai-completions.ts +61 -6
  783. package/src/engine/notices/pi-tui-LICENSE +21 -0
  784. package/src/engine/prewarm.ts +232 -0
  785. package/src/engine/provider-diagnostics.ts +6 -1
  786. package/src/engine/tui.ts +3 -0
  787. package/src/engine/worker-runtime.ts +58 -14
  788. package/src/engine/worker-tools.ts +5 -0
  789. package/src/entry/orchestrator.ts +46 -21
  790. package/src/entry/plugin-reload.ts +18 -0
  791. package/src/interactive/application-controller.ts +67 -21
  792. package/src/interactive/chat-loop-messages.ts +3 -0
  793. package/src/interactive/chat-loop.ts +32 -11
  794. package/src/interactive/chat-panel.ts +5 -1
  795. package/src/interactive/clio-editor.ts +53 -21
  796. package/src/interactive/command-output.ts +56 -1
  797. package/src/interactive/editor-submit.ts +65 -28
  798. package/src/interactive/fleet-run-preview.ts +2 -0
  799. package/src/interactive/follow-up-queue-panel.ts +3 -3
  800. package/src/interactive/footer/dashboard.ts +4 -1
  801. package/src/interactive/footer/notifications.ts +2 -2
  802. package/src/interactive/footer/widgets.ts +2 -2
  803. package/src/interactive/interactive-application.ts +209 -21
  804. package/src/interactive/interactive-input-runtime.ts +254 -7
  805. package/src/interactive/interactive-presentation.ts +41 -12
  806. package/src/interactive/interactive-slash-runtime.ts +37 -8
  807. package/src/interactive/keybinding-manager.ts +156 -79
  808. package/src/interactive/keyboard-owner.ts +37 -0
  809. package/src/interactive/leader-key.ts +49 -102
  810. package/src/interactive/leader-menu.ts +45 -0
  811. package/src/interactive/overlay-ask-user-lifecycle.ts +9 -1
  812. package/src/interactive/overlay-frame.ts +15 -0
  813. package/src/interactive/overlay-key-routing.ts +46 -47
  814. package/src/interactive/overlay-lifecycle.ts +30 -15
  815. package/src/interactive/overlay-resource-openers.ts +56 -46
  816. package/src/interactive/overlays/ask-user.ts +630 -556
  817. package/src/interactive/overlays/extension-panel.ts +70 -0
  818. package/src/interactive/overlays/extensions.ts +28 -5
  819. package/src/interactive/overlays/fleet-run-approval.ts +7 -3
  820. package/src/interactive/overlays/help-reference.ts +89 -40
  821. package/src/interactive/overlays/interop.ts +122 -3
  822. package/src/interactive/overlays/keybinding-detail.ts +2 -2
  823. package/src/interactive/overlays/library-lifecycle.ts +90 -0
  824. package/src/interactive/overlays/library-model.ts +574 -0
  825. package/src/interactive/overlays/library-review.ts +557 -0
  826. package/src/interactive/overlays/library-tabs.ts +3 -15
  827. package/src/interactive/overlays/library.ts +602 -0
  828. package/src/interactive/overlays/list-overlay.ts +218 -39
  829. package/src/interactive/overlays/model-selector.ts +28 -25
  830. package/src/interactive/overlays/session-selector.ts +9 -3
  831. package/src/interactive/overlays/settings.ts +25 -18
  832. package/src/interactive/overlays/tree-selector.ts +16 -11
  833. package/src/interactive/permission-hint.ts +13 -0
  834. package/src/interactive/permission-overlay.ts +77 -28
  835. package/src/interactive/prewarm.ts +11 -197
  836. package/src/interactive/prompt-source-label.ts +21 -0
  837. package/src/interactive/renderers/reference-card.ts +35 -0
  838. package/src/interactive/renderers/worker-entry.ts +35 -3
  839. package/src/interactive/session-usage-reseed.ts +2 -0
  840. package/src/interactive/side-question.ts +2 -0
  841. package/src/interactive/slash-autocomplete.ts +99 -1
  842. package/src/interactive/slash-commands.ts +390 -67
  843. package/src/interactive/slash-spec.ts +0 -1
  844. package/src/interactive/terminal-lease.ts +44 -12
  845. package/src/interactive/turn-context.ts +1 -0
  846. package/src/interactive/turn-persistence.ts +3 -0
  847. package/src/interactive/turn-prewarm.ts +126 -19
  848. package/src/interactive/turn-queues.ts +8 -7
  849. package/src/interactive/turn-runtime.ts +15 -1
  850. package/src/interactive/view/view-overlay.ts +19 -14
  851. package/src/interactive/welcome-dashboard.ts +525 -408
  852. package/src/tools/bootstrap.ts +8 -0
  853. package/src/tools/builtin-tool-catalog.ts +15 -6
  854. package/src/tools/context/index.ts +58 -5
  855. package/src/tools/context/library.ts +465 -0
  856. package/src/tools/context/surface.ts +20 -6
  857. package/src/tools/core-bootstrap.ts +3 -0
  858. package/src/tools/dispatch-admission.ts +24 -1
  859. package/src/tools/dispatch-arguments.ts +8 -0
  860. package/src/tools/dispatch-plan.ts +32 -0
  861. package/src/tools/dispatch-schema.ts +31 -1
  862. package/src/tools/dispatch-types.ts +3 -0
  863. package/src/tools/dispatch.ts +1 -1
  864. package/src/tools/harness-extensions.ts +169 -0
  865. package/src/tools/observation.ts +4 -0
  866. package/src/tools/policy.ts +7 -1
  867. package/src/tools/registry.ts +31 -3
  868. package/src/tools/worker-evidence.ts +5 -0
  869. package/src/worker/context-seed.ts +192 -0
  870. package/src/worker/entry.ts +1 -0
  871. package/src/worker/spec-contract.ts +35 -1
  872. package/dist/acp-UVEVWSDL.js +0 -130
  873. package/dist/agents-VWY37DTK.js +0 -148
  874. package/dist/auth-PCV2XQDV.js +0 -551
  875. package/dist/builtins-PD652MTK.js +0 -22
  876. package/dist/chunk-22PQ4TX4.js +0 -360
  877. package/dist/chunk-24T2YZ7A.js +0 -22648
  878. package/dist/chunk-2AKYFREM.js +0 -1008
  879. package/dist/chunk-2QPI3TR3.js +0 -162
  880. package/dist/chunk-3BJGMT2D.js +0 -324
  881. package/dist/chunk-3HQVBIKD.js +0 -308
  882. package/dist/chunk-3JLFK5MW.js +0 -193
  883. package/dist/chunk-3O2ZAUGQ.js +0 -283
  884. package/dist/chunk-3QKXHCGS.js +0 -59
  885. package/dist/chunk-3T4DXWP6.js +0 -668
  886. package/dist/chunk-3UW3KU4A.js +0 -21
  887. package/dist/chunk-3WBALRQH.js +0 -5076
  888. package/dist/chunk-4DU2TVI4.js +0 -108
  889. package/dist/chunk-4U4TKZFR.js +0 -5052
  890. package/dist/chunk-4VMV6OS6.js +0 -101
  891. package/dist/chunk-536HXRFE.js +0 -1257
  892. package/dist/chunk-5CMARGPF.js +0 -477
  893. package/dist/chunk-5CY4Q6QI.js +0 -141
  894. package/dist/chunk-5QRX7SLS.js +0 -1018
  895. package/dist/chunk-5VTJP36Q.js +0 -36
  896. package/dist/chunk-5ZSZEYJK.js +0 -119
  897. package/dist/chunk-6HAO6X5B.js +0 -197
  898. package/dist/chunk-6OII3LOK.js +0 -404
  899. package/dist/chunk-6RYDAVQT.js +0 -4338
  900. package/dist/chunk-6VRRJT7L.js +0 -146
  901. package/dist/chunk-6YA46L5A.js +0 -386
  902. package/dist/chunk-77CZRFTE.js +0 -634
  903. package/dist/chunk-7JSSKXG2.js +0 -1215
  904. package/dist/chunk-7WUZEB2M.js +0 -165
  905. package/dist/chunk-7YJWEF2L.js +0 -974
  906. package/dist/chunk-ACCKVSHU.js +0 -44
  907. package/dist/chunk-B3MOMLJ4.js +0 -433
  908. package/dist/chunk-BBV5TKE6.js +0 -35
  909. package/dist/chunk-BLTEAUCB.js +0 -668
  910. package/dist/chunk-BS4GAMUB.js +0 -233
  911. package/dist/chunk-BT7HGDOX.js +0 -3151
  912. package/dist/chunk-CBSKDFWL.js +0 -101
  913. package/dist/chunk-CXQ37WJH.js +0 -45
  914. package/dist/chunk-DXLUVUD4.js +0 -1490
  915. package/dist/chunk-DZK63TZJ.js +0 -1334
  916. package/dist/chunk-E7RF4KBO.js +0 -266
  917. package/dist/chunk-ECOMVOHP.js +0 -139
  918. package/dist/chunk-EHOSNVVM.js +0 -59
  919. package/dist/chunk-F5Z7EWMN.js +0 -4484
  920. package/dist/chunk-FQOOGXBH.js +0 -579
  921. package/dist/chunk-GLYBJ4RQ.js +0 -396
  922. package/dist/chunk-HHVJNVUJ.js +0 -179
  923. package/dist/chunk-HL26ADLH.js +0 -77
  924. package/dist/chunk-HPTJF6DG.js +0 -2465
  925. package/dist/chunk-HXHFDHB6.js +0 -962
  926. package/dist/chunk-HZ2T2YED.js +0 -1687
  927. package/dist/chunk-HZFBVU5X.js +0 -666
  928. package/dist/chunk-I2K2NCZQ.js +0 -659
  929. package/dist/chunk-IGLVVNEG.js +0 -402
  930. package/dist/chunk-IKRWLEDA.js +0 -1567
  931. package/dist/chunk-J2FYEQ5L.js +0 -33
  932. package/dist/chunk-JMPL7NUB.js +0 -108
  933. package/dist/chunk-K2HL3GBL.js +0 -998
  934. package/dist/chunk-KAFMW7WM.js +0 -386
  935. package/dist/chunk-KTFSFCVN.js +0 -46
  936. package/dist/chunk-LIN2K3EL.js +0 -66
  937. package/dist/chunk-LUL5Z66S.js +0 -221
  938. package/dist/chunk-LVFANMOH.js +0 -466
  939. package/dist/chunk-LY4N4TTL.js +0 -117
  940. package/dist/chunk-LZBMXNLP.js +0 -1196
  941. package/dist/chunk-M6TAYK66.js +0 -807
  942. package/dist/chunk-N2OKBREN.js +0 -243
  943. package/dist/chunk-NC4KV7EL.js +0 -39
  944. package/dist/chunk-NF3BK5WR.js +0 -42
  945. package/dist/chunk-NNWJYUNO.js +0 -573
  946. package/dist/chunk-NTK3WTHM.js +0 -141
  947. package/dist/chunk-ODUTYZIU.js +0 -688
  948. package/dist/chunk-OQM5IVJY.js +0 -1377
  949. package/dist/chunk-P5JMBPDJ.js +0 -7192
  950. package/dist/chunk-P5SC4DLJ.js +0 -47
  951. package/dist/chunk-P6M7RKMI.js +0 -97
  952. package/dist/chunk-PWWSIB2A.js +0 -52
  953. package/dist/chunk-PZN2LQY6.js +0 -674
  954. package/dist/chunk-Q7NNUHQV.js +0 -705
  955. package/dist/chunk-QCOV5DFY.js +0 -911
  956. package/dist/chunk-QNWRHOMF.js +0 -692
  957. package/dist/chunk-QRFXEO6Y.js +0 -17
  958. package/dist/chunk-QXFBABFJ.js +0 -380
  959. package/dist/chunk-RGZNQ5CT.js +0 -657
  960. package/dist/chunk-SRZOW5L2.js +0 -1865
  961. package/dist/chunk-SXQKNKRA.js +0 -223
  962. package/dist/chunk-T334FW2H.js +0 -362
  963. package/dist/chunk-T52TPHYC.js +0 -318
  964. package/dist/chunk-TJFDL26W.js +0 -658
  965. package/dist/chunk-TN5FW4XY.js +0 -172
  966. package/dist/chunk-U7H5WRG5.js +0 -123
  967. package/dist/chunk-UFYNOQK4.js +0 -302
  968. package/dist/chunk-URBGEOH3.js +0 -32
  969. package/dist/chunk-USZA7GWL.js +0 -341
  970. package/dist/chunk-UUQ5G6EZ.js +0 -697
  971. package/dist/chunk-VAHLPXPY.js +0 -399
  972. package/dist/chunk-VANN3GDP.js +0 -2488
  973. package/dist/chunk-VHMX7SXF.js +0 -1289
  974. package/dist/chunk-WJIMKGD6.js +0 -132
  975. package/dist/chunk-WKUYQBCG.js +0 -795
  976. package/dist/chunk-WNGF5AJX.js +0 -1911
  977. package/dist/chunk-WV6TDAPN.js +0 -3627
  978. package/dist/chunk-X3BI7HTV.js +0 -562
  979. package/dist/chunk-XWMSPJYN.js +0 -149
  980. package/dist/chunk-YPRSNUTC.js +0 -395
  981. package/dist/chunk-ZS43Y2FT.js +0 -438
  982. package/dist/clio-QQCMTUZE.js +0 -26
  983. package/dist/code-nav-GUPM6766.js +0 -686
  984. package/dist/components-TNIKXCBH.js +0 -674
  985. package/dist/config-IIM7YOD3.js +0 -621
  986. package/dist/configure-4ZM5HWJX.js +0 -73
  987. package/dist/context-44BZLJ3J.js +0 -344
  988. package/dist/context-AIDVQKMW.js +0 -278
  989. package/dist/context-OL5P3HEP.js +0 -1031
  990. package/dist/context-clear-OQV76MYP.js +0 -184
  991. package/dist/context-index-HP2JFUWA.js +0 -120
  992. package/dist/context-map-INTXD54D.js +0 -555
  993. package/dist/context-working-set-ZUW3EGLS.js +0 -1556
  994. package/dist/detail-UXPWRANG.js +0 -115
  995. package/dist/dispatch-runner-P3ZW6G77.js +0 -2438
  996. package/dist/docs-BL7ROUFN.js +0 -292
  997. package/dist/doctor-JJVGPZWT.js +0 -797
  998. package/dist/eval-V2RTN3UH.js +0 -4972
  999. package/dist/eval-inventory-QOYZERXJ.js +0 -315
  1000. package/dist/evidence-35PYWGSO.js +0 -225
  1001. package/dist/evidence-CIGGKI6J.js +0 -383
  1002. package/dist/evolve-33SM3NJP.js +0 -609
  1003. package/dist/extensions-YVRHX6ZI.js +0 -229
  1004. package/dist/fleet-LFO7SIPC.js +0 -908
  1005. package/dist/fleet-commands-RLK72GU3.js +0 -75
  1006. package/dist/fleet-decisions-3SBSCTCW.js +0 -159
  1007. package/dist/fleet-graph-JZIBCGMW.js +0 -134
  1008. package/dist/fleet-inspect-2L5YGGZJ.js +0 -469
  1009. package/dist/fleet-preflight-QRSHIIDE.js +0 -22
  1010. package/dist/fleet-validate-LIAVKDUU.js +0 -92
  1011. package/dist/fleet-verify-JIK3OQZR.js +0 -202
  1012. package/dist/fleet-view-IWHYGQKK.js +0 -118
  1013. package/dist/init-NJEDOTQW.js +0 -321
  1014. package/dist/interop-RVCAYECG.js +0 -115
  1015. package/dist/inventory-74UL7UHU.js +0 -118
  1016. package/dist/library-CTKZA7K3.js +0 -226
  1017. package/dist/memory-I76I5VUH.js +0 -512
  1018. package/dist/models-D4YDIQZQ.js +0 -289
  1019. package/dist/monitor-NKVD55HX.js +0 -775
  1020. package/dist/orchestrator-I6OATVPK.js +0 -39518
  1021. package/dist/panes-7BTYH3XQ.js +0 -103
  1022. package/dist/panes-WPOMEMRH.js +0 -74
  1023. package/dist/paths-J3KN7UAO.js +0 -67
  1024. package/dist/preload-EJAWJEIO.js +0 -116
  1025. package/dist/reset-4XMQKDNZ.js +0 -345
  1026. package/dist/resources-TVYVXK3Q.js +0 -125
  1027. package/dist/run-OWQNNSJU.js +0 -872
  1028. package/dist/share-NPZZMUXG.js +0 -290
  1029. package/dist/skills-OXFEAWMT.js +0 -510
  1030. package/dist/skills-eval-J5TCKSJA.js +0 -1034
  1031. package/dist/skills-inventory-7II4DM57.js +0 -133
  1032. package/dist/slash-commands-MDSDSWYF.js +0 -92
  1033. package/dist/steer-S7D7IKPU.js +0 -77
  1034. package/dist/support-5VAEC32W.js +0 -38
  1035. package/dist/targets-KILZEOCE.js +0 -1109
  1036. package/dist/tasks-R6P6OV22.js +0 -36
  1037. package/dist/terminal-lease-LZCG2KN3.js +0 -405
  1038. package/dist/tools-6OCQ6SVB.js +0 -28
  1039. package/dist/trace-JHLXWUMN.js +0 -572
  1040. package/dist/tui-primitives-RCY2HN7G.js +0 -17
  1041. package/dist/uninstall-FXYMFFRT.js +0 -407
  1042. package/dist/upgrade-D2OZH6IQ.js +0 -307
  1043. package/dist/usage-PIASNUS4.js +0 -1052
  1044. package/dist/verifiers-LTHUY2PX.js +0 -630
  1045. package/dist/verify-HYF7KBVE.js +0 -30
  1046. package/dist/web-fetch-MQN4FH3X.js +0 -788
  1047. package/dist/wiki-generate-RK7JQ5NP.js +0 -615
  1048. package/dist/with-panes-OFTNAR4Y.js +0 -628
  1049. package/skills/README.md +0 -418
  1050. package/skills/coding/ast-grep/SKILL.md +0 -213
  1051. package/skills/coding/ast-grep/evals.md +0 -82
  1052. package/skills/coding/coding-standards/SKILL.md +0 -164
  1053. package/skills/coding/coding-standards/evals.md +0 -57
  1054. package/skills/coding/prototype/SKILL.md +0 -151
  1055. package/skills/coding/prototype/evals.md +0 -61
  1056. package/skills/coding/tdd/SKILL.md +0 -134
  1057. package/skills/coding/tdd/evals.md +0 -61
  1058. package/skills/context/context-handoff/SKILL.md +0 -176
  1059. package/skills/context/context-handoff/evals.md +0 -101
  1060. package/skills/context/context-prime/SKILL.md +0 -131
  1061. package/skills/context/context-prime/evals.md +0 -99
  1062. package/skills/git/branch-closeout/SKILL.md +0 -132
  1063. package/skills/git/file-ticket/SKILL.md +0 -126
  1064. package/skills/git/fix-issue/SKILL.md +0 -136
  1065. package/skills/git/resolve-merge-conflicts/SKILL.md +0 -151
  1066. package/skills/git/resolve-merge-conflicts/evals.md +0 -85
  1067. package/skills/git/ship/SKILL.md +0 -134
  1068. package/skills/git/worktree-create/SKILL.md +0 -127
  1069. package/skills/git/worktree-merge/SKILL.md +0 -147
  1070. package/skills/meta/clio-coder-dev/SKILL.md +0 -101
  1071. package/skills/meta/clio-coder-dev/evals.md +0 -46
  1072. package/skills/meta/clio-coder-test/SKILL.md +0 -146
  1073. package/skills/meta/clio-coder-test/evals.md +0 -48
  1074. package/skills/meta/clio-coder-test/references/test-map.md +0 -96
  1075. package/skills/meta/credentials/SKILL.md +0 -132
  1076. package/skills/meta/credentials/evals.md +0 -104
  1077. package/skills/meta/find-skills/SKILL.md +0 -78
  1078. package/skills/meta/find-skills/evals.md +0 -47
  1079. package/skills/meta/herdr/SKILL.md +0 -133
  1080. package/skills/meta/herdr/evals.md +0 -38
  1081. package/skills/meta/skill-craft/SKILL.md +0 -125
  1082. package/skills/meta/skill-craft/evals.md +0 -41
  1083. package/skills/planning/archify/SKILL.md +0 -196
  1084. package/skills/planning/architecture/SKILL.md +0 -184
  1085. package/skills/planning/architecture/evals.md +0 -101
  1086. package/skills/planning/backlog/SKILL.md +0 -212
  1087. package/skills/planning/backlog/evals.md +0 -185
  1088. package/skills/planning/prd/SKILL.md +0 -129
  1089. package/skills/planning/prd/evals.md +0 -103
  1090. package/skills/planning/product-intent/SKILL.md +0 -173
  1091. package/skills/planning/product-intent/evals.md +0 -106
  1092. package/skills/planning/tech-spec/SKILL.md +0 -172
  1093. package/skills/planning/tech-spec/evals.md +0 -120
  1094. package/skills/registry.yaml +0 -144
  1095. package/skills/remote.yaml +0 -13
  1096. package/skills/research/arxiv-literature/SKILL.md +0 -168
  1097. package/skills/research/arxiv-literature/evals.md +0 -108
  1098. package/skills/research/experiment-protocol/SKILL.md +0 -148
  1099. package/skills/research/experiment-protocol/evals.md +0 -114
  1100. package/skills/research/scientific-debugging/SKILL.md +0 -149
  1101. package/skills/research/scientific-debugging/evals.md +0 -156
  1102. package/skills/research/scientific-modernization/SKILL.md +0 -170
  1103. package/skills/research/scientific-modernization/evals.md +0 -111
  1104. package/skills/skill-marketplace.json +0 -507
  1105. package/skills/workflow/cut-it/SKILL.md +0 -153
  1106. package/skills/workflow/cut-it/evals.md +0 -143
  1107. package/skills/workflow/design-council/SKILL.md +0 -233
  1108. package/skills/workflow/design-council/evals.md +0 -258
  1109. package/skills/workflow/grill-me/SKILL.md +0 -267
  1110. package/skills/workflow/grill-me/evals.md +0 -231
  1111. package/skills/workflow/workflow-distiller/SKILL.md +0 -201
  1112. package/skills/workflow/workflow-distiller/evals.md +0 -225
  1113. package/src/domains/extensions/resource-path.ts +0 -27
  1114. package/src/domains/extensions/resources.ts +0 -37
  1115. package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +0 -27
  1116. package/src/interactive/overlays/agents.ts +0 -70
  1117. package/src/interactive/overlays/library-install-confirm.ts +0 -152
  1118. package/src/interactive/overlays/prompts.ts +0 -89
  1119. package/src/interactive/overlays/skills-hub.ts +0 -615
  1120. /package/{skills → library/skills}/coding/ast-grep/references/rule_reference.md +0 -0
  1121. /package/{skills → library/skills}/coding/prototype/references/LOGIC.md +0 -0
  1122. /package/{skills → library/skills}/coding/prototype/references/UI.md +0 -0
  1123. /package/{skills → library/skills}/coding/tdd/references/mocking.md +0 -0
  1124. /package/{skills → library/skills}/coding/tdd/references/tests.md +0 -0
  1125. /package/{skills → library/skills}/context/context-handoff/scripts/new-handoff.sh +0 -0
  1126. /package/{skills → library/skills}/git/branch-closeout/evals.md +0 -0
  1127. /package/{skills → library/skills}/git/branch-closeout/references/closeout-checklist.md +0 -0
  1128. /package/{skills → library/skills}/git/file-ticket/assets/issue-template.md +0 -0
  1129. /package/{skills → library/skills}/git/file-ticket/evals.md +0 -0
  1130. /package/{skills → library/skills}/git/file-ticket/references/issue-discovery.md +0 -0
  1131. /package/{skills → library/skills}/git/fix-issue/evals.md +0 -0
  1132. /package/{skills → library/skills}/git/fix-issue/references/diagnosis-and-rca.md +0 -0
  1133. /package/{skills → library/skills}/git/resolve-merge-conflicts/references/conflict-matrix.md +0 -0
  1134. /package/{skills → library/skills}/git/ship/assets/pr-template.md +0 -0
  1135. /package/{skills → library/skills}/git/ship/evals.md +0 -0
  1136. /package/{skills → library/skills}/git/ship/references/remote-and-branch-policy.md +0 -0
  1137. /package/{skills → library/skills}/git/worktree-create/evals.md +0 -0
  1138. /package/{skills → library/skills}/git/worktree-create/references/worktree-setup.md +0 -0
  1139. /package/{skills → library/skills}/git/worktree-merge/evals.md +0 -0
  1140. /package/{skills → library/skills}/git/worktree-merge/references/merge-strategies.md +0 -0
  1141. /package/{skills → library/skills}/meta/clio-coder-test/references/harness.md +0 -0
  1142. /package/{skills → library/skills}/planning/archify/evals.md +0 -0
@@ -5,10 +5,11 @@ import {
5
5
  } from "../../domains/safety/decision-presentation.js";
6
6
  import {
7
7
  type Component,
8
+ Editor,
9
+ getKeybindings,
8
10
  Input,
9
11
  matchesKey,
10
12
  type OverlayHandle,
11
- type OverlayOptions,
12
13
  type SelectItem,
13
14
  SelectList,
14
15
  type TUI,
@@ -17,7 +18,7 @@ import {
17
18
  wrapTextWithAnsi,
18
19
  } from "../../engine/tui.js";
19
20
  import type { AskUserAnswer, AskUserQuestion, AskUserResult } from "../../tools/ask-user.js";
20
- import { ASK_USER_OTHER_LABEL, cancelledAskUserResult } from "../../tools/ask-user.js";
21
+ import { cancelledAskUserResult } from "../../tools/ask-user.js";
21
22
  import {
22
23
  buildHint,
23
24
  DEFAULT_SELECT_THEME,
@@ -25,7 +26,7 @@ import {
25
26
  type OverlayEscVerb,
26
27
  showClioOverlayFrame,
27
28
  } from "../overlay-frame.js";
28
- import { type ClioToken, clioTheme, dotSep, GLYPH, screenTitle } from "../theme/index.js";
29
+ import { type ClioToken, clioTheme, dotSep, editorTheme, GLYPH, rule, screenTitle } from "../theme/index.js";
29
30
  import { nextContentScrollOffset, type ViewScrollAction } from "../view/view-overlay.js";
30
31
 
31
32
  /**
@@ -40,81 +41,35 @@ export const ASK_USER_DECISION_TITLE = DEFAULT_ASK_USER_PRESENTATION.title;
40
41
  export const ASK_USER_WAITING_TITLE = "Ask User";
41
42
 
42
43
  /**
43
- * The shape a request draws itself in.
44
+ * One box for every request shape.
44
45
  *
45
- * One overlay served three interactions through one centered full-height modal,
46
- * so a two-option confirmation arrived as a thirty-five-row box with twenty-five
47
- * blank rows under the options. The surfaces differ in size and placement:
48
- *
49
- * - `compact` is a small bar low on the screen, where the operator's eyes
50
- * already are, for one question with a handful of choices.
51
- * - `panel` is a medium centered box for one question whose option list is too
52
- * long to read in a bar.
53
- * - `interview` is the full surface with the round strip and the answer ledger,
54
- * for a round of several questions or any round after the first.
55
- */
56
- export type AskUserSurface = "compact" | "panel" | "interview";
57
-
58
- /** Options a single question may carry and still be answered in the bar. */
59
- export const ASK_USER_COMPACT_MAX_OPTIONS = 3;
60
-
61
- /** The surface an overlay waits on before its first round decides one. */
62
- export const ASK_USER_DEFAULT_SURFACE: AskUserSurface = "panel";
63
-
64
- interface AskUserSurfaceGeometry {
65
- /** Box width in columns; zero fills the row. The frame clamps to the terminal. */
66
- width: number;
67
- /** Rows the body may use, before the borders. Zero means every row the frame has. */
68
- maxInnerRows: number;
69
- anchor: NonNullable<OverlayOptions["anchor"]>;
70
- margin: NonNullable<OverlayOptions["margin"]>;
71
- }
72
-
73
- /**
74
- * Where each surface sits and how big it is allowed to get.
75
- *
76
- * The compact bar anchors to the bottom because a decision that has taken the
77
- * keyboard should appear where the operator was already typing, not in the
78
- * middle of the transcript. It covers the composer, which is honest: the
79
- * composer accepts nothing while a decision is pending.
80
- *
81
- * Every surface's margin is one row top and bottom, which is what
82
- * `ASK_USER_FRAME_AND_MARGIN_ROWS` counts alongside the two border rows.
46
+ * Three surfaces used to answer three request shapes: a fixed 68-column bar for
47
+ * a short question, a fixed 88-column panel for a longer option list, and a
48
+ * full-screen workspace for an interview. The bar cut Daisy's four-part research
49
+ * question to two parts at every terminal width, and the workspace put twelve
50
+ * empty rows between the question and its options. The box now sizes itself to
51
+ * what it has to say, sits above the composer where the operator was typing,
52
+ * and takes a readable measure from the terminal rather than a constant.
83
53
  */
84
- export const ASK_USER_SURFACE_GEOMETRY: Record<AskUserSurface, AskUserSurfaceGeometry> = {
85
- compact: { width: 68, maxInnerRows: 14, anchor: "bottom-center", margin: { top: 1, right: 2, bottom: 1, left: 2 } },
86
- panel: { width: 88, maxInnerRows: 18, anchor: "center", margin: 1 },
87
- // The interview is a workspace, not a modal. A cap of 42 turned a 66-row
88
- // terminal into a centered box with twenty blank rows around it, so the
89
- // interview takes every row the frame has and lays its own chrome out inside
90
- // them. The one-row margin keeps the border off the top row and the footer.
91
- interview: { width: 0, maxInnerRows: 0, anchor: "center", margin: 1 },
92
- };
93
-
94
- const ASK_USER_FRAME_ROWS = 2;
54
+ export const ASK_USER_MAX_BOX_WIDTH = 100;
55
+ export const ASK_USER_MIN_BOX_WIDTH = 40;
56
+ const ASK_USER_MARGIN = { top: 1, right: 2, bottom: 1, left: 2 };
57
+ /** Two border rows plus the one-row margin above and below. */
95
58
  const ASK_USER_FRAME_AND_MARGIN_ROWS = 4;
96
- const MIN_VISIBLE_OPTIONS = 3;
97
- /** Interview rows spent on chrome before an option row: strip, header, blanks, status, one question row. */
98
- const INTERVIEW_CHROME_ROWS = 7;
99
- const MAX_VISIBLE_OPTIONS = 12;
100
- /** Below this the second column is too narrow to wrap a sentence into. */
101
- const MIN_DESCRIPTION_COLUMN = 28;
59
+ const MIN_INNER_ROWS = 6;
60
+ /** The question region keeps at least this many rows before the options window shrinks. */
61
+ const MIN_QUESTION_ROWS = 3;
62
+ /** Below this width an option's description goes under its label instead of beside it. */
63
+ const STACKED_OPTION_WIDTH = 56;
64
+ const MAX_LABEL_COLUMN = 30;
65
+ const MIN_LABEL_COLUMN = 12;
102
66
  const ELLIPSIS = "…";
67
+ const CONTINUATION_INDENT = " ";
68
+ const TEXT_ASKING_DESCRIPTION = "opens a text field for your answer";
103
69
 
104
- /**
105
- * The surface a request renders on, from the request shape alone.
106
- *
107
- * `priorRounds` is what makes an interview an interview. The tool's `mode`
108
- * cannot answer this: `single_question` is the mode an interview asks its
109
- * rounds in, and a first single question is exactly the confirmation that
110
- * should not take the screen. A round that follows an answered one has captured
111
- * answers to keep on screen, which is the full surface's whole job.
112
- */
113
- function askUserSurface(questions: ReadonlyArray<AskUserQuestion>, priorRounds = 0): AskUserSurface {
114
- if (priorRounds > 0 || questions.length !== 1) return "interview";
115
- const question = questions[0];
116
- if (!question) return "interview";
117
- return (question.options?.length ?? 0) <= ASK_USER_COMPACT_MAX_OPTIONS ? "compact" : "panel";
70
+ export function askUserBoxWidth(columns: number): number {
71
+ if (!Number.isFinite(columns) || columns <= 0) return ASK_USER_MAX_BOX_WIDTH;
72
+ return Math.max(ASK_USER_MIN_BOX_WIDTH, Math.min(ASK_USER_MAX_BOX_WIDTH, Math.floor(columns) - 4));
118
73
  }
119
74
 
120
75
  export interface OpenAskUserOverlayDeps {
@@ -134,6 +89,7 @@ type InterviewPhase = "waiting" | "asking" | "closed";
134
89
  interface QuestionState {
135
90
  mode: Mode;
136
91
  selected: Set<number>;
92
+ committedSelected: Set<number>;
137
93
  customAnswer: string;
138
94
  inputValue: string;
139
95
  answer: string;
@@ -148,11 +104,11 @@ interface QuestionState {
148
104
  focusedValue?: string;
149
105
  }
150
106
 
151
- interface AskUserOverlayViewDeps extends OpenAskUserOverlayDeps {
107
+ export interface AskUserOverlayViewDeps extends OpenAskUserOverlayDeps {
152
108
  getTerminalRows: () => number;
153
109
  requestRender: () => void;
154
- /** Called when the request shape moves the body to a differently sized box. */
155
- onSurface: (surface: AskUserSurface) => void;
110
+ /** The engine, for the multi-line answer editor. Absent, the field is a one-line input. */
111
+ tui?: TUI;
156
112
  }
157
113
 
158
114
  function questionHasOptions(question: AskUserQuestion): boolean {
@@ -167,6 +123,7 @@ function createQuestionState(question: AskUserQuestion): QuestionState {
167
123
  return {
168
124
  mode: initialMode(question),
169
125
  selected: new Set<number>(),
126
+ committedSelected: new Set<number>(),
170
127
  customAnswer: "",
171
128
  inputValue: "",
172
129
  answer: "",
@@ -174,11 +131,15 @@ function createQuestionState(question: AskUserQuestion): QuestionState {
174
131
  };
175
132
  }
176
133
 
177
- function isOtherOption(label: string): boolean {
178
- const normalized = label
134
+ function normalizedLabel(label: string): string {
135
+ return label
179
136
  .toLowerCase()
180
- .replace(/[^a-z0-9]+/g, " ")
137
+ .replace(/[^a-z0-9']+/g, " ")
181
138
  .trim();
139
+ }
140
+
141
+ function isOtherOption(label: string): boolean {
142
+ const normalized = normalizedLabel(label);
182
143
  return (
183
144
  normalized === "other" ||
184
145
  normalized === "custom" ||
@@ -187,6 +148,23 @@ function isOtherOption(label: string): boolean {
187
148
  );
188
149
  }
189
150
 
151
+ /**
152
+ * An option whose label says the operator is about to type.
153
+ *
154
+ * wtf-MS interviews offer "Provided details" beside "help me explore"; the
155
+ * candidates round offers "Combine or edit; I'll describe". Choosing such an
156
+ * option used to record the bare label, and the model then had a route with no
157
+ * details on it and asked again. Enter on one of these opens the text field
158
+ * with the label kept, the same path `t` takes.
159
+ */
160
+ export function optionAsksForText(label: string): boolean {
161
+ if (isOtherOption(label)) return true;
162
+ const normalized = normalizedLabel(label);
163
+ return /\b(provided? details|i'?ll (describe|type|explain|specify|provide|write)|i (will|can) (describe|type|explain|specify|provide|write)|let me (describe|type|explain|specify)|type (it|my|the)|describe (it|my|the|below))\b/u.test(
164
+ normalized,
165
+ );
166
+ }
167
+
190
168
  function optionItems(question: AskUserQuestion, selected: ReadonlySet<number>): SelectItem[] {
191
169
  const options = question.options ?? [];
192
170
  const explicitOtherIndex = options.findIndex((option) => isOtherOption(option.label));
@@ -204,12 +182,15 @@ function optionItems(question: AskUserQuestion, selected: ReadonlySet<number>):
204
182
  label,
205
183
  };
206
184
  if (option.description) item.description = option.description;
185
+ else if (optionAsksForText(option.label)) item.description = TEXT_ASKING_DESCRIPTION;
207
186
  items.push(item);
208
187
  }
209
188
  if (explicitOtherIndex === -1) {
189
+ // The implicit choice is one word on screen; its description says what it
190
+ // does, and the tool's longer label is what the answer records.
210
191
  items.push({
211
192
  value: "other",
212
- label: question.multi_select === true ? `[ ] ${ASK_USER_OTHER_LABEL}` : ASK_USER_OTHER_LABEL,
193
+ label: question.multi_select === true ? "[ ] Other" : "Other",
213
194
  description: "type your answer",
214
195
  });
215
196
  }
@@ -250,8 +231,8 @@ function wrapLabeledValue(prefix: string, value: string, width: number): string[
250
231
  const safeWidth = Math.max(1, width);
251
232
  const prefixWidth = visibleWidth(prefix);
252
233
  const valueWidth = Math.max(1, safeWidth - prefixWidth);
253
- return wrapTextWithAnsi(value, valueWidth).map((line, index) =>
254
- fitLine(`${index === 0 ? prefix : " ".repeat(prefixWidth)}${line}`, safeWidth),
234
+ return wrapTextWithAnsi(value, valueWidth).map(
235
+ (line, index) => `${index === 0 ? prefix : " ".repeat(prefixWidth)}${line}`,
255
236
  );
256
237
  }
257
238
 
@@ -259,25 +240,248 @@ function compactTitle(question: AskUserQuestion): string {
259
240
  return (question.header ?? question.question).replace(/\s+/g, " ").trim();
260
241
  }
261
242
 
243
+ const BOLD_SPAN = /\*\*([^*\n]+?)\*\*/gu;
244
+ const LIST_MARKER = /^(\s*)(\d{1,2}[.)]|[-*•])\s+(.*)$/u;
245
+
246
+ function inlineMarkdown(text: string): string {
247
+ const theme = clioTheme();
248
+ return text.replace(BOLD_SPAN, (_match, inner: string) => theme.paint(inner, { bold: true }));
249
+ }
250
+
251
+ /**
252
+ * The question, wrapped to the width and never cut.
253
+ *
254
+ * A model writes a question the way it writes prose: `**Domain**` for emphasis
255
+ * and `1.` lists for the parts it wants answered. The old body rendered the
256
+ * asterisks literally and wrapped a list item's second row back to column zero,
257
+ * where it read as a new paragraph. Bold spans render bold, list items hang
258
+ * under their marker, and blank lines survive one deep so paragraphs stay apart.
259
+ */
260
+ export function formatAskUserQuestion(text: string, width: number): string[] {
261
+ const safeWidth = Math.max(4, width);
262
+ const lines: string[] = [];
263
+ let previousBlank = true;
264
+ for (const raw of text.replace(/\r\n?/g, "\n").split("\n")) {
265
+ const line = raw.replace(/\t/g, " ").trimEnd();
266
+ if (line.trim().length === 0) {
267
+ if (!previousBlank) lines.push("");
268
+ previousBlank = true;
269
+ continue;
270
+ }
271
+ previousBlank = false;
272
+ const list = LIST_MARKER.exec(line);
273
+ if (list) {
274
+ const lead = list[1] ?? "";
275
+ const marker = (list[2] ?? "").replace(/^[-*]$/u, "•");
276
+ const rest = list[3] ?? "";
277
+ const prefix = `${lead}${marker} `;
278
+ const indent = " ".repeat(visibleWidth(prefix));
279
+ const wrapped = wrapTextWithAnsi(inlineMarkdown(rest), Math.max(4, safeWidth - indent.length));
280
+ wrapped.forEach((part, index) => {
281
+ lines.push(`${index === 0 ? prefix : indent}${part}`);
282
+ });
283
+ continue;
284
+ }
285
+ // An indented line is a continuation the author laid out by hand, such as
286
+ // the "In: … · Out: …" row under a candidate; its wrapped rows keep the
287
+ // indent so they read as part of it rather than as a new paragraph.
288
+ const lead = /^\s*/u.exec(line)?.[0] ?? "";
289
+ const wrapped = wrapTextWithAnsi(inlineMarkdown(line.slice(lead.length)), Math.max(4, safeWidth - lead.length));
290
+ lines.push(...wrapped.map((part) => `${lead}${part}`));
291
+ }
292
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
293
+ return lines;
294
+ }
295
+
296
+ /** The typed answer box: a small editor whose chrome is a caption above and a key hint below. */
297
+ class AnswerEditor extends Editor {
298
+ caption = "";
299
+
300
+ constructor(tui: TUI) {
301
+ super(tui, editorTheme(clioTheme()), { paddingX: 0 });
302
+ // The overlay forwards keys itself, so the engine never focuses this
303
+ // editor; the flag is what draws the cursor.
304
+ this.focused = true;
305
+ }
306
+
307
+ protected override renderTopBorder(width: number): string {
308
+ const theme = clioTheme();
309
+ return fitLine(theme.fg("dim", this.caption), width);
310
+ }
311
+
312
+ protected override renderBottomBorder(width: number, hiddenLineCount: number): string {
313
+ if (hiddenLineCount <= 0) return "";
314
+ return fitLine(clioTheme().fg("dim", `${GLYPH.down} ${hiddenLineCount} more rows`), width);
315
+ }
316
+ }
317
+
318
+ /** One shape over the two text controls, so the view never asks which it holds. */
319
+ interface TextControl {
320
+ render(width: number): string[];
321
+ handleInput(data: string): void;
322
+ applyEdit?(operation: "undo"): void;
323
+ getText(): string;
324
+ setText(text: string): void;
325
+ invalidate(): void;
326
+ }
327
+
328
+ class InputTextControl implements TextControl {
329
+ constructor(
330
+ private readonly input: Input,
331
+ private readonly caption: string,
332
+ ) {}
333
+
334
+ render(width: number): string[] {
335
+ const theme = clioTheme();
336
+ const rows = this.input
337
+ .render(width)
338
+ .map((line) =>
339
+ fitLine(line.startsWith("> ") ? `${theme.fg("accent", `${GLYPH.cursor} `)}${line.slice(2)}` : line, width),
340
+ );
341
+ return [fitLine(theme.fg("dim", this.caption), width), ...rows];
342
+ }
343
+
344
+ applyEdit(operation: "undo"): void {
345
+ this.input.applyEdit(operation);
346
+ }
347
+ handleInput(data: string): void {
348
+ this.input.handleInput(data);
349
+ }
350
+
351
+ getText(): string {
352
+ return this.input.getValue();
353
+ }
354
+
355
+ setText(text: string): void {
356
+ this.input.setValue(text);
357
+ }
358
+
359
+ invalidate(): void {
360
+ this.input.invalidate();
361
+ }
362
+ }
363
+
364
+ interface OptionRowLayout {
365
+ /** Rows per item, in item order. */
366
+ rows: string[][];
367
+ }
368
+
369
+ /**
370
+ * Every option, with its whole description.
371
+ *
372
+ * A description is what the operator is choosing between: "+Latency,
373
+ * +Simplicity. Good for: Financial, Auth" is the trade-off, and the old table
374
+ * cut it to its first clause on every row the cursor was not on. Every row now
375
+ * wraps its description beside the label, or under it when the box is too
376
+ * narrow for two columns, and the row carries no key: the footer does.
377
+ */
378
+ function layoutOptionRows(items: ReadonlyArray<SelectItem>, focusedIndex: number, width: number): OptionRowLayout {
379
+ const theme = clioTheme();
380
+ const safeWidth = Math.max(8, width);
381
+ const stacked = safeWidth < STACKED_OPTION_WIDTH;
382
+ const widestLabel = items.reduce((max, item) => Math.max(max, visibleWidth(item.label)), 0);
383
+ const labelColumn = Math.max(
384
+ MIN_LABEL_COLUMN,
385
+ Math.min(MAX_LABEL_COLUMN, Math.floor(safeWidth * 0.42), widestLabel + 2),
386
+ );
387
+ const rows: string[][] = [];
388
+ for (let index = 0; index < items.length; index += 1) {
389
+ const item = items[index];
390
+ if (!item) continue;
391
+ const focused = index === focusedIndex;
392
+ const prefix = focused ? theme.fg("accent", `${GLYPH.cursor} `) : " ";
393
+ const description = item.description?.replace(/[\r\n]+/g, " ").trim() ?? "";
394
+ const paint = (text: string): string => (focused ? theme.style("accent", text, { bold: true }) : text);
395
+ const describe = (text: string): string => (focused ? text : theme.fg("muted", text));
396
+ if (description.length === 0) {
397
+ rows.push(
398
+ wrapTextWithAnsi(paint(item.label), Math.max(4, safeWidth - 2)).map(
399
+ (line, at) => `${at === 0 ? prefix : " "}${line}`,
400
+ ),
401
+ );
402
+ continue;
403
+ }
404
+ if (stacked || visibleWidth(item.label) > labelColumn - 1) {
405
+ const labelRows = wrapTextWithAnsi(paint(item.label), Math.max(4, safeWidth - 2)).map(
406
+ (line, at) => `${at === 0 ? prefix : " "}${line}`,
407
+ );
408
+ const descriptionRows = wrapTextWithAnsi(
409
+ describe(description),
410
+ Math.max(4, safeWidth - CONTINUATION_INDENT.length),
411
+ ).map((line) => `${CONTINUATION_INDENT}${line}`);
412
+ rows.push([...labelRows, ...descriptionRows]);
413
+ continue;
414
+ }
415
+ const spacing = " ".repeat(Math.max(1, labelColumn - visibleWidth(item.label)));
416
+ const descriptionWidth = Math.max(4, safeWidth - 2 - labelColumn);
417
+ const wrapped = wrapTextWithAnsi(describe(description), descriptionWidth);
418
+ const indent = " ".repeat(2 + labelColumn);
419
+ rows.push([
420
+ `${prefix}${paint(item.label)}${spacing}${wrapped[0] ?? ""}`,
421
+ ...wrapped.slice(1).map((line) => `${indent}${line}`),
422
+ ]);
423
+ }
424
+ return { rows };
425
+ }
426
+
427
+ /**
428
+ * The rows of a windowed list: every option when they fit, otherwise a run of
429
+ * whole options around the focused one, with a position marker. An option is
430
+ * never split across the window edge, so a description is read whole or not
431
+ * at all, and one arrow key brings any hidden option back.
432
+ */
433
+ function windowOptionRows(layout: OptionRowLayout, focusedIndex: number, rowBudget: number, width: number): string[] {
434
+ const theme = clioTheme();
435
+ const total = layout.rows.reduce((sum, rows) => sum + rows.length, 0);
436
+ if (total <= rowBudget) return layout.rows.flat();
437
+ const budget = Math.max(1, rowBudget - 1);
438
+ const focusedRows = layout.rows[focusedIndex]?.length ?? 1;
439
+ let start = focusedIndex;
440
+ let end = focusedIndex + 1;
441
+ let used = focusedRows;
442
+ // Grow around the focus, later options first so the reading order continues
443
+ // downward, then earlier ones.
444
+ let grew = true;
445
+ while (grew) {
446
+ grew = false;
447
+ const after = layout.rows[end]?.length;
448
+ if (after !== undefined && used + after <= budget) {
449
+ used += after;
450
+ end += 1;
451
+ grew = true;
452
+ }
453
+ const before = layout.rows[start - 1]?.length;
454
+ if (before !== undefined && used + before <= budget) {
455
+ used += before;
456
+ start -= 1;
457
+ grew = true;
458
+ }
459
+ }
460
+ const visible = layout.rows.slice(start, end).flat();
461
+ const marker = ` (${focusedIndex + 1}/${layout.rows.length})${start > 0 ? ` ${GLYPH.up}` : ""}${end < layout.rows.length ? ` ${GLYPH.down}` : ""}`;
462
+ return [...visible, fitLine(theme.fg("dim", marker), width)];
463
+ }
464
+
262
465
  class AskUserOverlayView implements Component {
263
466
  private phase: InterviewPhase = "waiting";
264
- private surface: AskUserSurface = ASK_USER_DEFAULT_SURFACE;
265
467
  private index = 0;
266
468
  private status = "";
267
469
  private questions: ReadonlyArray<AskUserQuestion> = [];
268
470
  private states: QuestionState[] = [];
269
471
  private history: AskUserAnswer[] = [];
472
+ /** Rounds answered so far; the strip and header count these, not answers. */
473
+ private roundsAnswered = 0;
270
474
  private list: SelectList | null = null;
271
- private input: Input | null = null;
475
+ private text: TextControl | null = null;
272
476
  private resolveCurrent: ((result: AskUserResult) => void) | null = null;
273
- /** Rows of the interview's scrollable middle already scrolled past. */
274
- private contentScrollOffset = 0;
275
- /** Whether the last interview render had more content than the region held. */
276
- private contentOverflows = false;
277
- /** Rows the scroll region drew on the last render, for a page-sized jump. */
278
- private contentRegionHeight = 1;
279
- /** Rows the scroll region wanted on the last render. */
280
- private contentTotalRows = 0;
477
+ private optionDetailRows: string[] = [];
478
+ /** Rows of the question region already scrolled past. */
479
+ private questionScroll = 0;
480
+ private questionOverflows = false;
481
+ private questionRegionRows = 1;
482
+ private questionTotalRows = 0;
483
+ private ledgerExpanded = false;
484
+ private detailsExpanded = false;
281
485
  private presentation: DecisionPresentation = DEFAULT_ASK_USER_PRESENTATION;
282
486
 
283
487
  constructor(private readonly deps: AskUserOverlayViewDeps) {}
@@ -295,11 +499,9 @@ class AskUserOverlayView implements Component {
295
499
  this.presentation = presentation;
296
500
  this.states = this.questions.map((question) => createQuestionState(question));
297
501
  this.list = null;
298
- this.input = null;
299
- this.resetContentScroll();
300
- // Before the controls, because how many option rows fit is a property of
301
- // the box this round is about to be drawn in.
302
- this.setSurface(askUserSurface(this.questions, this.history.length));
502
+ this.text = null;
503
+ this.detailsExpanded = false;
504
+ this.resetQuestionScroll();
303
505
  this.rebuildControl();
304
506
  this.deps.requestRender();
305
507
  return new Promise<AskUserResult>((resolve) => {
@@ -325,10 +527,6 @@ class AskUserOverlayView implements Component {
325
527
  return this.phase === "asking";
326
528
  }
327
529
 
328
- currentSurface(): AskUserSurface {
329
- return this.surface;
330
- }
331
-
332
530
  decisionTitle(): string {
333
531
  return this.presentation.title;
334
532
  }
@@ -339,29 +537,33 @@ class AskUserOverlayView implements Component {
339
537
 
340
538
  invalidate(): void {
341
539
  this.list?.invalidate();
342
- this.input?.invalidate();
540
+ this.text?.invalidate();
343
541
  }
344
542
 
543
+ get keyboardScope(): "edit" | "review" {
544
+ return this.currentState()?.mode === "text" ? "edit" : "review";
545
+ }
546
+ undoInput(): boolean {
547
+ if (this.currentState()?.mode !== "text") return false;
548
+ this.text?.applyEdit?.("undo");
549
+ return true;
550
+ }
345
551
  handleInput(data: string): void {
346
552
  if (this.phase !== "asking") return;
347
553
  const question = this.currentQuestion();
348
554
  const state = this.currentState();
349
555
  if (!question || !state) return;
350
556
 
351
- // Scrolling the transcript must not cost the operator their place in the
557
+ // Scrolling the question must not cost the operator their place in the
352
558
  // options, so the region has keys of its own that neither control claims.
353
559
  if (this.handleScrollInput(data, state.mode)) return;
354
560
 
355
561
  if (state.mode === "text") {
356
- if (this.isTextModePreviousKey(data)) {
357
- this.goToRelativeQuestion(-1);
562
+ if (matchesKey(data, "escape")) {
563
+ this.escapeFromTextInput(question, state);
358
564
  return;
359
565
  }
360
- if (this.isTextModeNextKey(data)) {
361
- this.goToRelativeQuestion(1);
362
- return;
363
- }
364
- this.input?.handleInput(data);
566
+ this.text?.handleInput(data);
365
567
  return;
366
568
  }
367
569
 
@@ -385,17 +587,25 @@ class AskUserOverlayView implements Component {
385
587
  this.chooseFocusedAndOpenText(question, state);
386
588
  return;
387
589
  }
590
+ if (data === "a" && this.ledgerEntries().length > 0) {
591
+ this.ledgerExpanded = !this.ledgerExpanded;
592
+ this.deps.requestRender();
593
+ return;
594
+ }
595
+ if (data === "?") {
596
+ this.detailsExpanded = !this.detailsExpanded;
597
+ this.deps.requestRender();
598
+ return;
599
+ }
388
600
  this.list?.handleInput(data);
389
601
  }
390
602
 
391
603
  /**
392
- * The body, sized to what it has to say.
393
- *
394
- * Nothing pads to a target height any more. A two-option confirmation is six
395
- * rows and draws six rows; the round strip and the answer ledger are what
396
- * make an interview tall, not filler. The trailing slice is the guarantee the
397
- * compact bar rests on: whatever the question and its options come to, the
398
- * body never returns more rows than its surface owns.
604
+ * The body, top to bottom: where the round stands, the question, its
605
+ * options or the answer field, the status, and the answers so far. The
606
+ * question region is the one part that scrolls; everything the operator has
607
+ * to act on stays on screen at its full row cost, and the box draws only the
608
+ * rows it needs.
399
609
  */
400
610
  render(width: number): string[] {
401
611
  const safeWidth = Math.max(1, width);
@@ -404,102 +614,59 @@ class AskUserOverlayView implements Component {
404
614
  const question = this.currentQuestion();
405
615
  if (!question) return [clioTheme().fg("muted", "No questions.")];
406
616
 
407
- if (this.surface === "interview") return this.renderInterview(question, safeWidth, maxRows);
408
-
409
- this.contentOverflows = false;
410
- const controlLines = this.renderControlLines(safeWidth);
411
- const statusLines =
412
- this.status.length > 0 ? ["", ...wrapTextWithAnsi(clioTheme().fg("dim", this.status), safeWidth)] : [];
413
- const headerLine = this.renderQuestionHeader(question, safeWidth);
414
- const headerLines = headerLine.length > 0 ? [headerLine] : [];
415
- const bottom = [...statusLines, "", ...controlLines];
416
- const topBudget = Math.max(1, maxRows - bottom.length);
417
- const contextLines = this.renderDecisionContext(safeWidth);
418
- const top = [
419
- contextLines[0] ?? "",
420
- ...headerLines,
421
- ...wrapTextWithAnsi(question.question, safeWidth),
422
- ...contextLines.slice(1),
423
- ].slice(0, topBudget);
424
-
425
- return [...top, ...bottom].slice(0, maxRows);
426
- }
427
-
428
- /**
429
- * The interview, as a workspace with fixed chrome and a scrolling middle.
430
- *
431
- * The round strip and the question header hold the top; the status line and
432
- * the active control hold the bottom, at their full row cost, so the thing the
433
- * operator has to act on is never the thing that gets clipped. The question
434
- * text and the answer ledger share whatever is left and scroll through it.
435
- * Everything is re-derived per render, so a resize is just the next frame.
436
- */
437
- private renderInterview(question: AskUserQuestion, width: number, maxRows: number): string[] {
438
- const theme = clioTheme();
439
- const controlLines = this.renderControlLines(width);
440
- const statusLines = this.status.length > 0 ? ["", ...wrapTextWithAnsi(theme.fg("dim", this.status), width)] : [];
441
- const headerLine = this.renderQuestionHeader(question, width);
442
- const headerLines = headerLine.length > 0 ? [headerLine] : [];
443
- const stripLines = [this.renderQuestionStrip(width), ""];
444
- const content = [
445
- ...wrapTextWithAnsi(question.question, width),
446
- "",
447
- ...this.renderDecisionContext(width),
448
- ...this.renderAnswerLedger(width),
449
- ];
617
+ const strip = this.renderQuestionStrip(safeWidth);
618
+ const header = this.renderQuestionHeader(question, safeWidth);
619
+ const details = this.renderDecisionContext(safeWidth);
620
+ const status = this.status.length > 0 ? wrapTextWithAnsi(clioTheme().fg("dim", this.status), safeWidth) : [];
621
+ const body = formatAskUserQuestion(question.question, safeWidth);
622
+ const fixedTop = [...strip, ...header, ...details];
623
+ // The control is sized after the question has claimed its minimum, so a
624
+ // long option list cannot push a short question off the box.
625
+ const controlBudget = Math.max(
626
+ 4,
627
+ maxRows - fixedTop.length - 1 - status.length - Math.min(body.length, MIN_QUESTION_ROWS) - 1,
628
+ );
629
+ this.optionDetailRows = [];
630
+ const control = this.renderControlLines(safeWidth, controlBudget);
631
+ body.push(...this.optionDetailRows);
632
+ const ledgerBudget = Math.max(
633
+ 1,
634
+ maxRows - fixedTop.length - 1 - control.length - status.length - Math.min(body.length, MIN_QUESTION_ROWS),
635
+ );
636
+ const ledger = this.renderAnswerLedger(safeWidth, ledgerBudget);
637
+ const fixed = fixedTop.length + 1 + control.length + status.length + ledger.length;
450
638
 
451
- // Chrome sheds from the outside in when the terminal is too short to carry
452
- // all of it: the round strip first, then the header, then the status, and
453
- // the control only if a terminal is short enough that nothing else is left.
454
- let top = [...stripLines, ...headerLines];
455
- let bottom = [...statusLines, "", ...controlLines];
456
- const region = (): number => maxRows - top.length - bottom.length;
457
- if (region() < 1) top = [...headerLines];
458
- if (region() < 1) top = [];
459
- if (region() < 1) bottom = ["", ...controlLines];
460
- if (region() < 1) bottom = [...controlLines];
461
- const regionRows = Math.max(1, region());
462
-
463
- const overflows = content.length > regionRows;
464
- this.contentOverflows = overflows;
465
- this.contentTotalRows = content.length;
466
- // One row of the region pays for the indicator when there is more than the
467
- // region holds, so the marker never covers a row it is reporting on.
639
+ const regionRows = Math.max(1, Math.min(body.length, maxRows - fixed));
640
+ const overflows = body.length > regionRows;
641
+ this.questionOverflows = overflows;
642
+ this.questionTotalRows = body.length;
468
643
  const viewRows = overflows ? Math.max(1, regionRows - 1) : regionRows;
469
- this.contentRegionHeight = viewRows;
470
- this.contentScrollOffset = Math.max(0, Math.min(this.contentScrollOffset, Math.max(0, content.length - viewRows)));
471
-
472
- const visible = content.slice(this.contentScrollOffset, this.contentScrollOffset + viewRows);
473
- const middle = [...visible];
474
- if (overflows) middle.push(fitLine(theme.fg("dim", this.scrollIndicator(content.length, viewRows)), width));
475
- while (middle.length < regionRows) middle.push("");
644
+ this.questionRegionRows = viewRows;
645
+ this.questionScroll = Math.max(0, Math.min(this.questionScroll, Math.max(0, body.length - viewRows)));
646
+ const visible = body.slice(this.questionScroll, this.questionScroll + viewRows);
647
+ if (overflows) visible.push(fitLine(clioTheme().fg("dim", this.scrollIndicator(body.length, viewRows)), safeWidth));
476
648
 
477
- return [...top, ...middle, ...bottom].slice(0, maxRows);
649
+ return [...fixedTop, ...visible, "", ...control, ...status, ...ledger].slice(0, maxRows);
478
650
  }
479
651
 
480
652
  private scrollIndicator(total: number, viewRows: number): string {
481
- const above = this.contentScrollOffset;
653
+ const above = this.questionScroll;
482
654
  const below = Math.max(0, total - viewRows - above);
483
655
  const parts: string[] = [];
484
- if (above > 0) parts.push(`↑ ${above} more`);
485
- if (below > 0) parts.push(`↓ ${below} more`);
486
- parts.push(`(${Math.min(total, above + viewRows)}/${total})`);
656
+ if (above > 0) parts.push(`${GLYPH.up} ${above} more`);
657
+ if (below > 0) parts.push(`${GLYPH.down} ${below} more`);
658
+ parts.push("PgUp/PgDn");
487
659
  return parts.join(" ");
488
660
  }
489
661
 
490
- /** True when the key belonged to the scroll region rather than to a control. */
662
+ /** True when the key belonged to the question region rather than to a control. */
491
663
  private handleScrollInput(data: string, mode: Mode): boolean {
492
- if (this.surface !== "interview") return false;
664
+ if (!this.questionOverflows) return false;
493
665
  const action = this.scrollAction(data, mode);
494
666
  if (!action) return false;
495
- const next = nextContentScrollOffset(
496
- this.contentScrollOffset,
497
- this.contentTotalRows,
498
- this.contentRegionHeight,
499
- action,
500
- );
501
- if (next !== this.contentScrollOffset) {
502
- this.contentScrollOffset = next;
667
+ const next = nextContentScrollOffset(this.questionScroll, this.questionTotalRows, this.questionRegionRows, action);
668
+ if (next !== this.questionScroll) {
669
+ this.questionScroll = next;
503
670
  this.deps.requestRender();
504
671
  }
505
672
  return true;
@@ -515,99 +682,83 @@ class AskUserOverlayView implements Component {
515
682
  return null;
516
683
  }
517
684
 
518
- private resetContentScroll(): void {
519
- this.contentScrollOffset = 0;
685
+ private resetQuestionScroll(): void {
686
+ this.questionScroll = 0;
520
687
  }
521
688
 
522
689
  footerHint(): string {
523
- if (this.phase !== "asking") {
524
- return buildHint([]);
525
- }
690
+ if (this.phase !== "asking") return buildHint([]);
526
691
  const question = this.currentQuestion();
527
692
  const state = this.currentState();
528
693
  if (!question || !state) return buildHint([]);
529
694
  const recordAnswer =
530
695
  this.presentation.requiredActions.find((action) => action.id === "record-answer")?.label.toLowerCase() ??
531
696
  "record answer";
697
+ const scroll: HintEntry[] = this.questionOverflows ? [{ key: "PgUp/PgDn", verb: "scroll" }] : [];
532
698
  if (state.mode === "text") {
533
699
  // Esc means two different things depending on what is behind the field.
534
700
  // A question with options has a list to fall back to, so the key goes
535
701
  // back; a question without one has nothing behind it and the key still
536
702
  // leaves the interview. The footer says which one this is.
537
703
  const escapeVerb: OverlayEscVerb = questionHasOptions(question) ? "back" : "close";
538
- return this.questions.length > 1
539
- ? this.withScrollHint(
540
- [
541
- { key: "Enter", verb: recordAnswer },
542
- { key: "Alt+Left/Right", verb: "question" },
543
- ],
544
- escapeVerb,
545
- )
546
- : this.withScrollHint([{ key: "Enter", verb: recordAnswer }], escapeVerb);
704
+ const entries: HintEntry[] = [{ key: "Enter", verb: recordAnswer }];
705
+ if (this.deps.tui) entries.push({ key: getKeybindings().getKeys("tui.input.newLine").join("/"), verb: "newline" });
706
+ return buildHint([...entries, ...scroll], escapeVerb);
547
707
  }
548
- // The classified action label distinguishes recording an answer from
549
- // granting authority. Esc on the option list is the way out of the whole
550
- // interview, which is the one place the product spells `close`.
708
+ const entries: HintEntry[] = [];
709
+ if (this.questions.length > 1) entries.push({ key: "Left/Right", verb: "question" });
710
+ if (question.multi_select === true) entries.push({ key: "Space", verb: "toggle" });
551
711
  // `t` is on every select footer because the operator cannot tell from a
552
712
  // label whether the option needs a figure attached until they read it.
553
- const addText: HintEntry = { key: "t", verb: "add text", short: "text" };
554
- if (question.multi_select === true) {
555
- return this.questions.length > 1
556
- ? this.withScrollHint([
557
- { key: "Left/Right", verb: "question" },
558
- { key: "Space", verb: "toggle" },
559
- addText,
560
- { key: "Enter", verb: recordAnswer },
561
- ])
562
- : this.withScrollHint([{ key: "Space", verb: "toggle" }, addText, { key: "Enter", verb: recordAnswer }]);
563
- }
564
- return this.questions.length > 1
565
- ? this.withScrollHint([{ key: "Left/Right", verb: "question" }, addText, { key: "Enter", verb: recordAnswer }])
566
- : this.withScrollHint([addText, { key: "Enter", verb: recordAnswer }]);
567
- }
568
-
569
- private renderDecisionContext(width: number): string[] {
570
- const theme = clioTheme();
571
- const tier = theme.style(this.presentation.semanticToken, this.presentation.tierLabel, { bold: true });
572
- const requested = theme.fg("dim", `requested by ${this.presentation.requestedByCopy}`);
573
- const effect = `${theme.fg("dim", "Effect:")} ${theme.fg("muted", this.presentation.authorizationCopy)}`;
574
- return [
575
- fitLine(`${tier}${dotSep(theme)}${requested}`, width),
576
- ...wrapTextWithAnsi(effect, width).map((line) => fitLine(line, width)),
577
- ...wrapTextWithAnsi(theme.fg("muted", this.presentation.reversibilityCopy), width).map((line) =>
578
- fitLine(line, width),
579
- ),
580
- ];
713
+ entries.push({ key: "t", verb: "add text", short: "text" });
714
+ entries.push({ key: "Enter", verb: recordAnswer });
715
+ if (this.ledgerEntries().length > 0) entries.push({ key: "a", verb: "answers" });
716
+ entries.push({ key: "?", verb: "details" });
717
+ return buildHint([...entries, ...scroll]);
581
718
  }
582
719
 
583
720
  /**
584
- * The keys for this question, plus the scroll keys when there is something to
585
- * scroll. A footer that always advertised PgUp would be advertising a key that
586
- * does nothing on most rounds.
721
+ * The classified context, folded.
722
+ *
723
+ * Tier, requester, effect, and reversibility used to take four rows above
724
+ * every question, and for a local conversational answer they say nothing the
725
+ * title ("Answer a question") does not. They render on `?`; a tier that is
726
+ * not the default one keeps its one-line identity on screen because that is
727
+ * the row that says the answer reaches outside the workspace.
587
728
  */
588
- private withScrollHint(entries: ReadonlyArray<HintEntry>, esc: OverlayEscVerb = "close"): string {
589
- if (this.surface !== "interview" || !this.contentOverflows) return buildHint(entries, esc);
590
- return buildHint([...entries, { key: "PgUp/PgDn", verb: "scroll" }], esc);
591
- }
592
-
593
- private setSurface(next: AskUserSurface): void {
594
- if (this.surface === next) return;
595
- this.surface = next;
596
- this.deps.onSurface(next);
729
+ private renderDecisionContext(width: number): string[] {
730
+ const theme = clioTheme();
731
+ const lines: string[] = [];
732
+ const defaultTier = this.presentation.tier === DEFAULT_ASK_USER_PRESENTATION.tier;
733
+ if (!defaultTier || this.detailsExpanded) {
734
+ const tier = theme.style(this.presentation.semanticToken, this.presentation.tierLabel, { bold: true });
735
+ const requested = theme.fg("dim", `requested by ${this.presentation.requestedByCopy}`);
736
+ lines.push(fitLine(`${tier}${dotSep(theme)}${requested}`, width));
737
+ }
738
+ if (this.detailsExpanded) {
739
+ lines.push(
740
+ ...wrapTextWithAnsi(
741
+ `${theme.fg("dim", "Effect:")} ${theme.fg("muted", this.presentation.authorizationCopy)}`,
742
+ width,
743
+ ),
744
+ ...wrapTextWithAnsi(theme.fg("muted", this.presentation.reversibilityCopy), width),
745
+ "",
746
+ );
747
+ }
748
+ return lines;
597
749
  }
598
750
 
599
751
  private finish(result: AskUserResult): void {
600
752
  const resolve = this.resolveCurrent;
601
753
  this.resolveCurrent = null;
602
754
  this.list = null;
603
- this.input = null;
755
+ this.text = null;
604
756
  this.status = "";
605
- if (result.cancelled !== true) this.history.push(...result.answers);
757
+ if (result.cancelled !== true) {
758
+ this.history.push(...result.answers);
759
+ this.roundsAnswered += 1;
760
+ }
606
761
  if (this.phase !== "closed") this.phase = "waiting";
607
- // A round that follows an answered one is an interview, so the box grows
608
- // here rather than at the next `begin`. The waiting frame between rounds
609
- // is then already the size of the round it is waiting for.
610
- if (this.history.length > 0) this.setSurface("interview");
611
762
  this.deps.requestRender();
612
763
  resolve?.(result);
613
764
  }
@@ -615,56 +766,27 @@ class AskUserOverlayView implements Component {
615
766
  private renderWaiting(width: number, maxRows: number): string[] {
616
767
  const theme = clioTheme();
617
768
  const lines = [
618
- screenTitle(theme, "Interview"),
619
- "",
620
- ...wrapTextWithAnsi(
621
- theme.fg(
769
+ fitLine(
770
+ `${screenTitle(theme, "Interview")}${dotSep(theme)}${theme.fg(
622
771
  "muted",
623
- this.history.length > 0
624
- ? "Answer sent. Waiting for Clio to prepare the next interview question."
625
- : "Waiting for Clio to prepare the interview.",
626
- ),
772
+ this.history.length > 0 ? "answer sent · waiting for the next question" : "waiting for the first question",
773
+ )}`,
627
774
  width,
628
775
  ),
629
776
  ];
630
- if (this.history.length > 0) {
631
- lines.push("", theme.fg("dim", "── Collected answers"));
632
- for (let index = 0; index < this.history.length; index += 1) {
633
- const answer = this.history[index];
634
- if (!answer) continue;
635
- lines.push(...wrapLabeledValue(`${theme.fg("dim", `${index + 1}.`)} `, theme.fg("muted", answer.answer), width));
636
- }
637
- }
638
- return lines.map((line) => fitLine(line, width)).slice(0, maxRows);
777
+ const ledger = this.renderAnswerLedger(width, Math.max(1, maxRows - lines.length));
778
+ return [...lines, ...ledger].slice(0, maxRows);
639
779
  }
640
780
 
641
781
  /**
642
- * Rows the body may draw on this surface.
643
- *
644
- * The frame budgets the same number, because its `maxHeight` is this cap plus
645
- * the two border rows and both clamp to the terminal the same way. That is
646
- * what keeps `fitBody`'s "… N more rows" off the compact bar: the body never
647
- * hands the frame more than the frame is prepared to draw.
782
+ * Rows the body may draw. The frame budgets the same number from the live
783
+ * terminal, so the body never hands the frame more than it can draw and the
784
+ * frame's "… N more rows" cut never fires on a decision.
648
785
  */
649
786
  private maxInnerRows(): number {
650
- const cap = ASK_USER_SURFACE_GEOMETRY[this.surface].maxInnerRows;
651
787
  const rows = this.deps.getTerminalRows();
652
- // A zero cap is the interview's "every row the frame has", which is the
653
- // same number the frame budgets itself when the mount passes no maxHeight.
654
- if (!Number.isFinite(rows) || rows <= 0) return cap > 0 ? cap : 1;
655
- const available = Math.max(1, Math.floor(rows) - ASK_USER_FRAME_AND_MARGIN_ROWS);
656
- return cap > 0 ? Math.min(cap, available) : available;
657
- }
658
-
659
- private maxVisibleOptions(): number {
660
- const inner = this.maxInnerRows();
661
- // The interview's option list is budgeted off the live row count rather
662
- // than a fixed heuristic: the strip, header, blank, status, blank and one
663
- // row of question are what the control shares the workspace with.
664
- if (this.surface === "interview") {
665
- return Math.max(MIN_VISIBLE_OPTIONS, Math.min(MAX_VISIBLE_OPTIONS, inner - INTERVIEW_CHROME_ROWS));
666
- }
667
- return Math.max(1, Math.min(MAX_VISIBLE_OPTIONS, inner - 4));
788
+ if (!Number.isFinite(rows) || rows <= 0) return 40;
789
+ return Math.max(MIN_INNER_ROWS, Math.floor(rows) - ASK_USER_FRAME_AND_MARGIN_ROWS);
668
790
  }
669
791
 
670
792
  private currentQuestion(): AskUserQuestion | null {
@@ -683,14 +805,6 @@ class AskUserOverlayView implements Component {
683
805
  return this.questions.length > 1 && matchesKey(data, "right");
684
806
  }
685
807
 
686
- private isTextModePreviousKey(data: string): boolean {
687
- return this.questions.length > 1 && (matchesKey(data, "alt+left") || matchesKey(data, "ctrl+left"));
688
- }
689
-
690
- private isTextModeNextKey(data: string): boolean {
691
- return this.questions.length > 1 && (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right"));
692
- }
693
-
694
808
  /**
695
809
  * The key that adds typed text to the option under the cursor.
696
810
  *
@@ -711,212 +825,141 @@ class AskUserOverlayView implements Component {
711
825
  private chooseFocusedAndOpenText(question: AskUserQuestion, state: QuestionState): void {
712
826
  const current = this.list?.getSelectedItem();
713
827
  const optionIndex = current && current.value !== "other" ? optionIndexFromValue(current.value) : null;
828
+ let label: string | undefined;
829
+ if (optionIndex === null && !question.multi_select) state.selected = new Set();
714
830
  if (optionIndex !== null) {
715
831
  if (question.multi_select === true) state.selected.add(optionIndex);
716
832
  else state.selected = new Set<number>([optionIndex]);
717
833
  state.focusedValue = `option:${optionIndex}`;
834
+ label = question.options?.[optionIndex]?.label;
718
835
  }
719
- this.openTextInput(state.selected.size > 0 ? "Add your answer to the chosen option" : "Other answer");
836
+ this.openTextInput(label ? `Your answer, with "${label}"` : "Your answer");
720
837
  }
721
838
 
722
839
  /**
723
- * The row above the question, or nothing.
724
- *
725
- * `Question 1/1` was a counter over a set of one, printed on every single
726
- * confirmation. A one-question surface shows the question's own header if it
727
- * has one and skips the row entirely if it does not.
840
+ * The row above the question: its header, and the round for a phased
841
+ * interview. `Question 1/1` was a counter over a set of one, printed on every
842
+ * confirmation; a round of several questions names them in the strip instead.
728
843
  */
729
- private renderQuestionHeader(question: AskUserQuestion, width: number): string {
844
+ private renderQuestionHeader(question: AskUserQuestion, width: number): string[] {
730
845
  const theme = clioTheme();
731
846
  const parts: string[] = [];
732
- if (this.questions.length > 1) parts.push(theme.fg("dim", `Question ${this.index + 1}/${this.questions.length}`));
733
847
  if (question.header) parts.push(screenTitle(theme, question.header));
848
+ if (this.questions.length <= 1 && this.roundsAnswered > 0) {
849
+ parts.push(theme.fg("dim", `Round ${this.roundsAnswered + 1}`));
850
+ }
734
851
  if (this.currentState()?.answer.trim()) parts.push(theme.fg("muted", "answered"));
735
- return parts.length > 0 ? fitLine(parts.join(dotSep(theme)), width) : "";
852
+ return parts.length > 0 ? wrapTextWithAnsi(parts.join(dotSep(theme)), width) : [];
736
853
  }
737
854
 
738
- private renderQuestionStrip(width: number): string {
855
+ /**
856
+ * The questions of this round, by name.
857
+ *
858
+ * Four fixed slots at 80 columns left three characters of each header
859
+ * ("Con…", "Sam…"), which is the strip saying nothing. The names now flow on
860
+ * one row and wrap to a second when they must; a question that has been
861
+ * answered carries a check, the active one carries the cursor.
862
+ */
863
+ private renderQuestionStrip(width: number): string[] {
739
864
  const theme = clioTheme();
740
- const total = this.questions.length;
741
- // A single-question interview round is the phased shape: one question per
742
- // round, several rounds. Its position is the round number, since there is
743
- // no set of siblings to draw a strip across.
744
- if (total <= 1) {
745
- const round = theme.fg("dim", `Round ${this.history.length + 1}`);
746
- return fitLine(`${screenTitle(theme, "Interview")}${dotSep(theme)}${round}`, width);
747
- }
748
- const gap = " ";
749
- const slotWidth = Math.max(10, Math.floor((width - visibleWidth(gap) * (total - 1)) / total));
865
+ if (this.questions.length <= 1) return [];
750
866
  const parts = this.questions.map((question, index) => {
751
867
  const state = this.states[index];
752
868
  const active = index === this.index;
753
- const marker = active ? theme.fg("accent", `${GLYPH.cursor} `) : " ";
754
- const answerState = state?.answer.trim() ? theme.fg("muted", "answered") : theme.fg("dim", "pending");
869
+ const answered = Boolean(state?.answer.trim());
870
+ const mark = answered ? `${theme.fg("success", GLYPH.ok)} ` : "";
871
+ const name = `Q${index + 1} ${compactTitle(question)}`;
755
872
  const title = active
756
- ? theme.style("accent", compactTitle(question), { bold: true })
757
- : theme.fg("muted", compactTitle(question));
758
- return fitLine(`${marker}${theme.fg("dim", `Q${index + 1}`)} ${answerState} ${title}`, slotWidth);
873
+ ? `${theme.fg("accent", GLYPH.cursor)} ${theme.style("accent", name, { bold: true })}`
874
+ : theme.fg(answered ? "muted" : "dim", name);
875
+ return `${mark}${title}`;
759
876
  });
760
- return fitLine(parts.join(gap), width);
877
+ const round = this.roundsAnswered > 0 ? [theme.fg("dim", `Round ${this.roundsAnswered + 1}`)] : [];
878
+ const strip = wrapTextWithAnsi([...round, ...parts].join(dotSep(theme)), width).slice(0, 2);
879
+ return [...strip, ""];
761
880
  }
762
881
 
763
- private renderControlLines(width: number): string[] {
882
+ private renderControlLines(width: number, rowBudget: number): string[] {
764
883
  this.ensureControl();
765
884
  const state = this.currentState();
766
- if (state?.mode === "text") return this.renderTextInput(width);
767
- return this.renderSelectControl(width);
885
+ if (state?.mode === "text") return this.text?.render(width) ?? [""];
886
+ return this.renderSelectControl(width, rowBudget);
887
+ }
888
+
889
+ /** Earlier rounds first, then this round's answered siblings. */
890
+ private ledgerEntries(): Array<{ label: string; answer: string }> {
891
+ const entries: Array<{ label: string; answer: string }> = [];
892
+ this.history.forEach((answer, index) => {
893
+ entries.push({ label: `${index + 1}.`, answer: answer.answer });
894
+ });
895
+ if (this.questions.length > 1) {
896
+ this.states.forEach((state, index) => {
897
+ const answer = state.answer.trim();
898
+ if (answer.length > 0 && index !== this.index) entries.push({ label: `Q${index + 1}`, answer });
899
+ });
900
+ }
901
+ return entries;
768
902
  }
769
903
 
770
904
  /**
771
- * Everything this interview has captured, rounds already answered first.
905
+ * What the interview has captured, folded to one row.
772
906
  *
773
- * The ledger used to read only the current round's states, so a phased
774
- * interview asking one question per round showed nothing at all: each round
775
- * was a set of one and the summary bailed out. What the operator has already
776
- * told Clio is the reason the full surface exists.
907
+ * The full ledger used to sit between the question and its options and grow
908
+ * by the whole typed answer every round, so by the fourth question the
909
+ * options were twenty rows below the sentence they answered. The ledger is
910
+ * one dim row under the options until `a` opens it.
777
911
  */
778
- private renderAnswerLedger(width: number): string[] {
912
+ private renderAnswerLedger(width: number, rowBudget: number): string[] {
779
913
  const theme = clioTheme();
914
+ const entries = this.ledgerEntries();
915
+ if (entries.length === 0) return [];
916
+ const count = `${entries.length} earlier answer${entries.length === 1 ? "" : "s"}`;
917
+ if (!this.ledgerExpanded) return ["", fitLine(theme.fg("dim", `${count} · a to review`), width)];
780
918
  const rows: string[] = [];
781
- for (let index = 0; index < this.history.length; index += 1) {
782
- const answer = this.history[index];
783
- if (!answer) continue;
784
- rows.push(...wrapLabeledValue(`${theme.fg("dim", `${index + 1}.`)} `, theme.fg("muted", answer.answer), width));
785
- }
786
- // A round of one question needs no per-question roll: the strip already
787
- // names the round and the question is on screen above this.
788
- if (this.questions.length > 1) {
789
- for (let index = 0; index < this.questions.length; index += 1) {
790
- const answer = this.states[index]?.answer.trim();
791
- const value = answer && answer.length > 0 ? theme.fg("muted", answer) : theme.fg("dim", "pending");
792
- rows.push(...wrapLabeledValue(`${theme.fg("dim", `Q${index + 1}`)} `, value, width));
793
- }
919
+ for (const entry of entries) {
920
+ rows.push(...wrapLabeledValue(`${theme.fg("dim", entry.label)} `, theme.fg("muted", entry.answer), width));
794
921
  }
795
- if (rows.length === 0) return [];
796
- return ["", fitLine(theme.fg("dim", "── Answers"), width), ...rows];
797
- }
798
-
799
- private renderTextInput(width: number): string[] {
800
- const theme = clioTheme();
801
- return (this.input?.render(width) ?? [""]).map((line) =>
802
- fitLine(line.startsWith("> ") ? `${theme.fg("accent", `${GLYPH.cursor} `)}${line.slice(2)}` : line, width),
803
- );
922
+ const head = ["", fitLine(rule(theme, width, { left: "Answers", leftToken: "dim" }), width)];
923
+ const budget = Math.max(1, rowBudget - head.length);
924
+ if (rows.length <= budget) return [...head, ...rows];
925
+ const kept = rows.slice(0, Math.max(0, budget - 1));
926
+ return [
927
+ ...head,
928
+ ...kept,
929
+ fitLine(theme.fg("dim", `${GLYPH.ellipsis} ${rows.length - kept.length} more on /decisions`), width),
930
+ ];
804
931
  }
805
932
 
806
- private renderSelectControl(width: number): string[] {
933
+ private renderSelectControl(width: number, rowBudget: number): string[] {
807
934
  const question = this.currentQuestion();
808
935
  const state = this.currentState();
809
936
  const selectedItem = this.list?.getSelectedItem();
810
937
  if (!question || !state || !selectedItem) return [""];
811
- const theme = clioTheme();
812
938
  const items = optionItems(question, state.selected);
813
- const selectedIndex = Math.max(
939
+ const focusedIndex = Math.max(
814
940
  0,
815
941
  items.findIndex((item) => item.value === selectedItem.value),
816
942
  );
817
- const primaryColumnWidth = this.primaryColumnWidth(items, width);
818
- const acceptKey = question.multi_select === true ? "Space" : "Enter";
819
- // The focused option's description wraps in full, so the rows it needs come
820
- // out of the list rather than out of the sentence. Options the operator
821
- // cannot currently read are what a windowed list is for, and one arrow key
822
- // brings any of them back; a cut explanation has no such recovery.
823
- const focusedRows = this.renderOptionRow(
824
- items[selectedIndex] as SelectItem,
825
- true,
826
- width,
827
- primaryColumnWidth,
828
- acceptKey,
829
- ).length;
830
- const budget = Math.max(1, this.maxVisibleOptions() - (focusedRows - 1));
831
- const visibleCount = Math.min(budget, Math.max(1, items.length));
832
- const start = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), items.length - visibleCount));
833
- const end = Math.min(items.length, start + visibleCount);
834
- const lines: string[] = [];
835
-
836
- for (let index = start; index < end; index += 1) {
837
- const item = items[index];
838
- if (!item) continue;
839
- lines.push(...this.renderOptionRow(item, index === selectedIndex, width, primaryColumnWidth, acceptKey));
840
- }
841
-
842
- if (start > 0 || end < items.length) {
843
- lines.push(fitLine(theme.fg("dim", ` (${selectedIndex + 1}/${items.length})`), width));
844
- }
845
- return lines;
846
- }
847
-
848
- private primaryColumnWidth(items: ReadonlyArray<SelectItem>, width: number): number {
849
- const widest = items.reduce((max, item) => Math.max(max, visibleWidth(item.label) + 2), 0);
850
- const bounded = Math.max(24, Math.min(38, widest));
851
- return Math.max(1, Math.min(bounded, Math.max(1, width - 4)));
852
- }
853
-
854
- /**
855
- * One option, as the rows it needs.
856
- *
857
- * The focused option's description wraps under its own column and is never
858
- * cut: a description is what the operator is choosing between, and half of
859
- * one reads as the whole thing. An unfocused row stays a one-line table cell
860
- * whose ellipsis says there is more, which the operator reads by moving to
861
- * it.
862
- */
863
- private renderOptionRow(
864
- item: SelectItem,
865
- selected: boolean,
866
- width: number,
867
- primaryColumnWidth: number,
868
- acceptKey: string,
869
- ): string[] {
870
- const theme = clioTheme();
871
- const prefix = selected ? theme.fg("accent", `${GLYPH.cursor} `) : " ";
872
- // The key that answers rides on the focused row. An operator whose eyes are
873
- // already on the option they want should not have to travel to the footer
874
- // to learn what commits it.
875
- const affordance = selected ? ` ${theme.style("accent", `[${acceptKey}]`, { bold: true })}` : "";
876
- const available = Math.max(1, width - 2 - visibleWidth(affordance));
877
- const description = item.description?.replace(/[\r\n]+/g, " ").trim();
878
-
879
- if (!description || width <= 40) {
880
- const label = truncateToWidth(item.label, available, ELLIPSIS, false);
881
- const body = selected ? theme.style("accent", label, { bold: true }) : label;
882
- return [fitLine(`${prefix}${body}${affordance}`, width)];
883
- }
884
-
885
- const labelWidth = Math.max(1, Math.min(primaryColumnWidth - 2, available - 4));
886
- const label = truncateToWidth(item.label, labelWidth, ELLIPSIS, false);
887
- const spacing = " ".repeat(Math.max(1, primaryColumnWidth - visibleWidth(label)));
888
- const descriptionWidth = Math.max(1, available - visibleWidth(label) - visibleWidth(spacing));
889
- if (!selected) {
890
- const desc = truncateToWidth(description, descriptionWidth, ELLIPSIS, false);
891
- return [fitLine(`${prefix}${label}${theme.fg("muted", `${spacing}${desc}`)}`, width)];
892
- }
893
- // A gutter this narrow wraps a sentence into slivers and breaks words, so
894
- // below the floor the description drops to its own full-width lines under
895
- // the label instead of squeezing into the second column.
896
- if (descriptionWidth < MIN_DESCRIPTION_COLUMN) {
897
- const indent = " ";
898
- const body = theme.style("accent", label, { bold: true });
899
- return [
900
- fitLine(`${prefix}${body}${affordance}`, width),
901
- ...wrapTextWithAnsi(description, Math.max(1, width - indent.length)).map((line) =>
902
- fitLine(`${indent}${theme.style("accent", line, { bold: true })}`, width),
903
- ),
943
+ const layout = layoutOptionRows(items, focusedIndex, width);
944
+ if ((layout.rows[focusedIndex]?.length ?? 0) >= rowBudget) {
945
+ this.optionDetailRows = [
946
+ "",
947
+ ...wrapTextWithAnsi(`Option details: ${selectedItem.label}`, width),
948
+ ...wrapTextWithAnsi(selectedItem.description ?? "", width),
904
949
  ];
950
+ layout.rows[focusedIndex] = wrapTextWithAnsi(
951
+ `${GLYPH.cursor} ${selectedItem.label} (details above; PgUp/PgDn)`,
952
+ width,
953
+ );
905
954
  }
906
- const wrapped = wrapTextWithAnsi(description, descriptionWidth);
907
- const head = wrapped[0] ?? "";
908
- const indent = " ".repeat(visibleWidth(prefix) + visibleWidth(label) + visibleWidth(spacing));
909
- return [
910
- fitLine(`${prefix}${theme.style("accent", `${label}${spacing}${head}`, { bold: true })}${affordance}`, width),
911
- ...wrapped.slice(1).map((line) => fitLine(`${indent}${theme.style("accent", line, { bold: true })}`, width)),
912
- ];
955
+ return windowOptionRows(layout, focusedIndex, rowBudget, width);
913
956
  }
914
957
 
915
958
  private ensureControl(): void {
916
959
  const question = this.currentQuestion();
917
960
  const state = this.currentState();
918
961
  if (!question || !state) return;
919
- if (state.mode === "text" && !this.input) this.rebuildTextInput(question, state);
962
+ if (state.mode === "text" && !this.text) this.rebuildTextInput(question, state, "Your answer");
920
963
  if (state.mode === "select" && !this.list) this.rebuildSelectList(question, state);
921
964
  }
922
965
 
@@ -924,59 +967,67 @@ class AskUserOverlayView implements Component {
924
967
  const question = this.currentQuestion();
925
968
  const state = this.currentState();
926
969
  if (!question || !state) {
927
- this.input = null;
970
+ this.text = null;
928
971
  this.list = null;
929
972
  return;
930
973
  }
931
- if (state.mode === "text") this.rebuildTextInput(question, state);
974
+ if (state.mode === "text") this.rebuildTextInput(question, state, "Your answer");
932
975
  else this.rebuildSelectList(question, state);
933
976
  }
934
977
 
935
- private rebuildTextInput(question: AskUserQuestion, state: QuestionState): void {
936
- const activeInput = new Input();
937
- activeInput.setValue(state.inputValue || state.customAnswer || state.answer);
938
- activeInput.onSubmit = (value) => {
939
- const answer = value.trim();
940
- if (answer.length === 0) {
941
- this.status = "Enter an answer or press Esc to cancel.";
942
- this.deps.requestRender();
943
- return;
944
- }
945
- state.inputValue = answer;
946
- // Verbatim, before any joining or trimming the display line does. The
947
- // typed text is the answer; the one-line `answer` is a rendering of it.
948
- state.rawValue = value;
949
- if (questionHasOptions(question) && question.multi_select === true) {
950
- state.customAnswer = answer;
951
- this.commitCurrentAnswer();
952
- return;
953
- }
954
- state.customAnswer = questionHasOptions(question) ? answer : "";
955
- // The chosen labels and the typed text compose, so a question answered
956
- // with an option that says "I will type it" keeps both. With nothing
957
- // selected, which is the implicit Other path, this is the text alone.
958
- state.answer = questionHasOptions(question) ? answerText(question, state.selected, state.customAnswer) : answer;
959
- this.finishIfCompleteOrAdvance();
960
- };
961
- activeInput.onEscape = () => this.escapeFromTextInput(question, state);
962
- this.input = activeInput;
978
+ private submitText(question: AskUserQuestion, state: QuestionState, value: string): void {
979
+ const answer = value.trim();
980
+ if (answer.length === 0) {
981
+ this.status = "Enter an answer or press Esc to cancel.";
982
+ this.deps.requestRender();
983
+ return;
984
+ }
985
+ state.inputValue = answer;
986
+ // Verbatim, before any joining or trimming the display line does. The
987
+ // typed text is the answer; the one-line `answer` is a rendering of it.
988
+ state.rawValue = value;
989
+ if (questionHasOptions(question) && question.multi_select === true) {
990
+ state.customAnswer = answer;
991
+ this.commitCurrentAnswer();
992
+ return;
993
+ }
994
+ state.customAnswer = questionHasOptions(question) ? answer : "";
995
+ // The chosen labels and the typed text compose, so a question answered
996
+ // with an option that says "I will type it" keeps both. With nothing
997
+ // selected, which is the implicit Other path, this is the text alone.
998
+ state.answer = questionHasOptions(question)
999
+ ? answerText(question, state.selected, state.customAnswer)
1000
+ : answer.replace(/\s*\n\s*/g, " ");
1001
+ this.finishIfCompleteOrAdvance();
1002
+ }
1003
+
1004
+ private rebuildTextInput(question: AskUserQuestion, state: QuestionState, caption: string): void {
1005
+ const initial = state.inputValue || state.customAnswer || state.answer;
1006
+ if (this.deps.tui) {
1007
+ const editor = new AnswerEditor(this.deps.tui);
1008
+ editor.caption = caption;
1009
+ editor.setText(initial);
1010
+ editor.onSubmit = (value) => this.submitText(question, state, value);
1011
+ this.text = editor;
1012
+ } else {
1013
+ const input = new Input();
1014
+ input.setValue(initial);
1015
+ input.onSubmit = (value) => this.submitText(question, state, value);
1016
+ this.text = new InputTextControl(input, caption);
1017
+ }
963
1018
  this.list = null;
964
1019
  }
965
1020
 
966
1021
  private rebuildSelectList(question: AskUserQuestion, state: QuestionState): void {
967
1022
  const items = optionItems(question, state.selected);
968
- const activeList = new SelectList(
969
- items,
970
- Math.min(this.maxVisibleOptions(), Math.max(1, items.length)),
971
- DEFAULT_SELECT_THEME,
972
- {
973
- minPrimaryColumnWidth: 24,
974
- maxPrimaryColumnWidth: 38,
975
- },
976
- );
1023
+ const activeList = new SelectList(items, Math.max(1, items.length), DEFAULT_SELECT_THEME, {
1024
+ minPrimaryColumnWidth: MIN_LABEL_COLUMN,
1025
+ maxPrimaryColumnWidth: MAX_LABEL_COLUMN,
1026
+ });
977
1027
  activeList.onSelect = (item) => {
978
1028
  if (item.value === "other") {
979
- this.openTextInput("Other answer");
1029
+ if (!question.multi_select) state.selected = new Set();
1030
+ this.openTextInput("Your answer");
980
1031
  return;
981
1032
  }
982
1033
  const optionIndex = optionIndexFromValue(item.value);
@@ -985,6 +1036,11 @@ class AskUserOverlayView implements Component {
985
1036
  this.toggleSelectionIndex(question, state, optionIndex);
986
1037
  return;
987
1038
  }
1039
+ const label = question.options?.[optionIndex]?.label ?? item.label;
1040
+ if (optionAsksForText(label)) {
1041
+ this.chooseFocusedAndOpenText(question, state);
1042
+ return;
1043
+ }
988
1044
  state.selected = new Set<number>([optionIndex]);
989
1045
  // Choosing a plain option is a label-only answer, so the typed text
990
1046
  // clears with the choice: it was given for a label the operator has
@@ -1004,7 +1060,7 @@ class AskUserOverlayView implements Component {
1004
1060
  };
1005
1061
  const selectedIndex = this.preferredSelectedIndex(question, state, items);
1006
1062
  if (selectedIndex >= 0) activeList.setSelectedIndex(selectedIndex);
1007
- this.input = null;
1063
+ this.text = null;
1008
1064
  this.list = activeList;
1009
1065
  }
1010
1066
 
@@ -1049,6 +1105,7 @@ class AskUserOverlayView implements Component {
1049
1105
  return;
1050
1106
  }
1051
1107
  state.inputValue = "";
1108
+ state.selected = new Set(state.committedSelected);
1052
1109
  state.mode = "select";
1053
1110
  this.status = "";
1054
1111
  this.rebuildSelectList(question, state);
@@ -1076,14 +1133,14 @@ class AskUserOverlayView implements Component {
1076
1133
  state.mode = "select";
1077
1134
  }
1078
1135
 
1079
- private openTextInput(nextStatus: string): void {
1136
+ private openTextInput(caption: string): void {
1080
1137
  const state = this.currentState();
1081
1138
  const question = this.currentQuestion();
1082
1139
  if (!state || !question) return;
1083
1140
  this.syncActiveControl();
1084
1141
  state.mode = "text";
1085
- this.status = nextStatus;
1086
- this.rebuildTextInput(question, state);
1142
+ this.status = "";
1143
+ this.rebuildTextInput(question, state, caption);
1087
1144
  this.deps.requestRender();
1088
1145
  }
1089
1146
 
@@ -1091,7 +1148,7 @@ class AskUserOverlayView implements Component {
1091
1148
  const current = this.list?.getSelectedItem();
1092
1149
  if (!current) return;
1093
1150
  if (current.value === "other") {
1094
- this.openTextInput("Other answer");
1151
+ this.openTextInput("Your answer");
1095
1152
  return;
1096
1153
  }
1097
1154
  const optionIndex = optionIndexFromValue(current.value);
@@ -1111,7 +1168,7 @@ class AskUserOverlayView implements Component {
1111
1168
  private commitMultiSelectOrOpenOther(state: QuestionState): void {
1112
1169
  const current = this.list?.getSelectedItem();
1113
1170
  if (current?.value === "other") {
1114
- this.openTextInput("Other answer");
1171
+ this.openTextInput("Your answer");
1115
1172
  return;
1116
1173
  }
1117
1174
  const optionIndex = current ? optionIndexFromValue(current.value) : null;
@@ -1138,6 +1195,8 @@ class AskUserOverlayView implements Component {
1138
1195
 
1139
1196
  private finishIfCompleteOrAdvance(): void {
1140
1197
  this.syncActiveControl();
1198
+ const current = this.currentState();
1199
+ if (current) current.committedSelected = new Set(current.selected);
1141
1200
  if (this.allAnswered()) {
1142
1201
  this.finish({ answers: this.answers() });
1143
1202
  return;
@@ -1145,7 +1204,7 @@ class AskUserOverlayView implements Component {
1145
1204
  const next = this.nextUnansweredIndex();
1146
1205
  if (next !== null) {
1147
1206
  this.index = next;
1148
- this.resetContentScroll();
1207
+ this.resetQuestionScroll();
1149
1208
  const nextState = this.currentState();
1150
1209
  const nextQuestion = this.currentQuestion();
1151
1210
  if (nextState && nextQuestion && !nextState.answer.trim()) nextState.mode = initialMode(nextQuestion);
@@ -1179,7 +1238,7 @@ class AskUserOverlayView implements Component {
1179
1238
  const state = this.states[index];
1180
1239
  const answer = state?.answer.trim();
1181
1240
  if (!question || !state || !answer || answer.length === 0) continue;
1182
- const options = selectedOptionLabels(question, state.selected);
1241
+ const options = selectedOptionLabels(question, state.committedSelected);
1183
1242
  answers.push({
1184
1243
  question: question.question,
1185
1244
  answer,
@@ -1196,7 +1255,7 @@ class AskUserOverlayView implements Component {
1196
1255
  this.parkTextMode();
1197
1256
  this.index = (this.index + delta + this.questions.length) % this.questions.length;
1198
1257
  this.status = "";
1199
- this.resetContentScroll();
1258
+ this.resetQuestionScroll();
1200
1259
  this.rebuildControl();
1201
1260
  this.deps.requestRender();
1202
1261
  }
@@ -1208,69 +1267,84 @@ class AskUserOverlayView implements Component {
1208
1267
  const current = this.list.getSelectedItem();
1209
1268
  if (current) state.focusedValue = current.value;
1210
1269
  }
1211
- if (this.input) state.inputValue = this.input.getValue();
1270
+ if (this.text) state.inputValue = this.text.getText();
1212
1271
  }
1213
1272
  }
1214
1273
 
1215
1274
  /**
1216
- * Show the ask_user overlay, on whichever surface the request calls for.
1275
+ * The view without a frame, for layout tests: it renders the body rows the
1276
+ * frame would wrap, at the width and row count the test names.
1277
+ */
1278
+ export interface AskUserViewForTesting {
1279
+ ask(questions: ReadonlyArray<AskUserQuestion>, presentation?: DecisionPresentation): Promise<AskUserResult>;
1280
+ render(width: number): string[];
1281
+ handleInput(data: string): void;
1282
+ footerHint(): string;
1283
+ cancel(): void;
1284
+ }
1285
+
1286
+ export function createAskUserViewForTesting(deps: {
1287
+ rows: number;
1288
+ tui?: TUI;
1289
+ onCancel?: () => void;
1290
+ }): AskUserViewForTesting {
1291
+ const view = new AskUserOverlayView({
1292
+ onCancel: deps.onCancel ?? (() => {}),
1293
+ getTerminalRows: () => deps.rows,
1294
+ requestRender: () => {},
1295
+ ...(deps.tui ? { tui: deps.tui } : {}),
1296
+ });
1297
+ return {
1298
+ ask: (questions, presentation) => view.begin(questions, presentation),
1299
+ render: (width) => view.render(width),
1300
+ handleInput: (data) => view.handleInput(data),
1301
+ footerHint: () => view.footerHint(),
1302
+ cancel: () => view.cancel(),
1303
+ };
1304
+ }
1305
+
1306
+ /**
1307
+ * Show the ask_user overlay.
1217
1308
  *
1218
- * A box's anchor, width, and height budget are fixed when the engine mounts it,
1219
- * so moving between surfaces means mounting a new frame around the same body and
1220
- * dropping the old one. That happens at most once per round, when the shape of
1221
- * the request actually changes; the new frame is shown before the old one is
1222
- * hidden so the keyboard passes straight across instead of returning to the
1223
- * composer for a frame.
1309
+ * One frame for the whole interview: it sits above the composer, takes its
1310
+ * width from the terminal, and its height from what the round has to say. A
1311
+ * shape change between rounds is just the next render; nothing is remounted.
1224
1312
  */
1225
1313
  export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskUserOverlaySession {
1226
- let handle: OverlayHandle | null = null;
1227
- let mounted: AskUserSurface | null = null;
1228
1314
  let closed = false;
1229
1315
 
1230
1316
  const view = new AskUserOverlayView({
1231
1317
  ...deps,
1232
1318
  getTerminalRows: () => tui.terminal?.rows ?? 0,
1233
1319
  requestRender: () => tui.requestRender(),
1234
- onSurface: (surface) => mount(surface),
1320
+ tui,
1235
1321
  });
1236
1322
 
1237
- const mount = (surface: AskUserSurface): void => {
1238
- if (closed || mounted === surface) return;
1239
- const geometry = ASK_USER_SURFACE_GEOMETRY[surface];
1240
- const previous = handle;
1241
- handle = showClioOverlayFrame(tui, view, {
1242
- anchor: geometry.anchor,
1243
- width: geometry.width,
1244
- // A zero cap means the frame keeps its own budget, which is every row
1245
- // the terminal has left after the margins.
1246
- ...(geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {}),
1247
- margin: geometry.margin,
1248
- // Not derived from the title: this modal swaps between a waiting title
1249
- // and a classified decision title without ever changing hands.
1250
- markerId: "ask-user",
1251
- title: () => (view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE),
1252
- tone: () => (view.isDecisionPending() ? view.decisionTone() : undefined),
1253
- footerHint: () => view.footerHint(),
1254
- });
1255
- mounted = surface;
1256
- previous?.hide();
1257
- };
1258
-
1259
- mount(view.currentSurface());
1323
+ const handle = showClioOverlayFrame(tui, view, {
1324
+ anchor: "bottom-center",
1325
+ width: askUserBoxWidth(tui.terminal?.columns ?? 0),
1326
+ margin: ASK_USER_MARGIN,
1327
+ // Not derived from the title: this modal swaps between a waiting title
1328
+ // and a classified decision title without ever changing hands.
1329
+ markerId: "ask-user",
1330
+ title: () => (view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE),
1331
+ tone: () => (view.isDecisionPending() ? view.decisionTone() : undefined),
1332
+ footerHint: () => view.footerHint(),
1333
+ });
1260
1334
 
1261
1335
  const close = (): void => {
1336
+ if (closed) return;
1262
1337
  closed = true;
1263
1338
  view.close();
1264
- handle?.hide();
1265
- handle = null;
1339
+ handle.hide();
1266
1340
  };
1267
1341
  return {
1268
- setHidden: (hidden) => handle?.setHidden(hidden),
1269
- isHidden: () => handle?.isHidden() ?? true,
1270
- focus: () => handle?.focus(),
1271
- unfocus: (options) => (options ? handle?.unfocus(options) : handle?.unfocus()),
1272
- isFocused: () => handle?.isFocused() ?? false,
1273
- getBounds: () => handle?.getBounds(),
1342
+ setHidden: (hidden) => handle.setHidden(hidden),
1343
+ isHidden: () => (closed ? true : handle.isHidden()),
1344
+ focus: () => handle.focus(),
1345
+ unfocus: (options) => (options ? handle.unfocus(options) : handle.unfocus()),
1346
+ isFocused: () => (closed ? false : handle.isFocused()),
1347
+ getBounds: () => (closed ? undefined : handle.getBounds()),
1274
1348
  ask: (questions, presentation) => view.begin(questions, presentation),
1275
1349
  cancel: () => view.cancel(),
1276
1350
  close,