@iowarp/clio-coder 0.4.2 → 0.4.4

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 (523) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-WNAYYF4F.js} +12 -13
  5. package/dist/{agents-5N5NG3XG.js → agents-3OKXHLOI.js} +60 -57
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-VKNNMGPU.js} +21 -19
  8. package/dist/{builtins-K6TNDT24.js → builtins-WGALA46I.js} +9 -4
  9. package/dist/{chunk-XE3PCIXH.js → chunk-23L32XTI.js} +12 -9
  10. package/dist/{chunk-I64IFBLB.js → chunk-25QBEXRS.js} +18 -11
  11. package/dist/{chunk-CDNVLKUX.js → chunk-26QSH3EJ.js} +13 -7
  12. package/dist/{chunk-QQLGQY2A.js → chunk-2ASED4PZ.js} +22 -22
  13. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  14. package/dist/chunk-2DSOYNFC.js +108 -0
  15. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  16. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  17. package/dist/{chunk-O3YUNJZ2.js → chunk-2ZSONWVL.js} +82 -25
  18. package/dist/{chunk-2NHR3NAY.js → chunk-36CT5VVL.js} +331 -42
  19. package/dist/{chunk-2X4RYJTJ.js → chunk-3GY4F45V.js} +3 -3
  20. package/dist/{chunk-ZW55JB7N.js → chunk-3ODX73FK.js} +4 -6
  21. package/dist/{chunk-PBP4B7XR.js → chunk-3UNOLWNZ.js} +3 -3
  22. package/dist/{chunk-4JDLP6ZS.js → chunk-3UUXNFEX.js} +14 -10
  23. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  24. package/dist/{chunk-ZW4HH5JJ.js → chunk-4M6Z5QVF.js} +6 -6
  25. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  26. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  27. package/dist/{chunk-FSP7CMNU.js → chunk-54X7T7DK.js} +61 -6
  28. package/dist/{chunk-54ODD65L.js → chunk-5636DCO5.js} +4 -4
  29. package/dist/chunk-57XXR6DR.js +3763 -0
  30. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  31. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  32. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  33. package/dist/{chunk-YJISEZKC.js → chunk-5TUB6SLS.js} +6 -6
  34. package/dist/{chunk-IMXMHHMQ.js → chunk-6OSVSQL5.js} +341 -57
  35. package/dist/{chunk-Q4XWMHX6.js → chunk-6PAZTBPA.js} +14 -2
  36. package/dist/{chunk-FVDGR2ZL.js → chunk-6Q3CYFD3.js} +112 -39
  37. package/dist/{chunk-IDNA72AH.js → chunk-6QOTUPRG.js} +155 -36
  38. package/dist/{chunk-X7IARSHT.js → chunk-6UINWWS6.js} +16 -10
  39. package/dist/{chunk-CYZW7JHJ.js → chunk-72YIHOZQ.js} +9 -9
  40. package/dist/{chunk-IKSLQ4XV.js → chunk-75W7L2E2.js} +752 -861
  41. package/dist/{chunk-CRFOIAX3.js → chunk-7UGL4MB5.js} +6 -6
  42. package/dist/{chunk-HIICAHCJ.js → chunk-AUPNRN7C.js} +2 -2
  43. package/dist/{chunk-7BHIY2MW.js → chunk-BJVFZO5U.js} +8 -50
  44. package/dist/{chunk-B74PXLU7.js → chunk-CUSRQKPU.js} +65 -3
  45. package/dist/chunk-DQOVN6KV.js +386 -0
  46. package/dist/{chunk-E7GT7O5N.js → chunk-DT3LWJOB.js} +7 -4
  47. package/dist/chunk-DXKJURES.js +671 -0
  48. package/dist/{chunk-JBCS7CRR.js → chunk-EL24TAU4.js} +10 -10
  49. package/dist/{chunk-TPEQIQIE.js → chunk-ELWDPP3Y.js} +8 -8
  50. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  51. package/dist/chunk-EXLD33WO.js +381 -0
  52. package/dist/chunk-FEAXX7B6.js +101 -0
  53. package/dist/{chunk-RLYRBIYQ.js → chunk-FFUPXJC4.js} +90 -331
  54. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  55. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  56. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  57. package/dist/chunk-GX5WYQO4.js +59 -0
  58. package/dist/chunk-GYV6VZOC.js +26 -0
  59. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  60. package/dist/chunk-I2DWJ4GM.js +390 -0
  61. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  62. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  63. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  64. package/dist/chunk-IRXAATOX.js +539 -0
  65. package/dist/chunk-IXIY2H4R.js +44 -0
  66. package/dist/{chunk-SSEYRH53.js → chunk-IZXGRF7P.js} +92 -147
  67. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  68. package/dist/{chunk-JWJGP5DQ.js → chunk-JEIYHLOR.js} +7 -7
  69. package/dist/{chunk-F2I26BDK.js → chunk-JQLNNIKT.js} +4 -4
  70. package/dist/{chunk-BYMNWQ7O.js → chunk-JSD46VO2.js} +315 -63
  71. package/dist/{chunk-AK5XEFVZ.js → chunk-JT2RFCC5.js} +64 -14
  72. package/dist/{chunk-MCMZMDAC.js → chunk-K6T2ZAMZ.js} +168 -6
  73. package/dist/{chunk-PGF63K6I.js → chunk-KFV5L5SK.js} +73 -4
  74. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  75. package/dist/{chunk-PJX3WQUQ.js → chunk-LLXSDWXS.js} +3 -3
  76. package/dist/{chunk-DZAW46HP.js → chunk-LTIKRKFL.js} +3 -3
  77. package/dist/{chunk-DZEK6CJN.js → chunk-N56KALIC.js} +21 -21
  78. package/dist/{chunk-B7HM5Z7T.js → chunk-NAI6ZFCY.js} +9 -5
  79. package/dist/{chunk-I66ZTYNP.js → chunk-NRO2BJRH.js} +2656 -2213
  80. package/dist/{chunk-ZGNYYXQ6.js → chunk-NXIMQY5W.js} +3 -3
  81. package/dist/{chunk-IKOZFYBN.js → chunk-NXYCB2VD.js} +149 -106
  82. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  83. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  84. package/dist/chunk-ODGTEFFI.js +50 -0
  85. package/dist/{chunk-3F7VUY77.js → chunk-OEJSLEPW.js} +2 -2
  86. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  87. package/dist/{chunk-5KW52TEP.js → chunk-Q4WO54TA.js} +132 -77
  88. package/dist/{chunk-W6NIE6OW.js → chunk-QUFRYSWI.js} +13 -7
  89. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  90. package/dist/chunk-R6Q67RJH.js +134 -0
  91. package/dist/{chunk-W4YEMFBX.js → chunk-RAY4OVGZ.js} +3 -3
  92. package/dist/{chunk-ZNT2M6TG.js → chunk-RQCKCSRL.js} +17 -17
  93. package/dist/{chunk-LJID3DYZ.js → chunk-RXTN6AKH.js} +3 -3
  94. package/dist/{chunk-P75RZCJW.js → chunk-RZDWV63N.js} +3 -3
  95. package/dist/{chunk-UH632ZYL.js → chunk-S6PYF2XF.js} +2 -2
  96. package/dist/{chunk-HJWWJ6IL.js → chunk-TOIVGRUX.js} +17 -5
  97. package/dist/{chunk-HLAFFSEK.js → chunk-TQAHXW6Y.js} +2 -2
  98. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  99. package/dist/{chunk-2HFQNRV3.js → chunk-UEPWCCTY.js} +12 -12
  100. package/dist/chunk-UOIZ7DA4.js +41 -0
  101. package/dist/{chunk-UH347SHR.js → chunk-USR47QNF.js} +11 -11
  102. package/dist/{chunk-AZ4WMN4W.js → chunk-V6HJFQZE.js} +2 -2
  103. package/dist/chunk-V76WTFTW.js +318 -0
  104. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  105. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  106. package/dist/{chunk-UBRFI4HS.js → chunk-XULDXHTN.js} +142 -50
  107. package/dist/chunk-XXYSBZIQ.js +283 -0
  108. package/dist/{chunk-HKMD33FO.js → chunk-Y55JBDO5.js} +405 -122
  109. package/dist/{chunk-XOXV5GKE.js → chunk-YD5GIKET.js} +17 -8
  110. package/dist/{chunk-XGDPUNND.js → chunk-YECAMM3D.js} +2 -2
  111. package/dist/{chunk-BO7Y52RY.js → chunk-YNFKXPEC.js} +7 -7
  112. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  113. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  114. package/dist/cli/index.js +42 -40
  115. package/dist/{clio-7VB377CC.js → clio-QLICPCF5.js} +7 -7
  116. package/dist/{code-nav-YVLCYA7V.js → code-nav-IJR2DBPR.js} +9 -9
  117. package/dist/{components-UBWCQSRW.js → components-2TGAI2RC.js} +5 -6
  118. package/dist/{config-4HVOS65E.js → config-IUA6OYNS.js} +88 -81
  119. package/dist/{configure-PIWO7B24.js → configure-VEPX4NMX.js} +26 -25
  120. package/dist/{context-KQYIWPWT.js → context-2DKHWH2T.js} +60 -45
  121. package/dist/{context-IYEHL3WQ.js → context-4MPR7WKB.js} +78 -69
  122. package/dist/{context-N6ZE3LGJ.js → context-BOYF5EJM.js} +15 -11
  123. package/dist/{context-clear-G4OGZJDS.js → context-clear-S4ZJCQUX.js} +73 -65
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-3I3FYX6Y.js} +18 -17
  126. package/dist/detail-A7JAVSIG.js +98 -0
  127. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-RJ5I2F2O.js} +99 -75
  128. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  129. package/dist/{doctor-LHBD36VU.js → doctor-DKICC2SN.js} +71 -48
  130. package/dist/{eval-C45FYRJ6.js → eval-OQOQUDHK.js} +308 -146
  131. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  132. package/dist/{evidence-6SHONYAF.js → evidence-4DQ25GUQ.js} +79 -175
  133. package/dist/evidence-4F5USFKH.js +208 -0
  134. package/dist/{evolve-KRKMV72X.js → evolve-GSS52E5J.js} +71 -67
  135. package/dist/{extensions-KPZ2UHBB.js → extensions-G7MFLYHT.js} +8 -9
  136. package/dist/{fleet-IVTCKDHT.js → fleet-Q37YHHAQ.js} +126 -118
  137. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-G7E4N7SM.js} +16 -13
  138. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-O7M6QBA2.js} +9 -8
  139. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-TOUBW6OW.js} +21 -20
  140. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-SW33JJNI.js} +65 -60
  141. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-CV2655TW.js} +4 -5
  142. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-KESZX2YH.js} +25 -24
  143. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-UQPMTVE3.js} +66 -61
  144. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-MN2VG4MR.js} +65 -60
  145. package/dist/{init-T2QORQ3Y.js → init-PXEXQSBF.js} +90 -82
  146. package/dist/{interop-IN5I2A66.js → interop-ZG5T62U3.js} +12 -13
  147. package/dist/inventory-C26CFDRR.js +101 -0
  148. package/dist/{library-LSCATDLZ.js → library-B2W4N74O.js} +29 -29
  149. package/dist/{memory-HYOKAGGJ.js → memory-YCANYS5A.js} +73 -69
  150. package/dist/{models-2GPMFYCM.js → models-GERTU3YI.js} +51 -47
  151. package/dist/{monitor-E4ASVUJH.js → monitor-CPNIUULB.js} +74 -67
  152. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-J4BSH4WQ.js} +1288 -1606
  153. package/dist/{panes-E3RUXOW5.js → panes-BOHAEGYC.js} +4 -4
  154. package/dist/{panes-IXKLOKA2.js → panes-NXSLDQZ2.js} +10 -11
  155. package/dist/{paths-L7LGY6RN.js → paths-VSUWNC22.js} +6 -7
  156. package/dist/reset-TNWTB5LU.js +343 -0
  157. package/dist/{resources-OTRSN34L.js → resources-4PXNMD5G.js} +29 -22
  158. package/dist/{run-5DEYH5QK.js → run-D6XJ34CN.js} +132 -132
  159. package/dist/{share-IHWTLO3M.js → share-2NWMJJEE.js} +27 -27
  160. package/dist/{skills-IYMXMKW4.js → skills-KR7WON5G.js} +40 -33
  161. package/dist/{skills-eval-DROHSJAR.js → skills-eval-O2ZNOLDS.js} +81 -77
  162. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-ZZOUBK7O.js} +23 -22
  163. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-ZXPJD64J.js} +47 -37
  164. package/dist/{steer-Z5DO23FJ.js → steer-XA25PSCS.js} +4 -4
  165. package/dist/{support-U7QOWY26.js → support-7EMVWYG2.js} +6 -6
  166. package/dist/{targets-P2FUC4IL.js → targets-OMH2XCSN.js} +50 -49
  167. package/dist/tasks-IPAGMEIX.js +36 -0
  168. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-C2J3JYRE.js} +4 -4
  169. package/dist/{tools-5B7RO6MV.js → tools-EFFEAIDP.js} +8 -9
  170. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  171. package/dist/uninstall-HALS6BLF.js +407 -0
  172. package/dist/upgrade-MS72RJEP.js +306 -0
  173. package/dist/{usage-ME5MPXGX.js → usage-NHG6MCJM.js} +162 -108
  174. package/dist/{verifiers-BVZ7IWOO.js → verifiers-7AUNVXDY.js} +155 -22
  175. package/dist/{verify-5K7ZKQFC.js → verify-FWYGPKMR.js} +14 -12
  176. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  177. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-743CIGJW.js} +99 -90
  178. package/dist/{with-panes-BYOJCLAM.js → with-panes-BDQEWBRT.js} +10 -10
  179. package/dist/worker/entry.js +72 -68
  180. package/docs/README.md +3 -2
  181. package/docs/architecture/acp.md +17 -0
  182. package/docs/architecture/artifact-placement.md +1 -0
  183. package/docs/architecture/artifact-versions.md +2 -2
  184. package/docs/architecture/context-engine.md +4 -0
  185. package/docs/architecture/dispatch-typed-intent.md +1 -1
  186. package/docs/architecture/evidence-and-memory.md +1 -1
  187. package/docs/architecture/middleware-and-components.md +1 -1
  188. package/docs/architecture/model-catalog.md +21 -10
  189. package/docs/architecture/observability.md +19 -2
  190. package/docs/architecture/prompt-envelope-and-tools.md +17 -5
  191. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  192. package/docs/architecture/safety-model.md +25 -22
  193. package/docs/architecture/tui-design.md +1 -1
  194. package/docs/guide/built-in-agents.md +25 -11
  195. package/docs/guide/commands-and-modes.md +18 -3
  196. package/docs/guide/configuration-and-targets.md +100 -10
  197. package/docs/guide/configuration-reference.md +17 -7
  198. package/docs/guide/environment-variables.md +4 -2
  199. package/docs/guide/installation-and-lifecycle.md +37 -4
  200. package/docs/guide/proactive-memory.md +66 -55
  201. package/docs/guide/skills-marketplace.md +18 -0
  202. package/docs/guide/tool-usage.md +78 -3
  203. package/docs/history/config-knobs-audit.md +2 -2
  204. package/docs/process/development-pipeline.md +40 -2
  205. package/docs/process/eval-runner.md +67 -3
  206. package/docs/process/git-commit-provenance.md +15 -0
  207. package/docs/process/release-cut-checklist.md +207 -0
  208. package/docs/process/scientific-validation.md +18 -17
  209. package/evals/behavioral-machinery-support.ts +1 -0
  210. package/evals/behavioral-machinery.yaml +1 -1
  211. package/evals/behavioral-model.yaml +3 -2
  212. package/package.json +2 -2
  213. package/skills/README.md +7 -5
  214. package/skills/coding/ast-grep/SKILL.md +101 -30
  215. package/skills/coding/ast-grep/evals.md +26 -0
  216. package/skills/coding/coding-standards/SKILL.md +47 -2
  217. package/skills/coding/coding-standards/evals.md +23 -0
  218. package/skills/coding/prototype/SKILL.md +87 -28
  219. package/skills/coding/prototype/evals.md +19 -0
  220. package/skills/coding/tdd/SKILL.md +80 -53
  221. package/skills/coding/tdd/evals.md +20 -0
  222. package/skills/context/context-handoff/SKILL.md +43 -2
  223. package/skills/context/context-handoff/evals.md +44 -0
  224. package/skills/context/context-prime/SKILL.md +45 -15
  225. package/skills/context/context-prime/evals.md +45 -0
  226. package/skills/git/branch-closeout/SKILL.md +132 -0
  227. package/skills/git/branch-closeout/evals.md +133 -0
  228. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  229. package/skills/git/file-ticket/SKILL.md +77 -63
  230. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  231. package/skills/git/file-ticket/evals.md +31 -26
  232. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  233. package/skills/git/fix-issue/SKILL.md +87 -64
  234. package/skills/git/fix-issue/evals.md +35 -31
  235. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  236. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  237. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  238. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  239. package/skills/git/ship/SKILL.md +103 -67
  240. package/skills/git/ship/assets/pr-template.md +21 -0
  241. package/skills/git/ship/evals.md +44 -28
  242. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  243. package/skills/git/worktree-create/SKILL.md +80 -50
  244. package/skills/git/worktree-create/evals.md +40 -33
  245. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  246. package/skills/git/worktree-merge/SKILL.md +112 -65
  247. package/skills/git/worktree-merge/evals.md +42 -34
  248. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  249. package/skills/planning/archify/SKILL.md +196 -0
  250. package/skills/planning/archify/evals.md +65 -0
  251. package/skills/planning/architecture/SKILL.md +61 -12
  252. package/skills/planning/architecture/evals.md +65 -0
  253. package/skills/planning/backlog/SKILL.md +130 -14
  254. package/skills/planning/backlog/evals.md +142 -0
  255. package/skills/planning/prd/SKILL.md +47 -6
  256. package/skills/planning/prd/evals.md +54 -0
  257. package/skills/planning/product-intent/SKILL.md +57 -2
  258. package/skills/planning/product-intent/evals.md +70 -0
  259. package/skills/planning/tech-spec/SKILL.md +53 -2
  260. package/skills/planning/tech-spec/evals.md +73 -0
  261. package/skills/registry.yaml +58 -50
  262. package/skills/remote.yaml +13 -0
  263. package/skills/research/arxiv-literature/SKILL.md +76 -18
  264. package/skills/research/arxiv-literature/evals.md +50 -0
  265. package/skills/research/experiment-protocol/SKILL.md +20 -1
  266. package/skills/research/experiment-protocol/evals.md +23 -0
  267. package/skills/research/scientific-debugging/SKILL.md +24 -1
  268. package/skills/research/scientific-debugging/evals.md +18 -0
  269. package/skills/research/scientific-modernization/SKILL.md +26 -1
  270. package/skills/research/scientific-modernization/evals.md +27 -0
  271. package/skills/skill-marketplace.json +63 -28
  272. package/skills/workflow/cut-it/SKILL.md +64 -5
  273. package/skills/workflow/cut-it/evals.md +101 -0
  274. package/skills/workflow/design-council/SKILL.md +112 -27
  275. package/skills/workflow/design-council/evals.md +161 -0
  276. package/skills/workflow/grill-me/SKILL.md +85 -10
  277. package/skills/workflow/grill-me/evals.md +153 -0
  278. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  279. package/skills/workflow/workflow-distiller/evals.md +118 -0
  280. package/src/cli/args.ts +0 -8
  281. package/src/cli/configure-interop.ts +105 -13
  282. package/src/cli/configure-oauth.ts +57 -0
  283. package/src/cli/configure-onboarding.ts +980 -0
  284. package/src/cli/configure-target.ts +594 -0
  285. package/src/cli/configure.ts +1084 -529
  286. package/src/cli/context-map.ts +114 -0
  287. package/src/cli/context.ts +4 -0
  288. package/src/cli/doctor-state-size.ts +1 -12
  289. package/src/cli/doctor-validation-contract.ts +28 -0
  290. package/src/cli/doctor.ts +5 -0
  291. package/src/cli/evidence-detail.ts +1 -75
  292. package/src/cli/evidence-inventory.ts +1 -167
  293. package/src/cli/index.ts +3 -0
  294. package/src/cli/lifecycle-presenter.ts +436 -0
  295. package/src/cli/models.ts +10 -2
  296. package/src/cli/modes/print.ts +5 -1
  297. package/src/cli/reset.ts +228 -106
  298. package/src/cli/run.ts +7 -4
  299. package/src/cli/select.ts +664 -0
  300. package/src/cli/skills.ts +9 -2
  301. package/src/cli/targets.ts +3 -0
  302. package/src/cli/tasks.ts +84 -0
  303. package/src/cli/uninstall.ts +233 -165
  304. package/src/cli/upgrade.ts +210 -150
  305. package/src/cli/usage.ts +92 -27
  306. package/src/cli/validate-model.ts +3 -3
  307. package/src/cli/verifiers.ts +147 -1
  308. package/src/cli/wiki-generate.ts +1 -0
  309. package/src/core/commit-attribution.ts +41 -1
  310. package/src/core/config.ts +56 -0
  311. package/src/core/external-diagnostic.ts +44 -0
  312. package/src/core/gateway-routing.ts +157 -0
  313. package/src/core/git-commit-attribution.ts +46 -3
  314. package/src/core/run-overrides.ts +0 -5
  315. package/src/core/safe-exec.ts +17 -2
  316. package/src/core/skill-activation.ts +92 -2
  317. package/src/core/tool-names.ts +5 -2
  318. package/src/domains/agents/builtins/architect.md +1 -1
  319. package/src/domains/agents/builtins/coder.md +1 -1
  320. package/src/domains/agents/builtins/documenter.md +1 -1
  321. package/src/domains/agents/builtins/git-master.md +1 -1
  322. package/src/domains/agents/builtins/provenance.md +7 -7
  323. package/src/domains/agents/builtins/tester.md +1 -1
  324. package/src/domains/agents/builtins/verifier.md +2 -2
  325. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  326. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  327. package/src/domains/agents/catalog.ts +1 -1
  328. package/src/domains/agents/result-contract.ts +70 -0
  329. package/src/domains/context/extension.ts +31 -7
  330. package/src/domains/context/refresh.ts +3 -0
  331. package/src/domains/context/wiki/frontmatter.ts +5 -2
  332. package/src/domains/context/wiki/generate.ts +6 -0
  333. package/src/domains/context/wiki/map-seed.ts +589 -0
  334. package/src/domains/context/wiki/plan.ts +2 -2
  335. package/src/domains/context/wiki/prompts.ts +43 -0
  336. package/src/domains/dispatch/active-route-planner.ts +4 -0
  337. package/src/domains/dispatch/admission.ts +29 -0
  338. package/src/domains/dispatch/agent-candidates.ts +10 -0
  339. package/src/domains/dispatch/budget-envelope.ts +86 -1
  340. package/src/domains/dispatch/capability-match.ts +1 -0
  341. package/src/domains/dispatch/capacity-lease.ts +17 -0
  342. package/src/domains/dispatch/code-step.ts +11 -4
  343. package/src/domains/dispatch/contract.ts +42 -8
  344. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  345. package/src/domains/dispatch/extension.ts +184 -56
  346. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  347. package/src/domains/dispatch/fleet-run.ts +1 -0
  348. package/src/domains/dispatch/host-verification.ts +114 -13
  349. package/src/domains/dispatch/intent.ts +28 -18
  350. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  351. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  352. package/src/domains/dispatch/reservation-store.ts +5 -3
  353. package/src/domains/dispatch/state.ts +15 -2
  354. package/src/domains/dispatch/types.ts +20 -2
  355. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  356. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  357. package/src/domains/eval/metrics/token-stream.ts +201 -31
  358. package/src/domains/eval/metrics/tracked.ts +40 -4
  359. package/src/domains/eval/runners/clio-run.ts +17 -11
  360. package/src/domains/eval/runners/context-index.ts +2 -7
  361. package/src/domains/eval/runners/context-init.ts +3 -6
  362. package/src/domains/eval/runners/external-command.ts +29 -11
  363. package/src/domains/eval/schema/suite.ts +28 -0
  364. package/src/domains/eval/schema/verdict.ts +2 -2
  365. package/src/domains/eval/suites/resolve.ts +13 -1
  366. package/src/domains/eval/suites/run.ts +24 -3
  367. package/src/domains/evidence/build.ts +102 -15
  368. package/src/domains/evidence/detail.ts +69 -0
  369. package/src/domains/evidence/eval.ts +13 -1
  370. package/src/domains/evidence/finish-contract-map.ts +5 -1
  371. package/src/domains/evidence/inventory.ts +167 -0
  372. package/src/domains/evidence/store.ts +16 -0
  373. package/src/domains/evidence/types.ts +12 -0
  374. package/src/domains/extensions/resources.ts +7 -0
  375. package/src/domains/interop/registry.ts +6 -2
  376. package/src/domains/interop/types.ts +4 -0
  377. package/src/domains/lifecycle/migrations/index.ts +4 -0
  378. package/src/domains/memory/task-memory-policy.ts +70 -26
  379. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  380. package/src/domains/middleware/index.ts +0 -1
  381. package/src/domains/middleware/marketplace-offer.ts +22 -35
  382. package/src/domains/middleware/memory-intervention.ts +127 -32
  383. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  384. package/src/domains/middleware/runtime.ts +7 -3
  385. package/src/domains/middleware/skills-reminder.ts +31 -2
  386. package/src/domains/mux/detect.ts +3 -6
  387. package/src/domains/observability/accountability.ts +15 -1
  388. package/src/domains/observability/compaction-usage.ts +118 -0
  389. package/src/domains/observability/contract.ts +52 -7
  390. package/src/domains/observability/cost.ts +1 -1
  391. package/src/domains/observability/evidence-index.ts +10 -0
  392. package/src/domains/observability/extension.ts +15 -6
  393. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  394. package/src/domains/observability/projection.ts +394 -45
  395. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  396. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  397. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  398. package/src/domains/providers/contract.ts +4 -1
  399. package/src/domains/providers/extension.ts +40 -9
  400. package/src/domains/providers/model-capabilities.ts +9 -0
  401. package/src/domains/providers/model-discovery.ts +3 -4
  402. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  403. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +48 -26
  404. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  405. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  406. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  407. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  408. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  409. package/src/domains/providers/support.ts +11 -5
  410. package/src/domains/providers/target-model-cache.ts +25 -2
  411. package/src/domains/providers/types/capability-flags.ts +2 -0
  412. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  413. package/src/domains/providers/types/target-descriptor.ts +19 -0
  414. package/src/domains/resources/index.ts +3 -0
  415. package/src/domains/resources/skills/install.ts +72 -7
  416. package/src/domains/resources/skills/loader.ts +7 -0
  417. package/src/domains/resources/skills/marketplace.ts +63 -11
  418. package/src/domains/safety/action-classifier.ts +7 -0
  419. package/src/domains/safety/autonomy.ts +15 -0
  420. package/src/domains/safety/default-path-policy.ts +2 -0
  421. package/src/domains/safety/finish-contract-registration.ts +29 -14
  422. package/src/domains/safety/finish-contract.ts +252 -40
  423. package/src/domains/safety/index.ts +21 -1
  424. package/src/domains/safety/path-policy.ts +1 -1
  425. package/src/domains/safety/policy-engine.ts +60 -17
  426. package/src/domains/safety/protected-artifacts.ts +191 -88
  427. package/src/domains/safety/rigor.ts +53 -39
  428. package/src/domains/safety/run-effects.ts +2 -22
  429. package/src/domains/safety/skill-authority.ts +55 -0
  430. package/src/domains/safety/validation-contract.ts +388 -0
  431. package/src/domains/session/archive-readers.ts +10 -1
  432. package/src/domains/session/compaction/compact.ts +72 -22
  433. package/src/domains/session/decision-board.ts +101 -2
  434. package/src/domains/session/entries.ts +50 -7
  435. package/src/domains/session/extension.ts +4 -4
  436. package/src/domains/session/handoff.ts +2 -1
  437. package/src/domains/session/manager.ts +2 -3
  438. package/src/domains/session/task-board.ts +14 -1
  439. package/src/domains/session/tree/fork.ts +1 -2
  440. package/src/domains/session/tree/navigator.ts +1 -1
  441. package/src/domains/session/usage.ts +3 -3
  442. package/src/domains/user-tasks/acceptance.ts +56 -0
  443. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  444. package/src/domains/user-tasks/store.ts +34 -3
  445. package/src/engine/acp/adapter.ts +24 -6
  446. package/src/engine/acp/server.ts +21 -4
  447. package/src/engine/acp/transport.ts +53 -8
  448. package/src/engine/acp/types.ts +4 -0
  449. package/src/engine/agent.ts +13 -3
  450. package/src/engine/ai.ts +26 -8
  451. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  452. package/src/engine/api-registry.ts +3 -0
  453. package/src/engine/apis/ollama-native.ts +15 -0
  454. package/src/engine/apis/openai-completions.ts +117 -14
  455. package/src/engine/claude/subprocess-runtime.ts +107 -60
  456. package/src/engine/external-subprocess.ts +122 -6
  457. package/src/entry/background-model-metadata.ts +18 -0
  458. package/src/entry/compaction-prompt.ts +57 -0
  459. package/src/entry/orchestrator.ts +416 -218
  460. package/src/entry/task-memory-lifecycle.ts +35 -0
  461. package/src/interactive/chat-loop-messages.ts +13 -4
  462. package/src/interactive/chat-loop.ts +65 -2
  463. package/src/interactive/chat-renderer.ts +1 -0
  464. package/src/interactive/cost-overlay.ts +26 -2
  465. package/src/interactive/dispatch-board.ts +46 -717
  466. package/src/interactive/fleet-run-preview.ts +2 -1
  467. package/src/interactive/interactive-application.ts +3 -2
  468. package/src/interactive/interactive-presentation.ts +55 -12
  469. package/src/interactive/interactive-slash-runtime.ts +4 -2
  470. package/src/interactive/oracle.ts +5 -2
  471. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  472. package/src/interactive/overlays/message-picker.ts +2 -2
  473. package/src/interactive/overlays/settings.ts +2 -2
  474. package/src/interactive/overlays/tree-selector.ts +2 -2
  475. package/src/interactive/renderers/branch-summary.ts +1 -1
  476. package/src/interactive/renderers/worker-entry.ts +32 -0
  477. package/src/interactive/slash-autocomplete.ts +4 -6
  478. package/src/interactive/slash-commands.ts +49 -45
  479. package/src/interactive/slash-spec.ts +28 -0
  480. package/src/interactive/theme/labels.ts +19 -13
  481. package/src/interactive/turn-context.ts +9 -5
  482. package/src/interactive/turn-recovery.ts +8 -0
  483. package/src/interactive/turn-runtime.ts +27 -11
  484. package/src/interactive/turn-state.ts +7 -0
  485. package/src/interactive/view/artifacts.ts +2 -0
  486. package/src/interactive/worker-receipts.ts +1 -0
  487. package/src/interactive/worker-stream.ts +13 -4
  488. package/src/tools/bootstrap.ts +4 -0
  489. package/src/tools/builtin-tool-catalog.ts +31 -0
  490. package/src/tools/compete-worktrees.ts +7 -1
  491. package/src/tools/context/index.ts +30 -9
  492. package/src/tools/core-bootstrap.ts +16 -0
  493. package/src/tools/decide.ts +136 -0
  494. package/src/tools/dispatch-admission.ts +21 -0
  495. package/src/tools/dispatch-arguments.ts +1 -0
  496. package/src/tools/dispatch-event-text.ts +10 -0
  497. package/src/tools/dispatch-plan.ts +6 -2
  498. package/src/tools/dispatch-runner.ts +27 -1
  499. package/src/tools/dispatch-types.ts +6 -0
  500. package/src/tools/evidence.ts +96 -0
  501. package/src/tools/limitation.ts +76 -0
  502. package/src/tools/policy.ts +9 -0
  503. package/src/tools/presentation.ts +3 -0
  504. package/src/tools/registry.ts +11 -5
  505. package/src/tools/result-shaping.ts +17 -5
  506. package/src/tools/task-worktree.ts +13 -3
  507. package/src/tools/tasks.ts +10 -1
  508. package/src/tools/verify/authoring.ts +170 -83
  509. package/src/tools/verify/catalog.ts +122 -5
  510. package/src/tools/verify/index.ts +2 -1
  511. package/src/tools/verify/numeric.ts +298 -0
  512. package/src/tools/verify/perf.ts +143 -0
  513. package/src/tools/verify/scripts.ts +229 -2
  514. package/src/tools/worker-evidence.ts +3 -1
  515. package/src/worker/spec-contract.ts +4 -0
  516. package/dist/chunk-2Z2IKEXI.js +0 -1554
  517. package/dist/chunk-RVG5JXAL.js +0 -41
  518. package/dist/chunk-T56WDKA5.js +0 -183
  519. package/dist/chunk-VN3SHNBN.js +0 -313
  520. package/dist/chunk-VPKWYKEY.js +0 -169
  521. package/dist/reset-OAQP3W4O.js +0 -230
  522. package/dist/uninstall-N34PCTGJ.js +0 -331
  523. package/dist/upgrade-PXK3S2YM.js +0 -325
