chainlesschain 0.162.49 → 0.162.60

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 (172) hide show
  1. package/bin/chainlesschain.js +1 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DmdtNmWd.js → AIOps-a2cSbSEu.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DkuYVafg.js → ActionButton-DwvSB5Pp.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Ba_h8Tub.js → Analytics-BqaRaBDD.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-yb8Zm9MX.js → AppLayout-Beck7v8t.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-BGjex2fm.js → Audit-UtJhPdXJ.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-IFQ2hOF2.js → Backup-HVZhcdll.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-W8AkPkrV.js → BaseInput-DRY_ZGmj.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-BgI7t-iW.js → Chat-D7Vuwfe2.js} +6 -6
  11. package/src/assets/web-panel/assets/ChatBubbleRenderer-BS2q_hPX.js +1 -0
  12. package/src/assets/web-panel/assets/{Checkbox-DuWsZP4g.js → Checkbox-B406i7N1.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DyoTNmYg.js → Codegen-BvTCqHi3.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DttmlDRk.js → Col-DRiyxTQP.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-D9nnIdKn.js → Community-DWmhxHQa.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-C8KVQaHb.js → Compact-DO1HBZEz.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-R2owqgjj.js → Compliance-D4j-VHwS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-DwGMMjRn.js → Cowork-BGBkWtat.js} +3 -3
  19. package/src/assets/web-panel/assets/{Cron-BSTcN_lK.js → Cron-Xa9PtMUQ.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-CTNuIbFD.js → Crosschain-wVWs4lqN.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-CgApGsFP.js → DID-DTkqiRuT.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-D_OJ3UN5.js → Dashboard-d9STUbrr.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-B84Jwra_.js → Dropdown-CrdxS-C8.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Bv-YO-6y.js → EmailListRenderer-D78XHUEp.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-drgZ408Y.js → FamilyGuardDashboard-iAeSETIP.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-CtzFkdW2.js → Federation-CTV1Sxqs.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-BFAvNhl9.js → FormItemContext-BtwNuQKK.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-DnuEyz_l.js → GenericCardRenderer-CdEgHjkl.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-jlHajmRJ.js → Git-BTo-PJr_.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-DmJC7PGL.js → Governance-DquOG94r.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-B-u7xD2n.js → Inference-DDtcBxRB.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-BaYCA2Cd.js → KnowledgeGraph-KUOmNj5C.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-DTNYQWfp.js → Logs-HKm7kRs7.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CUu1xYvo.js → Marketplace-IxrOcbFB.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-BmoeTyrC.js → McpTools-D6a1LM3S.js} +5 -5
  36. package/src/assets/web-panel/assets/{Memory-DxTU3QU7.js → Memory-lFkD2ZuM.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-CpcOlKAD.js → MobileBridge-xwuQTps5.js} +2 -2
  38. package/src/assets/web-panel/assets/MobileProjects-BYr1D3WO.js +1 -0
  39. package/src/assets/web-panel/assets/{Mtc-LfxwOm0x.js → Mtc-BXpJGrjm.js} +5 -5
  40. package/src/assets/web-panel/assets/{MtcAudit-D6A9Gjkh.js → MtcAudit-CWttaim1.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-Ch_jofPV.js → Multisig-jKgTuVLS.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-Bkvogg0I.js → NLProgramming-xl4RDzQj.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-C5t5Xihm.js → Notes-DPBOvscE.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-CTpDUNCb.js → NotificationSettings-8TkIkRo3.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-Dwa-XtoE.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-Dr37BaXa.js → Organization-CJ0xVwZM.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-ZGjsdP7N.js → Overflow-V7VuUslt.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-bWJU5Vxd.js → P2P-BxuccEGq.js} +2 -2
  49. package/src/assets/web-panel/assets/PdhVaultBrowser-DaP2Q5kU.js +7 -0
  50. package/src/assets/web-panel/assets/{Permissions-BOSnFZaC.js → Permissions-CPJFF0zU.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-X4SgjP6P.js → PersonalDataHub-Cmn2uiuw.js} +4 -4
  52. package/src/assets/web-panel/assets/{Pipeline-DoJhB9bj.js → Pipeline-0zX89_iz.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-OM9lDj-R.js → Privacy-DROUg3XE.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-BXQEOmLn.js → ProjectInit-c5KESOK4.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-DBXo3K5u.js → ProjectSettings-BfiCcnb_.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-CJ4DBJlS.js → Projects-BtZH5-Eh.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-Tk9SawmO.js → Providers-C9Rr_dOk.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DRI1-nTC.js → QuickAsk-Du4p90W6.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-DtrIVTu9.js → Recommend-B-DQenTl.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-DkH8ImwZ.js → Reputation-DvwlAVRZ.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-DpA9dlvi.js → Row-rTnbvkP-.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-DV3OhxWd.js → RssFeed-DwvsqWbB.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-QxdntiQx.js → Search-U_Xj5SvF.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CGuEnrD2.js → Security-CdjsVDQ8.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-BvwSSD8b.js → Services-DUd3mFXk.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-sx_8L3-5.js → Skeleton-CA2gCJmY.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-dWOwxRsu.js → Skills-BIw7Rb-m.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-zxXnfKrT.js → Sla-vySPesC0.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CmFlcNjr.js → SpeechSettings-EniuTjBJ.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-BeXeqURL.js → SyncSettings-DkrqbzYS.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-oyPnWRbw.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-DlgR3XFH.js → Templates-C2Kvn60U.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-0P8HgQaM.js → Tenant-x6jVMx4D.js} +1 -1
  74. package/src/assets/web-panel/assets/Terminal-C2nZbPBs.js +3 -0
  75. package/src/assets/web-panel/assets/{TimelineRenderer-hbO7agZs.js → TimelineRenderer-BF6HAETd.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-CsmhgTBO.js → Tokens-B-KcVAin.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-DnaF_2PP.js → Trigger-B-Caiptm.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-C1oafGj1.js → Trust-_8sq7pJp.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-eLL4DOmC.js → UkeySign-CLveUEgo.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-CX45sVq7.js → VideoEditing-iVc9jxt9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-aWPqpHdQ.js → Wallet-D1n5pidD.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-DMYV1MAo.js → WebAuthn-CA8kubXb.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-D9uRIJvH.js → WorkflowEditor-BXWwd_fB.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-BmWYfCxG.js → chat-DUNkQr1A.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-DqvTCkBe.js → colors-Dz5ozTcp.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-Bh0L0ejI.js → compact-item-CZ5-JSLh.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-r2qgp1mn.js → createContext-CFxlcPug.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-BMRVR8Xp.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-BcffXa-S.js → hasIn-CqWIkHJm.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DutDlDUF.js → index-3elHm6lI.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-XwbSqOB2.js → index-8l5LLWxH.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-C2-02rrp.js → index-BBq1ySIt.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CY8RXaZR.js → index-BI2EU3hC.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-B6pAm1iJ.js → index-BJt6sNTF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Dcjol7ot.js → index-BLLSLAC3.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-U86pxDyR.js → index-BLNgGXeg.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DPFT7J7I.js → index-BO-yo7Jv.js} +1 -1
  98. package/src/assets/web-panel/assets/index-BT2s_zxU.js +1 -0
  99. package/src/assets/web-panel/assets/{index-DRXcGa5y.js → index-BiAcVeea.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B7z0qK1W.js → index-C2SoMbLc.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DW1y18GR.js → index-C5XUilwu.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BCBqTRWH.js → index-C5zhjact.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BMn_luHQ.js → index-CBeASfAD.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-COrfHebA.js → index-CD7UjlnE.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DxajFkK2.js → index-CDq8IVvv.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CJt0iuep.js → index-CE-gpaY9.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BtyXyl3t.js → index-CL6wt2JN.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cbj6C3pA.js → index-CLu3Oyef.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CKnEtlZD.js → index-C_WWTpLE.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-De36_UgR.js → index-CbcHBDYj.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CAlxkpnv.js → index-CguUaiiY.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Ct8qhPZe.js → index-Ci1-8q-g.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BbMox24t.js → index-CvMZxZOn.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BLN-neIf.js → index-D6Nkerss.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-h4O0AcBt.js → index-DEzYXMgc.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BXXxkeij.js → index-DF0hXW5L.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-B3mmDuOv.js → index-DGAjS_D9.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CDR3GmaO.js → index-DO6mf95c.js} +1 -1
  119. package/src/assets/web-panel/assets/index-DOTL6NDc.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BeV-KoQl.js → index-DUyhvh0L.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bma_yHcC.js → index-DW18L-o6.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-585fuGAN.js → index-Dc-5Ulgt.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CCyB-RK5.js → index-DvUlrMy-.js} +3 -3
  124. package/src/assets/web-panel/assets/{index-DWlDSE0F.js → index-FsYDYVUk.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BXae4ZyX.js → index-GVbsyUQm.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C9nh3ANl.js → index-noQc_RpT.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BQlAPNSU.js → index-rR060KAF.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-S4E77Aer.js → index-xaZX6ZDL.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-C1d8I-BX.js → initDefaultProps-PIetywTX.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Dq7fiy4Y.js → motion-gQJEK3wO.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-Bqb2dySM.js → move-ML1nRxts.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-BUYqb4My.js → omit-wUWsw3YL.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DeytiKlZ.js → pickAttrs-A0Wlomih.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-xrXZWCqG.js → placementArrow-C5RKYdxT.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-CcL2K-YY.js → responsiveObserve-DIxNVSJl.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DmCWaic7.js → slide-B-tNesVu.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-CqNrFif7.js → statusUtils-CftzO200.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-C436m5Xy.js → styleChecker-CMgvWu90.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-CVhutCN8.js → useFlexGapSupport-sxqoDNhZ.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-DUd49Bui.js → useFs-CowEXz4v.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-fuS9raic.js → usePersonalDataHub-6ISRG7V-.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-C3kmDmk-.js → vnode-C2mnXfmw.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-hX-F1dT-.js → zoom-DMsur0jx.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +66 -28
  146. package/src/harness/mcp-client.js +48 -2
  147. package/src/lib/llm-pricing.js +9 -0
  148. package/src/lib/permission-rules.cjs +11 -1
  149. package/src/lib/personal-data-hub-wiring.js +24 -0
  150. package/src/lib/repl-multiline.js +64 -0
  151. package/src/lib/repl-rewind.js +65 -2
  152. package/src/lib/repl-vim.js +445 -0
  153. package/src/lib/safe-mode.js +17 -3
  154. package/src/lib/skill-loader.js +45 -1
  155. package/src/lib/slash-commands.js +13 -3
  156. package/src/lib/status-line.cjs +33 -3
  157. package/src/repl/agent-repl.js +253 -27
  158. package/src/repl/session-cost.js +98 -1
  159. package/src/runtime/agent-core.js +23 -8
  160. package/src/runtime/fallback-model.js +125 -30
  161. package/src/runtime/headless-runner.js +2 -0
  162. package/src/runtime/headless-stream.js +2 -0
  163. package/src/runtime/mcp-config.js +14 -3
  164. package/src/assets/web-panel/assets/ChatBubbleRenderer-CfpKEQUF.js +0 -1
  165. package/src/assets/web-panel/assets/MobileProjects-Bjh_z16l.js +0 -1
  166. package/src/assets/web-panel/assets/OrderTableRenderer-ST2lr-Bi.js +0 -1
  167. package/src/assets/web-panel/assets/PdhVaultBrowser-BRVoW-ye.js +0 -7
  168. package/src/assets/web-panel/assets/Tasks-iImd8xSO.js +0 -1
  169. package/src/assets/web-panel/assets/Terminal-B5VDEEHD.js +0 -3
  170. package/src/assets/web-panel/assets/devWarning-CusWDjWW.js +0 -1
  171. package/src/assets/web-panel/assets/index-BhYltBvN.js +0 -1
  172. package/src/assets/web-panel/assets/index-CZiIHw4e.js +0 -1
@@ -0,0 +1,445 @@
1
+ /**
2
+ * Vim-mode line editing for the agent REPL (Claude-Code `/vim` parity).
3
+ *
4
+ * Node's readline owns rich INSERT-mode editing (history, kill-ring, tab
5
+ * completion), so this module is a pure NORMAL-mode command interpreter — the
6
+ * thing readline does NOT give you. The REPL wiring (agent-repl.js) consults it
7
+ * only while in normal mode; insert↔normal transitions seed state from the live
8
+ * `rl.line` / `rl.cursor`, so insert-mode edits never need to round-trip here.
9
+ *
10
+ * Everything is a pure function over an explicit `{ line, cursor, ... }` state so
11
+ * the whole keymap is unit-testable without a TTY. `feedNormalKey()` takes one
12
+ * keypress and returns the next state plus, when relevant, `mode:"insert"`
13
+ * (hand control back to readline, seeded with the returned line/cursor) or
14
+ * `submit:true` (Enter in normal mode → run the line).
15
+ *
16
+ * Supported (a deliberately useful subset, not all of vim):
17
+ * motions h l 0 ^ $ w b e, f<c> F<c> t<c> T<c>, numeric counts (3w, 2l)
18
+ * enter ins i I a A, and o/O → A/I (the input is a single line)
19
+ * edits x X D C s S ~ r<c>, and operators d/c/y with the motions above
20
+ * plus doubled dd/cc/yy (whole line); p/P paste the register
21
+ * Not (yet): undo/redo, registers beyond the unnamed one, visual mode.
22
+ */
23
+
24
+ /** Fresh state. mode starts "insert" so a just-toggled REPL keeps typing. */
25
+ export function createVimState(line = "", cursor = 0) {
26
+ return {
27
+ mode: "insert",
28
+ line,
29
+ cursor,
30
+ register: { text: "", linewise: false },
31
+ pending: null, // { op:"d"|"c"|"y", count:number } awaiting a motion
32
+ count: "", // accumulating numeric prefix
33
+ awaitChar: null, // { kind:"f"|"F"|"t"|"T"|"r" } awaiting one more char
34
+ message: null, // transient note for the wiring (e.g. unknown key → bell)
35
+ };
36
+ }
37
+
38
+ const isWord = (c) => !!c && /[A-Za-z0-9_]/.test(c);
39
+ const isSpace = (c) => !!c && /\s/.test(c);
40
+ const isPunct = (c) => !!c && !isWord(c) && !isSpace(c);
41
+ const clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
42
+
43
+ /** First non-blank column (vim `^`). */
44
+ function firstNonBlank(line) {
45
+ const i = line.search(/\S/);
46
+ return i === -1 ? 0 : i;
47
+ }
48
+
49
+ /** Start of the next word (vim `w`). */
50
+ function wordForward(line, cursor) {
51
+ const n = line.length;
52
+ let i = cursor;
53
+ if (i >= n) return n;
54
+ const cls = isWord(line[i]) ? isWord : isPunct(line[i]) ? isPunct : null;
55
+ if (cls) while (i < n && cls(line[i])) i++;
56
+ while (i < n && isSpace(line[i])) i++;
57
+ return i;
58
+ }
59
+
60
+ /** Start of the previous word (vim `b`). */
61
+ function wordBackward(line, cursor) {
62
+ let i = cursor;
63
+ if (i <= 0) return 0;
64
+ i--;
65
+ while (i > 0 && isSpace(line[i])) i--;
66
+ if (i <= 0) return 0;
67
+ const cls = isWord(line[i]) ? isWord : isPunct;
68
+ while (i > 0 && cls(line[i - 1])) i--;
69
+ return i;
70
+ }
71
+
72
+ /** End of the current/next word (vim `e`, inclusive). */
73
+ function wordEnd(line, cursor) {
74
+ const n = line.length;
75
+ let i = cursor;
76
+ if (i >= n - 1) return Math.max(0, n - 1);
77
+ i++;
78
+ while (i < n && isSpace(line[i])) i++;
79
+ if (i >= n) return n - 1;
80
+ const cls = isWord(line[i]) ? isWord : isPunct;
81
+ while (i + 1 < n && cls(line[i + 1])) i++;
82
+ return i;
83
+ }
84
+
85
+ /**
86
+ * Resolve a motion key to a target column + whether it's inclusive (the target
87
+ * char is part of an operator's range). Returns null for "not a motion".
88
+ *
89
+ * @returns {{ target:number, inclusive:boolean }|null}
90
+ */
91
+ function resolveMotion(state, ch, count, charArg) {
92
+ const { line, cursor } = state;
93
+ const n = line.length;
94
+ const rep = Math.max(1, count);
95
+ let pos;
96
+ switch (ch) {
97
+ case "h":
98
+ return { target: Math.max(0, cursor - rep), inclusive: false };
99
+ case "l":
100
+ case " ":
101
+ return { target: Math.min(n, cursor + rep), inclusive: false };
102
+ case "0":
103
+ return { target: 0, inclusive: false };
104
+ case "^":
105
+ return { target: firstNonBlank(line), inclusive: false };
106
+ case "$":
107
+ return { target: n, inclusive: false };
108
+ case "w":
109
+ pos = cursor;
110
+ for (let k = 0; k < rep; k++) pos = wordForward(line, pos);
111
+ return { target: pos, inclusive: false };
112
+ case "b":
113
+ pos = cursor;
114
+ for (let k = 0; k < rep; k++) pos = wordBackward(line, pos);
115
+ return { target: pos, inclusive: false };
116
+ case "e":
117
+ pos = cursor;
118
+ for (let k = 0; k < rep; k++) pos = wordEnd(line, pos);
119
+ return { target: pos, inclusive: true };
120
+ case "f":
121
+ case "t": {
122
+ if (charArg == null) return null;
123
+ let from = cursor;
124
+ for (let k = 0; k < rep; k++) {
125
+ const idx = line.indexOf(charArg, from + 1);
126
+ if (idx === -1) return { target: cursor, inclusive: false, fail: true };
127
+ from = idx;
128
+ }
129
+ return {
130
+ target: ch === "t" ? Math.max(cursor, from - 1) : from,
131
+ inclusive: true,
132
+ };
133
+ }
134
+ case "F":
135
+ case "T": {
136
+ if (charArg == null) return null;
137
+ let from = cursor;
138
+ for (let k = 0; k < rep; k++) {
139
+ const idx = line.lastIndexOf(charArg, from - 1);
140
+ if (idx === -1) return { target: cursor, inclusive: false, fail: true };
141
+ from = idx;
142
+ }
143
+ return { target: ch === "T" ? from + 1 : from, inclusive: false };
144
+ }
145
+ default:
146
+ return null;
147
+ }
148
+ }
149
+
150
+ const NORMAL_CURSOR_MAX = (line) => Math.max(0, line.length - 1);
151
+
152
+ /** Delete [from,to) from line, stash into register (charwise). */
153
+ function cutRange(state, from, to) {
154
+ const a = Math.min(from, to);
155
+ const b = Math.max(from, to);
156
+ const text = state.line.slice(a, b);
157
+ return {
158
+ line: state.line.slice(0, a) + state.line.slice(b),
159
+ cursor: a,
160
+ register: { text, linewise: false },
161
+ };
162
+ }
163
+
164
+ /** Apply an operator (d/c/y) over a resolved motion span. */
165
+ function applyOperator(state, op, span) {
166
+ const { line, cursor } = state;
167
+ let to = span.target;
168
+ if (span.inclusive) to = Math.min(line.length, to + 1);
169
+ if (op === "y") {
170
+ const a = Math.min(cursor, to);
171
+ const b = Math.max(cursor, to);
172
+ const next = {
173
+ ...state,
174
+ register: { text: line.slice(a, b), linewise: false },
175
+ cursor: a,
176
+ pending: null,
177
+ count: "",
178
+ awaitChar: null,
179
+ };
180
+ return next;
181
+ }
182
+ const cut = cutRange(state, cursor, to);
183
+ const base = {
184
+ ...state,
185
+ line: cut.line,
186
+ register: cut.register,
187
+ pending: null,
188
+ count: "",
189
+ awaitChar: null,
190
+ };
191
+ if (op === "c") {
192
+ return { ...base, cursor: cut.cursor, mode: "insert" };
193
+ }
194
+ // d: keep cursor in-bounds for normal mode
195
+ return { ...base, cursor: clamp(cut.cursor, 0, NORMAL_CURSOR_MAX(cut.line)) };
196
+ }
197
+
198
+ /** Whole-line operator (dd / cc / yy). */
199
+ function applyLinewiseOperator(state, op) {
200
+ const reg = { text: state.line, linewise: true };
201
+ if (op === "y") {
202
+ return {
203
+ ...state,
204
+ register: reg,
205
+ pending: null,
206
+ count: "",
207
+ awaitChar: null,
208
+ };
209
+ }
210
+ const cleared = {
211
+ ...state,
212
+ line: "",
213
+ cursor: 0,
214
+ register: reg,
215
+ pending: null,
216
+ count: "",
217
+ awaitChar: null,
218
+ };
219
+ return op === "c" ? { ...cleared, mode: "insert" } : cleared;
220
+ }
221
+
222
+ const enterInsert = (state, patch = {}) => ({
223
+ ...state,
224
+ ...patch,
225
+ mode: "insert",
226
+ pending: null,
227
+ count: "",
228
+ awaitChar: null,
229
+ message: null,
230
+ });
231
+
232
+ const bell = (state, patch = {}) => ({
233
+ ...state,
234
+ ...patch,
235
+ pending: null,
236
+ count: "",
237
+ awaitChar: null,
238
+ message: "bell",
239
+ });
240
+
241
+ /**
242
+ * Feed one keypress to the NORMAL-mode interpreter.
243
+ *
244
+ * @param {object} state from createVimState (mode is assumed "normal")
245
+ * @param {string} ch printable character (may be "" for special keys)
246
+ * @param {object} [key] node readline key descriptor ({ name, ctrl, ... })
247
+ * @returns {object} next state. `mode:"insert"` ⇒ hand back to readline seeded
248
+ * with line/cursor; `submit:true` ⇒ run the line.
249
+ */
250
+ export function feedNormalKey(state, ch, key = {}) {
251
+ const s = { ...state, message: null };
252
+ const line = s.line;
253
+
254
+ // Enter → submit the line (parity with insert-mode Enter).
255
+ if (key.name === "return" || key.name === "enter") {
256
+ return { ...s, submit: true, pending: null, count: "", awaitChar: null };
257
+ }
258
+ // Esc clears any pending state but stays in normal mode.
259
+ if (key.name === "escape") {
260
+ return { ...s, pending: null, count: "", awaitChar: null };
261
+ }
262
+
263
+ // Awaiting a single char (f/F/t/T target, or r replacement).
264
+ if (s.awaitChar) {
265
+ const { kind, forOp } = s.awaitChar;
266
+ if (!ch) return { ...s, awaitChar: null }; // a non-char key cancels
267
+ if (kind === "r") {
268
+ if (s.cursor >= line.length) return bell(s);
269
+ const next = line.slice(0, s.cursor) + ch + line.slice(s.cursor + 1);
270
+ return { ...s, line: next, awaitChar: null };
271
+ }
272
+ const span = resolveMotion(s, kind, parseInt(s.count || "1", 10), ch);
273
+ if (!span || span.fail) return bell(s, { awaitChar: null });
274
+ // Operator+find (e.g. df,) → apply the operator over the span; otherwise
275
+ // f/F/t/T is a standalone cursor motion.
276
+ if (forOp) {
277
+ return applyOperator({ ...s, awaitChar: null }, forOp, span);
278
+ }
279
+ return {
280
+ ...s,
281
+ cursor: clamp(span.target, 0, NORMAL_CURSOR_MAX(line)),
282
+ awaitChar: null,
283
+ count: "",
284
+ };
285
+ }
286
+
287
+ // Count prefix: digits 1-9 always; 0 only continues an existing count
288
+ // (otherwise 0 is the "start of line" motion).
289
+ if (/[0-9]/.test(ch) && !(ch === "0" && !s.count)) {
290
+ return { ...s, count: s.count + ch };
291
+ }
292
+
293
+ const count = parseInt(s.count || "1", 10);
294
+
295
+ // Operator pending (d/c/y already pressed): this key is the motion.
296
+ if (s.pending) {
297
+ const op = s.pending.op;
298
+ // Doubled operator → linewise (dd, cc, yy).
299
+ if (ch === op) return applyLinewiseOperator(s, op);
300
+ // f/F/t/T need one more char before they can resolve.
301
+ if ("fFtT".includes(ch)) {
302
+ return { ...s, awaitChar: { kind: ch, forOp: op }, count: String(count) };
303
+ }
304
+ const span = resolveMotion(s, ch, count, null);
305
+ if (!span || span.fail) return bell(s);
306
+ return applyOperator(s, op, span);
307
+ }
308
+
309
+ // No pending operator — top-level normal commands.
310
+ switch (ch) {
311
+ case "i":
312
+ return enterInsert(s);
313
+ case "I":
314
+ return enterInsert(s, { cursor: firstNonBlank(line) });
315
+ case "a":
316
+ return enterInsert(s, { cursor: Math.min(line.length, s.cursor + 1) });
317
+ case "A":
318
+ return enterInsert(s, { cursor: line.length });
319
+ case "o": // single-line input: open ≈ append at end
320
+ return enterInsert(s, { cursor: line.length });
321
+ case "O":
322
+ return enterInsert(s, { cursor: firstNonBlank(line) });
323
+ case "x": {
324
+ if (s.cursor >= line.length) return bell(s);
325
+ const to = Math.min(line.length, s.cursor + count);
326
+ const cut = cutRange(s, s.cursor, to);
327
+ return {
328
+ ...s,
329
+ line: cut.line,
330
+ register: cut.register,
331
+ cursor: clamp(cut.cursor, 0, NORMAL_CURSOR_MAX(cut.line)),
332
+ count: "",
333
+ };
334
+ }
335
+ case "X": {
336
+ if (s.cursor <= 0) return bell(s);
337
+ const from = Math.max(0, s.cursor - count);
338
+ const cut = cutRange(s, from, s.cursor);
339
+ return {
340
+ ...s,
341
+ line: cut.line,
342
+ register: cut.register,
343
+ cursor: cut.cursor,
344
+ count: "",
345
+ };
346
+ }
347
+ case "D": {
348
+ const cut = cutRange(s, s.cursor, line.length);
349
+ return {
350
+ ...s,
351
+ line: cut.line,
352
+ register: cut.register,
353
+ cursor: clamp(cut.cursor, 0, NORMAL_CURSOR_MAX(cut.line)),
354
+ };
355
+ }
356
+ case "C": {
357
+ const cut = cutRange(s, s.cursor, line.length);
358
+ return enterInsert(s, {
359
+ line: cut.line,
360
+ register: cut.register,
361
+ cursor: cut.cursor,
362
+ });
363
+ }
364
+ case "s": {
365
+ if (s.cursor >= line.length) return enterInsert(s);
366
+ const cut = cutRange(
367
+ s,
368
+ s.cursor,
369
+ Math.min(line.length, s.cursor + count),
370
+ );
371
+ return enterInsert(s, {
372
+ line: cut.line,
373
+ register: cut.register,
374
+ cursor: cut.cursor,
375
+ });
376
+ }
377
+ case "S":
378
+ return applyLinewiseOperator({ ...s }, "c");
379
+ case "~": {
380
+ if (s.cursor >= line.length) return bell(s);
381
+ const c = line[s.cursor];
382
+ const flipped = c === c.toLowerCase() ? c.toUpperCase() : c.toLowerCase();
383
+ const next = line.slice(0, s.cursor) + flipped + line.slice(s.cursor + 1);
384
+ return {
385
+ ...s,
386
+ line: next,
387
+ cursor: clamp(s.cursor + 1, 0, NORMAL_CURSOR_MAX(next)),
388
+ };
389
+ }
390
+ case "r":
391
+ return { ...s, awaitChar: { kind: "r" } };
392
+ case "d":
393
+ case "c":
394
+ case "y":
395
+ return { ...s, pending: { op: ch, count }, count: "" };
396
+ case "p": {
397
+ const { text, linewise } = s.register;
398
+ if (!text) return bell(s);
399
+ const at = linewise ? line.length : Math.min(line.length, s.cursor + 1);
400
+ const next = line.slice(0, at) + text + line.slice(at);
401
+ return {
402
+ ...s,
403
+ line: next,
404
+ cursor: clamp(at + text.length - 1, 0, NORMAL_CURSOR_MAX(next)),
405
+ };
406
+ }
407
+ case "P": {
408
+ const { text } = s.register;
409
+ if (!text) return bell(s);
410
+ const at = s.cursor;
411
+ const next = line.slice(0, at) + text + line.slice(at);
412
+ return {
413
+ ...s,
414
+ line: next,
415
+ cursor: clamp(at + text.length - 1, 0, NORMAL_CURSOR_MAX(next)),
416
+ };
417
+ }
418
+ case "f":
419
+ case "F":
420
+ case "t":
421
+ case "T":
422
+ return { ...s, awaitChar: { kind: ch }, count: String(count) };
423
+ default: {
424
+ // Pure motions (h l 0 ^ $ w b e) and Backspace/arrows mapped to h/l.
425
+ let motionCh = ch;
426
+ if (key.name === "backspace") motionCh = "h";
427
+ else if (key.name === "left") motionCh = "h";
428
+ else if (key.name === "right") motionCh = "l";
429
+ const span = resolveMotion(s, motionCh, count, null);
430
+ if (span && !span.fail) {
431
+ return {
432
+ ...s,
433
+ cursor: clamp(span.target, 0, NORMAL_CURSOR_MAX(line)),
434
+ count: "",
435
+ };
436
+ }
437
+ return bell(s);
438
+ }
439
+ }
440
+ }
441
+
442
+ /** Short label for the prompt/status indicator. */
443
+ export function modeLabel(state) {
444
+ return state.mode === "normal" ? "NORMAL" : "INSERT";
445
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * `cc agent --safe-mode` — Claude-Code 2.1.169 parity ("--safe-mode flag
3
- * disables customizations"): one flag flips every customization kill-switch
4
- * so a misbehaving hook / memory file / persona can be isolated by running
5
- * the agent bare.
3
+ * AND CLAUDE_CODE_SAFE_MODE env var disable customizations"): one flag (or an
4
+ * ambient env var) flips every customization kill-switch so a misbehaving
5
+ * hook / memory file / persona can be isolated by running the agent bare.
6
6
  *
7
7
  * Deliberate divergence from Claude Code: settings PERMISSION RULES stay
8
8
  * active — deny rules are a safety surface, and "debug my customizations"
@@ -29,3 +29,17 @@ export function applySafeMode(env = process.env) {
29
29
  }
30
30
  return Object.keys(SAFE_MODE_SWITCHES);
31
31
  }
32
+
33
+ /**
34
+ * Whether safe mode is requested — via the explicit `--safe-mode` flag OR an
35
+ * ambient env var: `CC_SAFE_MODE` (native) or `CLAUDE_CODE_SAFE_MODE`
36
+ * (Claude-Code 2.1.169 portability). Truthy env values: 1 / true / yes / on.
37
+ * @param {{ safeMode?: boolean }} [opts]
38
+ * @param {object} [env=process.env]
39
+ * @returns {boolean}
40
+ */
41
+ export function safeModeRequested(opts = {}, env = process.env) {
42
+ if (opts && opts.safeMode === true) return true;
43
+ const raw = env.CC_SAFE_MODE || env.CLAUDE_CODE_SAFE_MODE;
44
+ return raw != null && /^(1|true|yes|on)$/i.test(String(raw).trim());
45
+ }
@@ -13,6 +13,10 @@
13
13
  * ".claude/skills auto-load" behavior): a repo carrying Claude-Code skills
