@iowarp/clio-coder 0.3.1 → 0.3.3

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 (601) hide show
  1. package/CHANGELOG.md +233 -373
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.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-2DJ2KNFG.js +2095 -0
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
  12. package/dist/chunk-2VTFPG5O.js +48 -0
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4VP4KH3K.js +962 -0
  16. package/dist/chunk-4XUGQOHA.js +797 -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-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
  21. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  22. package/dist/chunk-65DEGPJ6.js +52 -0
  23. package/dist/chunk-6EJMN2Y3.js +17 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6SGHMWE3.js +277 -0
  26. package/dist/chunk-6XLNIQDB.js +27 -0
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/chunk-7MNJORFF.js +22 -0
  29. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  30. package/dist/chunk-AGYYIBLL.js +1069 -0
  31. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  32. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  33. package/dist/chunk-CBCAPZAA.js +229 -0
  34. package/dist/chunk-CMZWFGD2.js +352 -0
  35. package/dist/chunk-COU2UHX6.js +400 -0
  36. package/dist/chunk-DSELYM6W.js +1077 -0
  37. package/dist/chunk-DUYJ5IO6.js +644 -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-FCSXB6T2.js +338 -0
  42. package/dist/chunk-FJ3H4MN5.js +48 -0
  43. package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G4BMMOKF.js +182 -0
  46. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  47. package/dist/chunk-HC4CLZ2Y.js +68 -0
  48. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  49. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  50. package/dist/chunk-I4HZDVNP.js +73 -0
  51. package/dist/chunk-IKCO5N3L.js +162 -0
  52. package/dist/chunk-IR4CFBFN.js +56 -0
  53. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  54. package/dist/chunk-J7CWMCQD.js +255 -0
  55. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  56. package/dist/chunk-KZWTDYJF.js +217 -0
  57. package/dist/chunk-LBMZMYH2.js +285 -0
  58. package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
  59. package/dist/chunk-LW6DSM3M.js +5135 -0
  60. package/dist/chunk-LWLEKMDQ.js +3482 -0
  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-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  64. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  65. package/dist/chunk-OAO4GE4M.js +619 -0
  66. package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
  67. package/dist/chunk-OKGUZO2U.js +34 -0
  68. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  69. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  70. package/dist/chunk-OQE5J4C6.js +73 -0
  71. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  72. package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
  73. package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
  74. package/dist/chunk-POHLU5DW.js +1186 -0
  75. package/dist/chunk-QKMUKYO7.js +4961 -0
  76. package/dist/chunk-SRDMMSEP.js +16405 -0
  77. package/dist/chunk-SST6Z5JA.js +80 -0
  78. package/dist/chunk-STBPMHSX.js +2456 -0
  79. package/dist/chunk-T6YILFSB.js +80 -0
  80. package/dist/chunk-TZK7PACC.js +174 -0
  81. package/dist/chunk-TZTZS7QK.js +227 -0
  82. package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
  83. package/dist/chunk-UOV2BYIW.js +107 -0
  84. package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
  85. package/dist/chunk-VAKQQHWR.js +434 -0
  86. package/dist/chunk-VG7TBQIY.js +128 -0
  87. package/dist/chunk-VJWL6YS5.js +244 -0
  88. package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-X4RCMKVQ.js +641 -0
  91. package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
  92. package/dist/chunk-XBXAASKX.js +18 -0
  93. package/dist/chunk-XN3L4EYL.js +46 -0
  94. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  95. package/dist/chunk-YHZX5GEU.js +193 -0
  96. package/dist/chunk-YXLYO42X.js +91 -0
  97. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  98. package/dist/chunk-ZI647VB5.js +37 -0
  99. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  100. package/dist/chunk-ZWMF7253.js +1882 -0
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-JOU4FXVA.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-XCDVKR23.js} +92 -55
  108. package/dist/configure-4GAP54ZW.js +42 -0
  109. package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
  110. package/dist/context-5VKGUVJJ.js +866 -0
  111. package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
  126. package/dist/monitor-GE4ID3IA.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
  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-ZU3QMZPZ.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
  137. package/dist/terminal-lease-BNAHVHBS.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-JQHHPQ4K.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
  143. package/dist/verify-375KUB3Y.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.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 +387 -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 +45 -9
  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 +715 -278
  444. package/src/interactive/chat-renderer.ts +306 -85
  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 +253 -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 +42 -22
  456. package/src/interactive/footer-panel.ts +8 -3
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +203 -17
  459. package/src/interactive/interactive-event-projection.ts +18 -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 +184 -117
  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/ask-user.ts +146 -24
  474. package/src/interactive/overlays/decisions.ts +300 -0
  475. package/src/interactive/overlays/help-reference.ts +15 -10
  476. package/src/interactive/overlays/model-selector.ts +34 -16
  477. package/src/interactive/overlays/session-selector.ts +18 -0
  478. package/src/interactive/overlays/settings.ts +105 -17
  479. package/src/interactive/overlays/skills-hub.ts +4 -4
  480. package/src/interactive/overlays/tree-selector.ts +41 -6
  481. package/src/interactive/render-trace.ts +499 -90
  482. package/src/interactive/renderers/compaction-summary.ts +2 -2
  483. package/src/interactive/renderers/diff.ts +115 -104
  484. package/src/interactive/renderers/mermaid.ts +53 -0
  485. package/src/interactive/renderers/tool-execution.ts +516 -168
  486. package/src/interactive/renderers/worker-entry.ts +20 -4
  487. package/src/interactive/session-switch-settlement.ts +10 -0
  488. package/src/interactive/slash-autocomplete.ts +6 -114
  489. package/src/interactive/slash-commands.ts +135 -47
  490. package/src/interactive/slash-spec.ts +9 -38
  491. package/src/interactive/status/controller.ts +5 -1
  492. package/src/interactive/status/index.ts +12 -1
  493. package/src/interactive/status/reasoning.ts +87 -0
  494. package/src/interactive/status/summary.ts +13 -2
  495. package/src/interactive/stdout-backpressure.ts +99 -0
  496. package/src/interactive/stream-pacer.ts +530 -0
  497. package/src/interactive/stream-pacing-policy.ts +66 -0
  498. package/src/interactive/tasks-overlay.ts +368 -14
  499. package/src/interactive/terminal-lease.ts +485 -0
  500. package/src/interactive/theme/tokens.ts +1 -1
  501. package/src/interactive/transcript-detail.ts +120 -0
  502. package/src/interactive/turn-context.ts +4 -3
  503. package/src/interactive/turn-persistence.ts +30 -13
  504. package/src/interactive/turn-queues.ts +12 -0
  505. package/src/interactive/turn-recovery.ts +25 -8
  506. package/src/interactive/turn-runtime.ts +79 -12
  507. package/src/interactive/turn-state.ts +10 -0
  508. package/src/interactive/view/artifacts.ts +114 -4
  509. package/src/interactive/view/view-overlay.ts +3 -0
  510. package/src/interactive/welcome-dashboard.ts +17 -16
  511. package/src/interactive/worker-receipts.ts +52 -3
  512. package/src/interactive/worker-stream.ts +5 -1
  513. package/src/tools/agent-tools.ts +23 -3
  514. package/src/tools/artifact.ts +2 -2
  515. package/src/tools/ask-user.ts +23 -13
  516. package/src/tools/bash.ts +30 -2
  517. package/src/tools/bootstrap.ts +34 -431
  518. package/src/tools/builtin-tool-catalog.ts +271 -0
  519. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  520. package/src/tools/codewiki/code-nav.ts +8 -22
  521. package/src/tools/codewiki/shared.ts +41 -38
  522. package/src/tools/context/docs-engine.ts +14 -3
  523. package/src/tools/context/index.ts +107 -28
  524. package/src/tools/context/surface.ts +19 -0
  525. package/src/tools/core-bootstrap.ts +168 -0
  526. package/src/tools/credential-present.ts +5 -5
  527. package/src/tools/dispatch-admission.ts +533 -0
  528. package/src/tools/dispatch-background.ts +54 -0
  529. package/src/tools/dispatch-event-text.ts +6 -0
  530. package/src/tools/dispatch-plan.ts +9 -4
  531. package/src/tools/dispatch-run-events.ts +238 -0
  532. package/src/tools/dispatch-runner.ts +2370 -0
  533. package/src/tools/dispatch-scout-admission.ts +295 -0
  534. package/src/tools/dispatch-types.ts +77 -0
  535. package/src/tools/dispatch.ts +67 -3161
  536. package/src/tools/find.ts +4 -2
  537. package/src/tools/grep.ts +2 -2
  538. package/src/tools/lazy-tool.ts +60 -0
  539. package/src/tools/ledger.ts +3 -3
  540. package/src/tools/monitor-surface.ts +36 -0
  541. package/src/tools/monitor.ts +2 -32
  542. package/src/tools/observers.ts +2 -2
  543. package/src/tools/presentation.ts +107 -0
  544. package/src/tools/registry.ts +45 -27
  545. package/src/tools/safe-exec.ts +2 -2
  546. package/src/tools/steer-surface.ts +17 -0
  547. package/src/tools/steer.ts +2 -13
  548. package/src/tools/tasks.ts +108 -11
  549. package/src/tools/truncate.ts +25 -184
  550. package/src/tools/verify/frontend.ts +3 -1
  551. package/src/tools/verify/index.ts +3 -38
  552. package/src/tools/verify/surface.ts +46 -0
  553. package/src/tools/web-fetch-surface.ts +23 -0
  554. package/src/tools/web-fetch.ts +2 -20
  555. package/src/tools/write.ts +7 -2
  556. package/src/worker/entry.ts +39 -2
  557. package/src/worker/spec-contract.ts +26 -5
  558. package/dist/chunk-7SS2CTV2.js +0 -61361
  559. package/dist/chunk-DKGKUHFA.js +0 -924
  560. package/dist/chunk-GEP36Y4X.js +0 -12796
  561. package/dist/chunk-XYWBQRDM.js +0 -137
  562. package/dist/clio-BZVGEUFJ.js +0 -58
  563. package/dist/configure-S7S6F6CL.js +0 -32
  564. package/docs/html/agents_blueprint.html +0 -936
  565. package/docs/html/alcf_blueprint.html +0 -324
  566. package/docs/html/architecture_blueprint.html +0 -850
  567. package/docs/html/commands_blueprint.html +0 -939
  568. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  569. package/docs/html/configuration_blueprint.html +0 -1080
  570. package/docs/html/context_blueprint.html +0 -603
  571. package/docs/html/documentation_blueprint.html +0 -832
  572. package/docs/html/environment_blueprint.html +0 -404
  573. package/docs/html/eval_blueprint.html +0 -743
  574. package/docs/html/evals_internal_blueprint.html +0 -190
  575. package/docs/html/evolution_blueprint.html +0 -674
  576. package/docs/html/extensions_blueprint.html +0 -2065
  577. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  578. package/docs/html/index.html +0 -919
  579. package/docs/html/lifecycle_blueprint.html +0 -723
  580. package/docs/html/memory_blueprint.html +0 -699
  581. package/docs/html/middleware_blueprint.html +0 -664
  582. package/docs/html/models_blueprint.html +0 -2366
  583. package/docs/html/observability_blueprint.html +0 -683
  584. package/docs/html/provider_adapter_blueprint.html +0 -245
  585. package/docs/html/safety_blueprint.html +0 -1386
  586. package/docs/html/shared.css +0 -571
  587. package/docs/html/shared.js +0 -143
  588. package/docs/html/skills_blueprint.html +0 -671
  589. package/docs/html/soak_blueprint.html +0 -182
  590. package/docs/html/tool_usage_blueprint.html +0 -350
  591. package/docs/html/tools_blueprint.html +0 -2249
  592. package/docs/html/trace_blueprint.html +0 -235
  593. package/docs/html/tui_design_blueprint.html +0 -374
  594. package/docs/html/validation_blueprint.html +0 -961
  595. package/docs/html/worker_dispatch_blueprint.html +0 -231
  596. package/src/core/release.ts +0 -2
  597. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  598. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  599. package/src/engine/apis/lmstudio-native.ts +0 -1438
  600. package/src/engine/apis/thinking-replay.ts +0 -11
  601. package/src/tools/string-enum.ts +0 -15
