mulmoterminal 2.8.0 → 2.9.1

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 (159) hide show
  1. package/README.md +44 -13
  2. package/common/fileWriteChannel.ts +6 -0
  3. package/common/sessionAgent.ts +21 -8
  4. package/common/terminalSubmit.ts +29 -0
  5. package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-rGHffQn8.js → abnfDiagram-VRR7QNED-RjiYivmv-6JcEFHs8.js} +1 -1
  6. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-BREDaItQ.js +1 -0
  7. package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-ClBqZt07.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-Dt6beUAa.js} +1 -1
  8. package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CRpyIN7j.js → blockDiagram-677ZJIJ3-BPuAJQRW-DbJk7wmt.js} +1 -1
  9. package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-CP3Nkj32.js → c4Diagram-LMCZKHZV-C0LAqQso-DWJ7HKC8.js} +1 -1
  10. package/dist/assets/channel-7wUqSdoX-CS5090lk.js +1 -0
  11. package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-bBx9h5ar.js → chunk-32BRIVSS-BRrYpgtb-BNBEavcV.js} +1 -1
  12. package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-CJsYFk5u.js → chunk-52WLFC77-BJ-ss3Xr-Dw7VDdHR.js} +1 -1
  13. package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-CySmS4KO.js → chunk-C7G6YPKG-BZEucKEL-BVh_Wzgs.js} +1 -1
  14. package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-Bd6UgUyI.js → chunk-EX3LRPZG-DLS6FBN1-wddVin31.js} +1 -1
  15. package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-NTtO_c6Q.js → chunk-FWX5IMBZ-DHLSFw1H-Dzm5lyFF.js} +2 -2
  16. package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-CZ6o_X29.js → chunk-HOUHSVGY-Bhlt8hXJ-BuAcpLyo.js} +1 -1
  17. package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-CLxX8cQ2.js → chunk-ICXQ74PX-DwgHBX_g-D2eZGz0O.js} +1 -1
  18. package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-LKJvD71K.js → chunk-MOJQB5TN-CMZRaeqt-CAFPabY4.js} +1 -1
  19. package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-DaWsU33j.js → chunk-OGEWGWER-8Qy4a8b5-CYju6v7J.js} +1 -1
  20. package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-zTDqq542.js → chunk-PUDLZKDR-DcrWQRYh-Dir94jUH.js} +1 -1
  21. package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-Dtx2iGhH.js → chunk-Q4XR5HBZ-ZXVGkG8Z-8Yiftf85.js} +1 -1
  22. package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-BgwCMqp5.js → chunk-V7JOEXUC-DmGdheTX-BvC9wePo.js} +1 -1
  23. package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-FXdeVJH3.js → chunk-VAUOI2AC-CS9QJ4yz-Ciy2Wc1T.js} +1 -1
  24. package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-huBR399y.js → chunk-VR4S4FIN-C6a91eNY-BNwIxkVv.js} +1 -1
  25. package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-B9k6Do_6.js → chunk-WYO6CB5R-BlzOfotS-CzBk75nc.js} +1 -1
  26. package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-17x7kiDu.js → chunk-ZGVPDNZ5-BYwxNFTK-6KINEIo8.js} +1 -1
  27. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-DrbnF3u0.js +1 -0
  28. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-DrbnF3u0.js +1 -0
  29. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-DX3AmKtr.js +1 -0
  30. package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--BmU0gwz5.js → cynefinDiagram-TSTJHNR4-DHA9iPo--BuHuPfiX.js} +1 -1
  31. package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-Cf0skqxL.js → dagre-VKFMJZFB--oJKqXBZ-B8zf7Xc1.js} +1 -1
  32. package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-XUWMFcgP.js → diagram-FQU43EPY-CcJCB9bG-CGCT5uJZ.js} +1 -1
  33. package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-B6HfMZd3.js → diagram-G47NLZAW-C_o-WGG1-DKGjGj34.js} +1 -1
  34. package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--BACwoprc.js → diagram-NH7WQ7WH-CXJCYvY--7ufwhy_r.js} +1 -1
  35. package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-zoxV9d9i.js → diagram-OA4YK3LP-BMzeJ87A-CV2UfBf9.js} +1 -1
  36. package/dist/assets/{diagram-WEI45ONY-D_93NKqo-e-eIgWvy.js → diagram-WEI45ONY-D_93NKqo-CgZTR0Dm.js} +1 -1
  37. package/dist/assets/{dist-BHoD_w15.js → dist-CbNR6PAR.js} +1 -1
  38. package/dist/assets/{dist-Cm6rSKic.js → dist-Cmej5EB5.js} +1 -1
  39. package/dist/assets/{dist-BWiUENTZ.js → dist-DmHJIf6m.js} +1 -1
  40. package/dist/assets/{dist-CX4c9UBB.js → dist-uWJ0Qyt6.js} +1 -1
  41. package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-DujO8khX.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-DPEx6v7k.js} +1 -1
  42. package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-DaHESKPI.js → erDiagram-Q63AITRT-CiABnA0s-CZ8VaEZt.js} +1 -1
  43. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-DEo0X0zh.js +1 -0
  44. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-B1sNFZqk.js +1 -0
  45. package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-De_ebWFp.js → ganttDiagram-NO4QXBWP-DQZvdFo1-hARAuFGd.js} +1 -1
  46. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-v8I7oFiq.js +1 -0
  47. package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-C4RETxbL.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-GRz42gkQ.js} +1 -1
  48. package/dist/assets/{index-COmdTh0t.js → index-BqtdqAIq.js} +199 -199
  49. package/dist/assets/index-Fs9P1rOl.css +1 -0
  50. package/dist/assets/info-DKCQHKI2-Dplx5kMp-CtJA9Anl.js +1 -0
  51. package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-DNsURlj_.js → infoDiagram-FWYZ7A6U-7UnoB5AP-6_ENywpQ.js} +1 -1
  52. package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-QyeLiJnO.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-DcdiqKjY.js} +1 -1
  53. package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-CFkMurTe.js → journeyDiagram-5HDEW3XC-c5xIah9o-Jb5j4dIB.js} +1 -1
  54. package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-DsoZE32R.js → kanban-definition-HUTT4EX6-Cnt6loYD-QXnU2uHY.js} +1 -1
  55. package/dist/assets/{lib-DC86gNgf.js → lib-Jez9I7EX.js} +1 -1
  56. package/dist/assets/{line-D7ziSjKi-Dz37a1T7.js → line-D7ziSjKi-C6xSc_fT.js} +1 -1
  57. package/dist/assets/{marp-B-h6KS_6.js → marp-643zocwQ.js} +1 -1
  58. package/dist/assets/{mermaid-parser.core-DxEa8E3F-DteVxl8X.js → mermaid-parser.core-DxEa8E3F-CjD-ngh3.js} +2 -2
  59. package/dist/assets/{mermaid.core-V0OYwIz3-DAwdqlFZ.js → mermaid.core-V0OYwIz3-BuB2lfOL.js} +3 -3
  60. package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-B4GX796F.js → mindmap-definition-LN4V7U3C-CexN3O6L-CAqtxvts.js} +1 -1
  61. package/dist/assets/packet-7NZHBO7P-CQI3flND-CIe7lXlT.js +1 -0
  62. package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-BWBzRiaC.js → pegDiagram-2B236MQR-BiM4G0cW-CpsegN79.js} +1 -1
  63. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-XMBuzZo9.js +1 -0
  64. package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-CsBnZfAY.js → pieDiagram-ENE6RG2P-B_fBBS-2-CVj5FmJ-.js} +1 -1
  65. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-B1j3aQ5d.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-BZdqEKH0.js} +1 -1
  66. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-BKTDPHbI.js +1 -0
  67. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-BqUDb7_4.js +1 -0
  68. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-mbw2glBD.js +1 -0
  69. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-RcP3qRz4.js +1 -0
  70. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-zyLxt_bL.js +1 -0
  71. package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-BjmRh1tU.js → railroadDiagram-RFXS5EU6--lNIlG64-TBu5nqSt.js} +1 -1
  72. package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-UJKUKfTI.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-D_MdSOAJ.js} +1 -1
  73. package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-Bsrgx8aw.js → sankeyDiagram-HTMAVEWB-DlutaXDv-LV1DgYi4.js} +1 -1
  74. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-BpxKPxVk.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-DbxBoWMe.js} +1 -1
  75. package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-90vLBL-t.js → stateDiagram-2N3HPSRC-jf_dXUEU-BRqp2FEy.js} +1 -1
  76. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-qCgJwWlN.js +1 -0
  77. package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-BQj65nKh.js → swimlanes-5IMT3BWC-CYjtALQH-b1Ce-MJ1.js} +1 -1
  78. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-CjXzlY9S.js +8 -0
  79. package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-Brr9I81k.js → timeline-definition-FHXFAJF6-DYJ4oUm8-hoe4WWV4.js} +1 -1
  80. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-CXRd3aAt.js +1 -0
  81. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-BgWzZ7nd.js +1 -0
  82. package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-BFYXaKjS.js → vennDiagram-L72KCM5P-CdKAHoek-D05wTugE.js} +1 -1
  83. package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-DoMXWfpE.js +1 -0
  84. package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-BN4fz_6C.js → wardleyDiagram-EHGQE667-BF6c4_CW-BfIBktNx.js} +1 -1
  85. package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-CJAizw9P.js → xychartDiagram-FW5EYKEG-CGiKngj7-Bn0CDsQy.js} +1 -1
  86. package/dist/index.html +2 -2
  87. package/package.json +1 -1
  88. package/server/agents/rate-limit-probe.spec.ts +59 -5
  89. package/server/agents/rate-limit-probe.ts +6 -1
  90. package/server/agents/rate-limit-routes.ts +2 -2
  91. package/server/agents/rate-limit-store.spec.ts +77 -12
  92. package/server/agents/rate-limit-store.ts +78 -37
  93. package/server/agents/statusline.spec.ts +45 -1
  94. package/server/agents/statusline.ts +28 -0
  95. package/server/backends/feed-summary.ts +1 -1
  96. package/server/backends/remoteHost/collectionPage.ts +10 -4
  97. package/server/backends/remoteHost/getFeed.spec.ts +2 -1
  98. package/server/backends/remoteHost/handlers/deps.ts +43 -0
  99. package/server/backends/remoteHost/handlers/getCollection.ts +17 -0
  100. package/server/backends/remoteHost/handlers/getFeed.ts +22 -0
  101. package/server/backends/remoteHost/handlers/getRemoteView.ts +18 -0
  102. package/server/backends/remoteHost/handlers/getRemoteViewItems.ts +27 -0
  103. package/server/backends/remoteHost/handlers/index.ts +62 -0
  104. package/server/backends/remoteHost/handlers/listAccountingBooks.ts +12 -0
  105. package/server/backends/remoteHost/handlers/listCollections.ts +11 -0
  106. package/server/backends/remoteHost/handlers/listFeeds.ts +18 -0
  107. package/server/backends/remoteHost/handlers/listShortcuts.ts +10 -0
  108. package/server/backends/remoteHost/handlers/listSkills.ts +16 -0
  109. package/server/backends/remoteHost/handlers/mutateRemoteView.ts +29 -0
  110. package/server/backends/remoteHost/handlers/startChat.ts +51 -0
  111. package/server/backends/remoteHost/handlers/terminalSession.ts +63 -0
  112. package/server/backends/remoteHost/handlers.spec.ts +2 -1
  113. package/server/backends/remoteHost/index.ts +2 -1
  114. package/server/backends/remoteHost/terminalInput.spec.ts +166 -19
  115. package/server/backends/remoteHost/terminalInput.ts +24 -2
  116. package/server/backends/remoteView.ts +1 -1
  117. package/server/config/workspace.ts +45 -15
  118. package/server/files/pick-file.ts +5 -1
  119. package/server/files/win-folder-dialog.ts +3 -0
  120. package/server/files/win-powershell-utf8.ts +17 -0
  121. package/server/index.ts +8 -2
  122. package/server/routes/app-routes.ts +4 -4
  123. package/server/routes/dir-routes.ts +30 -22
  124. package/server/routes/routeParams.ts +19 -1
  125. package/server/routes/session-routes.ts +13 -11
  126. package/server/routes/ws-routes.ts +80 -43
  127. package/server/session/antigravity-conversations.ts +71 -0
  128. package/server/session/cost.ts +6 -3
  129. package/server/session/draft-injection.ts +34 -8
  130. package/server/session/last-turn.ts +0 -4
  131. package/server/session/registry.ts +52 -2
  132. package/server/session/session-reads.ts +2 -6
  133. package/server/session/spawn-antigravity.ts +7 -5
  134. package/server/session/spawn-claude.ts +7 -3
  135. package/server/session/tmux-size-sync.ts +0 -2
  136. package/server/skills/mulmoterminal-notify/SKILL.md +13 -0
  137. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-Dg-O9e_V.js +0 -1
  138. package/dist/assets/channel-7wUqSdoX-Cc3ae2uC.js +0 -1
  139. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-gmGwQCKi.js +0 -1
  140. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-gmGwQCKi.js +0 -1
  141. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-C5PuFrCb.js +0 -1
  142. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-D1MuSTYF.js +0 -1
  143. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DT6n3-Ft.js +0 -1
  144. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-CNsR02rf.js +0 -1
  145. package/dist/assets/index-BivYtiDo.css +0 -1
  146. package/dist/assets/info-DKCQHKI2-Dplx5kMp-BeUJlQSg.js +0 -1
  147. package/dist/assets/packet-7NZHBO7P-CQI3flND-C_pMfWi_.js +0 -1
  148. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-bM2FdcNh.js +0 -1
  149. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-BB9wirGo.js +0 -1
  150. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-CbOoELZg.js +0 -1
  151. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-uE3YbEg2.js +0 -1
  152. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-u4DQhs69.js +0 -1
  153. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-B2vytEWW.js +0 -1
  154. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-B-UjCOGA.js +0 -1
  155. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-BMxuxpet.js +0 -8
  156. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DvTQoVxm.js +0 -1
  157. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-DCrIc8uR.js +0 -1
  158. package/dist/assets/wardley-OPB4EBWU-CbHWsb2k--RdJ9IY0.js +0 -1
  159. package/server/backends/remoteHost/handlers.ts +0 -311
