@iowarp/clio-coder 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/NOTICE +33 -0
  3. package/README.md +7 -2
  4. package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
  8. package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
  11. package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
  12. package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
  13. package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
  14. package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
  15. package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
  16. package/dist/chunk-42FMPA75.js +101 -0
  17. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  18. package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
  19. package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
  20. package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
  21. package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
  22. package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
  23. package/dist/chunk-B7OBL7PK.js +1405 -0
  24. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  25. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  26. package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
  27. package/dist/chunk-BKFM6EJV.js +462 -0
  28. package/dist/chunk-BMS5RKQY.js +27 -0
  29. package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
  30. package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
  31. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  32. package/dist/chunk-C2LTL2W6.js +2447 -0
  33. package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
  34. package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
  35. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  36. package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
  37. package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
  38. package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
  39. package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
  40. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  41. package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
  42. package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
  43. package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
  44. package/dist/chunk-HHV2GANA.js +88 -0
  45. package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
  46. package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
  47. package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
  48. package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
  49. package/dist/chunk-HZHHCK24.js +1631 -0
  50. package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
  51. package/dist/chunk-IBEBSCYA.js +564 -0
  52. package/dist/chunk-IQ7KR472.js +362 -0
  53. package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
  54. package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
  55. package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
  56. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  57. package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
  58. package/dist/chunk-KMVISBZR.js +132 -0
  59. package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
  60. package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
  61. package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
  62. package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
  63. package/dist/chunk-NEKRRTYW.js +56 -0
  64. package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
  65. package/dist/chunk-NHLBIGRH.js +1506 -0
  66. package/dist/chunk-NQQH3YT7.js +302 -0
  67. package/dist/chunk-NYS75XW5.js +15 -0
  68. package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
  69. package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
  70. package/dist/chunk-OQBA45DZ.js +97 -0
  71. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  72. package/dist/chunk-PMZCIOCJ.js +25 -0
  73. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  74. package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
  75. package/dist/chunk-QDC3K2U3.js +262 -0
  76. package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
  77. package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
  78. package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
  79. package/dist/chunk-QSNYB6ZV.js +195 -0
  80. package/dist/chunk-R346GLFC.js +31 -0
  81. package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
  82. package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
  83. package/dist/chunk-SUCTJL45.js +45 -0
  84. package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
  85. package/dist/chunk-T56WDKA5.js +183 -0
  86. package/dist/chunk-TVHHYFHE.js +255 -0
  87. package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
  88. package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
  89. package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
  90. package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
  91. package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
  92. package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
  93. package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
  94. package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
  95. package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
  96. package/dist/chunk-VO67MWHC.js +75 -0
  97. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  98. package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
  99. package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
  100. package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
  101. package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
  102. package/dist/chunk-X75E3D2N.js +686 -0
  103. package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
  104. package/dist/chunk-YJX4SHTD.js +40 -0
  105. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  106. package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
  107. package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
  108. package/dist/cli/index.js +47 -36
  109. package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
  110. package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
  111. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  112. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  113. package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
  114. package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
  115. package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
  116. package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
  117. package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
  118. package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
  119. package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
  120. package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
  121. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  122. package/dist/doctor-QOA5FNY5.js +313 -0
  123. package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
  124. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  125. package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
  126. package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
  127. package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
  128. package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
  129. package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
  130. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  131. package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
  132. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  133. package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
  134. package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
  135. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  136. package/dist/fleet-view-ZCON35AG.js +102 -0
  137. package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
  138. package/dist/install-XGLBQY5E.js +13 -0
  139. package/dist/interop-OZBKXAYL.js +114 -0
  140. package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
  141. package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
  142. package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
  143. package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
  144. package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
  145. package/dist/panes-HMABYVO4.js +58 -0
  146. package/dist/panes-KY6W3V2E.js +103 -0
  147. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  148. package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
  149. package/dist/resources-BB3MVJMD.js +111 -0
  150. package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
  151. package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
  152. package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
  153. package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
  154. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  155. package/dist/slash-commands-ZSGASKJC.js +77 -0
  156. package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
  157. package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
  158. package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
  159. package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
  160. package/dist/tools-DAF3DI3C.js +27 -0
  161. package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
  162. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  163. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  164. package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
  165. package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
  166. package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
  167. package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
  168. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  169. package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
  170. package/dist/with-panes-MKB46MPQ.js +782 -0
  171. package/dist/worker/entry.js +101 -66
  172. package/docs/README.md +3 -2
  173. package/docs/acp.md +24 -3
  174. package/docs/alcf-provider.md +1 -1
  175. package/docs/architecture.md +2 -2
  176. package/docs/artifact-versions.md +4 -1
  177. package/docs/built-in-agents.md +1 -1
  178. package/docs/capacity-and-scheduling.md +43 -5
  179. package/docs/commands-and-modes.md +31 -2
  180. package/docs/configuration-and-targets.md +40 -10
  181. package/docs/context-engine.md +1 -1
  182. package/docs/development-pipeline.md +19 -0
  183. package/docs/dispatch-typed-intent.md +385 -0
  184. package/docs/documentation-coverage.md +2 -2
  185. package/docs/documentation-guide.md +1 -1
  186. package/docs/environment-variables.md +1 -0
  187. package/docs/eval-runner.md +1 -1
  188. package/docs/evals-internal.md +1 -1
  189. package/docs/evidence-and-memory.md +2 -2
  190. package/docs/evolution.md +1 -1
  191. package/docs/exit-codes-and-output.md +1 -1
  192. package/docs/extensions-and-sharing.md +25 -1
  193. package/docs/fleet-dispatch.md +22 -1
  194. package/docs/installation-and-lifecycle.md +15 -2
  195. package/docs/middleware-and-components.md +1 -1
  196. package/docs/model-catalog.md +10 -1
  197. package/docs/observability.md +3 -3
  198. package/docs/proactive-memory.md +1 -1
  199. package/docs/prompt-envelope-and-tools.md +1 -1
  200. package/docs/provider-adapter-cookbook.md +1 -1
  201. package/docs/safety-model.md +2 -2
  202. package/docs/scientific-validation.md +3 -3
  203. package/docs/session-lifecycle.md +1 -1
  204. package/docs/skills-marketplace.md +1 -1
  205. package/docs/tool-usage.md +18 -8
  206. package/docs/trace-store.md +1 -1
  207. package/docs/troubleshooting.md +1 -1
  208. package/docs/tui-design.md +1 -1
  209. package/docs/worker-dispatch-mechanics.md +1 -1
  210. package/package.json +4 -2
  211. package/src/cli/acp.ts +6 -2
  212. package/src/cli/argv.ts +25 -0
  213. package/src/cli/configure.ts +23 -21
  214. package/src/cli/doctor-panes.ts +124 -0
  215. package/src/cli/doctor-toolchain.ts +57 -0
  216. package/src/cli/doctor.ts +20 -1
  217. package/src/cli/eval-inventory.ts +436 -0
  218. package/src/cli/eval.ts +13 -0
  219. package/src/cli/evidence-detail.ts +88 -0
  220. package/src/cli/evidence-inventory.ts +183 -0
  221. package/src/cli/evidence.ts +30 -5
  222. package/src/cli/fleet-decisions.ts +69 -0
  223. package/src/cli/fleet-inspect.ts +334 -0
  224. package/src/cli/fleet-verify.ts +133 -0
  225. package/src/cli/fleet-view.ts +810 -0
  226. package/src/cli/fleet.ts +148 -37
  227. package/src/cli/index.ts +14 -2
  228. package/src/cli/interop-inspect.ts +128 -0
  229. package/src/cli/interop.ts +34 -0
  230. package/src/cli/panes.ts +35 -0
  231. package/src/cli/reset.ts +5 -2
  232. package/src/cli/run.ts +58 -0
  233. package/src/cli/skills-inventory.ts +185 -0
  234. package/src/cli/skills.ts +16 -13
  235. package/src/cli/tools.ts +321 -0
  236. package/src/cli/trace-inspect.ts +252 -0
  237. package/src/cli/trace.ts +23 -2
  238. package/src/cli/verifiers-inspect.ts +347 -0
  239. package/src/cli/verifiers.ts +9 -0
  240. package/src/core/bus-events.ts +4 -0
  241. package/src/core/config.ts +49 -0
  242. package/src/core/defaults.ts +93 -7
  243. package/src/core/response-schema.ts +44 -4
  244. package/src/core/theme-token-hex.ts +43 -0
  245. package/src/core/tool-names.ts +2 -1
  246. package/src/core/xdg.ts +1 -1
  247. package/src/domains/agents/fleets/build-review.md +0 -3
  248. package/src/domains/agents/fleets/build-test.md +0 -3
  249. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  250. package/src/domains/agents/result-contract.ts +164 -35
  251. package/src/domains/config/classify.ts +3 -0
  252. package/src/domains/dispatch/admission-error.ts +9 -0
  253. package/src/domains/dispatch/admission.ts +22 -21
  254. package/src/domains/dispatch/capacity-lease.ts +21 -1
  255. package/src/domains/dispatch/council-topology.ts +398 -0
  256. package/src/domains/dispatch/extension.ts +222 -50
  257. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  258. package/src/domains/dispatch/fleet-plan.ts +7 -2
  259. package/src/domains/dispatch/fleet-run.ts +64 -1
  260. package/src/domains/dispatch/gate-decisions.ts +11 -1
  261. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  262. package/src/domains/dispatch/gate-topology.ts +289 -0
  263. package/src/domains/dispatch/index.ts +19 -0
  264. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  265. package/src/domains/dispatch/intent.ts +85 -1
  266. package/src/domains/dispatch/reservation-store.ts +24 -4
  267. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  268. package/src/domains/dispatch/run-event-journal.ts +598 -0
  269. package/src/domains/dispatch/state.ts +45 -1
  270. package/src/domains/dispatch/types.ts +13 -0
  271. package/src/domains/dispatch/validation.ts +33 -8
  272. package/src/domains/eval/compare/compare.ts +3 -16
  273. package/src/domains/eval/inventory.ts +113 -0
  274. package/src/domains/eval/schema/serving.ts +31 -0
  275. package/src/domains/evidence/store.ts +6 -0
  276. package/src/domains/memory/index.ts +0 -1
  277. package/src/domains/memory/task-memory-policy.ts +6 -10
  278. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  279. package/src/domains/middleware/memory-intervention.ts +33 -21
  280. package/src/domains/mux/contract.ts +434 -0
  281. package/src/domains/mux/detect.ts +158 -0
  282. package/src/domains/mux/extension.ts +47 -0
  283. package/src/domains/mux/index.ts +96 -0
  284. package/src/domains/mux/manifest.ts +6 -0
  285. package/src/domains/mux/operations.ts +164 -0
  286. package/src/domains/mux/pane-registry.ts +90 -0
  287. package/src/domains/mux/protocol.ts +49 -0
  288. package/src/domains/mux/socket-client.ts +816 -0
  289. package/src/domains/mux/types.ts +222 -0
  290. package/src/domains/mux/viewer-command.ts +59 -0
  291. package/src/domains/mux/yazi/assets/init.lua +2 -0
  292. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  293. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  294. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  295. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  296. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  297. package/src/domains/mux/yazi/event-stream.ts +180 -0
  298. package/src/domains/mux/yazi/profile.ts +299 -0
  299. package/src/domains/mux/yazi/session.ts +228 -0
  300. package/src/domains/mux/yazi/theme.ts +30 -0
  301. package/src/domains/observability/cost.ts +21 -0
  302. package/src/domains/observability/index.ts +2 -0
  303. package/src/domains/observability/trace-store.ts +42 -0
  304. package/src/domains/providers/endpoint-capacity.ts +154 -22
  305. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  306. package/src/domains/providers/extension.ts +20 -3
  307. package/src/domains/providers/index.ts +22 -0
  308. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  309. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  310. package/src/domains/providers/runtimes/builtins.ts +2 -0
  311. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  312. package/src/domains/providers/support.ts +1 -0
  313. package/src/domains/providers/target-model-cache.ts +124 -0
  314. package/src/domains/resources/index.ts +3 -0
  315. package/src/domains/resources/skills/loader.ts +33 -0
  316. package/src/domains/safety/action-classifier.ts +6 -0
  317. package/src/domains/toolchain/archive.ts +175 -0
  318. package/src/domains/toolchain/contract.ts +28 -0
  319. package/src/domains/toolchain/extension.ts +47 -0
  320. package/src/domains/toolchain/index.ts +39 -0
  321. package/src/domains/toolchain/install.ts +327 -0
  322. package/src/domains/toolchain/manifest.ts +8 -0
  323. package/src/domains/toolchain/paths.ts +34 -0
  324. package/src/domains/toolchain/registry.ts +265 -0
  325. package/src/domains/toolchain/remove.ts +218 -0
  326. package/src/domains/toolchain/resolve.ts +182 -0
  327. package/src/domains/toolchain/types.ts +113 -0
  328. package/src/domains/toolchain/version.ts +88 -0
  329. package/src/engine/acp/server.ts +413 -70
  330. package/src/engine/acp/types.ts +19 -1
  331. package/src/engine/claude/sdk-module.ts +98 -0
  332. package/src/engine/claude/sdk-runtime.ts +19 -11
  333. package/src/engine/tui-primitives.ts +21 -0
  334. package/src/engine/tui.ts +1 -0
  335. package/src/engine/worker-runtime.ts +2 -13
  336. package/src/entry/boot-options.ts +2 -0
  337. package/src/entry/orchestrator.ts +103 -5
  338. package/src/entry/panes-activation.ts +31 -0
  339. package/src/entry/with-panes.ts +20 -0
  340. package/src/interactive/context-overlay.ts +1 -0
  341. package/src/interactive/cost-overlay.ts +2 -0
  342. package/src/interactive/council-grid.ts +1 -3
  343. package/src/interactive/council.ts +11 -0
  344. package/src/interactive/dispatch-board.ts +26 -16
  345. package/src/interactive/interactive-application.ts +153 -6
  346. package/src/interactive/interactive-input-runtime.ts +7 -0
  347. package/src/interactive/interactive-slash-runtime.ts +36 -6
  348. package/src/interactive/memory-overlay.ts +1 -0
  349. package/src/interactive/modal-marker.ts +170 -0
  350. package/src/interactive/mux-bridge.ts +214 -0
  351. package/src/interactive/overlay-frame.ts +58 -2
  352. package/src/interactive/overlay-general-openers.ts +1 -0
  353. package/src/interactive/overlay-key-routing.ts +14 -3
  354. package/src/interactive/overlay-lifecycle.ts +19 -3
  355. package/src/interactive/overlay-model-selectors.ts +40 -3
  356. package/src/interactive/overlay-permission-lifecycle.ts +90 -22
  357. package/src/interactive/overlay-transitions.ts +18 -4
  358. package/src/interactive/overlays/agents.ts +1 -0
  359. package/src/interactive/overlays/ask-user.ts +136 -30
  360. package/src/interactive/overlays/auth-dialog.ts +1 -0
  361. package/src/interactive/overlays/context-reset.ts +1 -0
  362. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  363. package/src/interactive/overlays/decisions.ts +11 -11
  364. package/src/interactive/overlays/extensions.ts +1 -0
  365. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  366. package/src/interactive/overlays/handoff-review.ts +1 -0
  367. package/src/interactive/overlays/help-reference.ts +2 -0
  368. package/src/interactive/overlays/interop.ts +1 -0
  369. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  370. package/src/interactive/overlays/library-tabs.ts +28 -0
  371. package/src/interactive/overlays/list-overlay.ts +10 -1
  372. package/src/interactive/overlays/message-picker.ts +1 -0
  373. package/src/interactive/overlays/model-scope.ts +86 -0
  374. package/src/interactive/overlays/model-selector.ts +1 -0
  375. package/src/interactive/overlays/prompts.ts +1 -0
  376. package/src/interactive/overlays/session-selector.ts +1 -0
  377. package/src/interactive/overlays/settings-sections.ts +30 -0
  378. package/src/interactive/overlays/settings.ts +579 -44
  379. package/src/interactive/overlays/side-question.ts +1 -0
  380. package/src/interactive/overlays/skills-hub.ts +3 -11
  381. package/src/interactive/overlays/tree-selector.ts +1 -0
  382. package/src/interactive/pane-policy.ts +46 -0
  383. package/src/interactive/panes-runtime.ts +292 -0
  384. package/src/interactive/renderers/compaction-summary.ts +29 -0
  385. package/src/interactive/renderers/worker-entry.ts +122 -14
  386. package/src/interactive/slash-commands.ts +251 -15
  387. package/src/interactive/tasks-overlay.ts +1 -0
  388. package/src/interactive/theme/tokens.ts +3 -14
  389. package/src/interactive/turn-context.ts +47 -2
  390. package/src/interactive/view/artifacts.ts +109 -1
  391. package/src/interactive/view/view-overlay.ts +1 -0
  392. package/src/interactive/watch-pane.ts +152 -0
  393. package/src/interactive/worker-progress.ts +7 -1
  394. package/src/interactive/worker-receipts.ts +19 -1
  395. package/src/interactive/worker-stream.ts +5 -0
  396. package/src/interactive/yazi-bridge.ts +444 -0
  397. package/src/tools/bootstrap.ts +26 -2
  398. package/src/tools/builtin-tool-catalog.ts +15 -0
  399. package/src/tools/compete-worktrees.ts +83 -2
  400. package/src/tools/core-bootstrap.ts +2 -1
  401. package/src/tools/dispatch-admission.ts +14 -3
  402. package/src/tools/dispatch-arguments.ts +20 -20
  403. package/src/tools/dispatch-run-events.ts +134 -19
  404. package/src/tools/dispatch-runner.ts +29 -7
  405. package/src/tools/dispatch-types.ts +15 -3
  406. package/src/tools/dispatch.ts +1 -1
  407. package/src/tools/executables.ts +17 -14
  408. package/src/tools/observation.ts +54 -4
  409. package/src/tools/panes-surface.ts +38 -0
  410. package/src/tools/panes.ts +112 -0
  411. package/src/tools/policy.ts +10 -1
  412. package/src/tools/presentation.ts +1 -0
  413. package/dist/chunk-D73KXYPF.js +0 -37
  414. package/dist/chunk-MLOK6ZOS.js +0 -2888
  415. package/dist/chunk-YKOFT37S.js +0 -279
  416. package/dist/doctor-TWBWFK5V.js +0 -165