@@ -1,16 +1,19 @@
1
1
  /**
2
2
  * Coalescing wrapper around chat events (slice 12.5d).
3
3
  *
4
- * Streaming responses fire `text_delta` / `thinking_delta` events at very
5
- * high frequency. The TUI's per-event `requestRender()` call rebuilt the
4
+ * Streaming responses fire text, thinking, and cumulative tool-result updates
5
+ * at very high frequency. The TUI's per-event `requestRender()` call rebuilt the
6
6
  * entire transcript on every delta, which scaled linearly with response
7
- * length and made long answers visibly lag. This wrapper applies every
8
- * event to the panel synchronously (so internal state stays consistent)
9
- * but defers `requestRender()` for delta events to a single coalesced
10
- * timer (~16ms = one frame at 60fps). Non-delta events render
11
- * synchronously so finalizers like `message_end` are never deferred.
7
+ * length and made long answers visibly lag. This wrapper applies events to
8
+ * the panel synchronously (so internal state stays consistent) but defers
9
+ * `requestRender()` for delta events to a single coalesced timer (~16ms =
10
+ * one frame at 60fps). Non-delta events render synchronously so finalizers
11
+ * like `message_end` are never deferred. The one exception is the raw
12
+ * text/thinking wrapper, which is dropped before the panel entirely; see
13
+ * `isTransparentAssistantWrapper`.
12
14
  */