@@ -0,0 +1,980 @@
1
+ /**
2
+ * The first screen a new user sees.
3
+ *
4
+ * `clio-coder configure` on an unconfigured home used to be a column of
5
+ * readline questions: a numbered category menu, a numbered runtime menu, four
6
+ * typed values, a paragraph explaining four credential words before asking for
7
+ * one of them, and a run of `[y/N]` questions with no way back. The settings
8
+ * menus had already moved to the arrow-key picker and the lifecycle rail; this
9
+ * is the same treatment for the path that comes before them.
10
+ *
11
+ * The flow is a list of steps with a cursor. Each step draws its prompt, erases
12
+ * it once answered, and leaves one row on the rail saying what was chosen, so
13
+ * the screen is the answers so far plus the question at hand. Escape moves the
14
+ * cursor back one step and rewinds the rail to that step's row, which is what
15
+ * makes going back cheap: nothing is written to disk until the last step, so
16
+ * there is never a half-configured home to undo.
17
+ *
18
+ * Steps that do not apply are stepped over in whichever direction the cursor is
19
+ * moving, so a runtime with no URL and no credential is three questions and a
20
+ * model, and backing out of the model step lands on the runtime rather than on
21
+ * a prompt that was never shown.
22
+ */
23
+ import { createInterface } from "node:readline/promises";
24
+ import chalk from "chalk";
25
+
26
+ import {
27
+ bindAgentProfileInSettings,
28
+ type ClioSettings,
29
+ readSettings,
30
+ settingsPath,
31
+ updateSettings,
32
+ } from "../core/config.js";
33
+ import { THINKING_LEVELS, type ThinkingLevel } from "../core/defaults.js";
34
+ import { resolveOnPath } from "../domains/interop/detect.js";
35
+ import { authStoragePath, openAuthStorage, targetRequiresAuth } from "../domains/providers/auth/index.js";
36
+ import {
37
+ buildProviderSupportEntry,
38
+ isOrchestratorEligibleRuntime,
39
+ listProviderSupportEntries,
40
+ type ProviderSupportEntry,
41
+ recordTargetModelSnapshot,
42
+ resolveRuntimeAuthTarget,
43
+ } from "../domains/providers/index.js";
44
+ import { fingerprintNativeRuntime } from "../domains/providers/probe/fingerprint.js";
45
+ import { getRuntimeRegistry } from "../domains/providers/registry.js";
46
+ import { greetLmStudio } from "../domains/providers/runtimes/common/lmstudio-http.js";
47
+ import type { ProbeResult, RuntimeDescriptor } from "../domains/providers/types/runtime-descriptor.js";
48
+ import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
49
+ import { reviewInteropAgents } from "./configure-interop.js";
50
+ import { CONFIGURE_CATEGORY_CHOICES, type ConfigureCategory } from "./configure-layout.js";
51
+ import { loginOAuthRuntime } from "./configure-oauth.js";
52
+ import {
53
+ applyTarget,
54
+ buildDescriptor,
55
+ contextWindowUndiscovered,
56
+ defaultUrlFor,
57
+ deriveTargetId,
58
+ describeAuthStatus,
59
+ inventoryGap,
60
+ modelChoiceRefusal,
61
+ modelSupportsThinking,
62
+ normalizeUrl,
63
+ PROTOCOL_COMPAT_RUNTIME_IDS,
64
+ probeReadings,
65
+ railPrefix,
66
+ resolveSupportedWireModels,
67
+ runtimeProbe,
68
+ runtimesForCategory,
69
+ setOrchestratorPointer,
70
+ setWorkerDefaultPointer,
71
+ setWorkerProfilePointer,
72
+ type WireModelInventory,
73
+ } from "./configure-target.js";
74
+ import { createLifecyclePresenter, type LifecyclePresenter, shortenPath } from "./lifecycle-presenter.js";
75
+ import { canSelect, promptSelect, promptText } from "./select.js";
76
+ import { credentialWriteFailed, printError, printPlaintextCredentialWarning } from "./shared.js";
77
+ import { truncate } from "./text-layout.js";
78
+
79
+ export interface OnboardingStreams {
80
+ in: NodeJS.ReadableStream;
81
+ out: NodeJS.WritableStream;
82
+ }
83
+
84
+ /**
85
+ * Whether the wizard can run at all. Without a terminal on both ends there is
86
+ * nothing to read a keypress from, and the caller keeps the numbered readline
87
+ * flow, exactly as the settings menus do.
88
+ */
89
+ export function canRunOnboarding(streams: OnboardingStreams): boolean {
90
+ return canSelect(streams.in as NodeJS.ReadStream, streams.out as NodeJS.WriteStream);
91
+ }
92
+
93
+ /** Every answer row uses this label column, so the values line up down the rail. */
94
+ const LABEL_WIDTH = 11;
95
+
96
+ type CredentialSource = "env" | "stored" | "keep" | "skip" | "oauth-connect" | "oauth-skip";
97
+
98
+ // Every field is `| undefined` on purpose, not decoratively: the repo compiles
99
+ // with exactOptionalPropertyTypes, and going back a step clears the answers
100
+ // that depended on the one being changed by assigning undefined to them.
101
+ interface Answers {
102
+ category?: ConfigureCategory | undefined;
103
+ runtime?: RuntimeDescriptor | undefined;
104
+ targetId?: string | undefined;
105
+ url?: string | undefined;
106
+ /** Native runtime the URL turned out to be serving, when it is not the chosen one. */
107
+ detected?: { runtimeId: string; displayName: string } | undefined;
108
+ credential?: CredentialSource | undefined;
109
+ apiKeyEnv?: string | undefined;
110
+ apiKeyLiteral?: string | undefined;
111
+ inventory?: WireModelInventory | undefined;
112
+ /** The inputs the cached inventory was read for, so a changed URL re-reads it. */
113
+ inventoryKey?: string | undefined;
114
+ model?: string | undefined;
115
+ thinking?: ThinkingLevel | undefined;
116
+ probe?: ProbeResult | null | undefined;
117
+ antigravity?: { targetId: string; model: string } | undefined;
118
+ wiredPeers?: string[] | undefined;
119
+ }
120
+
121
+ type StepOutcome = "next" | "back" | "quit";
122
+
123
+ interface Wizard {
124
+ streams: OnboardingStreams;
125
+ presenter: LifecyclePresenter;
126
+ rail: string;
127
+ input: NodeJS.ReadStream;
128
+ output: NodeJS.WriteStream;
129
+ /** Row on the rail, at the wizard's shared label column. */
130
+ answer: (label: string, value: string) => void;
131
+ }
132
+
133
+ interface Step {
134
+ id: string;
135
+ applies: (answers: Answers) => boolean;
136
+ run: (wizard: Wizard, answers: Answers) => Promise<StepOutcome>;
137
+ }
138
+
139
+ /**
140
+ * The presenter's stream, counting the lines that went through it.
141
+ *
142
+ * Going back a step has to erase the row the step left behind, and the only
143
+ * honest way to know how many lines that is, is to count what was written. The
144
+ * pickers are not counted because they erase their own frame before returning.
145
+ */
146
+ interface RailWriter {
147
+ stream: NodeJS.WritableStream;
148
+ mark: () => number;
149
+ rewindTo: (mark: number) => void;
150
+ }
151
+
152
+ function railWriter(out: NodeJS.WritableStream): RailWriter {
153
+ let lines = 0;
154
+ const stream = {
155
+ write(chunk: string | Uint8Array): boolean {
156
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
157
+ for (const char of text) if (char === "\n") lines += 1;
158
+ return out.write(chunk);
159
+ },
160
+ isTTY: (out as { isTTY?: boolean }).isTTY,
161
+ columns: (out as { columns?: number }).columns,
162
+ } as unknown as NodeJS.WritableStream;
163
+ return {
164
+ stream,
165
+ mark: () => lines,
166
+ rewindTo(mark) {
167
+ const back = lines - mark;
168
+ if (back > 0) out.write(`\u001B[${back}A\u001B[0J`);
169
+ lines = mark;
170
+ },
171
+ };
172
+ }
173
+
174
+ function supportFor(runtime: RuntimeDescriptor): ProviderSupportEntry {
175
+ return buildProviderSupportEntry(runtime);
176
+ }
177
+
178
+ function allEntries(): ProviderSupportEntry[] {
179
+ return listProviderSupportEntries(getRuntimeRegistry().list());
180
+ }
181
+
182
+ /** The descriptor as it stands mid-wizard, for a probe or a model read. */
183
+ function draftDescriptor(answers: Answers, runtime: RuntimeDescriptor, withModel: boolean): TargetDescriptor {
184
+ const apiKeyRef = answers.credential === "stored" || answers.credential === "keep" ? runtime.id : undefined;
185
+ return buildDescriptor(runtime, answers.targetId ?? runtime.id, {
186
+ ...(answers.url !== undefined ? { url: answers.url } : {}),
187
+ ...(withModel && answers.model !== undefined ? { model: answers.model } : {}),
188
+ ...(answers.apiKeyEnv !== undefined ? { apiKeyEnv: answers.apiKeyEnv } : {}),
189
+ ...(apiKeyRef !== undefined ? { apiKeyRef } : {}),
190
+ ...(runtime.auth === "oauth" ? { oauthProfile: runtime.oauthProviderId ?? runtime.id } : {}),
191
+ });
192
+ }
193
+
194
+ const CATEGORY_STEP: Step = {
195
+ id: "category",
196
+ applies: () => true,
197
+ run: async (wizard, answers) => {
198
+ const current = CONFIGURE_CATEGORY_CHOICES.findIndex((choice) => choice.category === answers.category);
199
+ const result = await promptSelect<ConfigureCategory>({
200
+ heading: ["", chalk.bold("How will you connect Clio to a model?")],
201
+ choices: CONFIGURE_CATEGORY_CHOICES.map((choice) => ({
202
+ value: choice.category,
203
+ label: choice.label,
204
+ hint: choice.summary,
205
+ })),
206
+ initialIndex: current >= 0 ? current : 0,
207
+ railPrefix: wizard.rail,
208
+ backLabel: "cancel",
209
+ clearOnExit: true,
210
+ input: wizard.input,
211
+ output: wizard.output,
212
+ });
213
+ if (result.kind === "quit") return "quit";
214
+ if (result.kind === "back") return "back";
215
+ if (answers.category !== result.value) {
216
+ answers.runtime = undefined;
217
+ answers.detected = undefined;
218
+ }
219
+ answers.category = result.value;
220
+ const choice = CONFIGURE_CATEGORY_CHOICES.find((entry) => entry.category === result.value);
221
+ wizard.answer("Connection", choice?.label ?? result.value);
222
+ return "next";
223
+ },
224
+ };
225
+
226
+ const RUNTIME_STEP: Step = {
227
+ id: "runtime",
228
+ applies: () => true,
229
+ run: async (wizard, answers) => {
230
+ const registry = getRuntimeRegistry();
231
+ const chatEntries = allEntries().filter((entry) => {
232
+ const runtime = registry.get(entry.runtimeId);
233
+ return runtime !== null && isOrchestratorEligibleRuntime(runtime);
234
+ });
235
+ const entries = answers.category ? runtimesForCategory(chatEntries, answers.category) : chatEntries;
236
+ const usable = entries.length > 0 ? entries : chatEntries;
237
+ const previous = usable.findIndex((entry) => entry.runtimeId === answers.runtime?.id);
238
+ const featured = usable.findIndex((entry) => entry.featured);
239
+ const result = await promptSelect<string>({
240
+ heading: ["", chalk.bold("Which runtime?")],
241
+ choices: usable.map((entry) => ({ value: entry.runtimeId, label: entry.runtimeId, hint: entry.summary })),
242
+ initialIndex: previous >= 0 ? previous : featured >= 0 ? featured : 0,
243
+ railPrefix: wizard.rail,
244
+ backLabel: "back",
245
+ clearOnExit: true,
246
+ input: wizard.input,
247
+ output: wizard.output,
248
+ });
249
+ if (result.kind === "quit") return "quit";
250
+ if (result.kind === "back") return "back";
251
+ const runtime = registry.get(result.value);
252
+ if (!runtime) return "back";
253
+ if (answers.runtime?.id !== runtime.id) {
254
+ answers.targetId = undefined;
255
+ answers.url = undefined;
256
+ answers.detected = undefined;
257
+ answers.credential = undefined;
258
+ answers.apiKeyEnv = undefined;
259
+ answers.apiKeyLiteral = undefined;
260
+ answers.model = undefined;
261
+ answers.inventory = undefined;
262
+ answers.inventoryKey = undefined;
263
+ }
264
+ answers.runtime = runtime;
265
+ wizard.answer("Runtime", `${runtime.id} ${chalk.dim(supportFor(runtime).summary)}`);
266
+ return "next";
267
+ },
268
+ };
269
+
270
+ const TARGET_ID_STEP: Step = {
271
+ id: "target-id",
272
+ applies: () => true,
273
+ run: async (wizard, answers) => {
274
+ const runtime = answers.runtime;
275
+ if (!runtime) return "back";
276
+ const result = await promptText({
277
+ heading: ["", chalk.bold("Target id")],
278
+ initial: answers.targetId ?? deriveTargetId(runtime.id, readSettings().targets),
279
+ hint: "the name you will use for this target in `clio-coder targets`",
280
+ railPrefix: wizard.rail,
281
+ backLabel: "back",
282
+ clearOnExit: true,
283
+ validate: (value) => {
284
+ if (value.length === 0) return "a target id is required";
285
+ if (/\s/u.test(value)) return "a target id cannot contain spaces";
286
+ return null;
287
+ },
288
+ input: wizard.input,
289
+ output: wizard.output,
290
+ });
291
+ if (result.kind === "quit") return "quit";
292
+ if (result.kind === "back") return "back";
293
+ if (answers.targetId !== result.value) answers.inventoryKey = undefined;
294
+ answers.targetId = result.value;
295
+ wizard.answer("Target id", result.value);
296
+ return "next";
297
+ },
298
+ };
299
+
300
+ const URL_STEP: Step = {
301
+ id: "url",
302
+ applies: (answers) => answers.runtime !== undefined && supportFor(answers.runtime).supportsCustomUrl,
303
+ run: async (wizard, answers) => {
304
+ const runtime = answers.runtime;
305
+ if (!runtime) return "back";
306
+ const local = supportFor(runtime).group === "local-http";
307
+ const result = await promptText({
308
+ heading: ["", chalk.bold(local ? "Where is the server?" : "Base URL")],
309
+ initial: answers.url ?? defaultUrlFor(runtime.id),
310
+ hint: "host:port is enough; Clio fills in the scheme and the port it knows",
311
+ railPrefix: wizard.rail,
312
+ backLabel: "back",
313
+ clearOnExit: true,
314
+ validate: (value) => (value.length === 0 ? "a URL is required for this runtime" : null),
315
+ input: wizard.input,
316
+ output: wizard.output,
317
+ });
318
+ if (result.kind === "quit") return "quit";
319
+ if (result.kind === "back") return "back";
320
+ const url = normalizeUrl(result.value, runtime.id);
321
+ if (answers.url !== url) {
322
+ answers.inventoryKey = undefined;
323
+ answers.detected = undefined;
324
+ }
325
+ answers.url = url;
326
+ wizard.answer("URL", url);
327
+ await reportReachability(wizard, answers, runtime, url);
328
+ return "next";
329
+ },
330
+ };
331
+
332
+ /**
333
+ * Say what is at the URL, and never refuse over it.
334
+ *
335
+ * The readline wizard treated an unreachable endpoint as a question ("save this
336
+ * target anyway?") and an LM Studio URL that did not greet back as a hard
337
+ * refusal. Neither belongs in a first run: the server is frequently not started
338
+ * yet, and the fix is one `clio-coder configure --url` away.
339
+ */
340
+ async function reportReachability(
341
+ wizard: Wizard,
342
+ answers: Answers,
343
+ runtime: RuntimeDescriptor,
344
+ url: string,
345
+ ): Promise<void> {
346
+ const draft = draftDescriptor(answers, runtime, false);
347
+ // A freshly typed "store the key" answer is not on disk yet (that write
348
+ // happens at the end of the wizard), so it has to ride along as an explicit
349
+ // token the same way the model step already does; env and stored-from-before
350
+ // credentials resolve on their own through the descriptor's auth fields.
351
+ const probe = await runtimeProbe(runtime, draft, answers.apiKeyLiteral);
352
+ if (probe !== null) {
353
+ if (probe.ok) {
354
+ const readings = probeReadings(probe);
355
+ wizard.presenter.step(`reachable, ${readings.length > 0 ? readings.join(", ") : "no model list offered"}`);
356
+ } else if (probe.authFailed) {
357
+ wizard.presenter.warn("rejected the key");
358
+ } else {
359
+ wizard.presenter.warn(`not reachable, you can fix this later: ${probe.error ?? "no reply"}`);
360
+ }
361
+ }
362
+ if (probe?.ok && runtime.id === "lmstudio") {
363
+ const greeting = await greetLmStudio(draft, { credentialsPresent: new Set(), httpTimeoutMs: 750 });
364
+ if (!greeting.ok) wizard.presenter.warn(`${url} answered, but not with the LM Studio greeting`);
365
+ }
366
+ if (PROTOCOL_COMPAT_RUNTIME_IDS.has(runtime.id)) {
367
+ const fingerprint = await fingerprintNativeRuntime(url);
368
+ if (fingerprint && getRuntimeRegistry().get(fingerprint.runtimeId)) {
369
+ answers.detected = { runtimeId: fingerprint.runtimeId, displayName: fingerprint.displayName };
370
+ }
371
+ }
372
+ }
373
+
374
+ const DETECTED_RUNTIME_STEP: Step = {
375
+ id: "detected-runtime",
376
+ applies: (answers) => answers.detected !== undefined && answers.runtime !== undefined,
377
+ run: async (wizard, answers) => {
378
+ const detected = answers.detected;
379
+ const runtime = answers.runtime;
380
+ if (!detected || !runtime) return "next";
381
+ const native = getRuntimeRegistry().get(detected.runtimeId);
382
+ if (!native) return "next";
383
+ const result = await promptSelect<boolean>({
384
+ heading: ["", chalk.bold(`That URL is serving ${detected.displayName}.`)],
385
+ choices: [
386
+ {
387
+ value: true,
388
+ label: `Use ${native.id}`,
389
+ hint: "its own runtime, with resident-model lifecycle and its real capabilities",
390
+ },
391
+ { value: false, label: `Keep ${runtime.id}`, hint: "the generic protocol runtime you picked" },
392
+ ],
393
+ initialIndex: 0,
394
+ railPrefix: wizard.rail,
395
+ backLabel: "back",
396
+ clearOnExit: true,
397
+ input: wizard.input,
398
+ output: wizard.output,
399
+ });
400
+ if (result.kind === "quit") return "quit";
401
+ if (result.kind === "back") return "back";
402
+ if (result.value) {
403
+ answers.runtime = native;
404
+ answers.inventoryKey = undefined;
405
+ wizard.answer("Detected", `${detected.displayName}, using runtime ${native.id}`);
406
+ } else {
407
+ wizard.answer("Detected", `${detected.displayName}, keeping runtime ${runtime.id}`);
408
+ }
409
+ return "next";
410
+ },
411
+ };
412
+
413
+ /** The credential source the screen opens on, which is the one that is usually right. */
414
+ function defaultCredentialSource(runtime: RuntimeDescriptor, targetId: string): CredentialSource {
415
+ const status = openAuthStorage().statusForTarget(resolveRuntimeAuthTarget(runtime), { includeFallback: false });
416
+ if (status.source === "stored-api-key") return "keep";
417
+ if (status.source === "environment") return "env";
418
+ // A local llama.cpp server wants no credential, and offering `env` sent the
419
+ // user to an unexplained variable name with nothing correct to type into it.
420
+ return targetRequiresAuth({ id: targetId, runtime: runtime.id }, runtime) ? "env" : "skip";
421
+ }
422
+
423
+ /**
424
+ * Run the browser sign-in on a readline interface that exists only for it.
425
+ *
426
+ * A readline interface left open across the wizard echoes every keypress the
427
+ * pickers read, which puts a stray line under each menu and throws off the
428
+ * erase that replaces it with the answer row. So the wizard owns no readline,
429
+ * and the one flow that needs one opens and closes it around the call.
430
+ */
431
+ async function connectOAuth(wizard: Wizard, runtime: RuntimeDescriptor): Promise<boolean> {
432
+ const rl = createInterface({ input: wizard.streams.in, output: wizard.streams.out });
433
+ try {
434
+ return await loginOAuthRuntime(rl, runtime);
435
+ } finally {
436
+ rl.close();
437
+ }
438
+ }
439
+
440
+ const CREDENTIAL_STEP: Step = {
441
+ id: "credential",
442
+ applies: (answers) => answers.runtime?.auth === "api-key" || answers.runtime?.auth === "oauth",
443
+ run: async (wizard, answers) => {
444
+ const runtime = answers.runtime;
445
+ if (!runtime) return "back";
446
+ const stored = describeAuthStatus(runtime);
447
+ if (runtime.auth === "oauth") {
448
+ const result = await promptSelect<CredentialSource>({
449
+ heading: ["", chalk.bold(`Sign in to ${runtime.displayName}`), chalk.dim(`credential: ${stored}`)],
450
+ choices: [
451
+ { value: "oauth-connect", label: "Connect now", hint: "opens your browser and waits for the callback" },
452
+ {
453
+ value: "oauth-skip",
454
+ label: "Later",
455
+ hint: `run \`clio-coder auth login ${runtime.id}\` before the first turn`,
456
+ },
457
+ ],
458
+ initialIndex: answers.credential === "oauth-skip" ? 1 : 0,
459
+ railPrefix: wizard.rail,
460
+ backLabel: "back",
461
+ clearOnExit: true,
462
+ input: wizard.input,
463
+ output: wizard.output,
464
+ });
465
+ if (result.kind === "quit") return "quit";
466
+ if (result.kind === "back") return "back";
467
+ answers.credential = result.value;
468
+ if (result.value === "oauth-connect") {
469
+ const connected = await connectOAuth(wizard, runtime);
470
+ if (!connected) {
471
+ wizard.presenter.warn(`sign-in did not complete; run \`clio-coder auth login ${runtime.id}\` when ready`);
472
+ wizard.answer("Credential", "not connected yet");
473
+ return "next";
474
+ }
475
+ wizard.answer("Credential", `connected to ${runtime.id}`);
476
+ return "next";
477
+ }
478
+ wizard.answer("Credential", "not connected yet");
479
+ return "next";
480
+ }
481
+
482
+ const hasStored = stored !== "none stored";
483
+ const choices = [
484
+ {
485
+ value: "env" as CredentialSource,
486
+ label: "Environment variable",
487
+ hint: "read at call time; nothing is written to disk",
488
+ },
489
+ {
490
+ value: "stored" as CredentialSource,
491
+ label: "Store the key",
492
+ hint: "written to credentials.yaml, mode 0600, not encrypted",
493
+ },
494
+ ...(hasStored ? [{ value: "keep" as CredentialSource, label: "Keep what is there", hint: stored }] : []),
495
+ { value: "skip" as CredentialSource, label: "No key", hint: "this target needs no credential" },
496
+ ];
497
+ const current = answers.credential ?? defaultCredentialSource(runtime, answers.targetId ?? runtime.id);
498
+ const initial = Math.max(
499
+ 0,
500
+ choices.findIndex((choice) => choice.value === current),
501
+ );
502
+ const result = await promptSelect<CredentialSource>({
503
+ heading: ["", chalk.bold("How should Clio get the API key?"), chalk.dim(`currently: ${stored}`)],
504
+ choices,
505
+ initialIndex: initial,
506
+ railPrefix: wizard.rail,
507
+ backLabel: "back",
508
+ clearOnExit: true,
509
+ input: wizard.input,
510
+ output: wizard.output,
511
+ });
512
+ if (result.kind === "quit") return "quit";
513
+ if (result.kind === "back") return "back";
514
+ answers.credential = result.value;
515
+ if (result.value !== "env") answers.apiKeyEnv = undefined;
516
+ if (result.value !== "stored") answers.apiKeyLiteral = undefined;
517
+ if (result.value === "keep") wizard.answer("Credential", stored);
518
+ if (result.value === "skip") wizard.answer("Credential", "none");
519
+ return "next";
520
+ },
521
+ };
522
+
523
+ const CREDENTIAL_VALUE_STEP: Step = {
524
+ id: "credential-value",
525
+ applies: (answers) => answers.credential === "env" || answers.credential === "stored",
526
+ run: async (wizard, answers) => {
527
+ const runtime = answers.runtime;
528
+ if (!runtime) return "back";
529
+ const wantsEnv = answers.credential === "env";
530
+ const result = await promptText({
531
+ heading: ["", chalk.bold(wantsEnv ? "Which environment variable?" : "Paste the API key")],
532
+ initial: wantsEnv ? (answers.apiKeyEnv ?? runtime.credentialsEnvVar ?? "") : "",
533
+ hint: wantsEnv
534
+ ? "Clio reads it every time it calls the provider, so the key never lands on disk"
535
+ : `stored at ${shortenPath(authStoragePath())}`,
536
+ ...(wantsEnv ? {} : { mask: true }),
537
+ railPrefix: wizard.rail,
538
+ backLabel: "back",
539
+ clearOnExit: true,
540
+ input: wizard.input,
541
+ output: wizard.output,
542
+ });
543
+ if (result.kind === "quit") return "quit";
544
+ if (result.kind === "back") return "back";
545
+ if (wantsEnv) {
546
+ answers.apiKeyEnv = result.value.length > 0 ? result.value : undefined;
547
+ answers.inventoryKey = undefined;
548
+ wizard.answer("Credential", result.value.length > 0 ? `$${result.value}` : "none");
549
+ return "next";
550
+ }
551
+ answers.apiKeyLiteral = result.value.length > 0 ? result.value : undefined;
552
+ answers.inventoryKey = undefined;
553
+ wizard.answer("Credential", result.value.length > 0 ? "stored in credentials.yaml" : "none");
554
+ return "next";
555
+ },
556
+ };
557
+
558
+ /** Read the model list once per distinct set of connection answers. */
559
+ async function modelInventory(answers: Answers, runtime: RuntimeDescriptor): Promise<WireModelInventory> {
560
+ const key = [
561
+ runtime.id,
562
+ answers.targetId,
563
+ answers.url,
564
+ answers.apiKeyEnv,
565
+ answers.apiKeyLiteral ? "literal" : "",
566
+ ].join("|");
567
+ if (answers.inventoryKey === key && answers.inventory) return answers.inventory;
568
+ const inventory = await resolveSupportedWireModels(
569
+ runtime,
570
+ draftDescriptor(answers, runtime, false),
571
+ undefined,
572
+ answers.apiKeyLiteral,
573
+ );
574
+ answers.inventory = inventory;
575
+ answers.inventoryKey = key;
576
+ return inventory;
577
+ }
578
+
579
+ const MODEL_STEP: Step = {
580
+ id: "model",
581
+ applies: () => true,
582
+ run: async (wizard, answers) => {
583
+ const runtime = answers.runtime;
584
+ if (!runtime) return "back";
585
+ const support = supportFor(runtime);
586
+ const inventory = await modelInventory(answers, runtime);
587
+ // A catalog-ordered list has no head worth recommending: openai's first of
588
+ // 38 ids is `gpt-4` because g sorts early.
589
+ const catalogOrdered = support.modelSource === "catalog";
590
+ const preferred =
591
+ answers.model ??
592
+ (inventory.source === "probe" ? inventory.models[0] : support.defaultModel) ??
593
+ (catalogOrdered ? undefined : inventory.models[0]) ??
594
+ undefined;
595
+
596
+ if (inventory.models.length === 0) {
597
+ wizard.presenter.warn(inventoryGap(runtime, { url: answers.url }));
598
+ const result = await promptText({
599
+ heading: ["", chalk.bold("Which model?")],
600
+ initial: preferred ?? "",
601
+ hint: "the wire id the provider documents; Clio sends it as written",
602
+ railPrefix: wizard.rail,
603
+ backLabel: "back",
604
+ clearOnExit: true,
605
+ validate: (value) => (value.length === 0 ? "a model id is required" : null),
606
+ input: wizard.input,
607
+ output: wizard.output,
608
+ });
609
+ if (result.kind === "quit") return "quit";
610
+ if (result.kind === "back") return "back";
611
+ answers.model = result.value;
612
+ wizard.answer("Model", result.value);
613
+ await readModelCapabilities(answers, runtime);
614
+ return "next";
615
+ }
616
+
617
+ const selected = inventory.models.indexOf(preferred ?? "");
618
+ const result = await promptSelect<string>({
619
+ heading: [
620
+ "",
621
+ chalk.bold("Which model?"),
622
+ chalk.dim(
623
+ inventory.source === "probe"
624
+ ? "read live from the target just now"
625
+ : inventory.source === "cache"
626
+ ? "cached model snapshot; not verified in this run"
627
+ : "model hints; not a live account catalog",
628
+ ),
629
+ ],
630
+ choices: inventory.models.map((model) => {
631
+ const state = inventory.modelStates?.[model]?.state;
632
+ const hint = model === preferred ? "default" : state !== undefined && state !== "unknown" ? state : undefined;
633
+ const label = inventory.labels?.[model];
634
+ return {
635
+ value: model,
636
+ label: label && label !== model ? `${model} — ${label}` : model,
637
+ ...(hint === undefined ? {} : { hint }),
638
+ };
639
+ }),
640
+ initialIndex: selected >= 0 ? selected : 0,
641
+ railPrefix: wizard.rail,
642
+ backLabel: "back",
643
+ clearOnExit: true,
644
+ input: wizard.input,
645
+ output: wizard.output,
646
+ });
647
+ if (result.kind === "quit") return "quit";
648
+ if (result.kind === "back") return "back";
649
+ const refusal = modelChoiceRefusal(
650
+ runtime,
651
+ { id: answers.targetId ?? runtime.id, url: answers.url },
652
+ result.value,
653
+ inventory,
654
+ );
655
+ if (refusal !== null) {
656
+ wizard.presenter.warn(refusal);
657
+ return "back";
658
+ }
659
+ answers.model = result.value;
660
+ wizard.answer("Model", result.value);
661
+ await readModelCapabilities(answers, runtime);
662
+ return "next";
663
+ },
664
+ };
665
+
666
+ /**
667
+ * Probe once more with the chosen model set, because a model's context window
668
+ * and whether it reasons are per-model facts and the earlier probe ran before
669
+ * there was a model to ask about.
670
+ */
671
+ async function readModelCapabilities(answers: Answers, runtime: RuntimeDescriptor): Promise<void> {
672
+ const descriptor = draftDescriptor(answers, runtime, true);
673
+ const probe = await runtimeProbe(runtime, descriptor);
674
+ answers.probe = probe;
675
+ if (probe?.ok && probe.models) {
676
+ recordTargetModelSnapshot(descriptor, probe.models, probe.modelLabels ? { modelLabels: probe.modelLabels } : {});
677
+ }
678
+ }
679
+
680
+ const THINKING_HINTS: Readonly<Record<ThinkingLevel, string>> = {
681
+ off: "never ask this model to think before answering",
682
+ minimal: "a sentence of reasoning",
683
+ low: "short reasoning, the usual choice",
684
+ medium: "more reasoning on hard turns",
685
+ high: "long reasoning, slower and more expensive",
686
+ xhigh: "longer still, where the model offers it",
687
+ max: "everything the model will spend",
688
+ };
689
+
690
+ const THINKING_STEP: Step = {
691
+ id: "thinking",
692
+ applies: (answers) => {
693
+ const runtime = answers.runtime;
694
+ if (!runtime || answers.model === undefined) return false;
695
+ // A generic OpenAI/Anthropic-compatible endpoint reports nothing about
696
+ // reasoning, so the answer here is also what tells Clio whether the model
697
+ // has it at all.
698
+ if (PROTOCOL_COMPAT_RUNTIME_IDS.has(runtime.id)) return true;
699
+ return modelSupportsThinking(runtime, draftDescriptor(answers, runtime, true), answers.probe ?? null);
700
+ },
701
+ run: async (wizard, answers) => {
702
+ const current = THINKING_LEVELS.indexOf(answers.thinking ?? "low");
703
+ const result = await promptSelect<ThinkingLevel>({
704
+ heading: ["", chalk.bold("How hard should it think?"), chalk.dim("changeable any time in `clio-coder configure`")],
705
+ choices: THINKING_LEVELS.map((level) => ({ value: level, label: level, hint: THINKING_HINTS[level] })),
706
+ initialIndex: current >= 0 ? current : 0,
707
+ railPrefix: wizard.rail,
708
+ backLabel: "back",
709
+ clearOnExit: true,
710
+ input: wizard.input,
711
+ output: wizard.output,
712
+ });
713
+ if (result.kind === "quit") return "quit";
714
+ if (result.kind === "back") return "back";
715
+ answers.thinking = result.value;
716
+ wizard.answer("Thinking", result.value);
717
+ return "next";
718
+ },
719
+ };
720
+
721
+ const ANTIGRAVITY_COLLEAGUE_STEP: Step = {
722
+ id: "antigravity-colleague",
723
+ applies: (answers) =>
724
+ answers.runtime !== undefined &&
725
+ isOrchestratorEligibleRuntime(answers.runtime) &&
726
+ resolveOnPath(["agy"]).presence === "present" &&
727
+ !readSettings().targets.some((target) => target.runtime === "antigravity-code"),
728
+ run: async (wizard, answers) => {
729
+ const runtime = getRuntimeRegistry().get("antigravity-code");
730
+ if (!runtime) return "next";
731
+ const choice = await promptSelect<boolean>({
732
+ heading: [
733
+ "",
734
+ chalk.bold("Add your local Antigravity research colleague?"),
735
+ chalk.dim("optional, experimental, dispatch-only; Clio does not authenticate or inspect its session"),
736
+ ],
737
+ choices: [
738
+ { value: false, label: "Not now", hint: "your primary Clio target is unchanged" },
739
+ { value: true, label: "Add read-only colleague", hint: "probe the non-generating model catalog" },
740
+ ],
741
+ initialIndex: answers.antigravity ? 1 : 0,
742
+ railPrefix: wizard.rail,
743
+ backLabel: "back",
744
+ clearOnExit: true,
745
+ input: wizard.input,
746
+ output: wizard.output,
747
+ });
748
+ if (choice.kind === "quit") return "quit";
749
+ if (choice.kind === "back") return "back";
750
+ if (!choice.value) {
751
+ answers.antigravity = undefined;
752
+ wizard.answer("Colleague", "not added");
753
+ return "next";
754
+ }
755
+ const targetId = deriveTargetId(runtime.id, readSettings().targets);
756
+ const descriptor = buildDescriptor(runtime, targetId, {});
757
+ const inventory = await resolveSupportedWireModels(runtime, descriptor);
758
+ if (inventory.source !== "probe" || inventory.models.length === 0) {
759
+ answers.antigravity = undefined;
760
+ wizard.presenter.warn(
761
+ inventory.probeError ??
762
+ "Antigravity did not return a live catalog. Run `agy` yourself, complete sign-in, then add it from Configure → Targets.",
763
+ );
764
+ wizard.answer("Colleague", "not added; sign in with agy first");
765
+ return "next";
766
+ }
767
+ const picked = await promptSelect<string>({
768
+ heading: ["", chalk.bold("Antigravity research model"), chalk.dim("live account catalog")],
769
+ choices: inventory.models.map((model) => ({
770
+ value: model,
771
+ label: inventory.labels?.[model] ? `${model} — ${inventory.labels[model]}` : model,
772
+ })),
773
+ initialIndex: 0,
774
+ railPrefix: wizard.rail,
775
+ backLabel: "back",
776
+ clearOnExit: true,
777
+ input: wizard.input,
778
+ output: wizard.output,
779
+ });
780
+ if (picked.kind === "quit") return "quit";
781
+ if (picked.kind === "back") return "back";
782
+ answers.antigravity = { targetId, model: picked.value };
783
+ wizard.answer("Colleague", `${targetId}/${picked.value} → world-knowledge`);
784
+ return "next";
785
+ },
786
+ };
787
+
788
+ const PEERS_STEP: Step = {
789
+ id: "peers",
790
+ applies: () => true,
791
+ run: async (wizard, answers) => {
792
+ // No readline: the multi-select reads keys directly, and an interface left
793
+ // open would echo them over the frame it is drawing.
794
+ const outcome = await reviewInteropAgents({
795
+ rl: null,
796
+ streams: wizard.streams,
797
+ presenter: wizard.presenter,
798
+ rail: wizard.rail,
799
+ quiet: true,
800
+ });
801
+ if (outcome.back) return "back";
802
+ answers.wiredPeers = outcome.wired;
803
+ return "next";
804
+ },
805
+ };
806
+
807
+ // Credential steps come before the URL step: a runtime that needs or may need a
808
+ // key (litellm, every cloud runtime, keyed openai-compat) has to have one in
809
+ // hand before the URL step probes reachability, or the probe reads as
810
+ // unreachable when the gateway is live and only the key was missing. A runtime
811
+ // with no credential at all skips both credential steps and keeps this order
812
+ // exactly as it was.
813
+ const STEPS: ReadonlyArray<Step> = [
814
+ CATEGORY_STEP,
815
+ RUNTIME_STEP,
816
+ TARGET_ID_STEP,
817
+ CREDENTIAL_STEP,
818
+ CREDENTIAL_VALUE_STEP,
819
+ URL_STEP,
820
+ DETECTED_RUNTIME_STEP,
821
+ MODEL_STEP,
822
+ THINKING_STEP,
823
+ ANTIGRAVITY_COLLEAGUE_STEP,
824
+ PEERS_STEP,
825
+ ];
826
+
827
+ /** Write everything the wizard collected, in one settings update. */
828
+ function persist(answers: Answers, descriptor: TargetDescriptor, chatEligible: boolean): void {
829
+ updateSettings((settings: ClioSettings): void => {
830
+ applyTarget(settings, descriptor);
831
+ // The first target is the one everything points at. A second target is a
832
+ // choice, and that is what the settings menu is for; asking a new user
833
+ // which of their one target should answer chat is not a question.
834
+ if (chatEligible) setOrchestratorPointer(settings, descriptor, answers.model ?? null);
835
+ setWorkerDefaultPointer(settings, descriptor, answers.model ?? null);
836
+ if (answers.thinking !== undefined) {
837
+ settings.chat.thinkingLevel = answers.thinking;
838
+ settings.fleet.default.thinkingLevel = answers.thinking;
839
+ }
840
+ if (answers.antigravity !== undefined) {
841
+ const antigravityRuntime = getRuntimeRegistry().get("antigravity-code");
842
+ if (!antigravityRuntime) throw new Error("Antigravity runtime disappeared before settings were written");
843
+ const external = buildDescriptor(antigravityRuntime, answers.antigravity.targetId, {
844
+ model: answers.antigravity.model,
845
+ });
846
+ applyTarget(settings, external);
847
+ setWorkerProfilePointer(settings, "world-knowledge-external", external, answers.antigravity.model);
848
+ bindAgentProfileInSettings(settings, "world-knowledge", "world-knowledge-external");
849
+ }
850
+ });
851
+ }
852
+
853
+ export async function runOnboardingWizard(streams: OnboardingStreams): Promise<number> {
854
+ const writer = railWriter(streams.out);
855
+ const presenter = createLifecyclePresenter({ stream: writer.stream });
856
+ const rail = railPrefix(presenter.isPlain());
857
+ const columns = (streams.out as { columns?: number }).columns ?? 80;
858
+ const wizard: Wizard = {
859
+ streams,
860
+ presenter,
861
+ rail,
862
+ input: streams.in as NodeJS.ReadStream,
863
+ output: streams.out as NodeJS.WriteStream,
864
+ answer: (label, value) => {
865
+ presenter.fields([[label.padEnd(LABEL_WIDTH), truncate(value, Math.max(12, columns - LABEL_WIDTH - 6))]]);
866
+ },
867
+ };
868
+
869
+ presenter.header("Welcome to Clio Coder", "configure");
870
+ presenter.note("Pick the model you want Clio to talk to; escape goes back a step and changes nothing.");
871
+ presenter.note(`Result: ${shortenPath(settingsPath())}`);
872
+
873
+ const answers: Answers = {};
874
+ const marks = new Array<number>(STEPS.length).fill(writer.mark());
875
+ let cursor = 0;
876
+ let direction = 1;
877
+
878
+ while (cursor < STEPS.length) {
879
+ const step = STEPS[cursor];
880
+ if (step === undefined) break;
881
+ if (!step.applies(answers)) {
882
+ cursor += direction;
883
+ if (cursor < 0) return cancel(presenter);
884
+ continue;
885
+ }
886
+ marks[cursor] = writer.mark();
887
+ const outcome = await step.run(wizard, answers);
888
+ if (outcome === "quit") return cancel(presenter);
889
+ if (outcome === "back") {
890
+ direction = -1;
891
+ cursor -= 1;
892
+ // Rewind past the row the step we are returning to left behind, so it
893
+ // can ask again in the same place rather than under its own answer.
894
+ while (cursor >= 0 && !(STEPS[cursor]?.applies(answers) ?? false)) cursor -= 1;
895
+ if (cursor < 0) return cancel(presenter);
896
+ writer.rewindTo(marks[cursor] ?? writer.mark());
897
+ continue;
898
+ }
899
+ direction = 1;
900
+ cursor += 1;
901
+ }
902
+
903
+ return finish(wizard, answers);
904
+ }
905
+
906
+ function cancel(presenter: LifecyclePresenter): number {
907
+ // A first-run cancel really does leave Clio unconfigured, which is why this
908
+ // exits 130 where leaving the settings menu exits 0.
909
+ presenter.done("Cancelled, nothing written");
910
+ printError("configuration cancelled");
911
+ return 130;
912
+ }
913
+
914
+ function finish(wizard: Wizard, answers: Answers): number {
915
+ const runtime = answers.runtime;
916
+ const targetId = answers.targetId;
917
+ if (!runtime || targetId === undefined) return cancel(wizard.presenter);
918
+ const presenter = wizard.presenter;
919
+
920
+ if (answers.apiKeyLiteral !== undefined) {
921
+ const auth = openAuthStorage();
922
+ auth.setApiKey(runtime.id, answers.apiKeyLiteral);
923
+ // The settings write is still ahead of us, so refusing here leaves the
924
+ // whole run without an effect rather than half of one.
925
+ if (credentialWriteFailed(auth, `credential for ${runtime.id} was not stored; target '${targetId}' not saved`)) {
926
+ presenter.done("Nothing written");
927
+ return 1;
928
+ }
929
+ printPlaintextCredentialWarning();
930
+ }
931
+
932
+ const reasoning =
933
+ PROTOCOL_COMPAT_RUNTIME_IDS.has(runtime.id) && answers.thinking !== undefined
934
+ ? answers.thinking !== "off"
935
+ : undefined;
936
+ const descriptor = buildDescriptor(runtime, targetId, {
937
+ ...(answers.url !== undefined ? { url: answers.url } : {}),
938
+ ...(answers.model !== undefined ? { model: answers.model } : {}),
939
+ ...(answers.apiKeyEnv !== undefined ? { apiKeyEnv: answers.apiKeyEnv } : {}),
940
+ ...(answers.credential === "stored" || answers.credential === "keep" ? { apiKeyRef: runtime.id } : {}),
941
+ ...(runtime.auth === "oauth" ? { oauthProfile: runtime.oauthProviderId ?? runtime.id } : {}),
942
+ ...(reasoning === undefined ? {} : { reasoning }),
943
+ });
944
+ const chatEligible = isOrchestratorEligibleRuntime(runtime);
945
+
946
+ try {
947
+ persist(answers, descriptor, chatEligible);
948
+ } catch (error) {
949
+ presenter.fail("settings were not written", error instanceof Error ? error.message : String(error));
950
+ presenter.done("Nothing written");
951
+ return 1;
952
+ }
953
+
954
+ presenter.completedStep(`target ${targetId} saved, runtime ${runtime.id}`);
955
+ if (chatEligible)
956
+ presenter.completedStep(`chat runs on ${targetId}${answers.model ? `, model ${answers.model}` : ""}`);
957
+ else presenter.completedStep(`${runtime.id} cannot answer chat; ${targetId} is registered for dispatch only`);
958
+ presenter.completedStep(`fleet default is ${targetId}`);
959
+ if (answers.thinking !== undefined) presenter.completedStep(`thinking level ${answers.thinking}`);
960
+ for (const peer of answers.wiredPeers ?? []) {
961
+ presenter.completedStep(`delegation agent ${peer} added; use \`/delegate ${peer} <task>\``);
962
+ }
963
+ if (answers.antigravity !== undefined) {
964
+ presenter.completedStep(
965
+ `world-knowledge bound read-only to ${answers.antigravity.targetId}/${answers.antigravity.model}`,
966
+ );
967
+ }
968
+ presenter.completedStep(`settings written to ${shortenPath(settingsPath())}`);
969
+
970
+ if (contextWindowUndiscovered(descriptor, answers.probe ?? null)) {
971
+ presenter.warn(
972
+ `${targetId} reported no context window, so Clio will use the runtime default as a guess. Set the real one with \`clio-coder configure --id ${targetId} --runtime ${runtime.id} --context-window <N>\`.`,
973
+ );
974
+ }
975
+
976
+ presenter.commandAdvice("Start Clio:", "clio-coder");
977
+ presenter.commandAdvice("Change any of this later:", "clio-coder configure");
978
+ presenter.done("Done");
979
+ return 0;
980
+ }