@iowarp/clio-coder 0.3.1 → 0.3.2

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 (595) hide show
  1. package/CHANGELOG.md +90 -2
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-BIYHVZIM.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-YT6SSRIT.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/{chunk-PV4JUBVJ.js → chunk-2EHAIA3X.js} +40 -21
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/chunk-2VTFPG5O.js +48 -0
  12. package/dist/{chunk-MAR7Y6HW.js → chunk-3ZXDFGR5.js} +23 -16
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4KLWL3UC.js +18 -0
  16. package/dist/chunk-4VP4KH3K.js +962 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  21. package/dist/chunk-65DEGPJ6.js +52 -0
  22. package/dist/chunk-6EJMN2Y3.js +17 -0
  23. package/dist/chunk-6EJV5X2W.js +16405 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6XLNIQDB.js +27 -0
  26. package/dist/{chunk-TEKV33Q5.js → chunk-77VKQEHF.js} +65 -33
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/{chunk-M5T5VO65.js → chunk-7EYHLWU7.js} +837 -635
  29. package/dist/chunk-7MNJORFF.js +22 -0
  30. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  31. package/dist/chunk-AGYYIBLL.js +1069 -0
  32. package/dist/chunk-AO4RKG4M.js +277 -0
  33. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  34. package/dist/chunk-ARBGF5F7.js +174 -0
  35. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  36. package/dist/chunk-CBCAPZAA.js +229 -0
  37. package/dist/chunk-CMZWFGD2.js +352 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/{chunk-RPTR2H26.js → chunk-EPVUXGXG.js} +21 -15
  42. package/dist/chunk-FCSXB6T2.js +338 -0
  43. package/dist/chunk-FJ3H4MN5.js +48 -0
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G2DE3C7R.js +644 -0
  46. package/dist/chunk-G4BMMOKF.js +182 -0
  47. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  48. package/dist/chunk-HC4CLZ2Y.js +68 -0
  49. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  50. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  51. package/dist/chunk-I4HZDVNP.js +73 -0
  52. package/dist/{chunk-4QKXUHSR.js → chunk-IGLFWIYI.js} +70 -20
  53. package/dist/chunk-IKCO5N3L.js +162 -0
  54. package/dist/chunk-IR4CFBFN.js +56 -0
  55. package/dist/{chunk-PFEFKVGL.js → chunk-J5HN4RYU.js} +13 -11
  56. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  57. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  58. package/dist/chunk-KJ5LWLOE.js +1077 -0
  59. package/dist/chunk-LBMZMYH2.js +285 -0
  60. package/dist/{chunk-G34LV2PF.js → chunk-LBNRH5WM.js} +84 -170
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/chunk-MAW544W2.js +1882 -0
  64. package/dist/chunk-MBS4V7ZP.js +217 -0
  65. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  66. package/dist/chunk-MNA4JGU4.js +255 -0
  67. package/dist/chunk-MQSRRFWA.js +3428 -0
  68. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  69. package/dist/chunk-OAO4GE4M.js +619 -0
  70. package/dist/chunk-OHHN2SO4.js +5135 -0
  71. package/dist/chunk-OKGUZO2U.js +34 -0
  72. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  73. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  74. package/dist/chunk-OQE5J4C6.js +73 -0
  75. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  76. package/dist/chunk-POHLU5DW.js +1186 -0
  77. package/dist/chunk-QKMUKYO7.js +4961 -0
  78. package/dist/{chunk-ASND7OZK.js → chunk-QTYWRVRA.js} +13 -13
  79. package/dist/{chunk-EYOKLTMF.js → chunk-SRF2PJNW.js} +17 -3
  80. package/dist/chunk-SST6Z5JA.js +80 -0
  81. package/dist/chunk-STBPMHSX.js +2456 -0
  82. package/dist/chunk-T6YILFSB.js +80 -0
  83. package/dist/chunk-TZTZS7QK.js +227 -0
  84. package/dist/chunk-UOV2BYIW.js +107 -0
  85. package/dist/{chunk-Q3RUPKEJ.js → chunk-V4RXGQ5Q.js} +58 -189
  86. package/dist/chunk-VAKQQHWR.js +434 -0
  87. package/dist/chunk-VG7TBQIY.js +128 -0
  88. package/dist/chunk-VJWL6YS5.js +244 -0
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-WMSVI4G2.js +2095 -0
  91. package/dist/chunk-WVO7V2QY.js +797 -0
  92. package/dist/chunk-X4RCMKVQ.js +641 -0
  93. package/dist/chunk-X75S7HFS.js +374 -0
  94. package/dist/chunk-XN3L4EYL.js +46 -0
  95. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  96. package/dist/chunk-YHZX5GEU.js +193 -0
  97. package/dist/chunk-YXLYO42X.js +91 -0
  98. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  99. package/dist/chunk-ZI647VB5.js +37 -0
  100. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-4LY5K2AC.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-GTLUW2PR.js} +92 -55
  108. package/dist/configure-R6A64DHX.js +42 -0
  109. package/dist/context-5VKGUVJJ.js +866 -0
  110. package/dist/{context-3KWFLHJG.js → context-JFZEJ7W5.js} +15 -13
  111. package/dist/{context-5RADCKTR.js → context-RW5HC47S.js} +71 -35
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-6ZHBAZZT.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-KI767GSN.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-UA6AWDQQ.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-QNTFGV6Z.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-Q7UOMUSG.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-WBB65ZHQ.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-MD3O64RI.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-BZU34YWD.js} +39 -25
  126. package/dist/monitor-MEQA5C3I.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-CGFKEP27.js} +11832 -8687
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-IV4Q6RLN.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-LQEKRDTN.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-3DC4HEWS.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-C4SSGQOB.js} +43 -27
  137. package/dist/terminal-lease-IT5JW2NR.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-7TT7SQ3G.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-GV4PKT3M.js} +62 -31
  143. package/dist/verify-G6V4D2G7.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-DQF6Z66B.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +362 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +42 -6
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +173 -47
  444. package/src/interactive/chat-renderer.ts +262 -72
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +228 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +20 -2
  456. package/src/interactive/footer-panel.ts +2 -2
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +201 -17
  459. package/src/interactive/interactive-event-projection.ts +6 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +176 -114
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/decisions.ts +300 -0
  474. package/src/interactive/overlays/help-reference.ts +15 -10
  475. package/src/interactive/overlays/model-selector.ts +34 -16
  476. package/src/interactive/overlays/session-selector.ts +18 -0
  477. package/src/interactive/overlays/settings.ts +105 -17
  478. package/src/interactive/overlays/skills-hub.ts +4 -4
  479. package/src/interactive/overlays/tree-selector.ts +41 -6
  480. package/src/interactive/render-trace.ts +499 -90
  481. package/src/interactive/renderers/compaction-summary.ts +2 -2
  482. package/src/interactive/renderers/diff.ts +115 -104
  483. package/src/interactive/renderers/mermaid.ts +53 -0
  484. package/src/interactive/renderers/tool-execution.ts +386 -133
  485. package/src/interactive/renderers/worker-entry.ts +20 -4
  486. package/src/interactive/session-switch-settlement.ts +10 -0
  487. package/src/interactive/slash-autocomplete.ts +6 -114
  488. package/src/interactive/slash-commands.ts +135 -47
  489. package/src/interactive/slash-spec.ts +9 -38
  490. package/src/interactive/status/controller.ts +5 -1
  491. package/src/interactive/stdout-backpressure.ts +99 -0
  492. package/src/interactive/stream-pacer.ts +530 -0
  493. package/src/interactive/stream-pacing-policy.ts +66 -0
  494. package/src/interactive/tasks-overlay.ts +368 -14
  495. package/src/interactive/terminal-lease.ts +485 -0
  496. package/src/interactive/theme/tokens.ts +1 -1
  497. package/src/interactive/turn-context.ts +4 -3
  498. package/src/interactive/turn-persistence.ts +30 -13
  499. package/src/interactive/turn-queues.ts +12 -0
  500. package/src/interactive/turn-recovery.ts +25 -8
  501. package/src/interactive/turn-runtime.ts +79 -12
  502. package/src/interactive/turn-state.ts +10 -0
  503. package/src/interactive/view/artifacts.ts +114 -4
  504. package/src/interactive/view/view-overlay.ts +3 -0
  505. package/src/interactive/welcome-dashboard.ts +17 -16
  506. package/src/interactive/worker-receipts.ts +52 -3
  507. package/src/interactive/worker-stream.ts +5 -1
  508. package/src/tools/agent-tools.ts +23 -3
  509. package/src/tools/artifact.ts +2 -2
  510. package/src/tools/ask-user.ts +23 -13
  511. package/src/tools/bash.ts +30 -2
  512. package/src/tools/bootstrap.ts +34 -431
  513. package/src/tools/builtin-tool-catalog.ts +265 -0
  514. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  515. package/src/tools/codewiki/code-nav.ts +8 -22
  516. package/src/tools/codewiki/shared.ts +41 -38
  517. package/src/tools/context/docs-engine.ts +14 -3
  518. package/src/tools/context/index.ts +107 -28
  519. package/src/tools/context/surface.ts +19 -0
  520. package/src/tools/core-bootstrap.ts +168 -0
  521. package/src/tools/credential-present.ts +5 -5
  522. package/src/tools/dispatch-admission.ts +533 -0
  523. package/src/tools/dispatch-background.ts +54 -0
  524. package/src/tools/dispatch-event-text.ts +6 -0
  525. package/src/tools/dispatch-plan.ts +9 -4
  526. package/src/tools/dispatch-run-events.ts +238 -0
  527. package/src/tools/dispatch-runner.ts +2370 -0
  528. package/src/tools/dispatch-scout-admission.ts +295 -0
  529. package/src/tools/dispatch-types.ts +77 -0
  530. package/src/tools/dispatch.ts +67 -3161
  531. package/src/tools/find.ts +4 -2
  532. package/src/tools/grep.ts +2 -2
  533. package/src/tools/lazy-tool.ts +60 -0
  534. package/src/tools/ledger.ts +3 -3
  535. package/src/tools/monitor-surface.ts +36 -0
  536. package/src/tools/monitor.ts +2 -32
  537. package/src/tools/observers.ts +2 -2
  538. package/src/tools/registry.ts +39 -27
  539. package/src/tools/safe-exec.ts +2 -2
  540. package/src/tools/steer-surface.ts +17 -0
  541. package/src/tools/steer.ts +2 -13
  542. package/src/tools/tasks.ts +108 -11
  543. package/src/tools/truncate.ts +25 -184
  544. package/src/tools/verify/frontend.ts +3 -1
  545. package/src/tools/verify/index.ts +3 -38
  546. package/src/tools/verify/surface.ts +46 -0
  547. package/src/tools/web-fetch-surface.ts +23 -0
  548. package/src/tools/web-fetch.ts +2 -20
  549. package/src/tools/write.ts +7 -2
  550. package/src/worker/entry.ts +39 -2
  551. package/src/worker/spec-contract.ts +26 -5
  552. package/dist/chunk-7SS2CTV2.js +0 -61361
  553. package/dist/chunk-DKGKUHFA.js +0 -924
  554. package/dist/chunk-GEP36Y4X.js +0 -12796
  555. package/dist/chunk-XYWBQRDM.js +0 -137
  556. package/dist/clio-BZVGEUFJ.js +0 -58
  557. package/dist/configure-S7S6F6CL.js +0 -32
  558. package/docs/html/agents_blueprint.html +0 -936
  559. package/docs/html/alcf_blueprint.html +0 -324
  560. package/docs/html/architecture_blueprint.html +0 -850
  561. package/docs/html/commands_blueprint.html +0 -939
  562. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  563. package/docs/html/configuration_blueprint.html +0 -1080
  564. package/docs/html/context_blueprint.html +0 -603
  565. package/docs/html/documentation_blueprint.html +0 -832
  566. package/docs/html/environment_blueprint.html +0 -404
  567. package/docs/html/eval_blueprint.html +0 -743
  568. package/docs/html/evals_internal_blueprint.html +0 -190
  569. package/docs/html/evolution_blueprint.html +0 -674
  570. package/docs/html/extensions_blueprint.html +0 -2065
  571. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  572. package/docs/html/index.html +0 -919
  573. package/docs/html/lifecycle_blueprint.html +0 -723
  574. package/docs/html/memory_blueprint.html +0 -699
  575. package/docs/html/middleware_blueprint.html +0 -664
  576. package/docs/html/models_blueprint.html +0 -2366
  577. package/docs/html/observability_blueprint.html +0 -683
  578. package/docs/html/provider_adapter_blueprint.html +0 -245
  579. package/docs/html/safety_blueprint.html +0 -1386
  580. package/docs/html/shared.css +0 -571
  581. package/docs/html/shared.js +0 -143
  582. package/docs/html/skills_blueprint.html +0 -671
  583. package/docs/html/soak_blueprint.html +0 -182
  584. package/docs/html/tool_usage_blueprint.html +0 -350
  585. package/docs/html/tools_blueprint.html +0 -2249
  586. package/docs/html/trace_blueprint.html +0 -235
  587. package/docs/html/tui_design_blueprint.html +0 -374
  588. package/docs/html/validation_blueprint.html +0 -961
  589. package/docs/html/worker_dispatch_blueprint.html +0 -231
  590. package/src/core/release.ts +0 -2
  591. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  592. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  593. package/src/engine/apis/lmstudio-native.ts +0 -1438
  594. package/src/engine/apis/thinking-replay.ts +0 -11
  595. package/src/tools/string-enum.ts +0 -15