13
15
 
16
+ import { ToolNames } from "../core/tool-names.js";
14
17
  import type {
15
18
  BashExecutionEntry,
16
19
  BranchSummaryEntry,
@@ -26,8 +29,17 @@ import type {
26
29
  WorkerRunEntry,
27
30
  } from "../domains/session/entries.js";
28
31
  import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
32
+ import {
33
+ type BashExecutionMessage,
34
+ BRANCH_SUMMARY_PREFIX,
35
+ BRANCH_SUMMARY_SUFFIX,
36
+ bashExecutionToText,
37
+ COMPACTION_SUMMARY_PREFIX,
38
+ COMPACTION_SUMMARY_SUFFIX,
39
+ } from "../engine/messages.js";
29
40
  import { wrapTextWithAnsi } from "../engine/tui.js";
30
41
  import type { AgentMessage } from "../engine/types.js";
42
+ import { toolPresentationPolicy } from "../tools/presentation.js";
31
43
  import type { ChatLoopEvent, RetryStatusPayload } from "./chat-loop.js";
32
44
  import { isSelfExplainingAbort } from "./chat-loop-messages.js";
33
45
  import type { ChatPanel } from "./chat-panel.js";
@@ -35,8 +47,16 @@ import { renderBranchSummaryEntry } from "./renderers/branch-summary.js";
35
47
  import { renderCompactionSummaryEntry } from "./renderers/compaction-summary.js";
36
48
  import { styleTaggedNotice } from "./renderers/notice.js";
37
49
  import { formatRetryStatus } from "./renderers/retry-status.js";
38
- import { renderToolResultOnly } from "./renderers/tool-execution.js";
39
- import { readWorkerReceiptFacts } from "./worker-receipts.js";
50
+ import { renderBashTranscriptExecution, renderToolResultOnly } from "./renderers/tool-execution.js";
51
+ import {
52
+ classifyStreamEvent,
53
+ createStreamPacer,
54
+ type SmoothStreamingMode,
55
+ type StreamPacer,
56
+ type StreamPacerSlice,
57
+ } from "./stream-pacer.js";
58
+ import { type FoldOverride, policyToolFold, resolveFold, type TranscriptDetailPolicy } from "./transcript-detail.js";
59
+ import { readWorkerReceiptFactsForReplay } from "./worker-receipts.js";
40
60
  import { workerEntriesFromRunEntries } from "./worker-replay.js";
41
61
  import type { WorkerReceiptReader } from "./worker-stream.js";
42
62
 
@@ -47,7 +67,28 @@ const MAX_REPLAY_TEXT_CHARS = 20_000;
47
67
  * Event kinds whose render is deferred into a coalesce window. All other
48
68
  * `ChatLoopEvent` kinds render synchronously and cancel any pending timer.
49
69
  */
50
- const DELTA_TYPES: ReadonlySet<ChatLoopEvent["type"]> = new Set(["text_delta", "thinking_delta"]);
70
+ const DELTA_TYPES: ReadonlySet<ChatLoopEvent["type"]> = new Set([
71
+ "text_delta",
72
+ "thinking_delta",
73
+ "tool_execution_update",
74
+ ]);
75
+
76
+ /**
77
+ * A raw `message_update` wrapper whose inner event is a text or thinking
78
+ * delta is transparent to the transcript: the panel ignores it, and the
79
+ * derived `text_delta`/`thinking_delta` emitted in the same stack (see
80
+ * turn-runtime's public-event fan-out) is the canonical display input. Yet
81
+ * this renderer classified the wrapper as non-delta, so every provider chunk
82
+ * cancelled the pending coalesce window and requested an immediate render.
83
+ * The coalescer was defeated exactly while streaming, which is the one time
84
+ * it matters. Wrappers carrying tool-call formation stay on the synchronous
85
+ * path, because the panel consumes those directly.
86
+ */
87
+ function isTransparentAssistantWrapper(event: ChatLoopEvent): boolean {
88
+ if (event.type !== "message_update") return false;
89
+ const inner = (event as { assistantMessageEvent?: { type?: unknown } }).assistantMessageEvent;
90
+ return inner?.type === "text_delta" || inner?.type === "thinking_delta";
91
+ }
51
92
 
52
93
  export interface CreateCoalescingChatRendererDeps {
53
94
  chatPanel: ChatPanel;
@@ -58,14 +99,35 @@ export interface CreateCoalescingChatRendererDeps {
58
99
  setTimer?: (cb: () => void, ms: number) => unknown;
59
100
  /** Override for tests. Mirrors the clearTimeout signature. */
60
101
  clearTimer?: (id: unknown) => void;
61
- /** Opt-in instrument; counts deltas so a frame row can report how many it absorbed. */
102
+ /** Monotonic clock shared with the pacer; injectable for deterministic tests. */
103
+ now?: () => number;
104
+ /** Sequence captured at canonical projection ingress before this panel consumer runs. */
105
+ visibleEventSequence?: (event: ChatLoopEvent) => number | null;
106
+ onQueue?: (eventSeq: number, action: "admit" | "dequeue") => void;
107
+ onPanelApplied?: (eventSeq: number) => void;
108
+ /** Legacy aggregate callback retained for non-text cumulative tool-update observations. */
62
109
  onDelta?: () => void;
110
+ /** Canonical presentation-ingress identity; absent keeps the exact legacy coalescer. */
111
+ streamIngress?: (event: ChatLoopEvent) => { sequence: number; generation: string | number; ingressAt: number } | null;
112
+ getSmoothStreamingMode?: () => SmoothStreamingMode;
113
+ isAutoPacingAllowed?: () => boolean;
114
+ /** Force and await one actual frame plus any stdout drain. */
115
+ commitFrame?: (reason?: string) => Promise<unknown>;
63
116
  }
64
117
 
65
118
  export interface CoalescingChatRenderer {
66
119
  applyEvent(event: ChatLoopEvent): void;
67
120
  /** Cancel the pending coalesce timer (if any) and request one synchronous render. */
68
121
  flush(): void;
122
+ /** Ordered barrier for replay, worker, command-output, and other panel mutations. */
123
+ mutate(mutation: () => void, reason?: string): void;
124
+ /** Drop queued presentation content before replacing/resetting the transcript. */
125
+ reset(mutation: () => void): void;
126
+ /** Drain paced content and await the first committed frame containing it. */
127
+ flushAndCommit(reason?: string): Promise<void>;
128
+ /** Apply a live mode change as an immediate ordered drain boundary. */
129
+ setSmoothStreamingMode(mode: SmoothStreamingMode): void;
130
+ dispose(): void;
69
131
  }
70
132
 
71
133
  export function createCoalescingChatRenderer(deps: CreateCoalescingChatRendererDeps): CoalescingChatRenderer {
@@ -78,8 +140,13 @@ export function createCoalescingChatRenderer(deps: CreateCoalescingChatRendererD
78
140
  const coalesceMs = deps.coalesceMs ?? DEFAULT_COALESCE_MS;
79
141
 
80
142
  let pendingTimer: unknown = null;
143
+ let mutationDepth = 0;
144
+ let transactionNeedsRender = false;
145
+ let disposed = false;
146
+ let pacer: StreamPacer | null = null;
81
147
 
82
148
  const fireCoalesced = (): void => {
149
+ if (disposed) return;
83
150
  pendingTimer = null;
84
151
  deps.requestRender();
85
152
  };
@@ -91,24 +158,165 @@ export function createCoalescingChatRenderer(deps: CreateCoalescingChatRendererD
91
158
  return true;
92
159
  };
93
160
 
94
- return {
161
+ const requestTransactionalRender = (coalesce: boolean): void => {
162
+ if (mutationDepth > 0) {
163
+ transactionNeedsRender = true;
164
+ return;
165
+ }
166
+ if (!coalesce) {
167
+ cancelPending();
168
+ deps.requestRender();
169
+ return;
170
+ }
171
+ deps.onDelta?.();
172
+ if (pendingTimer === null) pendingTimer = setTimer(fireCoalesced, coalesceMs);
173
+ };
174
+ const transaction = (operation: () => void, coalesce = false): void => {
175
+ mutationDepth += 1;
176
+ try {
177
+ operation();
178
+ } finally {
179
+ mutationDepth -= 1;
180
+ if (mutationDepth === 0 && transactionNeedsRender) {
181
+ transactionNeedsRender = false;
182
+ requestTransactionalRender(coalesce);
183
+ }
184
+ }
185
+ };
186
+ const applySlice = (slice: StreamPacerSlice): void => {
187
+ const event =
188
+ slice.kind === "text"
189
+ ? ({ type: "text_delta", contentIndex: slice.contentIndex, delta: slice.text, partialText: "" } as const)
190
+ : ({ type: "thinking_delta", contentIndex: slice.contentIndex, delta: slice.text, partialThinking: "" } as const);
191
+ deps.chatPanel.applyEvent(event);
192
+ if (slice.finalForItem) {
193
+ deps.onPanelApplied?.(slice.sequence);
194
+ deps.onQueue?.(slice.sequence, "dequeue");
195
+ }
196
+ requestTransactionalRender(true);
197
+ };
198
+ if (deps.streamIngress && deps.getSmoothStreamingMode) {
199
+ pacer = createStreamPacer({
200
+ mode: deps.getSmoothStreamingMode(),
201
+ onSlice: applySlice,
202
+ onDiscard: (sequence) => deps.onQueue?.(sequence, "dequeue"),
203
+ ...(deps.now ? { now: deps.now } : {}),
204
+ ...(deps.setTimer ? { setTimer: deps.setTimer } : {}),
205
+ ...(deps.clearTimer ? { clearTimer: deps.clearTimer } : {}),
206
+ ...(deps.isAutoPacingAllowed ? { isAutoPacingAllowed: deps.isAutoPacingAllowed } : {}),
207
+ });
208
+ }
209
+ const syncPacerMode = (): SmoothStreamingMode => {
210
+ const mode = deps.getSmoothStreamingMode?.() ?? "off";
211
+ if (pacer && pacer.mode !== mode) transaction(() => pacer?.setMode(mode));
212
+ return mode;
213
+ };
214
+ const drainPacer = (reason: string): void => {
215
+ if (pacer?.snapshot().queuedItems) pacer.flush(reason);
216
+ };
217
+ const applyLegacy = (event: ChatLoopEvent): void => {
218
+ const visibleEventSeq = deps.visibleEventSequence?.(event) ?? null;
219
+ if (visibleEventSeq !== null) deps.onQueue?.(visibleEventSeq, "admit");
220
+ deps.chatPanel.applyEvent(event);
221
+ if (visibleEventSeq !== null) {
222
+ deps.onPanelApplied?.(visibleEventSeq);
223
+ deps.onQueue?.(visibleEventSeq, "dequeue");
224
+ }
225
+ if (DELTA_TYPES.has(event.type)) {
226
+ requestTransactionalRender(true);
227
+ return;
228
+ }
229
+ requestTransactionalRender(false);
230
+ };
231
+
232
+ const renderer: CoalescingChatRenderer = {
95
233
  applyEvent(event) {
96
- deps.chatPanel.applyEvent(event);
97
- if (DELTA_TYPES.has(event.type)) {
98
- deps.onDelta?.();
99
- if (pendingTimer !== null) return;
100
- pendingTimer = setTimer(fireCoalesced, coalesceMs);
234
+ if (disposed) return;
235
+ if (isTransparentAssistantWrapper(event)) return;
236
+ const mode = syncPacerMode();
237
+ const ingress = deps.streamIngress?.(event) ?? null;
238
+ if (!pacer || mode === "off") {
239
+ applyLegacy(event);
101
240
  return;
102
241
  }
103
- cancelPending();
104
- deps.requestRender();
242
+ const classification = classifyStreamEvent(event);
243
+ if (classification === "paced-display-content") {
244
+ if (ingress === null) {
245
+ applyLegacy(event);
246
+ return;
247
+ }
248
+ const delta = event as Extract<ChatLoopEvent, { type: "text_delta" | "thinking_delta" }>;
249
+ if (delta.delta.length === 0) {
250
+ applyLegacy(event);
251
+ return;
252
+ }
253
+ deps.onQueue?.(ingress.sequence, "admit");
254
+ transaction(() => {
255
+ pacer?.enqueue({
256
+ sequence: ingress.sequence,
257
+ generation: ingress.generation,
258
+ kind: delta.type === "text_delta" ? "text" : "thinking",
259
+ contentIndex: delta.contentIndex,
260
+ text: delta.delta,
261
+ ingressAt: ingress.ingressAt,
262
+ folded: delta.type === "thinking_delta" && !deps.chatPanel.isThinkingExpanded(),
263
+ });
264
+ }, true);
265
+ return;
266
+ }
267
+ if (classification === "cumulative-live-state") {
268
+ transaction(() => {
269
+ drainPacer("cumulative-live-state");
270
+ applyLegacy(event);
271
+ }, true);
272
+ return;
273
+ }
274
+ transaction(() => {
275
+ drainPacer(`boundary:${event.type}`);
276
+ applyLegacy(event);
277
+ });
105
278
  },
106
279
  flush() {
280
+ if (disposed) return;
281
+ transaction(() => drainPacer("explicit-flush"));
107
282
  const wasPending = cancelPending();
108
- if (!wasPending) return;
109
- deps.requestRender();
283
+ if (wasPending) deps.requestRender();
284
+ },
285
+ mutate(mutation, reason = "panel-mutation") {
286
+ if (disposed) return;
287
+ transaction(() => {
288
+ drainPacer(reason);
289
+ mutation();
290
+ requestTransactionalRender(false);
291
+ });
292
+ },
293
+ reset(mutation) {
294
+ if (disposed) return;
295
+ transaction(() => {
296
+ pacer?.invalidateEpoch();
297
+ mutation();
298
+ requestTransactionalRender(false);
299
+ });
300
+ },
301
+ async flushAndCommit(reason = "final-frame") {
302
+ if (disposed) return;
303
+ transaction(() => drainPacer(reason));
304
+ cancelPending();
305
+ if (deps.commitFrame) await deps.commitFrame(reason);
306
+ else deps.requestRender();
307
+ },
308
+ setSmoothStreamingMode(mode) {
309
+ if (disposed || !pacer || pacer.mode === mode) return;
310
+ transaction(() => pacer?.setMode(mode));
311
+ },
312
+ dispose() {
313
+ if (disposed) return;
314
+ transaction(() => pacer?.dispose("renderer-dispose"));
315
+ cancelPending();
316
+ disposed = true;
110
317
  },
111
318
  };
319
+ return renderer;
112
320
  }
113
321
 
114
322
  /**
@@ -263,18 +471,18 @@ function toolCallMessageFromEntry(entry: MessageEntry): AgentMessage {
263
471
  } as unknown as AgentMessage;
264
472
  }
265
473
 
266
- function toolResultContent(result: unknown): unknown[] {
474
+ function toolResultContent(result: unknown, unbounded = false): unknown[] {
267
475
  const obj = payloadObject(result);
268
476
  if (Array.isArray(obj?.content)) {
269
- return cloneContentBlocks(obj.content, MAX_REPLAY_TEXT_CHARS) ?? [];
477
+ return cloneContentBlocks(obj.content, unbounded ? undefined : MAX_REPLAY_TEXT_CHARS) ?? [];
270
478
  }
271
- if (isTextResult(result)) return [{ type: "text", text: truncateReplayText(result.text) }];
272
- if (typeof result === "string") return [{ type: "text", text: truncateReplayText(result) }];
273
- return [{ type: "text", text: stringifyPreview(result, 10_000) }];
479
+ if (isTextResult(result)) return [{ type: "text", text: unbounded ? result.text : truncateReplayText(result.text) }];
480
+ if (typeof result === "string") return [{ type: "text", text: unbounded ? result : truncateReplayText(result) }];
481
+ return [{ type: "text", text: stringifyPreview(result, unbounded ? Number.POSITIVE_INFINITY : 10_000) }];
274
482
  }
275
483
 
276
- function displayReplayToolResult(result: unknown): unknown {
277
- const content = toolResultContent(result);
484
+ function displayReplayToolResult(result: unknown, unbounded = false): unknown {
485
+ const content = toolResultContent(result, unbounded);
278
486
  // Preserve the details record (observation envelope, exec records) so the
279
487
  // replayed ledger line carries the same outcome facts as the live one.
280
488
  const details = payloadObject(result)?.details;
@@ -499,30 +707,29 @@ function appendReplayLine(chatPanel: ChatPanel, text: string): void {
499
707
  chatPanel.appendReplayBlock((width) => renderReplayLine(truncateReplayText(text), width));
500
708
  }
501
709
 
502
- const BASH_REPLAY_MAX_LINES = 12;
503
-
504
- export function renderBashExecutionEntry(entry: BashExecutionEntry, width: number): string[] {
505
- const lines: string[] = [];
506
- lines.push(...wrapTextWithAnsi(`bash: $ ${entry.command}`, width));
507
- const output = truncateReplayText(entry.output.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\s+$/g, ""));
508
- if (output.length > 0) {
509
- const outputLines = output.split("\n");
510
- const hidden = Math.max(0, outputLines.length - BASH_REPLAY_MAX_LINES);
511
- const visible = outputLines.slice(-BASH_REPLAY_MAX_LINES);
512
- if (hidden > 0) lines.push(...wrapTextWithAnsi(` ... ${hidden} earlier lines`, width));
513
- for (const line of visible) {
514
- lines.push(...wrapTextWithAnsi(` ${line}`, width));
515
- }
516
- } else {
517
- lines.push(...wrapTextWithAnsi(" (no output)", width));
518
- }
519
- const status: string[] = [];
520
- if (entry.cancelled) status.push("cancelled");
521
- if (entry.exitCode !== null && entry.exitCode !== 0) status.push(`exit ${entry.exitCode}`);
522
- if (entry.truncated) status.push(entry.fullOutputPath ? `truncated: ${entry.fullOutputPath}` : "truncated");
523
- if (entry.excludeFromContext) status.push("excluded from context");
524
- if (status.length > 0) lines.push(...wrapTextWithAnsi(` (${status.join(", ")})`, width));
525
- return lines;
710
+ function renderBashExecutionEntry(
711
+ entry: BashExecutionEntry,
712
+ width: number,
713
+ folded: boolean,
714
+ unbounded: boolean,
715
+ ): string[] {
716
+ const normalizedOutput = entry.output.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\s+$/g, "");
717
+ return renderBashTranscriptExecution(
718
+ {
719
+ command: entry.command,
720
+ output: unbounded ? normalizedOutput : truncateReplayText(normalizedOutput),
721
+ running: false,
722
+ exitCode: entry.exitCode,
723
+ cancelled: entry.cancelled,
724
+ truncated: entry.truncated,
725
+ fullOutputPath: entry.fullOutputPath,
726
+ excludeFromContext: entry.excludeFromContext,
727
+ folded,
728
+ },
729
+ width,
730
+ undefined,
731
+ { unbounded, diffStyle: "plain" },
732
+ );
526
733
  }
527
734
 
528
735
  function renderRetryStatusEntry(entry: CustomEntry, width: number): string[] {
@@ -746,33 +953,26 @@ function dropLegacyToolResultAssistantDuplicates(entries: ReadonlyArray<SessionE
746
953
  }
747
954
 
748
955
  function compactionContextText(entry: CompactionSummaryEntry): string {
749
- return [
750
- "The conversation history before this point was compacted into the following summary:",
751
- "",
752
- "<summary>",
753
- entry.summary,
754
- "</summary>",
755
- ].join("\n");
956
+ return `${COMPACTION_SUMMARY_PREFIX}${entry.summary}${COMPACTION_SUMMARY_SUFFIX}`;
756
957
  }
757
958
 
758
959
  function branchContextText(entry: BranchSummaryEntry): string {
759
- return [
760
- "The following is a summary of a branch that this conversation came back from:",
761
- "",
762
- "<summary>",
763
- entry.summary,
764
- "</summary>",
765
- ].join("\n");
960
+ return `${BRANCH_SUMMARY_PREFIX}${entry.summary}${BRANCH_SUMMARY_SUFFIX}`;
766
961
  }
767
962
 
963
+ /** Project Clio's ledger entry onto pi's bash message so pi owns the replay wording. */
768
964
  function bashContextText(entry: BashExecutionEntry): string {
769
- let text = `Ran \`${entry.command}\`\n`;
770
- const output = truncateReplayText(entry.output);
771
- text += output.length > 0 ? `\`\`\`\n${output}\n\`\`\`` : "(no output)";
772
- if (entry.cancelled) text += "\n\n(command cancelled)";
773
- else if (entry.exitCode !== null && entry.exitCode !== 0) text += `\n\nCommand exited with code ${entry.exitCode}`;
774
- if (entry.truncated && entry.fullOutputPath) text += `\n\n[Output truncated. Full output: ${entry.fullOutputPath}]`;
775
- return text;
965
+ const message: BashExecutionMessage = {
966
+ role: "bashExecution",
967
+ command: entry.command,
968
+ output: truncateReplayText(entry.output),
969
+ exitCode: entry.exitCode ?? undefined,
970
+ cancelled: entry.cancelled,
971
+ truncated: entry.truncated,
972
+ ...(entry.fullOutputPath !== undefined ? { fullOutputPath: entry.fullOutputPath } : {}),
973
+ timestamp: Date.parse(entry.timestamp) || 0,
974
+ };
975
+ return bashExecutionToText(message);
776
976
  }
777
977
 
778
978
  function appendContextMessage(out: AgentMessage[], role: "user" | "assistant", text: string, timestamp: string): void {
@@ -838,6 +1038,7 @@ export function buildReplayAgentMessagesFromTurns(
838
1038
  case "label":
839
1039
  case "protectedArtifact":
840
1040
  case "taskLedger":
1041
+ case "decisionLedger":
841
1042
  // A worker's answer is not the operator's words and not the model's.
842
1043
  // It reaches the model only when an operator shares it, and a share
843
1044
  // is already a user message by the time it lands in the ledger.
@@ -877,19 +1078,21 @@ export function rehydrateChatPanelFromTurns(
877
1078
  options: RehydrateChatPanelOptions = {},
878
1079
  ): void {
879
1080
  const pendingToolIds: string[] = [];
1081
+ let runAssistantMessages: AgentMessage[] = [];
880
1082
  const selected = selectReplayEntries(turns, options);
881
1083
  // One block per assignment, drawn where its first attempt started. Later
882
1084
  // attempts of the same assignment fold into that block as `↻` rail lines, so
883
1085
  // a failover replays as the one run it was rather than as two.
884
1086
  const workerStates = workerEntriesFromRunEntries(
885
1087
  selected.filter((entry): entry is WorkerRunEntry => entry.kind === "workerRun"),
886
- options.readWorkerReceipt ?? readWorkerReceiptFacts,
1088
+ options.readWorkerReceipt ?? readWorkerReceiptFactsForReplay,
887
1089
  );
888
1090
  const placedAssignments = new Set<string>();
889
1091
  for (const entry of selected) {
890
1092
  switch (entry.kind) {
891
1093
  case "message": {
892
1094
  if (entry.role === "user") {
1095
+ runAssistantMessages = [];
893
1096
  const text = truncateReplayText(replayedUserText(entry));
894
1097
  if (text.length > 0) chatPanel.appendUser(text);
895
1098
  break;
@@ -905,7 +1108,8 @@ export function rehydrateChatPanelFromTurns(
905
1108
  (message as { stopReason?: string; errorMessage?: string }).errorMessage = failure.errorMessage;
906
1109
  }
907
1110
  chatPanel.applyEvent({ type: "message_end", message });
908
- chatPanel.applyEvent({ type: "agent_end", messages: [message] });
1111
+ runAssistantMessages.push(message);
1112
+ chatPanel.applyEvent({ type: "agent_end", messages: runAssistantMessages });
909
1113
  }
910
1114
  break;
911
1115
  }
@@ -918,6 +1122,7 @@ export function rehydrateChatPanelFromTurns(
918
1122
  toolName: call.name,
919
1123
  args: call.args,
920
1124
  });
1125
+ chatPanel.markToolReplayed?.(call.id);
921
1126
  break;
922
1127
  }
923
1128
  if (entry.role === "tool_result") {
@@ -930,7 +1135,7 @@ export function rehydrateChatPanelFromTurns(
930
1135
  type: "tool_execution_end",
931
1136
  toolCallId: fallbackId,
932
1137
  toolName: result.name,
933
- result: displayReplayToolResult(result.result),
1138
+ result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
934
1139
  isError: result.isError,
935
1140
  ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
936
1141
  ...(result.resultSummary !== undefined ? { resultSummary: result.resultSummary } : {}),
@@ -943,7 +1148,7 @@ export function rehydrateChatPanelFromTurns(
943
1148
  {
944
1149
  toolCallId: result.id ?? "",
945
1150
  toolName: result.name,
946
- result: displayReplayToolResult(result.result),
1151
+ result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
947
1152
  isError: result.isError,
948
1153
  ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
949
1154
  ...(result.resultSummary !== undefined ? { resultSummary: result.resultSummary } : {}),
@@ -969,9 +1174,25 @@ export function rehydrateChatPanelFromTurns(
969
1174
  }
970
1175
  break;
971
1176
  }
972
- case "bashExecution":
973
- chatPanel.appendReplayBlock((width) => renderBashExecutionEntry(entry, width));
1177
+ case "bashExecution": {
1178
+ let fold: FoldOverride;
1179
+ const presentation = toolPresentationPolicy(ToolNames.Bash, undefined);
1180
+ const policyFold = (detail: TranscriptDetailPolicy) => policyToolFold(detail, presentation);
1181
+ const unbounded = options.unboundedToolBodies === true;
1182
+ chatPanel.appendReplayBlock(
1183
+ (width, detail) =>
1184
+ renderBashExecutionEntry(entry, width, resolveFold(fold, policyFold(detail)) === "folded", unbounded),
1185
+ undefined,
1186
+ {
1187
+ policyFold,
1188
+ fold: () => fold,
1189
+ setFold: (next: FoldOverride) => {
1190
+ fold = next;
1191
+ },
1192
+ },
1193
+ );
974
1194
  break;
1195
+ }
975
1196
  case "custom":
976
1197
  if (rendersCustomEntry(entry)) chatPanel.appendReplayBlock((width) => renderCustomEntry(entry, width));
977
1198
  break;
@@ -1017,6 +1238,7 @@ export function rehydrateChatPanelFromTurns(
1017
1238
  }
1018
1239
  case "label":
1019
1240
  case "taskLedger":
1241
+ case "decisionLedger":
1020
1242
  break;
1021
1243
  }
1022
1244
  }
@@ -1029,10 +1251,9 @@ export function rehydrateChatPanelFromTurns(
1029
1251
  isError: true,
1030
1252
  });
1031
1253
  }
1032
- // Replay reproduces the settled view: tools collapse to their one-line
1033
- // ledger summary. The live path collapses tools because they fire while the
1034
- // assistant is streaming (pending), a state replay cannot reconstruct
1035
- // per-tool, so collapse them explicitly here. /export re-expands afterward
1036
- // via toggleAllToolsExpanded; /resume and /fork keep the collapsed form.
1037
- chatPanel.collapseAllTools();
1254
+ // A rehydrated transcript starts from the transcript detail policy alone:
1255
+ // whatever the operator had opened or folded before the switch belonged to
1256
+ // the transcript they left. /export reaches the same policy with a verbose
1257
+ // panel, so nothing here is terminal-only.
1258
+ chatPanel.clearFoldOverrides();
1038
1259
  }
@@ -1,18 +1,13 @@
1
- import { Editor, type TUI, truncateToWidth, visibleWidth } from "../engine/tui.js";
1
+ import { Editor, stripTerminalSequences, type TUI, truncateToWidth, visibleWidth } from "../engine/tui.js";
2
2
  import type { ClioTheme } from "./theme/index.js";
3
3
  import { clioTheme, editorTheme, GLYPH, rule } from "./theme/index.js";
4
4
 
5
- const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
6
5
  const REVERSE_VIDEO_BLANK = `${String.fromCharCode(27)}[7m ${String.fromCharCode(27)}[0m`;
7
6
  const EMPTY_PROMPT = "Ask Clio… / for commands";
8
7
  const MIN_HINT_WIDTH = 60;
9
8
 
10
- function stripAnsi(text: string): string {
11
- return text.replace(ANSI, "");
12
- }
13
-
14
9
  function hasScrollIndicator(line: string): boolean {
15
- const stripped = stripAnsi(line);
10
+ const stripped = stripTerminalSequences(line);
16
11
  return stripped.includes(GLYPH.up) || stripped.includes(GLYPH.down);
17
12
  }
18
13
 
@@ -91,7 +86,7 @@ function renderEmptyPrompt(line: string, width: number, theme: ClioTheme): strin
91
86
  function findBottomRail(lines: readonly string[], width: number): number {
92
87
  const rail = "─".repeat(Math.max(0, width));
93
88
  for (let index = 1; index < lines.length; index += 1) {
94
- if (stripAnsi(lines[index] ?? "") === rail) return index;
89
+ if (stripTerminalSequences(lines[index] ?? "") === rail) return index;
95
90
  }
96
91
  return -1;
97
92
  }
@@ -12,12 +12,12 @@ export function runCompactWithNotice(
12
12
  instructions: string | undefined,
13
13
  ): void {
14
14
  if (!onCompact) {
15
- emitCommandNotice(notice, "error", "compact", "compaction not wired; pass onCompact to startInteractive");
15
+ emitCommandNotice(notice, "error", "context compact", "compaction not wired; pass onCompact to startInteractive");
16
16
  return;
17
17
  }
18
18
  const task = onCompact(instructions);
19
19
  void task.catch((err) => {
20
20
  const msg = err instanceof Error ? err.message : String(err);
21
- emitCommandNotice(notice, "error", "compact", msg);
21
+ emitCommandNotice(notice, "error", "context compact", msg);
22
22
  });
23
23
  }
@@ -1,3 +1,5 @@
1
+ import { homedir } from "node:os";
2
+ import { relative } from "node:path";
1
3
  import { BusChannels } from "../core/bus-events.js";
2
4
  import type { SafeEventBus } from "../core/event-bus.js";
3
5
  import type { ContextLedger, ContextLedgerGroup } from "../domains/session/context-ledger.js";
@@ -40,6 +42,27 @@ function contextWindowProvenanceLabel(source: ContextLedger["contextWindowSource
40
42
  }
41
43
  }
42
44
 
45
+ /** A handbook path the operator can read at a glance: workspace-relative when
46
+ * the file sits under the current directory, `~`-shortened otherwise. */
47
+ function displayHandbookPath(filePath: string): string {
48
+ const rel = relative(process.cwd(), filePath);
49
+ if (rel.length > 0 && !rel.startsWith("..")) return rel;
50
+ const home = homedir();
51
+ return home.length > 0 && filePath.startsWith(`${home}/`) ? `~${filePath.slice(home.length)}` : filePath;
52
+ }
53
+
54
+ /**
55
+ * Which CLIO-CODER*.md file(s) the compiled prompt actually selected. With
56
+ * override semantics the operator cannot infer the winner from the preload
57
+ * size alone, so the effective chain is named explicitly, nearest last.
58
+ */
59
+ function handbookProvenanceLines(handbookFiles: ReadonlyArray<string> | null): string[] {
60
+ if (!handbookFiles || handbookFiles.length === 0) return [];
61
+ const first = handbookFiles[0];
62
+ if (handbookFiles.length === 1 && first !== undefined) return [`handbook: ${displayHandbookPath(first)}`];
63
+ return ["handbooks (ancestor → nearest):", ...handbookFiles.map((filePath) => ` ${displayHandbookPath(filePath)}`)];
64
+ }
65
+
43
66
  function gridDimensions(ledger: ContextLedger, contentWidth: number): { cols: number; rows: number } {
44
67
  const cols = Math.max(12, Math.min(contentWidth, 40));
45
68
  const rows = ledger.contextWindow >= 200_000 ? 8 : 6;
@@ -58,7 +81,7 @@ function legendRow(group: ContextLedgerGroup, contentWidth: number): string {
58
81
  return `${swatch} ${theme.fg(labelToken, labelText)} ${theme.fg("muted", right)}`;
59
82
  }
60
83
 
61
- function renderContextLedgerLines(ledger: ContextLedger, contentWidth: number): string[] {
84
+ export function renderContextLedgerLines(ledger: ContextLedger, contentWidth: number): string[] {
62
85
  const theme = clioTheme();
63
86
  const lines: string[] = [];
64
87
 
@@ -92,6 +115,9 @@ function renderContextLedgerLines(ledger: ContextLedger, contentWidth: number):
92
115
  if (ledger.projectPreload && ledger.groups.some((group) => group.category === "project")) {
93
116
  lines.push(theme.fg("dim", `project preload: ${ledger.projectPreload}`));
94
117
  }
118
+ for (const handbookLine of handbookProvenanceLines(ledger.projectHandbookFiles)) {
119
+ lines.push(theme.fg("dim", handbookLine));
120
+ }
95
121
  const compaction =
96
122
  ledger.compactionThreshold !== null
97
123
  ? `autocompact at ${Math.round(ledger.compactionThreshold * 100)}% (${ledger.compactionAuto ? "auto" : "manual"})`