@@ -0,0 +1,152 @@
1
+ /**
2
+ * The workers-view watch pane: one pane, beside Clio's own, rendering whatever
3
+ * run the operator points at.
4
+ *
5
+ * The pane hosts `clio-coder fleet view --watch <selection-file>` (see
6
+ * src/cli/fleet-view.ts), a durable-state viewer that re-reads the selection
7
+ * file on every poll. Retargeting is therefore one small atomic file write:
8
+ * arrow keys in the Alt+W board follow the cursor with zero socket traffic and
9
+ * zero pane churn, which is what makes the navigation feel like part of the
10
+ * board rather than like driving a terminal multiplexer.
11
+ *
12
+ * Pane creation is deliberately lazy, but reclamation is eager. A surviving
13
+ * pane is adopted as soon as the controller starts so the relaunched session's
14
+ * inventory owns it before the next watch command. A new pane still opens only
15
+ * on the first watch (operator Enter, `/panes show`, or the panes tool), and is
16
+ * never closed by navigation: leaving the workers view parks it on the last
17
+ * selection, and it goes away when the operator quits the viewer (`q`), closes
18
+ * the pane, or runs `/panes close watch`.
19
+ */
20
+
21
+ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
22
+ import { dirname, join } from "node:path";
23
+ import { type ClioDirs, clioCacheDir, clioConfigDir, clioDataDir, clioStateDir } from "../core/xdg.js";
24
+ import type { MuxContract } from "../domains/mux/index.js";
25
+ import type { PanesWatchController, PanesWatchResult } from "../domains/mux/operations.js";
26
+ import { watchViewerCommand } from "../domains/mux/viewer-command.js";
27
+
28
+ /**
29
+ * One selection file per state root, deliberately not per session: every
30
+ * session over a state root shares one run ledger, so a surviving watch pane
31
+ * from a crashed session keeps working the moment a new session writes the
32
+ * same file.
33
+ */
34
+ function watchSelectionPath(stateDir: string = clioStateDir()): string {
35
+ return join(stateDir, "watch-selection");
36
+ }
37
+
38
+ export interface WatchPaneDeps {
39
+ mux: MuxContract;
40
+ getCwd: () => string;
41
+ /** Selection-file override for tests. */
42
+ selectionPath?: string;
43
+ /** Resolved-layout override for tests. Production pins this process's four cached roots. */
44
+ dirs?: Readonly<ClioDirs>;
45
+ /** Command override for tests; production runs this install's own CLI. */
46
+ command?: (selectionPath: string, dirs: Readonly<ClioDirs>) => ReadonlyArray<string>;
47
+ writeFile?: (path: string, content: string) => void;
48
+ }
49
+
50
+ /** Replace-by-rename so the viewer's poll never reads a torn line. */
51
+ function atomicWrite(path: string, content: string): void {
52
+ mkdirSync(dirname(path), { recursive: true });
53
+ const tmp = `${path}.tmp`;
54
+ writeFileSync(tmp, content, "utf8");
55
+ renameSync(tmp, path);
56
+ }
57
+
58
+ export function createWatchPaneController(deps: WatchPaneDeps): PanesWatchController {
59
+ const selectionPath = deps.selectionPath ?? watchSelectionPath();
60
+ const dirs =
61
+ deps.dirs ??
62
+ Object.freeze({
63
+ config: clioConfigDir(),
64
+ data: clioDataDir(),
65
+ state: clioStateDir(),
66
+ cache: clioCacheDir(),
67
+ });
68
+ const command = deps.command ?? ((path, layout) => watchViewerCommand(path, { dirs: layout }));
69
+ const write = deps.writeFile ?? atomicWrite;
70
+
71
+ let paneId: string | null = null;
72
+ let disposed = false;
73
+ let adoptionInFlight: Promise<boolean> | null = null;
74
+ const unsubscribe = deps.mux.onPaneGone((record) => {
75
+ if (record.ref.paneId === paneId) paneId = null;
76
+ });
77
+ const adoptExistingPane = (): Promise<boolean> => {
78
+ if (disposed || paneId !== null) return Promise.resolve(true);
79
+ if (adoptionInFlight !== null) return adoptionInFlight;
80
+ const attempt = Promise.resolve()
81
+ .then(() => deps.mux.adoptPane({ purpose: "watch", label: "watch" }))
82
+ .then((adopted) => {
83
+ if (!disposed && paneId === null && adopted !== null) paneId = adopted.paneId;
84
+ return true;
85
+ })
86
+ .catch(() => {
87
+ // Startup reclamation is best effort. An explicit watch retries the
88
+ // scan before it opens a replacement pane.
89
+ return false;
90
+ })
91
+ .finally(() => {
92
+ if (adoptionInFlight === attempt) adoptionInFlight = null;
93
+ });
94
+ adoptionInFlight = attempt;
95
+ return attempt;
96
+ };
97
+
98
+ const writeSelection = (runId: string): boolean => {
99
+ try {
100
+ write(selectionPath, `${runId}\n`);
101
+ return true;
102
+ } catch {
103
+ return false;
104
+ }
105
+ };
106
+
107
+ // Reclaim durable ownership for inventory and navigation immediately. The
108
+ // scan remains best effort and never opens a replacement pane on its own.
109
+ void adoptExistingPane();
110
+
111
+ return {
112
+ async watch(runId: string): Promise<PanesWatchResult> {
113
+ if (!writeSelection(runId)) {
114
+ return { status: "unavailable", reason: `cannot write the watch selection at ${selectionPath}` };
115
+ }
116
+ if (paneId !== null) return { status: "watching", runId, paneId, opened: false };
117
+ const scanned = await adoptExistingPane();
118
+ if (!scanned) await adoptExistingPane();
119
+ if (paneId !== null) return { status: "watching", runId, paneId, opened: false };
120
+ const opened = await deps.mux.openUtilityPane({
121
+ argv: command(selectionPath, dirs),
122
+ cwd: deps.getCwd(),
123
+ label: "watch",
124
+ title: "clio watch",
125
+ purpose: "watch",
126
+ direction: "right",
127
+ });
128
+ if (opened === null) {
129
+ return { status: "unavailable", reason: "the pane host refused to open the watch pane" };
130
+ }
131
+ paneId = opened.paneId;
132
+ return { status: "watching", runId, paneId, opened: true };
133
+ },
134
+
135
+ follow(runId: string): boolean {
136
+ // Navigation never opens anything; a closed watch pane stays closed
137
+ // until the operator asks again with Enter.
138
+ if (paneId === null) return false;
139
+ return writeSelection(runId);
140
+ },
141
+
142
+ isOpen(): boolean {
143
+ return paneId !== null;
144
+ },
145
+
146
+ dispose(): void {
147
+ // The pane parks; only the subscription goes.
148
+ disposed = true;
149
+ unsubscribe();
150
+ },
151
+ };
152
+ }
@@ -261,6 +261,8 @@ export function createWorkerProgressFold(): WorkerProgressFold {
261
261
  const toolNames: string[] = [];
262
262
  let settled = false;
263
263
  let durable = "";
264
+ /** True after one assistant message ended and before the next message emits prose. */
265
+ let betweenAssistantMessages = false;
264
266
  /** Starts still awaiting a finish, oldest first. */
265
267
  const pendingActions: WorkerAction[] = [];
266
268
  /** New streams resolve exact starts through this index. */
@@ -340,7 +342,9 @@ export function createWorkerProgressFold(): WorkerProgressFold {
340
342
 
341
343
  const delta = workerTextDelta(event);
342
344
  if (delta.length > 0) {
343
- changed = appendTail(admitDelta(delta, nowMs)) || changed;
345
+ const separator = betweenAssistantMessages && tailText.length > 0 && !tailText.endsWith("\n") ? "\n\n" : "";
346
+ betweenAssistantMessages = false;
347
+ changed = appendTail(admitDelta(`${separator}${delta}`, nowMs)) || changed;
344
348
  changed = setPhase("writing") || changed;
345
349
  } else if (isThinkingEvent(event)) {
346
350
  // The phase moves and nothing else: reasoning content is never kept.
@@ -355,6 +359,7 @@ export function createWorkerProgressFold(): WorkerProgressFold {
355
359
  if (delta.length === 0 && tailText.trim().length === 0) {
356
360
  changed = appendTail(durableText) || changed;
357
361
  }
362
+ betweenAssistantMessages = true;
358
363
  }
359
364
 
360
365
  const action = toolEventAction(event);
@@ -412,6 +417,7 @@ export function createWorkerProgressFold(): WorkerProgressFold {
412
417
  pendingActions.length = 0;
413
418
  pendingActionsById.clear();
414
419
  durable = "";
420
+ betweenAssistantMessages = false;
415
421
  settled = false;
416
422
  phase = "starting";
417
423
  touch();
@@ -19,7 +19,7 @@ import { clioStateDir } from "../core/xdg.js";
19
19
  import type { RunEnvelope, RunReceipt } from "../domains/dispatch/types.js";
20
20
  import { inspectRunReceiptTrustStatus } from "../domains/evidence/trust-status.js";
21
21
  import { receiptFilePath } from "./view/artifacts.js";
22
- import type { WorkerReceiptFacts, WorkerResultContract } from "./worker-stream.js";
22
+ import type { WorkerPresentedResultContract, WorkerReceiptFacts, WorkerResultContract } from "./worker-stream.js";
23
23
 
24
24
  function isRecord(value: unknown): value is Record<string, unknown> {
25
25
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -55,6 +55,22 @@ function contractFrom(receipt: Record<string, unknown>): WorkerResultContract |
55
55
  return conformance === "pass" || conformance === "fail" || conformance === "not-reached" ? conformance : undefined;
56
56
  }
57
57
 
58
+ const PRESENTED_RESULT_CONTRACTS: ReadonlyArray<WorkerPresentedResultContract> = [
59
+ "debugger-report",
60
+ "verifier-report",
61
+ "research-report",
62
+ "scout-report",
63
+ ];
64
+
65
+ /** The kind is integrity-covered inside the validator source id on current receipts. */
66
+ function contractKindFrom(receipt: Record<string, unknown>): WorkerPresentedResultContract | undefined {
67
+ const quality = isRecord(receipt.quality) ? receipt.quality : null;
68
+ const fact = quality !== null && isRecord(quality.resultContract) ? quality.resultContract : null;
69
+ const sourceId = optionalString(fact?.sourceId);
70
+ if (sourceId === undefined) return undefined;
71
+ return PRESENTED_RESULT_CONTRACTS.find((kind) => sourceId.startsWith(`agent-result-contract:${kind}:`));
72
+ }
73
+
58
74
  /**
59
75
  * Project a parsed receipt onto the facts a worker block renders. The input is
60
76
  * whatever JSON the file held, not a typed {@link RunReceipt}: receipts written
@@ -66,6 +82,7 @@ export function workerReceiptFacts(receipt: Record<string, unknown>): WorkerRece
66
82
  if (outcome === undefined) return null;
67
83
  const output = isRecord(receipt.output) ? receipt.output : null;
68
84
  const contract = contractFrom(receipt);
85
+ const contractKind = contractKindFrom(receipt);
69
86
  const text = optionalString(output?.text);
70
87
  const failureMessage = optionalString(receipt.failureMessage);
71
88
  const outcomeCode = optionalString(receipt.outcomeCode);
@@ -82,6 +99,7 @@ export function workerReceiptFacts(receipt: Record<string, unknown>): WorkerRece
82
99
  ...(durationMs !== undefined ? { durationMs } : {}),
83
100
  ...(toolCalls !== undefined ? { toolCalls } : {}),
84
101
  ...(contract !== undefined ? { contract } : {}),
102
+ ...(contractKind !== undefined ? { contractKind } : {}),
85
103
  ...(text !== undefined ? { text } : {}),
86
104
  };
87
105
  }
@@ -61,6 +61,9 @@ export interface WorkerAttempt {
61
61
 
62
62
  export type WorkerResultContract = "pass" | "fail" | "not-reached" | "unmeasured";
63
63
 
64
+ /** Result kinds whose parsed envelopes have a dedicated transcript presentation. */
65
+ export type WorkerPresentedResultContract = "debugger-report" | "verifier-report" | "research-report" | "scout-report";
66
+
64
67
  /**
65
68
  * Terminal facts a worker block reports. Sealed by the receipt when it can be
66
69
  * read, carried by the terminal event otherwise. Every unit is optional and
@@ -76,6 +79,8 @@ export interface WorkerReceiptSummary {
76
79
  durationMs?: number;
77
80
  toolCalls?: number;
78
81
  contract?: WorkerResultContract;
82
+ /** Parsed from the receipt's result-contract source id; absent on legacy and untyped receipts. */
83
+ contractKind?: WorkerPresentedResultContract;
79
84
  /** From an abort marker rather than a sealed terminal event; a later DispatchCompleted/DispatchFailed replaces it. */
80
85
  provisional?: boolean;
81
86
  /** The run is over, but no receipt could be read for it, and the ledger row could not explain why either. */
@@ -0,0 +1,444 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
+ import { isAbsolute, join, relative, resolve } from "node:path";
5
+ import { clioCacheDir } from "../core/xdg.js";
6
+ import type { MuxContract } from "../domains/mux/index.js";
7
+ import type { YaziEvent } from "../domains/mux/yazi/event-stream.js";
8
+ import { ensureYaziProfile, yaziProfileDir } from "../domains/mux/yazi/profile.js";
9
+ import {
10
+ createYaziSession,
11
+ resolveYaziBinaries,
12
+ type YaziProfileMode,
13
+ type YaziResolvedBinaries,
14
+ type YaziSession,
15
+ type YaziSessionMode,
16
+ type YaziSessionOpenResult,
17
+ type YaziSessionOptions,
18
+ } from "../domains/mux/yazi/session.js";
19
+
20
+ export const YAZI_PICK_MAX_PATHS = 32;
21
+ export const YAZI_PICK_MAX_CHARS = 4096;
22
+ export const YAZI_LIVENESS_MS = 5000;
23
+
24
+ export interface YaziBridgeSettings {
25
+ mode: YaziSessionMode;
26
+ profile: YaziProfileMode;
27
+ followCwd: boolean;
28
+ }
29
+
30
+ export interface YaziMentionBatch {
31
+ text: string;
32
+ inserted: number;
33
+ mentions: number;
34
+ plainPaths: number;
35
+ duplicates: number;
36
+ truncated: number;
37
+ }
38
+
39
+ export interface YaziBridgeStatus {
40
+ mode: YaziSessionMode | "closed";
41
+ paneId: string | null;
42
+ paneCwd: string | null;
43
+ lastLineAt: number | null;
44
+ droppedLines: number;
45
+ }
46
+
47
+ export type YaziBridgeOpenResult =
48
+ | { status: "opened"; mode: YaziSessionMode; paneId: string | null; existing: boolean }
49
+ | Exclude<YaziSessionOpenResult, { status: "opened" }>;
50
+
51
+ export interface YaziBridge {
52
+ open(options?: { once?: boolean }): Promise<YaziBridgeOpenResult>;
53
+ status(): Readonly<YaziBridgeStatus>;
54
+ dispose(): void;
55
+ }
56
+
57
+ export interface YaziBridgeDeps {
58
+ mux?: MuxContract;
59
+ getDraft: () => string;
60
+ setDraft: (text: string) => void;
61
+ requestRender: () => void;
62
+ notice: (level: "info" | "warning", text: string) => void;
63
+ getCwd: () => string;
64
+ getSettings?: () => Readonly<YaziBridgeSettings>;
65
+ openSession?: (options: YaziSessionOptions) => Promise<YaziSessionOpenResult>;
66
+ /** TUI lease used only by the no-mux, in-terminal chooser. */
67
+ stopUi?: () => void;
68
+ startUi?: () => void;
69
+ runTerminalChooser?: (options: { cwd: string; profileMode: YaziProfileMode }) => Promise<YaziTerminalChooserResult>;
70
+ statPath?: (path: string) => "file" | "directory" | "other";
71
+ livenessMs?: number;
72
+ pickToken?: () => string;
73
+ }
74
+
75
+ export type YaziTerminalChooserResult =
76
+ | { status: "chosen"; choice: { paths: ReadonlyArray<string>; cwd: string } }
77
+ | { status: "cancelled" }
78
+ | { status: "missing-binary"; binary: "yazi" | "ya"; detail: string }
79
+ | { status: "profile-error"; reason: string }
80
+ | { status: "unavailable"; reason: string };
81
+
82
+ export interface YaziTerminalChooserOptions {
83
+ cwd: string;
84
+ profileMode: YaziProfileMode;
85
+ stopUi: () => void;
86
+ startUi: () => void;
87
+ requestRender: () => void;
88
+ cacheDir?: string;
89
+ sessionId?: string;
90
+ resolveBinaries?: () => YaziResolvedBinaries;
91
+ ensureProfile?: typeof ensureYaziProfile;
92
+ spawnYazi?: (
93
+ file: string,
94
+ args: ReadonlyArray<string>,
95
+ options: { cwd: string; env: Readonly<NodeJS.ProcessEnv> },
96
+ ) => { error?: Error };
97
+ }
98
+
99
+ function readOptional(path: string): string {
100
+ try {
101
+ return readFileSync(path, "utf8");
102
+ } catch {
103
+ return "";
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Run Yazi as a full-screen chooser when no pane host is usable.
109
+ *
110
+ * Resolution, profile generation, and chooser-file truncation all happen
111
+ * before the TUI lease is released. A missing or below-floor binary therefore
112
+ * leaves the screen intact and returns the shared doctor sentence verbatim.
113
+ */
114
+ export async function runYaziTerminalChooser(options: YaziTerminalChooserOptions): Promise<YaziTerminalChooserResult> {
115
+ const binaries = (options.resolveBinaries ?? resolveYaziBinaries)();
116
+ if (!binaries.yaziPath) {
117
+ return { status: "missing-binary", binary: "yazi", detail: binaries.missingYaziDetail };
118
+ }
119
+ if (options.profileMode === "managed" && !binaries.yaPath) {
120
+ return {
121
+ status: "missing-binary",
122
+ binary: "ya",
123
+ detail: "the Yazi helper `ya` is not available; install the complete pinned Yazi tool",
124
+ };
125
+ }
126
+
127
+ const cacheDir = options.cacheDir ?? clioCacheDir();
128
+ const sessionsDir = join(cacheDir, "yazi", "sessions");
129
+ mkdirSync(sessionsDir, { recursive: true });
130
+ const sessionId = options.sessionId ?? randomUUID();
131
+ const chooserPath = join(sessionsDir, `${sessionId}.chooser`);
132
+ const cwdPath = join(sessionsDir, `${sessionId}.cwd`);
133
+ const profile =
134
+ options.profileMode === "managed"
135
+ ? (options.ensureProfile ?? ensureYaziProfile)({
136
+ yaPath: binaries.yaPath as string,
137
+ yaziPath: binaries.yaziPath,
138
+ profileDir: yaziProfileDir(cacheDir),
139
+ })
140
+ : null;
141
+ if (options.profileMode === "managed" && !profile) {
142
+ return { status: "profile-error", reason: "Clio could not generate a valid managed Yazi profile" };
143
+ }
144
+ writeFileSync(chooserPath, "");
145
+ writeFileSync(cwdPath, "");
146
+
147
+ const args = [options.cwd, "--chooser-file", chooserPath, "--cwd-file", cwdPath] as const;
148
+ const env = { ...process.env, ...(profile ? { YAZI_CONFIG_HOME: profile.dir } : {}) };
149
+ let processError: Error | undefined;
150
+ options.stopUi();
151
+ try {
152
+ const result = options.spawnYazi
153
+ ? options.spawnYazi(binaries.yaziPath, args, { cwd: options.cwd, env })
154
+ : spawnSync(binaries.yaziPath, args, { cwd: options.cwd, env, stdio: "inherit" });
155
+ processError = result.error;
156
+ } catch (error) {
157
+ processError = error instanceof Error ? error : new Error(String(error));
158
+ } finally {
159
+ options.startUi();
160
+ options.requestRender();
161
+ }
162
+ if (processError) return { status: "unavailable", reason: `yazi chooser failed: ${processError.message}` };
163
+
164
+ const paths = readOptional(chooserPath)
165
+ .split("\n")
166
+ .filter((path) => path.length > 0);
167
+ if (paths.length === 0) return { status: "cancelled" };
168
+ return {
169
+ status: "chosen",
170
+ choice: { paths, cwd: readOptional(cwdPath).trim() || options.cwd },
171
+ };
172
+ }
173
+
174
+ function containsAsciiWhitespace(value: string): boolean {
175
+ for (const character of value) {
176
+ const code = character.charCodeAt(0);
177
+ if (code === 32 || (code >= 9 && code <= 13)) return true;
178
+ }
179
+ return false;
180
+ }
181
+
182
+ function relativeWhenInside(path: string, cwd: string): string {
183
+ const candidate = relative(cwd, path);
184
+ if (
185
+ candidate.length > 0 &&
186
+ candidate !== ".." &&
187
+ !candidate.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) &&
188
+ !isAbsolute(candidate)
189
+ ) {
190
+ return candidate;
191
+ }
192
+ return path;
193
+ }
194
+
195
+ function draftContains(draft: string, item: string): boolean {
196
+ let from = 0;
197
+ while (from <= draft.length) {
198
+ const at = draft.indexOf(item, from);
199
+ if (at < 0) return false;
200
+ const before = at === 0 ? "" : (draft[at - 1] ?? "");
201
+ const after = draft[at + item.length] ?? "";
202
+ if ((before === "" || /\s/u.test(before)) && (after === "" || /\s/u.test(after))) return true;
203
+ from = at + item.length;
204
+ }
205
+ return false;
206
+ }
207
+
208
+ function pathKind(path: string): "file" | "directory" | "other" {
209
+ try {
210
+ const stat = statSync(path);
211
+ if (stat.isFile()) return "file";
212
+ if (stat.isDirectory()) return "directory";
213
+ return "other";
214
+ } catch {
215
+ return "other";
216
+ }
217
+ }
218
+
219
+ /** Format one DDS or chooser batch for the composer without ever submitting it. */
220
+ export function formatYaziMentions(
221
+ paths: ReadonlyArray<string>,
222
+ options: {
223
+ cwd: string;
224
+ draft: string;
225
+ statPath?: (path: string) => "file" | "directory" | "other";
226
+ maxPaths?: number;
227
+ maxChars?: number;
228
+ },
229
+ ): YaziMentionBatch {
230
+ const maxPaths = options.maxPaths ?? YAZI_PICK_MAX_PATHS;
231
+ const maxChars = options.maxChars ?? YAZI_PICK_MAX_CHARS;
232
+ const inspect = options.statPath ?? pathKind;
233
+ const rendered: string[] = [];
234
+ const seen = new Set<string>();
235
+ let mentions = 0;
236
+ let plainPaths = 0;
237
+ let duplicates = 0;
238
+ let truncated = Math.max(0, paths.length - maxPaths);
239
+
240
+ const candidates = paths.slice(0, maxPaths);
241
+ for (const [index, rawPath] of candidates.entries()) {
242
+ const absolute = isAbsolute(rawPath) ? rawPath : resolve(options.cwd, rawPath);
243
+ if (seen.has(absolute)) {
244
+ duplicates += 1;
245
+ continue;
246
+ }
247
+ seen.add(absolute);
248
+ const kind = inspect(absolute);
249
+ let display = relativeWhenInside(absolute, options.cwd);
250
+ if (kind === "directory" && display !== "/" && !display.endsWith("/")) display = `${display}/`;
251
+ const mention = kind === "file" && !containsAsciiWhitespace(display);
252
+ const item = mention ? `@${display}` : `\`${display.replaceAll("`", "\\`")}\``;
253
+ if (draftContains(options.draft, item)) {
254
+ duplicates += 1;
255
+ continue;
256
+ }
257
+ const nextLength = rendered.length === 0 ? item.length : rendered.join(" ").length + 1 + item.length;
258
+ if (nextLength > maxChars) {
259
+ truncated += candidates.length - index;
260
+ break;
261
+ }
262
+ rendered.push(item);
263
+ if (mention) mentions += 1;
264
+ else plainPaths += 1;
265
+ }
266
+
267
+ return {
268
+ text: rendered.join(" "),
269
+ inserted: rendered.length,
270
+ mentions,
271
+ plainPaths,
272
+ duplicates,
273
+ truncated,
274
+ };
275
+ }
276
+
277
+ function appendDraft(draft: string, addition: string): string {
278
+ if (addition.length === 0) return draft;
279
+ if (draft.length === 0) return addition;
280
+ return `${draft.replace(/\s+$/u, "")} ${addition}`;
281
+ }
282
+
283
+ /** Own the only path from a Yazi decision into the interactive composer. */
284
+ export function createYaziBridge(deps: YaziBridgeDeps): YaziBridge {
285
+ const openSession = deps.openSession ?? createYaziSession;
286
+ let active: YaziSession | null = null;
287
+ let paneCwd: string | null = null;
288
+ let droppedLines = 0;
289
+ let disposed = false;
290
+ let generation = 0;
291
+ let livenessTimer: NodeJS.Timeout | null = null;
292
+ let sawLine = false;
293
+
294
+ const clearLiveness = (): void => {
295
+ if (livenessTimer) clearTimeout(livenessTimer);
296
+ livenessTimer = null;
297
+ };
298
+
299
+ const insert = (paths: ReadonlyArray<string>, cwd: string): void => {
300
+ const draft = deps.getDraft();
301
+ const batch = formatYaziMentions(paths, { cwd, draft, ...(deps.statPath ? { statPath: deps.statPath } : {}) });
302
+ if (batch.inserted === 0) {
303
+ if (batch.truncated > 0) {
304
+ deps.notice(
305
+ "warning",
306
+ `${batch.truncated} file-pane path${batch.truncated === 1 ? " was" : "s were"} omitted by the pick limits`,
307
+ );
308
+ }
309
+ return;
310
+ }
311
+ deps.setDraft(appendDraft(draft, batch.text));
312
+ deps.requestRender();
313
+ deps.notice(
314
+ "info",
315
+ `${batch.inserted} ${batch.inserted === 1 ? "path" : "paths"} from the file pane added to the draft`,
316
+ );
317
+ if (batch.plainPaths > 0) {
318
+ deps.notice(
319
+ "info",
320
+ `${batch.plainPaths} ${batch.plainPaths === 1 ? "path was" : "paths were"} inserted as plain text because directories and spaced paths cannot be file mentions`,
321
+ );
322
+ }
323
+ if (batch.truncated > 0) {
324
+ deps.notice(
325
+ "warning",
326
+ `${batch.truncated} file-pane path${batch.truncated === 1 ? " was" : "s were"} omitted by the pick limits`,
327
+ );
328
+ }
329
+ };
330
+
331
+ const start = async (mode: YaziSessionMode, profileMode: YaziProfileMode): Promise<YaziBridgeOpenResult> => {
332
+ const mux = deps.mux;
333
+ if (!mux?.available()) return { status: "unavailable", reason: "the pane layer is not available" };
334
+ generation += 1;
335
+ const ownGeneration = generation;
336
+ const token = (deps.pickToken ?? randomUUID)();
337
+ sawLine = false;
338
+ paneCwd = deps.getCwd();
339
+ const result = await openSession({
340
+ mux,
341
+ mode,
342
+ profileMode,
343
+ cwd: deps.getCwd(),
344
+ pickToken: token,
345
+ onEvent: (event: YaziEvent) => {
346
+ if (disposed || ownGeneration !== generation) return;
347
+ sawLine = true;
348
+ if (event.kind === "cd") {
349
+ paneCwd = event.cwd;
350
+ return;
351
+ }
352
+ if (event.values[0] !== token) {
353
+ droppedLines += 1;
354
+ return;
355
+ }
356
+ insert(event.values.slice(1), paneCwd ?? deps.getCwd());
357
+ },
358
+ onChooser: (choice) => {
359
+ if (disposed || ownGeneration !== generation) return;
360
+ paneCwd = choice.cwd;
361
+ if (choice.paths.length > 0) insert(choice.paths, choice.cwd);
362
+ },
363
+ onStopped: () => {
364
+ if (ownGeneration !== generation) return;
365
+ clearLiveness();
366
+ active = null;
367
+ },
368
+ });
369
+ if (result.status !== "opened") return result;
370
+ if (disposed || ownGeneration !== generation) {
371
+ await result.session.close();
372
+ return { status: "unavailable", reason: "the file pane was closed while it was opening" };
373
+ }
374
+ active = result.session;
375
+ if (mode === "companion") {
376
+ livenessTimer = setTimeout(() => {
377
+ if (disposed || ownGeneration !== generation || sawLine || active === null) return;
378
+ const stale = active;
379
+ active = null;
380
+ deps.notice("warning", "the file-pane event stream did not start; reopening yazi in chooser mode");
381
+ void stale.close().finally(() => {
382
+ if (!disposed && ownGeneration === generation) void start("chooser", profileMode);
383
+ });
384
+ }, deps.livenessMs ?? YAZI_LIVENESS_MS);
385
+ livenessTimer.unref();
386
+ }
387
+ return { status: "opened", mode, paneId: result.session.pane.paneId, existing: false };
388
+ };
389
+
390
+ return {
391
+ async open(options = {}): Promise<YaziBridgeOpenResult> {
392
+ if (disposed) return { status: "unavailable", reason: "the file-pane bridge is closed" };
393
+ const settings = deps.getSettings?.() ?? { mode: "companion", profile: "managed", followCwd: true };
394
+ if (active) {
395
+ if (settings.followCwd) {
396
+ const cwd = deps.getCwd();
397
+ if (await active.pushCwd(cwd)) paneCwd = cwd;
398
+ }
399
+ return { status: "opened", mode: active.mode, paneId: active.pane.paneId, existing: true };
400
+ }
401
+ const mode = options.once || settings.profile === "user" ? "chooser" : settings.mode;
402
+ if (!deps.mux?.available()) {
403
+ const result = await (
404
+ deps.runTerminalChooser ??
405
+ ((choiceOptions) =>
406
+ runYaziTerminalChooser({
407
+ ...choiceOptions,
408
+ stopUi: deps.stopUi ?? (() => {}),
409
+ startUi: deps.startUi ?? (() => {}),
410
+ requestRender: deps.requestRender,
411
+ }))
412
+ )({ cwd: deps.getCwd(), profileMode: settings.profile });
413
+ if (result.status === "chosen") {
414
+ paneCwd = result.choice.cwd;
415
+ insert(result.choice.paths, result.choice.cwd);
416
+ }
417
+ if (result.status === "chosen" || result.status === "cancelled") {
418
+ return { status: "opened", mode: "chooser", paneId: null, existing: false };
419
+ }
420
+ return result;
421
+ }
422
+ return await start(mode, settings.profile);
423
+ },
424
+ status(): Readonly<YaziBridgeStatus> {
425
+ const snapshot = active?.snapshot();
426
+ return {
427
+ mode: active?.mode ?? "closed",
428
+ paneId: active?.pane.paneId ?? null,
429
+ paneCwd: paneCwd ?? snapshot?.paneCwd ?? null,
430
+ lastLineAt: snapshot?.lastLineAt ?? null,
431
+ droppedLines,
432
+ };
433
+ },
434
+ dispose(): void {
435
+ if (disposed) return;
436
+ disposed = true;
437
+ generation += 1;
438
+ clearLiveness();
439
+ const closing = active;
440
+ active = null;
441
+ if (closing) void closing.close();
442
+ },
443
+ };
444
+ }