@@ -41,17 +41,8 @@ export function openHelpOverlay(
41
41
  // Overlay key actions are deliberately not duplicated here: each
42
42
  // overlay's footer hint is the live source of its keys, and a static
43
43
  // copy would rot exactly like the old SLASH_HOTKEYS table did.
44
- detail: () => {
45
- const lines = [`# Command: /${ref.name}`, `**Usage:** \`${ref.usage}\``, `**Description:** ${ref.description}`];
46
- if (ref.aliases.length > 0) {
47
- lines.push(`**Aliases:** ${ref.aliases.map((a) => `/${a}`).join(", ")}`);
48
- }
49
- return lines;
50
- },
44
+ detail: () => [`# Command: /${ref.name}`, `**Usage:** \`${ref.usage}\``, `**Description:** ${ref.description}`],
51
45
  };
52
- if (ref.aliases.length > 0) {
53
- item.meta = ref.aliases.join(", ");
54
- }
55
46
  return item;
56
47
  });
57
48
 
@@ -109,6 +100,20 @@ export function openHelpOverlay(
109
100
  "**Tasks versus runs**: `/tasks` shows the agent's plan steps. Fleet runs are concrete delegated worker executions and remain in this board as recent terminal history.",
110
101
  ],
111
102
  },
103
+ {
104
+ id: "topic-steering-modes",
105
+ label: `${"steering modes".padEnd(30)}Interrupt, next slot, or end of turn: when a message lands mid-run`,
106
+ group: "Topics",
107
+ detail: () => [
108
+ "# Steering modes",
109
+ "While Clio is running, the key that submits a message chooses when it lands. The default is next slot.",
110
+ "**Next slot** (Enter): the message is delivered between tool batches, mid-run. The agent keeps going and reads it before its next model call.",
111
+ "**End of turn** (Alt+Enter by default): the message waits until the whole run settles and Clio would hand control back, then starts the next round.",
112
+ "**Interrupt** (Alt+I by default, or Ctrl+G then i): cancels the in-flight work the way Esc does, including a running bash child, then delivers the message as a fresh prompt. Anything already queued returns to the editor.",
113
+ "**Interrupt is refused** while an attached dispatch is running (the abort would kill the worker's run with no receipt; steer it with `@<agent>` or cancel it with Esc) and while a permission ask is parked (it is already waiting on you). In both cases the message is queued for the next slot and a notice says why.",
114
+ "**Alt+Up** restores queued messages to the editor. Workers accept next-slot steering only, through `@<agent>`.",
115
+ ],
116
+ },
112
117
  {
113
118
  id: "topic-autonomy",
114
119
  label: `${"autonomy & safety net".padEnd(30)}How the autonomy level and the always-on guardrails interact`,
@@ -22,6 +22,7 @@ import {
22
22
  } from "../../domains/providers/index.js";
23
23
  import {
24
24
  type Component,
25
+ fuzzyFilter,
25
26
  getKeybindings,
26
27
  matchesKey,
27
28
  type OverlayHandle,
@@ -45,6 +46,13 @@ const MODEL_OVERLAY_MAX_WIDTH = 120;
45
46
  const MODEL_OVERLAY_TERMINAL_PADDING = 8;
46
47
  const VISIBLE_ROWS = 10;
47
48
  const MODEL_MIN_WIDTH = 14;
49
+ /**
50
+ * Every other column pair is separated by an explicit space. The model cell
51
+ * instead relies on its own padding, so a label that fills the column exactly
52
+ * butts straight against the ctx figure. The gutter is reserved inside the
53
+ * model cell, which keeps the header and the rows on the same column stops.
54
+ */
55
+ const MODEL_COL_GUTTER = 1;
48
56
  const CONTEXT_COL_WIDTH = 7;
49
57
  const CAPS_COL_WIDTH = 5;
50
58
  const TARGET_COL_WIDTH_NARROW = 8;
@@ -549,6 +557,11 @@ function modelColumns(width: number): ModelColumns {
549
557
  };
550
558
  }
551
559
 
560
+ /** The model cell, always leaving `MODEL_COL_GUTTER` blank columns before ctx. */
561
+ function fitModelCell(text: string, columnWidth: number): string {
562
+ return fitCell(truncateMiddle(text, Math.max(1, columnWidth - MODEL_COL_GUTTER)), columnWidth);
563
+ }
564
+
552
565
  function fitCell(text: string, width: number, align: "left" | "right" = "left"): string {
553
566
  const clipped = truncateToWidth(text, width, "", true);
554
567
  const pad = " ".repeat(Math.max(0, width - visibleWidth(clipped)));
@@ -577,7 +590,7 @@ function formatModelHeader(width: number): string {
577
590
  const columns = modelColumns(width);
578
591
  let line =
579
592
  `${" ".repeat(SELECTED_PREFIX_WIDTH)}` +
580
- `${fitCell("model", columns.modelWidth)}` +
593
+ `${fitModelCell("model", columns.modelWidth)}` +
581
594
  `${fitCell("ctx", CONTEXT_COL_WIDTH, "right")} ` +
582
595
  `${fitCell("caps", CAPS_COL_WIDTH)}`;
583
596
  if (columns.showTarget) line += ` ${fitCell("target", columns.targetWidth)}`;
@@ -597,7 +610,7 @@ function formatModelRow(row: ModelRow, width: number, selected: boolean): string
597
610
  const health = theme.fg(healthToken(row), row.healthGlyph);
598
611
  const prefix = `${pointer} ${health} ${activeMark(row)} `;
599
612
  const modelLabel = row.model.length > 0 ? row.model : "(no model ids)";
600
- const modelCell = fitCell(truncateMiddle(modelLabel, columns.modelWidth), columns.modelWidth);
613
+ const modelCell = fitModelCell(modelLabel, columns.modelWidth);
601
614
  const model = selected ? theme.style("accent", modelCell, { bold: true }) : theme.fg("muted", modelCell);
602
615
  let line =
603
616
  prefix +
@@ -667,14 +680,18 @@ function formatModelDetail(row: ModelRow, width: number): string[] {
667
680
  ];
668
681
  }
669
682
 
670
- function matchesQuery(row: ModelRow, query: string): boolean {
671
- const tokens = query
672
- .toLowerCase()
673
- .split(/\s+/)
674
- .map((token) => token.trim())
675
- .filter(Boolean);
676
- if (tokens.length === 0) return true;
677
- const haystack = [
683
+ /**
684
+ * Keep the target-qualified ref ahead of the bare model id. This mirrors
685
+ * pi-coding-agent's provider-first model-selector search text, so an exact
686
+ * target/model query ranks ahead of a proxy target carrying that ref as its
687
+ * model id. Clio's runtime and availability facts remain searchable after the
688
+ * ranking prefix.
689
+ */
690
+ function modelSelectorSearchText(row: ModelRow): string {
691
+ const qualifiedModel = row.model.length > 0 ? `${row.target}/${row.model}` : row.target;
692
+ return [
693
+ row.target,
694
+ qualifiedModel,
678
695
  row.target,
679
696
  row.model,
680
697
  row.runtimeName,
@@ -690,10 +707,11 @@ function matchesQuery(row: ModelRow, query: string): boolean {
690
707
  row.maxTokens,
691
708
  row.authText,
692
709
  row.healthText,
693
- ]
694
- .join(" ")
695
- .toLowerCase();
696
- return tokens.every((token) => haystack.includes(token));
710
+ ].join(" ");
711
+ }
712
+
713
+ function filterModelRows(rows: ReadonlyArray<ModelRow>, query: string): ModelRow[] {
714
+ return fuzzyFilter([...rows], query, modelSelectorSearchText);
697
715
  }
698
716
 
699
717
  function visibleSlice<T>(
@@ -728,7 +746,7 @@ function renderModelOverlayLines(input: {
728
746
  const width = Math.max(1, input.width);
729
747
  const query = input.query.trim();
730
748
  const searching = query.length > 0;
731
- const allMatches = input.rows.filter((row) => matchesQuery(row, input.query));
749
+ const allMatches = filterModelRows(input.rows, input.query);
732
750
  const focusedMatches = allMatches.filter((row) => row.visibleByDefault !== false);
733
751
  const filtered = searching || input.showAll ? allMatches : focusedMatches;
734
752
  const selectableFiltered = filtered.filter((row) => row.selectable).length;
@@ -849,7 +867,7 @@ export class ModelOverlayView implements Component {
849
867
  }
850
868
 
851
869
  private filteredRows(): ModelRow[] {
852
- const matches = this.rows.filter((row) => matchesQuery(row, this.query));
870
+ const matches = filterModelRows(this.rows, this.query);
853
871
  if (this.query.trim().length > 0 || this.showAll) return matches;
854
872
  return matches.filter((row) => row.visibleByDefault !== false);
855
873
  }
@@ -1,6 +1,7 @@
1
1
  import { basename } from "node:path";
2
2
  import type { SessionContract, SessionMeta } from "../../domains/session/contract.js";
3
3
  import {
4
+ getKeybindings,
4
5
  Input,
5
6
  matchesKey,
6
7
  type OverlayHandle,
@@ -178,6 +179,14 @@ export function createSessionOverlayBox(
178
179
  closeOverlay();
179
180
  }
180
181
 
182
+ function moveSelectionByPage(direction: -1 | 1): void {
183
+ const selected = list.getSelectedItem();
184
+ const currentIndex = selected ? filtered.findIndex((session) => session.id === selected.value) : 0;
185
+ const targetIndex = currentIndex + direction * VISIBLE_ROWS;
186
+ list.setSelectedIndex(targetIndex);
187
+ box.invalidate();
188
+ }
189
+
181
190
  function closeOverlay(): void {
182
191
  clearPendingEscape();
183
192
  onClose();
@@ -200,6 +209,15 @@ export function createSessionOverlayBox(
200
209
  }
201
210
 
202
211
  function dispatchResolvedInput(data: string): void {
212
+ const keybindings = getKeybindings();
213
+ if (keybindings.matches(data, "tui.select.pageUp")) {
214
+ moveSelectionByPage(-1);
215
+ return;
216
+ }
217
+ if (keybindings.matches(data, "tui.select.pageDown")) {
218
+ moveSelectionByPage(1);
219
+ return;
220
+ }
203
221
  if (matchesKey(data, "up") || matchesKey(data, "down")) {
204
222
  list.handleInput(data);
205
223
  return;
@@ -118,7 +118,12 @@ class SettingsSelectList extends SelectList {
118
118
  * overlay only offers a global save that a restart picks up.
119
119
  */
120
120
  type SettingScope = "live" | "restart";
121
- const RESTART_REQUIRED_IDS = new Set<string>(["budget.concurrency", "runtimePlugins"]);
121
+ const RESTART_REQUIRED_IDS = new Set<string>([
122
+ "budget.concurrency",
123
+ "runtimePlugins",
124
+ "terminal.tuiMode",
125
+ "terminal.fullscreenScrollbar",
126
+ ]);
122
127
 
123
128
  export const SETTINGS_SECTIONS = [
124
129
  { id: "safety", label: "Autonomy & Safety", group: "CORE" },
@@ -140,12 +145,12 @@ const SETTINGS_SECTION_DESCRIPTIONS = {
140
145
  orchestrator: "Interactive chat routing and the optional proactive-memory model plane.",
141
146
  fleet: "Defaults, profiles, and agent bindings applied to dispatched workers, and where they run.",
142
147
  targets: "Configured inference targets: which one chat and the fleet use, and whether each answers.",
143
- models: "The /models picker, favorites, and Alt+J / Alt+K cycling.",
148
+ models: "The /model picker, favorites, and Alt+J / Alt+K cycling.",
144
149
  budget: "Cost ceiling, per-turn output budget, and worker concurrency.",
145
150
  compaction: "When and how the context window is summarized under pressure.",
146
151
  retry: "Automatic recovery from transient provider and network errors.",
147
152
  terminal: "Terminal integration and the Clio color palette.",
148
- advanced: "Identity, runtime plugins, delegation timeouts, and links to other surfaces.",
153
+ advanced: "Commit provenance, runtime plugins, delegation timeouts, and links to other surfaces.",
149
154
  } as const satisfies Record<SettingsSectionId, string>;
150
155
 
151
156
  export const SETTINGS_LABELS_BY_ID = {
@@ -157,6 +162,7 @@ export const SETTINGS_LABELS_BY_ID = {
157
162
  "workers.onPermission": "Fleet approvals routing",
158
163
  "delegation.defaults.toolGovernance": "Delegation governance",
159
164
  "skills.trustProjectCompatRoots": "Trust project skill roots",
165
+ "attribution.gitCommits": "Clio commit provenance",
160
166
  safetyNet: "Safety net",
161
167
  "orchestrator.thinkingLevel": "Thinking level",
162
168
  "orchestrator.target": "Target",
@@ -190,6 +196,9 @@ export const SETTINGS_LABELS_BY_ID = {
190
196
  "retry.maxDelayMs": "Max delay (ms)",
191
197
  "terminal.showTerminalProgress": "Terminal progress badges",
192
198
  "terminal.outputVerbosity": "Output detail",
199
+ "terminal.tuiMode": "TUI mode",
200
+ "terminal.fullscreenScrollbar": "Fullscreen scrollbar",
201
+ "terminal.smoothStreaming": "Smooth streaming",
193
202
  theme: "Theme",
194
203
  runtimePlugins: "Runtime plugins",
195
204
  "compaction.model": "Compaction model",
@@ -254,9 +263,17 @@ export const SETTINGS_SECTION_ROWS = {
254
263
  budget: ["budget.sessionCeilingUsd", "defaults.maxTokens", "budget.concurrency"],
255
264
  compaction: ["compaction.auto", "compaction.threshold", "compaction.excludeLastTurns"],
256
265
  retry: ["retry.enabled", "retry.maxRetries", "retry.baseDelayMs", "retry.maxDelayMs"],
257
- terminal: ["terminal.showTerminalProgress", "terminal.outputVerbosity", "theme"],
266
+ terminal: [
267
+ "terminal.showTerminalProgress",
268
+ "terminal.outputVerbosity",
269
+ "terminal.tuiMode",
270
+ "terminal.fullscreenScrollbar",
271
+ "terminal.smoothStreaming",
272
+ "theme",
273
+ ],
258
274
  advanced: [
259
275
  "runtimePlugins",
276
+ "attribution.gitCommits",
260
277
  "compaction.model",
261
278
  "compaction.systemPrompt",
262
279
  "delegation.defaults.connectTimeoutMs",
@@ -274,6 +291,8 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
274
291
  "How a worker resolves an approval ask: deny the call, fail the run, or escalate to this session.",
275
292
  "delegation.defaults.toolGovernance": "Tool policy for delegated external agents.",
276
293
  "skills.trustProjectCompatRoots": "Whether third-party project skill roots are loaded.",
294
+ "attribution.gitCommits":
295
+ "Add evidence-backed assistance, testing, review, and contributor trailers to commits created through Clio.",
277
296
  safetyNet: "Always-on rails; tuned in .clio-coder/safety.yaml.",
278
297
  "orchestrator.thinkingLevel": "Reasoning budget for the chat loop.",
279
298
  "orchestrator.target": "Active chat target id.",
@@ -293,8 +312,8 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
293
312
  "workers.agentBindings": "Pins native Clio agents, including shadow agents, to worker profiles. Enter adds one.",
294
313
  "workers.maxRetries": "Automatic retries for a retryable worker outcome.",
295
314
  scope: "Alt+J and Alt+K model cycle set.",
296
- "modelSelector.recentLimit": "How many recently used models /models remembers.",
297
- "modelSelector.favorites": "Exact target/model refs pinned in /models.",
315
+ "modelSelector.recentLimit": "How many recently used models /model remembers.",
316
+ "modelSelector.favorites": "Exact target/model refs pinned in /model.",
298
317
  "budget.sessionCeilingUsd": "Per-session cost cap.",
299
318
  "defaults.maxTokens": "Output tokens requested per turn, applied to every target.",
300
319
  "budget.concurrency": "Parallel workers allowed during dispatch.",
@@ -307,6 +326,9 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
307
326
  "retry.maxDelayMs": "Maximum retry delay in milliseconds.",
308
327
  "terminal.showTerminalProgress": "Emit OSC 9;4 progress badges during agent turns.",
309
328
  "terminal.outputVerbosity": "How much reasoning, tool input, and live tool output appears in the transcript.",
329
+ "terminal.tuiMode": "Use regular terminal scrollback or a fullscreen transcript with a sticky composer and footer.",
330
+ "terminal.fullscreenScrollbar": "When the draggable transcript scrollbar is visible in fullscreen mode.",
331
+ "terminal.smoothStreaming": "Presentation-only pacing for streamed assistant text and thinking.",
310
332
  theme: "Color palette. Clio ships a single tuned palette.",
311
333
  runtimePlugins: "npm packages exporting clioRuntimes: RuntimeDescriptor[].",
312
334
  "compaction.model": "Dedicated summarization model; blank uses the orchestrator.",
@@ -330,6 +352,8 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
330
352
  "budget.concurrency": "auto sizes to your machine. A fixed number caps how many workers run at once.",
331
353
  "skills.trustProjectCompatRoots":
332
354
  "Project roots like .claude/skills and .codex/skills are untrusted by default; enabling exposes them to the model.",
355
+ "attribution.gitCommits":
356
+ "Role trailers are added only when Clio has trusted evidence for that role. Disabling leaves subsequent commit messages entirely unchanged.",
333
357
  "workers.onPermission":
334
358
  "deny turns the ask into a tool denial and the run continues; fail stops the run as permission_required; escalate forwards the ask to you and falls back per workers.escalation on timeout.",
335
359
  "workers.agentBindings":
@@ -366,7 +390,7 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
366
390
  },
367
391
  "compaction.auto": {
368
392
  true: "compact automatically before a turn crosses the threshold",
369
- false: "context is only compacted when you run /compact",
393
+ false: "context is only compacted when you run /context compact",
370
394
  },
371
395
  "retry.enabled": {
372
396
  true: "retry transient provider errors automatically",
@@ -376,6 +400,10 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
376
400
  true: "load skills from .claude/.codex/.github/etc. project roots",
377
401
  false: "ignore third-party project skill roots",
378
402
  },
403
+ "attribution.gitCommits": {
404
+ enabled: "add only the Clio role trailers justified by trusted evidence",
405
+ disabled: "leave every subsequent commit message byte-for-byte unchanged",
406
+ },
379
407
  "terminal.showTerminalProgress": {
380
408
  true: "emit OSC 9;4 taskbar/tab progress badges during turns",
381
409
  false: "no terminal progress badges",
@@ -385,6 +413,20 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
385
413
  default: "balanced transcript; unfold the latest tool, worker, or reasoning block on demand",
386
414
  verbose: "transparent transcript; reasoning, arguments, and live tool output stay visible",
387
415
  },
416
+ "terminal.tuiMode": {
417
+ regular: "preserve terminal scrollback and render the composer below the transcript",
418
+ fullscreen: "use the alternate screen with an independently scrollable transcript and sticky composer/footer",
419
+ },
420
+ "terminal.fullscreenScrollbar": {
421
+ hidden: "never draw the fullscreen transcript scrollbar",
422
+ auto: "show the scrollbar while scrolling or dragging",
423
+ always: "reserve the rightmost column for the scrollbar",
424
+ },
425
+ "terminal.smoothStreaming": {
426
+ off: "preserve the current immediate 16ms-coalesced streaming behavior",
427
+ auto: "pace only on a capable local TTY without accessibility or backpressure risk",
428
+ on: "request grapheme-safe pacing; stdout backpressure still pauses presentation",
429
+ },
388
430
  };
389
431
 
390
432
  export type SettingSubmenuBuilder = NonNullable<SettingItem["submenu"]>;
@@ -1063,6 +1105,7 @@ function defaultValueFor(id: EditableSettingId): string | undefined {
1063
1105
  if (isRoutingPath(id)) return undefined;
1064
1106
  const raw = getAtPath(DEFAULT_SETTINGS, id);
1065
1107
  if (raw === null || raw === undefined || typeof raw === "object") return undefined;
1108
+ if (id === "attribution.gitCommits") return raw === true ? "enabled" : "disabled";
1066
1109
  return String(raw);
1067
1110
  }
1068
1111
 
@@ -1319,7 +1362,7 @@ export function buildSettingItems(
1319
1362
  values: ["6", "12", "20", "50"],
1320
1363
  }),
1321
1364
  settingItem("modelSelector.favorites", favorites.length > 0 ? `${favorites.length} pinned` : "(none)", {
1322
- affordance: "manage in /models",
1365
+ affordance: "manage in /model",
1323
1366
  readOnly: true,
1324
1367
  }),
1325
1368
  settingItem("budget.sessionCeilingUsd", String(settings.budget.sessionCeilingUsd), {
@@ -1359,6 +1402,15 @@ export function buildSettingItems(
1359
1402
  settingItem("terminal.outputVerbosity", terminal.outputVerbosity, {
1360
1403
  values: ["minimal", "default", "verbose"],
1361
1404
  }),
1405
+ settingItem("terminal.tuiMode", terminal.tuiMode, {
1406
+ values: ["regular", "fullscreen"],
1407
+ }),
1408
+ settingItem("terminal.fullscreenScrollbar", terminal.fullscreenScrollbar, {
1409
+ values: ["hidden", "auto", "always"],
1410
+ }),
1411
+ settingItem("terminal.smoothStreaming", terminal.smoothStreaming, {
1412
+ values: ["off", "auto", "on"],
1413
+ }),
1362
1414
  settingItem("theme", settings.theme, {
1363
1415
  affordance: "single clio-coder palette",
1364
1416
  readOnly: true,
@@ -1367,6 +1419,9 @@ export function buildSettingItems(
1367
1419
  submenu: editTextSubmenu("Edit runtime plugins comma-separated list", "Restart Clio to load changes."),
1368
1420
  affordance: "free text",
1369
1421
  }),
1422
+ settingItem("attribution.gitCommits", settings.attribution.gitCommits ? "enabled" : "disabled", {
1423
+ values: ["enabled", "disabled"],
1424
+ }),
1370
1425
  settingItem("compaction.model", compaction.model ?? "(orchestrator target)", {
1371
1426
  submenu: editTextSubmenu("Edit compaction model; blank uses the orchestrator"),
1372
1427
  affordance: "free text",
@@ -1734,6 +1789,9 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
1734
1789
  case "skills.trustProjectCompatRoots":
1735
1790
  if (value === "true" || value === "false") settings.skills.trustProjectCompatRoots = value === "true";
1736
1791
  return;
1792
+ case "attribution.gitCommits":
1793
+ if (value === "enabled" || value === "disabled") settings.attribution.gitCommits = value === "enabled";
1794
+ return;
1737
1795
  case "orchestrator.thinkingLevel":
1738
1796
  settings.orchestrator.thinkingLevel = thinkingLevelFromChoiceLabel(value) ?? settings.orchestrator.thinkingLevel;
1739
1797
  return;
@@ -1832,6 +1890,17 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
1832
1890
  case "terminal.outputVerbosity":
1833
1891
  if (value === "minimal" || value === "default" || value === "verbose") settings.terminal.outputVerbosity = value;
1834
1892
  return;
1893
+ case "terminal.tuiMode":
1894
+ if (value === "regular" || value === "fullscreen") settings.terminal.tuiMode = value;
1895
+ return;
1896
+ case "terminal.fullscreenScrollbar":
1897
+ if (value === "hidden" || value === "auto" || value === "always") {
1898
+ settings.terminal.fullscreenScrollbar = value;
1899
+ }
1900
+ return;
1901
+ case "terminal.smoothStreaming":
1902
+ if (value === "off" || value === "auto" || value === "on") settings.terminal.smoothStreaming = value;
1903
+ return;
1835
1904
  case "runtimePlugins":
1836
1905
  settings.runtimePlugins = value
1837
1906
  .split(",")
@@ -2446,7 +2515,11 @@ export class SettingsCenter implements Component {
2446
2515
  private submenuComponent: Component | null = null;
2447
2516
  /** Committed catalog filter; empty means unfiltered. */
2448
2517
  private filterQuery = "";
2449
- /** Draft while the filter editor owns input; null when it is closed. */
2518
+ /**
2519
+ * Draft while the filter editor owns input; null when it is closed. The
2520
+ * draft narrows the catalog live per keystroke, matching /model and /resume;
2521
+ * Enter commits it and Esc restores the committed query.
2522
+ */
2450
2523
  private filterDraft: string | null = null;
2451
2524
  /** Local cycle preview for the selected row; committed on Enter. */
2452
2525
  private pendingValue: string | null = null;
@@ -2606,13 +2679,25 @@ export class SettingsCenter implements Component {
2606
2679
  }
2607
2680
  if (data === "\x7f" || data === "\b") {
2608
2681
  this.filterDraft = draft.slice(0, -1);
2682
+ this.normalizeSelection();
2683
+ this.options.requestRender?.();
2609
2684
  return;
2610
2685
  }
2611
- if (isPrintableInput(data)) this.filterDraft = draft + data;
2686
+ if (isPrintableInput(data)) {
2687
+ this.filterDraft = draft + data;
2688
+ this.normalizeSelection();
2689
+ this.options.requestRender?.();
2690
+ }
2691
+ }
2692
+
2693
+ /** The query the catalog is narrowed by right now: the live draft while the
2694
+ * filter editor is open, the committed query otherwise. */
2695
+ private effectiveFilterQuery(): string {
2696
+ return (this.filterDraft ?? this.filterQuery).trim();
2612
2697
  }
2613
2698
 
2614
2699
  private matchesFilter(item: SettingsCenterItem): boolean {
2615
- const query = this.filterQuery.trim().toLowerCase();
2700
+ const query = this.effectiveFilterQuery().toLowerCase();
2616
2701
  if (query.length === 0) return true;
2617
2702
  return (
2618
2703
  item.label.toLowerCase().includes(query) ||
@@ -2628,7 +2713,7 @@ export class SettingsCenter implements Component {
2628
2713
  */
2629
2714
  private sections(): SettingsCenterSection[] {
2630
2715
  const all = buildSettingsSections(this.items);
2631
- if (this.filterQuery.trim().length === 0) return all;
2716
+ if (this.effectiveFilterQuery().length === 0) return all;
2632
2717
  return all
2633
2718
  .map((section) => ({ ...section, items: this.filterSectionItems(section.items) }))
2634
2719
  .filter((section) => section.items.some((item) => this.isSelectableRow(item)));
@@ -2692,10 +2777,13 @@ export class SettingsCenter implements Component {
2692
2777
 
2693
2778
  private normalizeSelection(): void {
2694
2779
  const sections = this.sections();
2695
- // A filter that hides everything leaves nowhere to drill into, so the stack
2696
- // sits at its top level and one Esc closes Settings from the empty state.
2780
+ // A committed filter that hides everything leaves nowhere to drill into, so
2781
+ // the stack sits at its top level and one Esc closes Settings from the
2782
+ // empty state. A live draft narrows per keystroke and may pass through the
2783
+ // empty state on its way to a match, so it must not disturb the level:
2784
+ // Esc's first press cancels the draft and lands back where editing began.
2697
2785
  if (sections.length === 0) {
2698
- this.level = "sections";
2786
+ if (this.filterDraft === null) this.level = "sections";
2699
2787
  return;
2700
2788
  }
2701
2789
  if (!sections.some((section) => section.id === this.selectedSectionId)) {
@@ -2909,7 +2997,7 @@ export class SettingsCenter implements Component {
2909
2997
  const theme = clioTheme();
2910
2998
  return fixedLines(
2911
2999
  [
2912
- theme.fg("muted", truncateToWidth(`No settings match “${this.filterQuery}”`, width, ELLIPSIS, true)),
3000
+ theme.fg("muted", truncateToWidth(`No settings match “${this.effectiveFilterQuery()}”`, width, ELLIPSIS, true)),
2913
3001
  theme.fg("dim", truncateToWidth("/ edit filter · empty Enter clears", width, ELLIPSIS, true)),
2914
3002
  ],
2915
3003
  width,
@@ -3014,7 +3102,7 @@ export class SettingsCenter implements Component {
3014
3102
  */
3015
3103
  private sectionCatalogRows(): Array<{ line: string; sectionId: SettingsSectionId | null }> {
3016
3104
  const theme = clioTheme();
3017
- const filtering = this.filterQuery.trim().length > 0;
3105
+ const filtering = this.effectiveFilterQuery().length > 0;
3018
3106
  const rows: Array<{ line: string; sectionId: SettingsSectionId | null }> = [];
3019
3107
  let previousGroup: string | null = null;
3020
3108
  for (const section of this.sections()) {
@@ -13,7 +13,7 @@ import { type ListOverlayItem, openListOverlay } from "./list-overlay.js";
13
13
  /**
14
14
  * The Skills Hub: one multipane surface for every skill Clio can reach.
15
15
  * Installed skills group by scope, marketplace rows come from the same local
16
- * marketplace lookup the installer and `/skill:<name>` resolve through, Enter
16
+ * marketplace lookup the installer and `/skill <name>` resolve through, Enter
17
17
  * inserts the invocation into the editor, and `i` installs in place. The hub
18
18
  * lists nothing the resolver cannot resolve; when the lookup is empty the hub
19
19
  * says so and names the remedy instead of drawing an inventory.
@@ -82,7 +82,7 @@ export function buildInstalledItems(list: ResourceList<Skill>): ListOverlayItem[
82
82
  detail: () => {
83
83
  const lines = [
84
84
  `# ${skill.name}`,
85
- `**Invoke:** \`/skill:${skill.name} [task]\``,
85
+ `**Invoke:** \`/skill ${skill.name} [task]\``,
86
86
  `**Source:** \`${skill.filePath}\``,
87
87
  `**Scope:** ${skill.scope}/${skill.source}${skill.trusted ? "" : " (untrusted)"}`,
88
88
  ];
@@ -119,7 +119,7 @@ export function buildMarketplaceItems(
119
119
  group: GROUP_MARKETPLACE,
120
120
  detail: () => [
121
121
  `# ${skill.name}`,
122
- `**Invoke:** \`/skill:${skill.name} [task]\` (prompts to install first)`,
122
+ `**Invoke:** \`/skill ${skill.name} [task]\` (prompts to install first)`,
123
123
  "**Install now:** press `i`",
124
124
  `**Source:** \`${skill.sourceUrl}\``,
125
125
  `**Origin:** ${skill.origin}${skill.category ? ` (${skill.category})` : ""}${skill.audit ? ` · audit ${skill.audit}` : ""}`,
@@ -201,7 +201,7 @@ export function openSkillsHub(tui: TUI, deps: SkillsHubDeps): OverlayHandle {
201
201
  onSelect: (item) => {
202
202
  if (item.group === GROUP_DIAGNOSTICS) return;
203
203
  const name = item.id.startsWith("marketplace:") ? item.id.slice("marketplace:".length) : item.id;
204
- deps.setEditorText(`/skill:${name} `);
204
+ deps.setEditorText(`/skill ${name} `);
205
205
  deps.onClose();
206
206
  },
207
207
  actions: {
@@ -38,6 +38,10 @@ interface TreeRow {
38
38
  depth: number;
39
39
  node: TreeSnapshotNode;
40
40
  sessionId: string;
41
+ /** This row is the session's active append point: where the next message lands. */
42
+ isActiveTip: boolean;
43
+ /** Enter on this row will move the append point; false for structural rows. */
44
+ switchable: boolean;
41
45
  }
42
46
 
43
47
  type Submode = "browse" | "edit-label";
@@ -108,12 +112,29 @@ function isLeaf(node: TreeSnapshotNode): boolean {
108
112
  return node.children.length === 0;
109
113
  }
110
114
 
115
+ /**
116
+ * `switchTurn` validates against message-tree nodes only (tree.json), so a
117
+ * structural row that /tree draws on top of it (a compaction marker or a
118
+ * fork's returned-from-branch summary) throws "turn not found" if Enter is
119
+ * pressed on it. Before issue #94 the row rendered exactly like a switchable
120
+ * one and gave no sign it would fail.
121
+ */
122
+ function isSwitchable(node: TreeSnapshotNode): boolean {
123
+ return node.kind !== "compaction" && node.kind !== "branch";
124
+ }
125
+
111
126
  function flattenTreeSnapshot(snapshot: TreeSnapshot): TreeRow[] {
112
127
  const rows: TreeRow[] = [];
113
128
  const walk = (id: string, depth: number): void => {
114
129
  const node = snapshot.nodesById[id];
115
130
  if (!node) return;
116
- rows.push({ depth, node, sessionId: snapshot.sessionId });
131
+ rows.push({
132
+ depth,
133
+ node,
134
+ sessionId: snapshot.sessionId,
135
+ isActiveTip: snapshot.leafId !== null && node.id === snapshot.leafId,
136
+ switchable: isSwitchable(node),
137
+ });
117
138
  // Indentation marks a fork, not a step. Every message is a child of the
118
139
  // one before it, so indenting per child made depth equal message count:
119
140
  // a seventeen-message session rendered as a seventeen-level staircase
@@ -137,24 +158,34 @@ const ROW_PREVIEW_BUDGET = 55;
137
158
  export function formatTreeRow(row: TreeRow, opts: { showTimestamps: boolean; width: number }): string {
138
159
  const theme = clioTheme();
139
160
  const indent = " ".repeat(row.depth);
140
- const glyph = isLeaf(row.node) ? GLYPH.running : "○";
161
+ // The active tip is where the next message lands; it gets its own glyph so
162
+ // a branched session shows exactly one live tip instead of every childless
163
+ // node reading the same as every other (issue #94). Other leaves still get
164
+ // a leaf glyph, just not the one that claims to be current.
165
+ const glyph = row.isActiveTip ? GLYPH.active : isLeaf(row.node) ? GLYPH.running : "○";
141
166
  const turnId = shortTurnId(row.node.id);
142
167
  const rawPreview = row.node.preview && row.node.preview.length > 0 ? row.node.preview : fallbackPreview(row.node);
143
168
  const labelText = row.node.label ? ` · label:"${row.node.label}"` : "";
169
+ // Enter cannot switch to a structural row (switchTurn only knows message
170
+ // turns), so it says so instead of looking like every switchable row.
171
+ const inertText = row.switchable ? "" : " (fixed)";
144
172
  // A plain copy of the prefix and label drives the width math; the rendered
145
173
  // versions below carry color, so every measurement uses visibleWidth.
146
174
  const plainPrefix = `${indent}${glyph} ${row.node.kind.padEnd(12)} ${turnId} `;
147
175
  const previewBudget = Math.min(
148
176
  ROW_PREVIEW_BUDGET,
149
- Math.max(1, opts.width - visibleWidth(plainPrefix) - visibleWidth(labelText)),
177
+ Math.max(1, opts.width - visibleWidth(plainPrefix) - visibleWidth(labelText) - visibleWidth(inertText)),
150
178
  );
151
179
  const preview = clampPreview(rawPreview, previewBudget);
152
180
  // The node glyph and kind are structural scaffolding and render dim; the turn
153
181
  // id and preview are content and render muted; the optional label reads as a
154
- // dim key with a muted value.
155
- const styledPrefix = `${theme.fg("dim", `${indent}${glyph} ${row.node.kind.padEnd(12)}`)} ${theme.fg("muted", turnId)} `;
182
+ // dim key with a muted value. The active tip's glyph renders in accent so it
183
+ // stands out against every other row.
184
+ const glyphStyled = row.isActiveTip ? theme.fg("accent", glyph) : theme.fg("dim", glyph);
185
+ const styledPrefix = `${indent}${glyphStyled}${theme.fg("dim", ` ${row.node.kind.padEnd(12)}`)} ${theme.fg("muted", turnId)} `;
156
186
  const styledLabel = labelText ? `${theme.fg("dim", " · label:")}${theme.fg("muted", `"${row.node.label}"`)}` : "";
157
- const main = `${styledPrefix}${theme.fg("muted", preview)}${styledLabel}`;
187
+ const styledInert = inertText ? theme.fg("dim", inertText) : "";
188
+ const main = `${styledPrefix}${theme.fg("muted", preview)}${styledLabel}${styledInert}`;
158
189
  if (!opts.showTimestamps) return truncateToWidth(main, opts.width, "", true);
159
190
  const ts = `${dateLocal(row.node.at)} ${clockLocal(row.node.at)}`;
160
191
  if (opts.width < ts.length + 12) return truncateToWidth(main, opts.width, "", true);
@@ -298,6 +329,10 @@ export class TreeOverlayView implements Component {
298
329
  if (matchesKey(data, "enter") || data === "\n") {
299
330
  const row = this.currentRow();
300
331
  if (!row) return;
332
+ if (!row.switchable) {
333
+ this.setStatus(`[tree] ${row.node.kind} rows mark a moment in the timeline; they are not a place to switch to`);
334
+ return;
335
+ }
301
336
  this.deps.onSwitchTurn(row.node.id);
302
337
  this.deps.onClose();
303
338
  return;