package/README.md CHANGED
@@ -74,15 +74,19 @@ npx mulmoterminal@latest # starts on http://localhost:34567 and opens you
74
74
 
75
75
  *Zoomed in, the **cockpit roster** replaces thumbnails with information: every session as a
76
76
  text row — directory, AI summary, your last prompt, the agent's latest reply, a status word,
77
- and the branch's **PR phase** badge. Click a row to swap the enlarged terminal.*
77
+ and the branch's **PR phase** badge. A row whose agent is **waiting on you** rings amber and
78
+ blinks; one that has merely **finished** rings green and stays still (Settings → Waiting rows
79
+ turns the movement off). Click a row to swap the enlarged terminal.*
78
80
 
79
81
  ### What it is, under the hood
80
82
 
81
83
  Each session runs as a real PTY on the server (the agent CLI in a pseudo-terminal) and is
82
84
  streamed to an [xterm.js](https://xtermjs.org/) terminal in the browser over a WebSocket. A
83
85
  sidebar lists every session for the project and reflects, in real time, which are **working**
84
- (the agent is thinking) and which **need attention** (waiting for input, or finished with
85
- output you haven't seen) driven by Claude/Codex activity hooks the server injects per spawn.
86
+ (the agent is thinking, a spinner), which are **waiting on you** (a permission prompt or a
87
+ question an amber dot; nothing proceeds until you answer) and which are **finished with output
88
+ you haven't seen** (a green dot) — driven by Claude/Codex activity hooks the server injects per
89
+ spawn. The horizontal tab bar carries the same two dots.
86
90
 
87
91
  ![Single view — one agent in focus, terminal on the left and a GUI panel on the right](https://raw.githubusercontent.com/receptron/mulmoterminal/main/docs/guide/images/single-view.png)
88
92
 
@@ -392,7 +396,9 @@ today — **Claude Code** (the default), **Codex**, and **Antigravity** (`agy`).
392
396
  `--model`). Antigravity runs on its own WebSocket (`/ws/antigravity`). Like Codex it mints its
393
397
  own conversation id, so the server watches `~/.gemini/antigravity-cli/brain/` (home overridable
394
398
  via `ANTIGRAVITY_HOME`) for the directory the new conversation creates — attributed only when
395
- unambiguous — and cold-resumes it with `--conversation <id>`.
399
+ unambiguous — and cold-resumes it with `--conversation <id>`. That mapping is appended to
400
+ `~/.mulmoterminal/antigravity-conversations.jsonl`, so a conversation is still resumable after
401
+ the server restarts.
396
402
 
397
403
  Its **GUI tools work differently**, because `agy` takes no MCP flag: it reads its servers from
398
404
  `.agents/mcp_config.json` in the working directory. MulmoTerminal writes that file from the
@@ -1028,8 +1034,8 @@ your **own HTTP MCP servers** into the single-view session via Settings → `use
1028
1034
  **Wiki.** The toolbar **Wiki** button opens a read-only browser over `<workspace>/data/wiki/`
1029
1035
  — an **index** (tag-filterable page catalog), rendered **pages** with `[[wiki links]]` and
1030
1036
  backlinks, a **graph** view (pages ranked by references), and a **lint** report (orphans /
1031
- broken links / tag drift). Read-only endpoints: `GET /api/wiki`, `/api/wiki/graph`,
1032
- `/api/wiki/lint`.
1037
+ broken links / tag drift) whose `[[links]]` are clickable too. Read-only endpoints:
1038
+ `GET /api/wiki`, `/api/wiki/graph`, `/api/wiki/lint`.
1033
1039
 
1034
1040
  **Collections.** The toolbar **Collections** button browses the workspace's collection
1035
1041
  "cards" (`@mulmoclaude/collection-plugin`). Running a collection **action** fetches a seed
@@ -1136,13 +1142,32 @@ newest first, including freshly-created sessions that aren't yet written to disk
1136
1142
  - `500 { "error": string }` on an unexpected filesystem error. A missing project
1137
1143
  directory is **not** an error — it yields an empty `sessions` array.
1138
1144
 
1145
+ ### Directories that cannot be used
1146
+
1147
+ Every `?cwd=` — on the terminal sockets and on the read routes alike — names the directory
1148
+ the request is about. When one is named and cannot be used, the server says so instead of
1149
+ quietly answering about the **default workspace** (#1151):
1150
+
1151
+ | Where | What happens |
1152
+ | --- | --- |
1153
+ | `/ws`, `/ws/codex`, `/ws/antigravity`, `/ws/launch`, `/ws/run` | The socket is closed with `{ type: "error", message }`, which the terminal shows as a red banner and does not retry. |
1154
+ | A session that is still running (`?session=` names a live PTY or a surviving tmux session) | **Attaches anyway**, with a warning in the server log. Moving or renaming a directory must not shut you out of an agent that is still working in it — and the cwd reported back comes from the running PTY, not from the request. |
1155
+ | `GET /api/scripts`, `/api/skills`, `/api/dir-config`, `/api/dir-sound`, `/api/git-status`, `/api/pr-phase`, `/api/header`, `/api/sessions`, `/api/codex/sessions`, `/api/session/:id`, `/api/transcript/*`, `/api/cost` | `404 { error, cwd }` — a directory that is not there. |
1156
+ | A `?cwd=` that cannot name a directory at all (relative, or repeated as `?cwd=a&cwd=b`) | `400 { error, cwd }`. |
1157
+
1158
+ A request that names **no** directory is unaffected: `CLAUDE_CWD` is then the answer it
1159
+ asked for. The wording of the refusal is the one a refused spawn already uses, so the same
1160
+ condition reads the same whether it is caught here or by `ptySpawn` itself.
1161
+
1139
1162
  ### HTTP: `GET /api/scripts`
1140
1163
 
1141
1164
  The runnable entries from `<cwd>/script.json` for a cell's chosen directory
1142
- (`?cwd=<dir>`, falling back to `CLAUDE_CWD`); see
1143
- [Scripts (Run menu)](#scripts-run-menu). The resolved `cwd` is echoed back (the
1144
- server may fall back from a bad path), and each entry carries its `index` (the
1145
- position the client sends back to `/ws/run`).
1165
+ (`?cwd=<dir>`, or `CLAUDE_CWD` when none is named); see
1166
+ [Scripts (Run menu)](#scripts-run-menu). The resolved `cwd` is echoed back, and each
1167
+ entry carries its `index` (the position the client sends back to `/ws/run`). A `?cwd=`
1168
+ that names a directory the server cannot enter is answered `404 { error, cwd }` rather
1169
+ than with the default workspace's scripts — see
1170
+ [Directories that cannot be used](#directories-that-cannot-be-used).
1146
1171
 
1147
1172
  ```jsonc
1148
1173
  // GET /api/scripts?cwd=/Users/me/proj
@@ -1160,8 +1185,8 @@ A missing or invalid `script.json` is **not** an error — it yields an empty
1160
1185
 
1161
1186
  ### HTTP: `GET /api/skills`
1162
1187
 
1163
- The Claude skills discoverable for a terminal's chosen directory (`?cwd=<dir>`,
1164
- falling back to `CLAUDE_CWD`) — project scope (`<cwd>/.claude/skills`) plus user scope
1188
+ The Claude skills discoverable for a terminal's chosen directory (`?cwd=<dir>`, or
1189
+ `CLAUDE_CWD` when none is named) — project scope (`<cwd>/.claude/skills`) plus user scope
1165
1190
  (`~/.claude/skills`), deduped by slug (project shadows user), **working-dir skills
1166
1191
  first**; see [Skills (Skill menu)](#skills-skill-menu). A `skills` allowlist in that
1167
1192
  directory's `.mulmoterminal.json` narrows and reorders the result; absent → all. The
@@ -1628,13 +1653,19 @@ src/
1628
1653
  Terminal.vue xterm.js terminal; /ws, /ws/codex, /ws/run
1629
1654
  AppToolbar.vue shared header + toolbar buttons
1630
1655
  GridView.vue, TerminalGrid.vue, TerminalCell.vue, CommandCell.vue, LauncherCell.vue
1656
+ CellLaunchForm.vue what an EMPTY cell shows: dir + target + resume /
1657
+ scripts / worktrees / tool groups
1631
1658
  GuiPanel.vue, PluginFrame.vue GUI panel (Canvas) + Shadow-DOM plugin host
1632
1659
  FilesOverlay.vue file browser + CodeMirror editor
1633
1660
  GitBranchChip.vue, ModelContextBadge.vue header chips / badges
1634
1661
  PrsOverlay.vue cross-repo PRs & Issues
1635
1662
  Wiki*View.vue, Collections*.vue, AccountingOverlay.vue workspace views
1636
1663
  TimelineOverlay.vue, ToolsPane.vue, NotificationBell.vue, RemoteHostControl.vue
1637
- SettingsModal.vue ⚙ settings
1664
+ SettingsModal.vue ⚙ settings — the dialog shell + section order
1665
+ settings/ one file per settings section (theme, sounds,
1666
+ web push, google, PR repos, launchers, quick
1667
+ commands, MCP, cost, shortcuts, …), plus the
1668
+ shared SettingsStepper / SettingsListRow
1638
1669
  composables/ useSessions, usePubSub, useGitStatus, useCost,
1639
1670
  useChatLauncher, useFilesView, useWikiBrowse,
1640
1671
  useCollectionBrowse, useNotifications, useVoiceInput, …
@@ -1,3 +1,5 @@
1
+ import { isRecord } from "./isRecord.js";
2
+
1
3
  // The pub/sub channel carrying "an agent just wrote this file". Both sides decide from it —
2
4
  // the server publishes, the editor listens to know its open file moved under it — so the name
3
5
  // and the payload shape live here rather than as a string literal on each side.
@@ -7,3 +9,7 @@ export const FILE_WRITE_CHANNEL = "file-write";
7
9
  export interface FileWriteEvent {
8
10
  file: string;
9
11
  }
12
+
13
+ /** Both sides go through this rather than re-spelling `{ file: string }`: the publisher pins its
14
+ * payload with `satisfies`, the subscriber narrows an untyped pub/sub frame with it. */
15
+ export const isFileWriteEvent = (data: unknown): data is FileWriteEvent => isRecord(data) && typeof data.file === "string";
@@ -18,12 +18,25 @@ export type TerminalAgent = (typeof TERMINAL_AGENTS)[number];
18
18
  // persisted cell (written before the field existed) means.
19
19
  export const asTerminalAgent = (value: unknown): TerminalAgent => (TERMINAL_AGENTS.some((agent) => agent === value) ? (value as TerminalAgent) : "claude");
20
20
 
21
- // A cell / connection / header button carries its agent as booleans, because Claude is the
22
- // default and the default is the absence of a flag. One function turns them back into the name,
23
- // so the precedence lives in one place rather than in a ternary repeated at every call site —
24
- // and adding a fourth agent is one edit here.
25
- export function terminalAgent(flags: { codex?: boolean | null; antigravity?: boolean | null }): TerminalAgent {
26
- if (flags.antigravity) return "antigravity";
27
- if (flags.codex) return "codex";
28
- return "claude";
21
+ export interface AgentBadge {
22
+ /** Where there is room for it a sidebar row, a header bar. */
23
+ full: string;
24
+ /** For the tab bar, which fits about two characters between the dot and the title. */
25
+ short: string;
26
+ }
27
+
28
+ // Claude has no badge on purpose: it is the default everywhere, so badging it would put one on
29
+ // nearly every row and stop the badge meaning "this one is different". A shell is not an agent
30
+ // and reaches this with no entry, which is the same answer.
31
+ const AGENT_BADGES: Partial<Record<TerminalAgent, AgentBadge>> = {
32
+ codex: { full: "codex", short: "cx" },
33
+ antigravity: { full: "agy", short: "agy" },
34
+ };
35
+
36
+ /** How a session row announces which agent it is running, or null when it wears no badge. The one
37
+ * place the wording lives: the sidebar and the tab bar show the SAME list, so a literal compared
38
+ * in each is how the two came to disagree about a third agent. */
39
+ export function agentBadge(agent: string | null | undefined): AgentBadge | null {
40
+ const known = TERMINAL_AGENTS.find((candidate) => candidate === agent);
41
+ return known ? (AGENT_BADGES[known] ?? null) : null;
29
42
  }
@@ -30,6 +30,35 @@ export const newlineSequence = (mode: TerminalSubmitMode): string => (mode === "
30
30
  // where ESC+CR is Alt+Enter, not submit.
31
31
  export const submitSequenceForAgent = (agent: string | undefined, mode: TerminalSubmitMode): string => (agent === "claude" ? submitSequence(mode) : CR);
32
32
 
33
+ // Text we are about to submit ON the user's behalf, ended so Claude Code has no completion menu
34
+ // open when the submit byte(s) arrive.
35
+ //
36
+ // Claude Code holds a completion menu open while the cursor sits at the end of a completion token
37
+ // — a `/command` or an `@path` — and an open menu consumes the key that should have submitted.
38
+ // Measured against Claude Code 2.1.220:
39
+ //
40
+ // `/help` + CR submits (the one case that always worked)
41
+ // `/help` + ESC CR does NOT submit — the menu's own `escape` binding eats the ESC, so the
42
+ // "esc-cr" submit never lands, however often it is sent
43
+ // `@path` + CR does NOT submit — the file picker takes that CR for itself
44
+ // either + a trailing space submits: the space ends the token, closing the menu
45
+ //
46
+ // So this is NOT specific to the reversed submit mapping (#1142 was reported there, but the
47
+ // `@path` half hits the default mapping too), and it is deliberately not a list of Claude Code's
48
+ // trigger characters: a trigger we failed to enumerate would silently restore a dead end that
49
+ // looks like a broken feature.
50
+ //
51
+ // For auto-submitted text only. A draft the user still edits keeps exactly what was handed over —
52
+ // their own Enter is a keystroke they can retry once they see the menu.
53
+ export const submittableLine = (text: string): string => (/\S$/.test(text) ? `${text} ` : text);
54
+
55
+ // The same Claude-only scoping as submitSequenceForAgent, and for a sharper reason: the completion
56
+ // menu is Claude Code's behaviour, while for every other target the trailing space would be REAL
57
+ // input. A shell line ending in `\` escapes the newline and waits for more (`echo foo\` → a
58
+ // continuation prompt); with a space appended it is an escaped space and runs instead. So a
59
+ // shell/codex/command session's bytes stay exactly what the sender wrote.
60
+ export const submittableLineForAgent = (agent: string | undefined, text: string): string => (agent === "claude" ? submittableLine(text) : text);
61
+
33
62
  // The structural shape of a keydown the override needs. A real DOM KeyboardEvent satisfies
34
63
  // it, and so does a plain test object — no DOM dependency, so this stays testable and shared.
35
64
  export interface EnterKeyEvent {
@@ -1 +1 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-DteVxl8X.js";import"./chunk-WYO6CB5R-BlzOfotS-B9k6Do_6.js";import"./chunk-VAUOI2AC-CS9QJ4yz-FXdeVJH3.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-LKJvD71K.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
1
+ import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-CjD-ngh3.js";import"./chunk-WYO6CB5R-BlzOfotS-CzBk75nc.js";import"./chunk-VAUOI2AC-CS9QJ4yz-Ciy2Wc1T.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-CAFPabY4.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import{f as e}from"./mermaid-parser.core-DxEa8E3F-CjD-ngh3.js";export{e as createArchitectureServices};