14
14
  * works in cc unchanged — same SKILL.md format — while native
15
15
  * `.chainlesschain/skills` still wins on name collisions.
16
+ *
17
+ * The bundled (layer 0) built-ins can be hidden via Claude-Code 2.1.169's
18
+ * `disableBundledSkills` (settings.json) or the `CC_DISABLE_BUNDLED_SKILLS`
19
+ * env var — see {@link bundledSkillsDisabled}.
16
20
  */
17
21
 
18
22
  import fs from "fs";
@@ -23,9 +27,45 @@ import { getElectronUserDataDir } from "./paths.js";
23
27
  import { findProjectRoot } from "./project-detector.js";
24
28
  import { findProjectRoot as findGitRoot } from "./project-instructions.js";
25
29
  import { parseSkillMcpServers } from "./skill-mcp.js";
30
+ import settingsLoader from "./settings-loader.cjs";
26
31
 
27
32
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
33
 
34
+ /**
35
+ * Whether the bundled (built-in) skill layer should be hidden — Claude-Code
36
+ * 2.1.169 `disableBundledSkills` parity. Precedence: explicit option >
37
+ * `CC_DISABLE_BUNDLED_SKILLS` env > `.claude/settings.json` `disableBundledSkills`
38
+ * (last layer wins). Best-effort: any read failure leaves bundled skills visible.
39
+ *
40
+ * @param {{ disableBundledSkills?: boolean, env?: object, cwd?: string,
41
+ * settingsFile?: string }} [opts]
42
+ * @returns {boolean}
43
+ */
44
+ export function bundledSkillsDisabled(opts = {}) {
45
+ if (typeof opts.disableBundledSkills === "boolean") {
46
+ return opts.disableBundledSkills;
47
+ }
48
+ const env = (opts.env || process.env).CC_DISABLE_BUNDLED_SKILLS;
49
+ if (env != null && String(env).trim() !== "") {
50
+ return /^(1|true|yes|on)$/i.test(String(env).trim());
51
+ }
52
+ try {
53
+ let disabled = false;
54
+ for (const file of settingsLoader.settingsPaths(
55
+ opts.cwd || process.cwd(),
56
+ opts.settingsFile,
57
+ )) {
58
+ const data = settingsLoader.readSettingsFile(file);
59
+ if (data && typeof data.disableBundledSkills === "boolean") {
60
+ disabled = data.disableBundledSkills; // last (highest) layer wins
61
+ }
62
+ }
63
+ return disabled;
64
+ } catch {
65
+ return false; // never fail skill loading over a settings read
66
+ }
67
+ }
68
+
29
69
  /** Layer names in priority order (lowest → highest) */
