mulmoterminal 2.7.0 → 2.9.0
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.
- package/README.md +110 -29
- package/common/bundledSkills.ts +31 -0
- package/common/fileWriteChannel.ts +6 -0
- package/common/launchAgent.ts +1 -1
- package/common/modelPresets.ts +1 -1
- package/common/pastedImageTypes.ts +23 -0
- package/common/sessionAgent.ts +35 -1
- package/common/terminalSubmit.ts +29 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-i7KLnuI-.js → abnfDiagram-VRR7QNED-RjiYivmv-FRF8kLeT.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-DrblApEd.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-CL3Or8CZ.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-D1pPQlOf.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-DFu8zII3.js → blockDiagram-677ZJIJ3-BPuAJQRW-d5-ljc-N.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-CByG3jFJ.js → c4Diagram-LMCZKHZV-C0LAqQso-BGlEwCQt.js} +1 -1
- package/dist/assets/channel-7wUqSdoX-BgZgYLWN.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-Dc3x0Hgh.js → chunk-32BRIVSS-BRrYpgtb-SXiKhjnH.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-D6efVaUm.js → chunk-52WLFC77-BJ-ss3Xr-DKigFAu-.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-CUdIiVHU.js → chunk-C7G6YPKG-BZEucKEL-Cb5wXOXv.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-BH94eoNV.js → chunk-EX3LRPZG-DLS6FBN1-CzTNcARU.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-Dw8tAp6e.js → chunk-FWX5IMBZ-DHLSFw1H-BZvgg4po.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-CEurvfOt.js → chunk-HOUHSVGY-Bhlt8hXJ-DL5Y_3yg.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-DKrDQRbg.js → chunk-ICXQ74PX-DwgHBX_g-BDvSo2KT.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-Ch-WBypm.js → chunk-MOJQB5TN-CMZRaeqt-BPbi0qBZ.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-BfJ1_Ye1.js → chunk-OGEWGWER-8Qy4a8b5-BmITYHk7.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-NggLAE5k.js → chunk-PUDLZKDR-DcrWQRYh-CXK8yMcj.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-DmtrERIM.js → chunk-Q4XR5HBZ-ZXVGkG8Z-SCGIXxMq.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-C8lSwSR3.js → chunk-V7JOEXUC-DmGdheTX-gZgY507i.js} +1 -1
- package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-DNlwzQL1.js → chunk-VAUOI2AC-CS9QJ4yz-BQv37MCq.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-DX2Te6ga.js → chunk-VR4S4FIN-C6a91eNY-ZNRLU9H7.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-BAOXGFjf.js → chunk-WYO6CB5R-BlzOfotS-Bx9N57Se.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-CS40JTYJ.js → chunk-ZGVPDNZ5-BYwxNFTK-Bg7w-yJo.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-0zi7Cgn2.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-0zi7Cgn2.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-Bi7e0oXc.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--DUt8F5SJ.js → cynefinDiagram-TSTJHNR4-DHA9iPo--abCAh98z.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-BhcFRdM7.js → dagre-VKFMJZFB--oJKqXBZ-CA9PU3rn.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-CBj7RNID.js → diagram-FQU43EPY-CcJCB9bG-BgmurMGW.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-DCAfQJQ2.js → diagram-G47NLZAW-C_o-WGG1-CZ6ymH57.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--133061sd.js → diagram-NH7WQ7WH-CXJCYvY--5Ej1o0wj.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-DPt25PXB.js → diagram-OA4YK3LP-BMzeJ87A-Br_Yz55A.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-D_93NKqo-Bb2DJ3v3.js → diagram-WEI45ONY-D_93NKqo-C2UMN3qi.js} +1 -1
- package/dist/assets/{dist-CDpjkjbB.js → dist-C8-kYY50.js} +1 -1
- package/dist/assets/{dist-BmZVepIx.js → dist-DOaExQFB.js} +1 -1
- package/dist/assets/{dist-4eLWDv4V.js → dist-_cuIeZ2Q.js} +1 -1
- package/dist/assets/{dist-Do1AxSN7.js → dist-nsS3Kg7p.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-CqvyVeoa.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-DHqre3sw.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-jrNyKW5P.js → erDiagram-Q63AITRT-CiABnA0s-BO4koo_A.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-BRn6nrMK.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-CnFfxuAw.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-Dz1MAsy4.js → ganttDiagram-NO4QXBWP-DQZvdFo1-BpNj4GGQ.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-65Nmo9BB.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-CV3qv2yf.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-C-DD21zp.js} +1 -1
- package/dist/assets/index-Fs9P1rOl.css +1 -0
- package/dist/assets/index-jKUTVgQT.js +616 -0
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-BRtvzheB.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-BZUR4_mA.js → infoDiagram-FWYZ7A6U-7UnoB5AP-Cjtq7aHi.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-BLGvRDOX.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-CJ7Dm1DX.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-D9zqo5k7.js → journeyDiagram-5HDEW3XC-c5xIah9o-DmEyfShK.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-BwfjKxOx.js → kanban-definition-HUTT4EX6-Cnt6loYD-CEonRig7.js} +1 -1
- package/dist/assets/{lib-BWq7d2p0.js → lib-BP6_kjsB.js} +1 -1
- package/dist/assets/{line-D7ziSjKi-ZOy-21z8.js → line-D7ziSjKi-ZN0lN31b.js} +1 -1
- package/dist/assets/{marp-IdFBx6Kk.js → marp-CUPxjfPv.js} +1 -1
- package/dist/assets/{mermaid-parser.core-DxEa8E3F-C_pDG6kL.js → mermaid-parser.core-DxEa8E3F-C7AoyBVj.js} +2 -2
- package/dist/assets/{mermaid.core-V0OYwIz3--tUcNILl.js → mermaid.core-V0OYwIz3-CXLOt97m.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-B08uU8xg.js → mindmap-definition-LN4V7U3C-CexN3O6L-DOiqhmPh.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-CmU_E5Rs.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-CAmtHOOs.js → pegDiagram-2B236MQR-BiM4G0cW-Dxt7poMV.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-CZEE10z2.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-1Te1k2OW.js → pieDiagram-ENE6RG2P-B_fBBS-2-COQUiiOs.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-Bwmyhd3u.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-B-eWI2XH.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-r15uJDA8.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-Dw_fxGlH.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-CKyTy_kj.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-DKG1VzHP.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-DfP1SMYD.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-B7viF_TQ.js → railroadDiagram-RFXS5EU6--lNIlG64-DeaxglIg.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-C5VA5j5p.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-1CMezKi3.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-DfD-musw.js → sankeyDiagram-HTMAVEWB-DlutaXDv-D7DwjyX1.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-DeYKXJK9.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-CsJUqii0.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-Bq6BSHXK.js → stateDiagram-2N3HPSRC-jf_dXUEU-DzQL5WUm.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-CVaAK8RL.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-BwKcwh8j.js → swimlanes-5IMT3BWC-CYjtALQH-C0XGpuvl.js} +1 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-Dgs9N-of.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-D5NlLypk.js → timeline-definition-FHXFAJF6-DYJ4oUm8-CV7c2zBI.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DBE7pPBH.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-DTrYIIrW.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-BAq00OzF.js → vennDiagram-L72KCM5P-CdKAHoek-D3MiyDV3.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-JRYl6PcW.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-Dvh8PYFx.js → wardleyDiagram-EHGQE667-BF6c4_CW-CJynIPoh.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-C2sC6_Lu.js → xychartDiagram-FW5EYKEG-CGiKngj7-Co6wcyGy.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +14 -8
- package/server/agents/agent-resume.ts +27 -0
- package/server/agents/antigravity-args.ts +39 -0
- package/server/agents/antigravity-mcp.ts +122 -0
- package/server/agents/antigravity-session.ts +68 -0
- package/server/agents/antigravity.ts +7 -0
- package/server/agents/rate-limit-probe.spec.ts +55 -2
- package/server/agents/rate-limit-probe.ts +6 -1
- package/server/agents/registry.ts +2 -0
- package/server/agents/types.ts +1 -1
- package/server/backends/feed-summary.ts +1 -1
- package/server/backends/google.ts +3 -1
- package/server/backends/remoteHost/collectionPage.ts +10 -4
- package/server/backends/remoteHost/getFeed.spec.ts +2 -1
- package/server/backends/remoteHost/handlers/deps.ts +43 -0
- package/server/backends/remoteHost/handlers/getCollection.ts +17 -0
- package/server/backends/remoteHost/handlers/getFeed.ts +22 -0
- package/server/backends/remoteHost/handlers/getRemoteView.ts +18 -0
- package/server/backends/remoteHost/handlers/getRemoteViewItems.ts +27 -0
- package/server/backends/remoteHost/handlers/index.ts +62 -0
- package/server/backends/remoteHost/handlers/listAccountingBooks.ts +12 -0
- package/server/backends/remoteHost/handlers/listCollections.ts +11 -0
- package/server/backends/remoteHost/handlers/listFeeds.ts +18 -0
- package/server/backends/remoteHost/handlers/listShortcuts.ts +10 -0
- package/server/backends/remoteHost/handlers/listSkills.ts +16 -0
- package/server/backends/remoteHost/handlers/mutateRemoteView.ts +29 -0
- package/server/backends/remoteHost/handlers/startChat.ts +51 -0
- package/server/backends/remoteHost/handlers/terminalSession.ts +63 -0
- package/server/backends/remoteHost/handlers.spec.ts +2 -1
- package/server/backends/remoteHost/index.ts +2 -1
- package/server/backends/remoteHost/launchTerminal.ts +2 -2
- package/server/backends/remoteHost/routes.spec.ts +13 -1
- package/server/backends/remoteHost/routes.ts +6 -3
- package/server/backends/remoteHost/terminalInput.spec.ts +166 -19
- package/server/backends/remoteHost/terminalInput.ts +24 -2
- package/server/backends/remoteHost/terminalScreen.ts +59 -2
- package/server/backends/remoteView.ts +1 -1
- package/server/config/header-config.ts +1 -1
- package/server/config/header-context.ts +1 -1
- package/server/config/workspace.ts +45 -15
- package/server/files/dirRequest.ts +2 -1
- package/server/files/pick-file.ts +7 -2
- package/server/files/win-folder-dialog.ts +3 -0
- package/server/files/win-powershell-utf8.ts +17 -0
- package/server/git/worktree-routes.ts +5 -4
- package/server/index.ts +58 -15
- package/server/infra/install-bundled-skills.ts +7 -8
- package/server/infra/tmux-routes.ts +3 -2
- package/server/infra/tmux.ts +65 -1
- package/server/mcp/bridge.mjs +66 -0
- package/server/mcp/broker.ts +43 -8
- package/server/mcp/gui-call-history.ts +96 -0
- package/server/routes/app-routes.ts +33 -6
- package/server/routes/dir-routes.ts +30 -22
- package/server/routes/gui-mcp-routes.ts +6 -0
- package/server/routes/mcp-routes.ts +41 -1
- package/server/routes/plugin-routes.ts +14 -4
- package/server/routes/routeParams.ts +23 -3
- package/server/routes/same-origin-guard.ts +17 -3
- package/server/routes/session-routes.ts +13 -11
- package/server/routes/terminal-ws-path.ts +2 -1
- package/server/routes/tool-routes.ts +14 -4
- package/server/routes/ws-routes.ts +159 -39
- package/server/session/antigravity-conversations.ts +71 -0
- package/server/session/background-chat.ts +24 -10
- package/server/session/command-summary.ts +2 -1
- package/server/session/cost.ts +6 -3
- package/server/session/draft-injection.ts +34 -8
- package/server/session/last-turn.ts +0 -4
- package/server/session/lifecycle.ts +4 -0
- package/server/session/pty-connection.ts +24 -0
- package/server/session/pty-relay.ts +35 -0
- package/server/session/registry.ts +66 -0
- package/server/session/session-reads.ts +4 -7
- package/server/session/spawn-antigravity.ts +85 -0
- package/server/session/spawn-claude.ts +12 -4
- package/server/session/spawn-codex.ts +3 -19
- package/server/session/spawn-deps.ts +2 -0
- package/server/session/spawners.ts +2 -0
- package/server/session/task-push.ts +2 -2
- package/server/session/tmux-size-sync.ts +182 -0
- package/server/session/tool-store.ts +11 -5
- package/server/session/types.ts +4 -0
- package/server/skills/mulmoterminal-config/SKILL.md +110 -521
- package/server/skills/mulmoterminal-dirs/SKILL.md +227 -0
- package/server/skills/{mulmoterminal-config → mulmoterminal-dirs}/palettes.json +1 -1
- package/server/skills/mulmoterminal-header/SKILL.md +165 -0
- package/server/skills/mulmoterminal-keys/SKILL.md +153 -0
- package/server/skills/mulmoterminal-model/SKILL.md +102 -0
- package/server/skills/mulmoterminal-notify/SKILL.md +124 -0
- package/server/skills/mulmoterminal-theme/SKILL.md +128 -0
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-DRxyAOZm.js +0 -1
- package/dist/assets/channel-7wUqSdoX-CryXErAP.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-Bh9HKUjT.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-Bh9HKUjT.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-CNhjzHRd.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-BzmuvEpa.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-D_olhK5u.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-6E2-f_ab.js +0 -1
- package/dist/assets/index-CbJGZlpM.js +0 -616
- package/dist/assets/index-Zds0SlQi.css +0 -1
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-DxJGF13X.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-C1R5Sk6K.js +0 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-fnUCv-1K.js +0 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-DfxsmsH_.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-Cc9H2ZqM.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-CedxYTf_.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-DFUbcN03.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-7OkZDahF.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-cQTUIwVP.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-SzBmSlrT.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-mArgqXY_.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-DcjdNdM5.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-C9OSQ_B-.js +0 -1
- package/server/agents/codex-resume.ts +0 -26
- package/server/backends/remoteHost/handlers.ts +0 -311
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mulmoterminal
|
|
2
2
|
|
|
3
|
-
**Run
|
|
3
|
+
**Run multiple Claude Code and Codex sessions in parallel — and see which one needs you.**
|
|
4
4
|
|
|
5
5
|
A **browser terminal** for **parallel AI coding agents**: several **Claude Code** and **Codex**
|
|
6
6
|
sessions side by side, each in its own cell, with the one that needs you marked in colour. Vibe
|
|
@@ -14,6 +14,9 @@ and a **phone push** reaches you when a turn finishes.
|
|
|
14
14
|
view, everyday workflows, the full feature list, configuration, and mobile push notifications.
|
|
15
15
|
- **ユーザーガイド:** [日本語](https://receptron.github.io/mulmoterminal/guide/ja/) —
|
|
16
16
|
グリッドの使い方・日々のワークフロー・機能一覧・設定・スマホ通知の設定はこちら。
|
|
17
|
+
- **Updates / アップデート情報:** new releases and features are announced **in Japanese** on X —
|
|
18
|
+
新バージョンや新機能のお知らせは X の
|
|
19
|
+
[Singularity Society (@SingularitySoci)](https://x.com/SingularitySoci) で。
|
|
17
20
|
|
|
18
21
|

|
|
19
22
|
|
|
@@ -71,15 +74,19 @@ npx mulmoterminal@latest # starts on http://localhost:34567 and opens you
|
|
|
71
74
|
|
|
72
75
|
*Zoomed in, the **cockpit roster** replaces thumbnails with information: every session as a
|
|
73
76
|
text row — directory, AI summary, your last prompt, the agent's latest reply, a status word,
|
|
74
|
-
and the branch's **PR phase** badge.
|
|
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.*
|
|
75
80
|
|
|
76
81
|
### What it is, under the hood
|
|
77
82
|
|
|
78
83
|
Each session runs as a real PTY on the server (the agent CLI in a pseudo-terminal) and is
|
|
79
84
|
streamed to an [xterm.js](https://xtermjs.org/) terminal in the browser over a WebSocket. A
|
|
80
85
|
sidebar lists every session for the project and reflects, in real time, which are **working**
|
|
81
|
-
(the agent is thinking)
|
|
82
|
-
|
|
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.
|
|
83
90
|
|
|
84
91
|

|
|
85
92
|
|
|
@@ -101,6 +108,17 @@ when the session ends, and any left by a crash are swept at the next start. Up t
|
|
|
101
108
|
file — the same ceiling as a phone attachment. **A session already running when you upgrade
|
|
102
109
|
was launched without that grant**, so drops into it still prompt; new sessions don't.
|
|
103
110
|
|
|
111
|
+
**Pasting a screenshot** — take a screenshot and paste it straight into the terminal
|
|
112
|
+
(`Cmd`/`Ctrl`+`V`). The image is saved to the session's own drop directory — the same place a
|
|
113
|
+
dropped file goes, with the same grant, the same 110 MiB ceiling and the same cleanup when the
|
|
114
|
+
session ends — and its **absolute path** is inserted at the cursor, so the agent can read it.
|
|
115
|
+
Unlike a drop, this does not need the browser to expose a path — the bytes are on the
|
|
116
|
+
clipboard — so it also covers Chrome, where dropping a file cannot insert a path. It works
|
|
117
|
+
wherever the browser puts the image on the clipboard as `image/png`, `image/jpeg`,
|
|
118
|
+
`image/gif`, or `image/webp`. Anything else is left to the terminal's own paste handling,
|
|
119
|
+
exactly as before — including a paste that carries **plain text** next to the image, which
|
|
120
|
+
copying from a web page usually does, so that pasting text keeps working.
|
|
121
|
+
|
|
104
122
|
<a id="clicking-a-file-path"></a>
|
|
105
123
|
|
|
106
124
|
**Clicking a file path** — the other direction. A path an agent *prints* becomes a link, and
|
|
@@ -215,7 +233,10 @@ and every CLI in the table above), seeds the launcher's **directory
|
|
|
215
233
|
presets** from the projects in your Claude Code history, and writes `~/.mulmoterminal/config.json`.
|
|
216
234
|
It's **idempotent** — re-run it any time to refresh the presets; it overwrites the managed parts
|
|
217
235
|
and keeps your other settings. When `claude` is installed it can hand off to the
|
|
218
|
-
`/mulmoterminal-config` skill for interactive tweaks
|
|
236
|
+
`/mulmoterminal-config` skill for interactive tweaks — it routes to the one that owns what you
|
|
237
|
+
want to change. Once the app is up you can also reach them from **Settings**: each section that a
|
|
238
|
+
skill can write ends in a button that starts that skill in a new session, which is how the settings
|
|
239
|
+
with no UI (a theme of your own, `keymap`) get written without hand-editing JSON.
|
|
219
240
|
|
|
220
241
|
**Google account (optional).** Link a Google account to enable the chat's `google` tool and the
|
|
221
242
|
phone's `google.calendar.*` commands: read/create events on any calendar (not just your primary),
|
|
@@ -352,12 +373,12 @@ SDK; we drive the real interactive CLI and relay its TTY over the WebSocket.
|
|
|
352
373
|
|
|
353
374
|
---
|
|
354
375
|
|
|
355
|
-
## Agents: Claude &
|
|
376
|
+
## Agents: Claude, Codex & Antigravity
|
|
356
377
|
|
|
357
378
|
MulmoTerminal drives **interactive coding-agent CLIs**, not just Claude. An
|
|
358
379
|
`AgentAdapter` seam abstracts the per-agent bits (which binary to spawn, how it resumes)
|
|
359
|
-
so the PTY, grid, persistence, and GUI-panel plumbing stay shared.
|
|
360
|
-
today — **Claude Code** (the default) and **
|
|
380
|
+
so the PTY, grid, persistence, and GUI-panel plumbing stay shared. Three adapters ship
|
|
381
|
+
today — **Claude Code** (the default), **Codex**, and **Antigravity** (`agy`).
|
|
361
382
|
|
|
362
383
|
- **Claude** — spawned as `claude` (override with `CLAUDE_BIN`). The server passes
|
|
363
384
|
`--session-id <uuid>`, so it knows the live session's id even before its transcript
|
|
@@ -371,10 +392,36 @@ today — **Claude Code** (the default) and **Codex**.
|
|
|
371
392
|
`CODEX_HOME`) and maps the new rollout to the session — attributed only when it's
|
|
372
393
|
unambiguous, never by "newest wins". Resume reattaches a live PTY, adopts a surviving
|
|
373
394
|
tmux session, or cold-resumes the rollout id.
|
|
395
|
+
- **Antigravity** — spawned as `agy` (override with `ANTIGRAVITY_BIN`; `ANTIGRAVITY_MODEL` sets
|
|
396
|
+
`--model`). Antigravity runs on its own WebSocket (`/ws/antigravity`). Like Codex it mints its
|
|
397
|
+
own conversation id, so the server watches `~/.gemini/antigravity-cli/brain/` (home overridable
|
|
398
|
+
via `ANTIGRAVITY_HOME`) for the directory the new conversation creates — attributed only when
|
|
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.
|
|
402
|
+
|
|
403
|
+
Its **GUI tools work differently**, because `agy` takes no MCP flag: it reads its servers from
|
|
404
|
+
`.agents/mcp_config.json` in the working directory. MulmoTerminal writes that file from the
|
|
405
|
+
directory's [Canvas switches](#wiki-collections--the-gui-panel) — the same switches Claude's cells read — so
|
|
406
|
+
one switch serves every agent, and rewrites it whenever a switch flips or an agy session starts.
|
|
407
|
+
Servers in it that MulmoTerminal did not write are left alone, the file is removed once no group
|
|
408
|
+
is on, and it is kept out of your `git status` through `.git/info/exclude` — a local switch on a
|
|
409
|
+
local machine, so it never reaches a diff or your team. The entry runs `server/mcp/bridge.mjs`, a stdio-to-HTTP shim onto the same in-process
|
|
410
|
+
GUI MCP server the other agents call. The **session id is never written into that file** — it is
|
|
411
|
+
per directory and shared by every session running there — and reaches the bridge through the agy
|
|
412
|
+
process's own environment instead.
|
|
413
|
+
|
|
414
|
+
The Docker sandbox does NOT cover agy: it stays claude-only until `buildDockerRunArgs` is
|
|
415
|
+
generalized (see `plans/feat-multi-agent-support.md`, PR#5). `agy` also ships as a standalone
|
|
416
|
+
binary rather than an npm package, so the sandbox image has nothing to install.
|
|
374
417
|
|
|
375
418
|
**Choosing an agent.** The single view has a **New Codex session** button; each grid
|
|
376
|
-
cell's launch form
|
|
377
|
-
choice is remembered).
|
|
419
|
+
cell's launch form carries a **Claude / Codex / Antigravity / Shell** toggle, and the
|
|
420
|
+
Collections browser a **Claude / Codex / Antigravity** one (your choice is remembered).
|
|
421
|
+
**Shell** is not an agent: it runs your OS default shell (`$SHELL`, or `/bin/sh`) in the
|
|
422
|
+
chosen directory, with nothing to install and nothing to configure. It starts a launcher
|
|
423
|
+
cell, so it has no model, no MCP registration, and no worktree — those rows disappear
|
|
424
|
+
while it is picked.
|
|
378
425
|
|
|
379
426
|
**Other models.**
|
|
380
427
|
Claude Code can run against any **Anthropic-compatible** backend (OpenRouter, Moonshot, a
|
|
@@ -501,7 +548,14 @@ the `claude` / `codex` sessions themselves.
|
|
|
501
548
|
| `CODEX_BIN` | `codex` | The Codex CLI binary to spawn. |
|
|
502
549
|
| `CODEX_MODEL`| codex default | Model passed to Codex as `--model` (unset = Codex's own default). |
|
|
503
550
|
| `CODEX_HOME` | `~/.codex` | Codex home — where its session rollouts and MulmoTerminal-mirrored skills live. |
|
|
551
|
+
| `ANTIGRAVITY_BIN` | `agy` | The Antigravity CLI binary to spawn. |
|
|
552
|
+
| `ANTIGRAVITY_MODEL` | agy default | Model passed to Antigravity as `--model` (unset = agy's own default). |
|
|
553
|
+
| `ANTIGRAVITY_HOME` | `~/.gemini/antigravity-cli` | Antigravity home directory containing session brain storage. |
|
|
504
554
|
| `MULMOTERMINAL_HOME` | `~/.mulmoterminal` | Root for managed **git worktrees**. |
|
|
555
|
+
| `CLAUDE_CONFIG_DIR` | `~` | Claude Code's own config directory. `.claude.json` lives **inside** it, so relocating your Claude Code config moves that file too — MulmoTerminal reads it to tell whether the per-project GUI MCP server is registered (`server/infra/gui-mcp-registration.ts`). Leave it unset and `~/.claude.json` is used. |
|
|
556
|
+
| `MULMOCLAUDE_WORKSPACE_PATH` | `~/mulmoclaude` | Where the managed MulmoClaude workspace lives. MulmoTerminal seeds presets/helps **only** into this directory, so launching in an arbitrary project never writes them there (`server/backends/workspaceSetup.ts`). Set it to the same value MulmoClaude uses. |
|
|
557
|
+
| `MULMOTERMINAL_NO_SKILL_INSTALL` | unset | Set to any value to skip installing the bundled skills (`mulmoterminal-config` and the `-dirs` / `-theme` / `-header` / `-keys` / `-model` / `-notify` / `-bug-report` / `-decisions` family) into `~/.claude/skills/` and the Codex skills root on startup. |
|
|
558
|
+
| `GEMINI_IMAGE_MODEL` | `gemini-3.1-flash-image-preview` | Model used for image generation (needs `GEMINI_API_KEY`). The default is a **preview** model Google schedules for retirement around mid-2026, so pin a stable one here (e.g. `gemini-2.5-flash-image`) rather than waiting for a code change. |
|
|
505
559
|
| `WAIT_REAP_GRACE_MS` | `1800000` | How long a **waiting** background session is kept before it's auto-reaped (`0` or negative = never). |
|
|
506
560
|
|
|
507
561
|
The Docker-sandbox variables (`MULMOTERMINAL_SANDBOX`, `MULMOTERMINAL_SANDBOX_IMAGE`,
|
|
@@ -531,7 +585,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
531
585
|
| `soundKinds` | Which moments beep — see [Notification sounds](#notification-sounds). Defaults to `["finished","waiting"]`; the other kinds are opt-in. |
|
|
532
586
|
| `sounds` | Per-kind sound: `{ "waiting": "preset:coin" }`. A `preset:<id>` reference or an absolute path; a kind with no entry falls back to `soundFile`. |
|
|
533
587
|
| `prRepos` | `owner/repo` entries whose open PRs/issues the cross-repo **PRs & Issues** view aggregates (via your `gh` login). |
|
|
534
|
-
| `launchers` | `{ label, command }` entries offered in a grid cell's launcher besides
|
|
588
|
+
| `launchers` | `{ label, command }` entries offered in a grid cell's launcher besides the agents — any interactive command. A plain shell needs no entry: the launch form's **Shell** toggle opens `$SHELL` unconfigured. |
|
|
535
589
|
| `quickCommands` | `{ label, text, agents? }` phrases the **phone** offers as chips on a session's terminal view. Tapping one puts `text` in the input box; it is not sent until you press send. `agents` (`"claude"` / `"codex"` / `"shell"`) scopes a chip to session kinds — omit it to offer the chip everywhere. Empty by default. |
|
|
536
590
|
| `userMcpServers` | `{ id, url }` HTTP MCP servers merged into the **single-view** Claude session's `--mcp-config` (a `localhost` URL is reached over `host.docker.internal` in the Docker sandbox). Takes effect on the next session. |
|
|
537
591
|
| `buttons` | Header action buttons — see [Header buttons](#header-buttons). Omit to keep the defaults; set to replace them. |
|
|
@@ -569,8 +623,8 @@ agent), or `"open"`. An `open` button targets one of `url` / `reveal` (OS file m
|
|
|
569
623
|
opened next to the current one) / `pr: true` (open the current branch's PR — the button is hidden when
|
|
570
624
|
there's no open PR) / `pickFile: true` (OS file dialog → insert the path).
|
|
571
625
|
`${dir}`, `${branch}`, `${repo}`, … substitute live context, and `when` (e.g. `"isGitRepo"`) gates
|
|
572
|
-
visibility. The `/mulmoterminal-
|
|
573
|
-
merge over the global ones by `id
|
|
626
|
+
visibility. The `/mulmoterminal-header` skill writes a valid config interactively; per-dir buttons
|
|
627
|
+
merge over the global ones by `id`, while `chips` replace the global list wholesale.
|
|
574
628
|
|
|
575
629
|
### Notification sounds
|
|
576
630
|
|
|
@@ -666,7 +720,7 @@ malformed file is ignored.
|
|
|
666
720
|
"colors": { "background": "#190a23", "cursor": "#ff2e63" }, // per-key palette overrides
|
|
667
721
|
"fontSize": 16, // terminal font size in px (8–32); overrides Settings
|
|
668
722
|
"fontFamily": "'Cica', monospace", // terminal font stack; overrides the global config
|
|
669
|
-
"orderPriority": 10, // rank in the grid's "priority"
|
|
723
|
+
"orderPriority": 10, // rank in the grid's "priority" order and the launcher chips (lowest first)
|
|
670
724
|
"sound": "./.mulmoterminal/alert.mp3", // attention sound, RELATIVE to this directory
|
|
671
725
|
"sounds": { "command-failed": "preset:gong" }, // per-notification-kind override
|
|
672
726
|
"appendSystemPrompt": false // no closing summary here; omit to follow the global setting
|
|
@@ -690,7 +744,7 @@ malformed file is ignored.
|
|
|
690
744
|
| `theme` | xterm palette for terminals in this directory (one of the built-in theme ids). |
|
|
691
745
|
| `colors` | Per-key xterm palette overrides applied on top of `theme` (or the app theme when `theme` is unset). Keys are xterm `ITheme` names (`background`, `foreground`, `cursor`, `selectionBackground`, the 16 ANSI colors, …); values are hex (`#rgb` / `#rrggbb` / `#rrggbbaa`). Unknown keys / bad values are dropped. |
|
|
692
746
|
| `fontSize` | Terminal font size in px for this directory (8–32), overriding the Settings value. A size outside the range is clamped; a non-number is ignored. Changing it re-fits the terminal, so the PTY learns the new width — unlike browser zoom, which leaves the two disagreeing. |
|
|
693
|
-
| `orderPriority` | This directory's rank in the grid's **priority** ordering — the third mode on the toolbar's ordering button, next to auto (attention-first) and manual (the move buttons). Any integer, **lowest first**; negatives are allowed. Directories that set nothing sort last, keeping their existing order, so adding the key to one project doesn't shuffle the rest.
|
|
747
|
+
| `orderPriority` | This directory's rank in the grid's **priority** ordering — the third mode on the toolbar's ordering button, next to auto (attention-first) and manual (the move buttons). Any integer, **lowest first**; negatives are allowed. Directories that set nothing sort last, keeping their existing order, so adding the key to one project doesn't shuffle the rest. The grid reads it in **priority** mode only; the launcher's directory chips always sort by it, so a project sits in the same place on both. |
|
|
694
748
|
| `fontFamily` | CSS font-family stack for this directory's terminals, overriding the global `fontFamily`. Use the names as your OS lists them (`"'Cica', 'MS Gothic', monospace"`). An unusable stack is ignored whole rather than half-applied; `monospace` is appended if you name no generic family. Prefer fonts whose fullwidth glyphs are exactly twice the Latin width, or box-drawing frames tear. |
|
|
695
749
|
| `sound` | Attention sound for this directory's sessions, a path **relative to the directory** (served at `GET /api/dir-sound`). The fallback for every kind. |
|
|
696
750
|
| `sounds` | Per-kind override of `sound`: `{ "command-failed": "preset:gong" }`. Each value is a `preset:<id>` or a directory-relative path, under the same confinement. |
|
|
@@ -701,7 +755,7 @@ malformed file is ignored.
|
|
|
701
755
|
paths and any `../` that escapes the directory are rejected, and the path is never taken from the
|
|
702
756
|
HTTP request, so an opened project can't point the player at arbitrary files.
|
|
703
757
|
**When changes take effect.** A write made *through Claude's tools* — which includes the
|
|
704
|
-
`mulmoterminal-
|
|
758
|
+
`mulmoterminal-dirs` skill — applies **live**: the tool hook that reports the write doubles
|
|
705
759
|
as the reload signal, so colors, palette, font size and grid order update without reopening
|
|
706
760
|
anything. There is no filesystem watcher, so an edit made **outside** a session (your own
|
|
707
761
|
editor) is picked up when the terminal is next opened.
|
|
@@ -759,8 +813,9 @@ alongside the Claude sessions. Scripts are **per-directory**: the cell reads the
|
|
|
759
813
|
different projects' scripts.
|
|
760
814
|
|
|
761
815
|
The same launcher also has an **or launch** row for your configured **launch commands**
|
|
762
|
-
—
|
|
763
|
-
|
|
816
|
+
— any interactive command — set in Settings (⚙) → **Launch commands** as
|
|
817
|
+
`{ label, command }` (e.g. `htop` → `htop`, `Codex` → `codex`). A plain shell needs no
|
|
818
|
+
entry here: the launch form's **Shell** toggle already opens `$SHELL`. Unlike
|
|
764
819
|
a one-shot script, a launcher runs as a **persistent terminal in the cell's directory**:
|
|
765
820
|
it survives grid page switches and reconnects, and its dot shows running vs. exited (it
|
|
766
821
|
has no Claude hooks, so no blocked/done states).
|
|
@@ -905,7 +960,7 @@ repo without colliding. Worktrees live under `~/.mulmoterminal/worktrees/` (over
|
|
|
905
960
|
|
|
906
961
|

|
|
907
962
|
|
|
908
|
-
*Every empty grid cell shows this launch form: toggle **Claude / Codex**, type a **working directory** (frequent ones autocomplete from your presets), or — in a git repo — name a task under **OR ISOLATE IN A WORKTREE** and hit **+ New worktree** to start the agent on its own isolated branch. **OR LAUNCH**
|
|
963
|
+
*Every empty grid cell shows this launch form: toggle **Claude / Codex / Antigravity / Shell**, type a **working directory** (frequent ones autocomplete from your presets), or — in a git repo — name a task under **OR ISOLATE IN A WORKTREE** and hit **+ New worktree** to start the agent on its own isolated branch. **Shell** runs your OS default shell there instead of an agent; **OR LAUNCH** runs one of your configured launch commands.*
|
|
909
964
|
|
|
910
965
|
A worktree cell's header carries a **diff badge** (`+<commits> ●<dirty>`); click it for a
|
|
911
966
|
**Changes vs `<base>`** panel (file list + patch) with actions:
|
|
@@ -979,8 +1034,8 @@ your **own HTTP MCP servers** into the single-view session via Settings → `use
|
|
|
979
1034
|
**Wiki.** The toolbar **Wiki** button opens a read-only browser over `<workspace>/data/wiki/`
|
|
980
1035
|
— an **index** (tag-filterable page catalog), rendered **pages** with `[[wiki links]]` and
|
|
981
1036
|
backlinks, a **graph** view (pages ranked by references), and a **lint** report (orphans /
|
|
982
|
-
broken links / tag drift). Read-only endpoints:
|
|
983
|
-
`/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`.
|
|
984
1039
|
|
|
985
1040
|
**Collections.** The toolbar **Collections** button browses the workspace's collection
|
|
986
1041
|
"cards" (`@mulmoclaude/collection-plugin`). Running a collection **action** fetches a seed
|
|
@@ -1087,13 +1142,32 @@ newest first, including freshly-created sessions that aren't yet written to disk
|
|
|
1087
1142
|
- `500 { "error": string }` on an unexpected filesystem error. A missing project
|
|
1088
1143
|
directory is **not** an error — it yields an empty `sessions` array.
|
|
1089
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
|
+
|
|
1090
1162
|
### HTTP: `GET /api/scripts`
|
|
1091
1163
|
|
|
1092
1164
|
The runnable entries from `<cwd>/script.json` for a cell's chosen directory
|
|
1093
|
-
(`?cwd=<dir>`,
|
|
1094
|
-
[Scripts (Run menu)](#scripts-run-menu). The resolved `cwd` is echoed back
|
|
1095
|
-
|
|
1096
|
-
|
|
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).
|
|
1097
1171
|
|
|
1098
1172
|
```jsonc
|
|
1099
1173
|
// GET /api/scripts?cwd=/Users/me/proj
|
|
@@ -1111,8 +1185,8 @@ A missing or invalid `script.json` is **not** an error — it yields an empty
|
|
|
1111
1185
|
|
|
1112
1186
|
### HTTP: `GET /api/skills`
|
|
1113
1187
|
|
|
1114
|
-
The Claude skills discoverable for a terminal's chosen directory (`?cwd=<dir>`,
|
|
1115
|
-
|
|
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
|
|
1116
1190
|
(`~/.claude/skills`), deduped by slug (project shadows user), **working-dir skills
|
|
1117
1191
|
first**; see [Skills (Skill menu)](#skills-skill-menu). A `skills` allowlist in that
|
|
1118
1192
|
directory's `.mulmoterminal.json` narrows and reorders the result; absent → all. The
|
|
@@ -1567,7 +1641,8 @@ server/
|
|
|
1567
1641
|
mcp/ per-session MCP broker
|
|
1568
1642
|
backends/ wiki, collections, feeds, accounting, notifier,
|
|
1569
1643
|
translation, whisper, remote-host, html, files
|
|
1570
|
-
skills/ bundled skills
|
|
1644
|
+
skills/ bundled skills: mulmoterminal-config (entry point + audit), -dirs, -theme,
|
|
1645
|
+
-header, -keys, -model, -notify, -bug-report, -decisions
|
|
1571
1646
|
fix-pty-perms.js postinstall: fixes node-pty binary permissions
|
|
1572
1647
|
src/
|
|
1573
1648
|
App.vue Layout; owns the active session + single/grid view
|
|
@@ -1578,13 +1653,19 @@ src/
|
|
|
1578
1653
|
Terminal.vue xterm.js terminal; /ws, /ws/codex, /ws/run
|
|
1579
1654
|
AppToolbar.vue shared header + toolbar buttons
|
|
1580
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
|
|
1581
1658
|
GuiPanel.vue, PluginFrame.vue GUI panel (Canvas) + Shadow-DOM plugin host
|
|
1582
1659
|
FilesOverlay.vue file browser + CodeMirror editor
|
|
1583
1660
|
GitBranchChip.vue, ModelContextBadge.vue header chips / badges
|
|
1584
1661
|
PrsOverlay.vue cross-repo PRs & Issues
|
|
1585
1662
|
Wiki*View.vue, Collections*.vue, AccountingOverlay.vue workspace views
|
|
1586
1663
|
TimelineOverlay.vue, ToolsPane.vue, NotificationBell.vue, RemoteHostControl.vue
|
|
1587
|
-
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
|
|
1588
1669
|
composables/ useSessions, usePubSub, useGitStatus, useCost,
|
|
1589
1670
|
useChatLauncher, useFilesView, useWikiBrowse,
|
|
1590
1671
|
useCollectionBrowse, useNotifications, useVoiceInput, …
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The skills MulmoTerminal ships, named once for both sides.
|
|
2
|
+
//
|
|
3
|
+
// Shared rather than mirrored because BOTH sides decide from these: the server COPIES these
|
|
4
|
+
// directories into the user's skills roots at boot, and the client LAUNCHES one by name from the
|
|
5
|
+
// Settings section that owns it. A name that lives in two places is a name that can drift, and it
|
|
6
|
+
// did — the grid view and the single view each spelled the appearance skill out, a rename updated
|
|
7
|
+
// one of them, and the same button then launched a different skill depending on which view you
|
|
8
|
+
// pressed it from. Each half was self-consistent, so nothing failed (Codex review on #1103).
|
|
9
|
+
// `BundledSkillName` is what keeps the Settings buttons honest now: a slug naming no shipped skill
|
|
10
|
+
// is a type error rather than an agent replying that it can't find one.
|
|
11
|
+
|
|
12
|
+
/** Every skill directory under `server/skills/`, in the order the installer walks them. A spec
|
|
13
|
+
* pins this list against what is actually on disk: adding a directory is not what ships it, and
|
|
14
|
+
* one nobody lists is copied nowhere with no error at any point. */
|
|
15
|
+
export const BUNDLED_SKILL_NAMES = [
|
|
16
|
+
"mulmoterminal-config",
|
|
17
|
+
"mulmoterminal-dirs",
|
|
18
|
+
"mulmoterminal-theme",
|
|
19
|
+
"mulmoterminal-header",
|
|
20
|
+
"mulmoterminal-keys",
|
|
21
|
+
"mulmoterminal-model",
|
|
22
|
+
"mulmoterminal-notify",
|
|
23
|
+
"mulmoterminal-bug-report",
|
|
24
|
+
"mulmoterminal-decisions",
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export type BundledSkillName = (typeof BUNDLED_SKILL_NAMES)[number];
|
|
28
|
+
|
|
29
|
+
/** The skill that owns `<project>/.mulmoterminal.json` — the one the generated JSON Schema ships
|
|
30
|
+
* beside, so the schema sits with the skill that actually writes that file. */
|
|
31
|
+
export const DIR_CONFIG_SKILL = "mulmoterminal-dirs";
|
|
@@ -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";
|
package/common/launchAgent.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Deliberately NOT the configured `launchers` list: those are arbitrary user commands, and
|
|
5
5
|
// the phone picks from a fixed set the host can name. "shell" is the OS default shell, the
|
|
6
6
|
// same thing the header's new-terminal button opens.
|
|
7
|
-
export const LAUNCH_AGENTS = ["shell", "claude", "codex"] as const;
|
|
7
|
+
export const LAUNCH_AGENTS = ["shell", "claude", "codex", "antigravity"] as const;
|
|
8
8
|
|
|
9
9
|
export type LaunchAgent = (typeof LAUNCH_AGENTS)[number];
|
|
10
10
|
|
package/common/modelPresets.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// backend, and how well each one actually drove Claude Code when it was measured.
|
|
3
3
|
//
|
|
4
4
|
// Shared across the build boundary (like themeIds / dirChrome): the launch picker reads
|
|
5
|
-
// it in the browser, and the mulmoterminal-
|
|
5
|
+
// it in the browser, and the mulmoterminal-model skill reads it to know what it may
|
|
6
6
|
// suggest. One list, one source of truth.
|
|
7
7
|
//
|
|
8
8
|
// WHY THE NUMBERS: a model that answers a plain prompt can still be unusable, because the
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The image types a pasted screenshot can be saved as. BOTH sides decide from this list, so
|
|
2
|
+
// it lives here rather than in either of them: the client only takes a paste away from xterm
|
|
3
|
+
// when the server will accept it, and the server only writes a type it can name a file for.
|
|
4
|
+
// Were the two to drift, a paste would vanish from the terminal and then fail to upload —
|
|
5
|
+
// the user would see nothing happen at all.
|
|
6
|
+
//
|
|
7
|
+
// SVG is deliberately absent: it is a script-bearing document, not a screenshot, and nothing
|
|
8
|
+
// puts one on the clipboard as an image.
|
|
9
|
+
const EXTENSION_BY_MIME: ReadonlyMap<string, string> = new Map([
|
|
10
|
+
["image/png", ".png"],
|
|
11
|
+
["image/jpeg", ".jpg"],
|
|
12
|
+
["image/gif", ".gif"],
|
|
13
|
+
["image/webp", ".webp"],
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
/** The file extension for a pasteable image type, or null when it isn't one. */
|
|
17
|
+
export function extensionForImageMime(mime: string): string | null {
|
|
18
|
+
return EXTENSION_BY_MIME.get(mime.toLowerCase().trim()) ?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isPasteableImageMime(mime: string): boolean {
|
|
22
|
+
return extensionForImageMime(mime) !== null;
|
|
23
|
+
}
|
package/common/sessionAgent.ts
CHANGED
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
// What a remote-host session is running. The server decides it (from the PtyEntry, or from
|
|
2
2
|
// what tmux says is in the pane) and the settings UI offers it as the scope for a quick
|
|
3
3
|
// command, so the list of kinds is a value both sides read.
|
|
4
|
-
export const SESSION_AGENTS = ["claude", "codex", "shell"] as const;
|
|
4
|
+
export const SESSION_AGENTS = ["claude", "codex", "antigravity", "shell"] as const;
|
|
5
5
|
|
|
6
6
|
// Null anywhere this appears means the host cannot tell — a session that outlived a restart
|
|
7
7
|
// exists only in tmux, and nothing recorded what launched it. That is distinct from "shell".
|
|
8
8
|
export type SessionAgent = (typeof SESSION_AGENTS)[number];
|
|
9
|
+
|
|
10
|
+
// The agents a terminal can be LAUNCHED as, which is the session kinds minus "shell" (a shell is
|
|
11
|
+
// a launcher, not an agent). Claude is first because it is the default everywhere below.
|
|
12
|
+
export const TERMINAL_AGENTS = ["claude", "codex", "antigravity"] as const;
|
|
13
|
+
|
|
14
|
+
export type TerminalAgent = (typeof TERMINAL_AGENTS)[number];
|
|
15
|
+
|
|
16
|
+
// A remembered agent — a persisted grid cell, a localStorage toggle, a query param — read back.
|
|
17
|
+
// Validated rather than cast: anything unrecognised is Claude, which is also what an older
|
|
18
|
+
// persisted cell (written before the field existed) means.
|
|
19
|
+
export const asTerminalAgent = (value: unknown): TerminalAgent => (TERMINAL_AGENTS.some((agent) => agent === value) ? (value as TerminalAgent) : "claude");
|
|
20
|
+
|
|
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;
|
|
42
|
+
}
|
package/common/terminalSubmit.ts
CHANGED
|
@@ -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-
|
|
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-C7AoyBVj.js";import"./chunk-WYO6CB5R-BlzOfotS-Bx9N57Se.js";import"./chunk-VAUOI2AC-CS9QJ4yz-BQv37MCq.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-BPbi0qBZ.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-C7AoyBVj.js";export{e as createArchitectureServices};
|