30
70
  export const LAYER_NAMES = [
31
71
  "bundled",
@@ -349,13 +389,17 @@ export class CLISkillLoader {
349
389
  * Higher-priority layers override same-name skills from lower layers.
350
390
  * @returns {object[]} Resolved skill list
351
391
  */
352
- loadAll() {
392
+ loadAll(options = {}) {
353
393
  const layers = this.getLayerPaths();
354
394
  const skillMap = new Map();
395
+ // Claude-Code `disableBundledSkills`: hide the built-in layer so only
396
+ // user/workspace/Claude-Code-portable skills load.
397
+ const dropBundled = bundledSkillsDisabled(options);
355
398
 
356
399
  // Process in priority order (lowest first, so higher layers overwrite)
357
400
  for (const { layer, path: layerPath, exists } of layers) {
358
401
  if (!exists) continue;
402
+ if (dropBundled && layer === "bundled") continue;
359
403
  const skills = this._loadFromDir(layerPath, layer);
360
404
  for (const skill of skills) {
361
405
  skillMap.set(skill.id, skill);
@@ -10,6 +10,7 @@
10
10
  * `model`. Body is the prompt template, with substitutions applied at run time:
11
11
  * $ARGUMENTS → all args joined by space
12
12
  * $1 $2 … $9 → positional args (missing → empty string)
13
+ * \$ARGUMENTS \$1 → escaped: kept literal (the backslash is dropped)
13
14
  * !`<cmd>` → run <cmd> in a shell, splice in its stdout (bang exec)
14
15
  * @path → splice in file/dir contents (via file-ref-expander)
15
16
  *
@@ -148,11 +149,20 @@ export function getCommand(name, cwd = process.cwd(), opts = {}) {
148
149
  return discoverCommands(cwd, opts).find((c) => c.name === wanted) || null;
149
150
  }
150
151
 
151
- /** Substitute $ARGUMENTS and $1..$9 in `text`. */
152
+ /**
153
+ * Substitute $ARGUMENTS and $1..$9 in `text`. A backslash escapes the `$` so a
154
+ * literal token survives (Claude-Code 2.1.163 `\$` escape): `\$5` → `$5`,
155
+ * `\$ARGUMENTS` → `$ARGUMENTS`. The negative lookbehind skips an escaped `$`
156
+ * during substitution; a final pass strips the escaping backslash.
157
+ */
152
158
  export function substituteArgs(text, args = []) {
153
159
  const list = Array.isArray(args) ? args : [];
154
- let out = text.replace(/\$ARGUMENTS/g, list.join(" "));
155
- out = out.replace(/\$([1-9])/g, (_, d) => list[Number(d) - 1] ?? "");
160
+ let out = text.replace(/(?<!\\)\$ARGUMENTS/g, list.join(" "));
161
+ out = out.replace(/(?<!\\)\$([1-9])/g, (_, d) => list[Number(d) - 1] ?? "");
162
+ // Unescape: a backslash immediately before $ARGUMENTS or $<digit> was an
163
+ // explicit "keep literal" marker — drop the backslash now that substitution
164
+ // has skipped it. Other backslashes are left untouched.
165
+ out = out.replace(/\\(\$(?:ARGUMENTS|[1-9]))/g, "$1");
156
166
  return out;
157
167
  }
158
168
 
@@ -102,6 +102,22 @@ function formatTokens(n) {
102
102
  return Number((v / 1_000_000).toFixed(1)) + "M";
103
103
  }
104
104
 
105
+ /**
106
+ * Resolve the terminal size to advertise to the status-line command, as
107
+ * positive integers or null. Explicit `columns`/`rows` options win (testing /
108
+ * non-REPL callers); otherwise the live `process.stdout` dimensions are used.
109
+ * Non-TTY (piped) stdout has no dimensions → null (env var simply omitted).
110
+ */
111
+ function terminalSize({ columns, rows } = {}) {
112
+ const out = process.stdout || {};
113
+ const cols = Number.isFinite(columns) ? columns : out.columns;
114
+ const rws = Number.isFinite(rows) ? rows : out.rows;
115
+ return {
116
+ columns: Number.isFinite(cols) && cols > 0 ? Math.floor(cols) : null,
117
+ rows: Number.isFinite(rws) && rws > 0 ? Math.floor(rws) : null,
118
+ };
119
+ }
120
+
105
121
  /** Home-relative compact path: the home dir collapses to "~". */
106
122
  function shortenPath(p) {
107
123
  const cwd = String(p || "");
@@ -159,13 +175,26 @@ function isStatusLineDisabled({ cwd = process.cwd(), settingsFile } = {}) {
159
175
  * Render the status line by running the command with the JSON context on stdin.
160
176
  * Returns the first stdout line (trimmed, ANSI preserved) or null.
161
177
  */
162
- function renderStatusLine(config, context = {}, { cwd, timeout = 5000 } = {}) {
178
+ function renderStatusLine(
179
+ config,
180
+ context = {},
181
+ { cwd, timeout = 5000, columns, rows } = {},
182
+ ) {
163
183
  if (!config || !config.command) return null;
184
+ // Claude-Code parity (2.1.153): hand the command the terminal width/height so
185
+ // a script can right-size its output. process.env is spread first so the
186
+ // command (run via shell) keeps PATH etc.; COLUMNS/LINES are only added when
187
+ // a real size is known (TTY) so a non-TTY run doesn't fake a width.
188
+ const { columns: cols, rows: rws } = terminalSize({ columns, rows });
189
+ const env = { ...process.env };
190
+ if (cols != null) env.COLUMNS = String(cols);
191
+ if (rws != null) env.LINES = String(rws);
164
192
  let res;
165
193
  try {
166
194
  res = _deps.spawnSync(config.command, {
167
195
  input: JSON.stringify(context),
168
196
  cwd: cwd || process.cwd(),
197
+ env,
169
198
  encoding: "utf-8",
170
199
  timeout,
171
200
  shell: true,
@@ -184,11 +213,11 @@ function renderStatusLine(config, context = {}, { cwd, timeout = 5000 } = {}) {
184
213
  }
185
214
 
186
215
  /** Convenience: load + render in one call (used by the REPL each turn). */
187
- function getStatusLine({ cwd, settingsFile, sessionId, model, provider, projectDir, timeout } = {}) {
216
+ function getStatusLine({ cwd, settingsFile, sessionId, model, provider, projectDir, timeout, columns, rows } = {}) {
188
217
  const config = loadStatusLineConfig({ cwd, settingsFile });
189
218
  if (!config) return null;
190
219
  const context = buildContext({ sessionId, model, provider, cwd, projectDir });
191
- return renderStatusLine(config, context, { cwd, timeout });
220
+ return renderStatusLine(config, context, { cwd, timeout, columns, rows });
192
221
  }
193
222
 
194
223
  module.exports = {
@@ -200,5 +229,6 @@ module.exports = {
200
229
  shortenPath,
201
230
  renderDefaultStatusLine,
202
231
  isStatusLineDisabled,
232
+ terminalSize,
203
233
  _deps,
204
234
  };