mulmoterminal 4.4.0 → 4.5.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.
- package/README.md +171 -69
- package/bin/mulmoterminal.js +40 -1
- package/common/chromeFromColor.ts +121 -0
- package/common/contrast.ts +41 -0
- package/common/customAgents.ts +104 -0
- package/common/dirConfigSource.ts +22 -2
- package/common/dirIcon.ts +65 -0
- package/common/guiMcpAgents.ts +44 -0
- package/common/iconSizes.ts +20 -0
- package/common/launchAgent.ts +1 -1
- package/common/repoJson.ts +81 -0
- package/common/sessionAgent.ts +9 -2
- package/common/worklogInterval.ts +12 -0
- package/common/worktreeEnv.ts +106 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-C43gBAG2.js → abnfDiagram-VRR7QNED-WSmThe6b-BmcQe1XL.js} +1 -1
- package/dist/assets/{arc-zT0wd74V-CquvnqHY.js → arc-Op_P1CyZ-BX8g6uBK.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-Cqhmai3f-BSJQ3ILE.js +1 -0
- package/dist/assets/architectureDiagram-ZJ3FMSHR-Tj3i7Oow-bjkL7mcs.js +36 -0
- package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CM2STruI.js → blockDiagram-677ZJIJ3-c-hNYBhI-BRXCKJUM.js} +5 -5
- package/dist/assets/c4Diagram-LMCZKHZV-WIGVajf5-C5Xls6HX.js +10 -0
- package/dist/assets/channel-OqG5cGJi-B30xONXZ.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-CoxhWbB9.js → chunk-32BRIVSS-CtSq3QOR-DK6LIJGr.js} +1 -1
- package/dist/assets/chunk-52WLFC77-2Up0T-6t-CJBQ51RT.js +10 -0
- package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-P5M5Yvpn.js → chunk-C7G6YPKG-ivengO-7-B2kbM724.js} +1 -1
- package/dist/assets/chunk-EX3LRPZG-BHoWQ6W8-37sgnHAN.js +231 -0
- package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-DWcCl8Yk.js → chunk-FWX5IMBZ-PvRXPghr-DaUpqKtt.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-vKYnv8Bj.js → chunk-HOUHSVGY-A3_zwY4q-1bJ7y7Qe.js} +1 -1
- package/dist/assets/chunk-ICXQ74PX-vzZqgurQ-ZHEqoby-.js +2 -0
- package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-gTPiZssc.js → chunk-MOJQB5TN-CorQ_Pl1-D5leNmku.js} +4 -4
- package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-C9Osj4JZ.js → chunk-OGEWGWER-BVhfs7WG-XChTqtZ9.js} +1 -1
- package/dist/assets/chunk-PUDLZKDR-CJo7KQJ3-Cmsgx92D.js +156 -0
- package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-Cco8LyAc.js → chunk-Q4XR5HBZ-DLkq8ZGO-Ba0ecFh2.js} +3 -3
- package/dist/assets/chunk-RYQCIY6F-CLxhXO9P-bjDO7t1Y.js +1 -0
- package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-CL2oW-GL.js → chunk-V7JOEXUC-DqefuUOb-BivTQjZV.js} +4 -4
- package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-DhyLweXm.js → chunk-VAUOI2AC-BKx35BBi-Dbs6oO5R.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-kS-cvTnu.js → chunk-VR4S4FIN-DpJgr4Ur-CUM4Z2vw.js} +1 -1
- package/dist/assets/chunk-WYO6CB5R-nS6zX9ct-DRBBjGWu.js +127 -0
- package/dist/assets/{chunk-XXDRQBXY-DFXH_lWn-3EDWq1FX.js → chunk-XXDRQBXY-DIOdXErg-BaEFQvBN.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-B_5ITunz.js → chunk-ZGVPDNZ5-DNtiHxpJ-5cVd7QTd.js} +5 -5
- package/dist/assets/{chunk-ZIRB5QZD-Biy6ZNFG-Cxpu0Ymf.js → chunk-ZIRB5QZD-DRLh0zc1-B04pRyTm.js} +2 -2
- package/dist/assets/classDiagram-OUVF2IWQ-C6h0nbT3-CEdJ3UMI.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-CzsXJ_wA-CEdJ3UMI.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-Rn6Qv2B3-BQD8cYke.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-DvSvuXnG-BW6kx41X.js +1 -0
- package/dist/assets/cynefinDiagram-TSTJHNR4-969y1qZK-CCqzAXh-.js +62 -0
- package/dist/assets/cytoscape.esm-C_CQJHFN-D3Xb5zKs.js +321 -0
- package/dist/assets/dagre-Iyo00jS2-DhomXx19.js +1 -0
- package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-B_Vz2rxF.js → dagre-VKFMJZFB-CZfgAZFk-DPH_d74f.js} +1 -1
- package/dist/assets/defaultLocale-DCiIiYpI-C5Twph8F.js +1 -0
- package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-3PKZ2Ka3.js → diagram-FQU43EPY-CSdnleGM-spKSc9IW.js} +3 -3
- package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-BMfEdJEI.js → diagram-G47NLZAW-DKYe-Mwx-DybtCNR4.js} +1 -1
- package/dist/assets/diagram-NH7WQ7WH-DMasKpwa-BCH0qp05.js +24 -0
- package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-yVWZgC-A.js → diagram-OA4YK3LP-CSyc0BDR-T46FP-Fw.js} +3 -3
- package/dist/assets/{diagram-WEI45ONY-D_93NKqo-JY2oY6TV.js → diagram-WEI45ONY-CcJW2fNK-D35VJLC8.js} +2 -2
- package/dist/assets/{dist-B5vu6eeE.js → dist-BKvAGUnN.js} +1 -1
- package/dist/assets/{dist-CAS06ruL.js → dist-BwYGyrSJ.js} +1 -1
- package/dist/assets/{dist-BQBs5pjy-uaeihEVi.js → dist-Bz_xjr9E-SUsJ4HlM.js} +1 -1
- package/dist/assets/{dist-cuEw-vsy.js → dist-CpGs_bug.js} +1 -1
- package/dist/assets/{dist-By4s_hax.js → dist-Dmmh5zR9.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-CSODjPeQ.js → ebnfDiagram-CCIWWBDH-CeBUH2N0-m1eORe2D.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-DMaAYLYB.js → erDiagram-Q63AITRT-1OOp34oV-BgXJIPds.js} +3 -3
- package/dist/assets/eventmodeling-45OFAUF4-jdctDg0T-DiTH6TbJ.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DK0InIny-4Udt9YCi.js +1 -0
- package/dist/assets/ganttDiagram-NO4QXBWP-DQT88dUZ-DZpddx9o.js +292 -0
- package/dist/assets/gitGraph-TEB2WS4Q-C4cKwl5j-D1JyoAdx.js +1 -0
- package/dist/assets/gitGraphDiagram-IHSO6WYX-CeZGS4qi-Do2IRAV_.js +106 -0
- package/dist/assets/index-B-HvyXSL.css +1 -0
- package/dist/assets/index-cTxEHcQq.js +614 -0
- package/dist/assets/info-DKCQHKI2-DPUCBYWS-D6AOsp-i.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-DCV7LBpV.js → infoDiagram-FWYZ7A6U-BDt8lBTy-C6oCIj1p.js} +1 -1
- package/dist/assets/init-DzP_5IO6-C-OQMol4.js +1 -0
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-DbEPqoeF.js → ishikawaDiagram-FXEZZL3T-DOHYs_AA-BaNL3U7o.js} +3 -3
- package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-YWImF3-J.js → journeyDiagram-5HDEW3XC-B33IW9yy-B34QvuAn.js} +4 -4
- package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-Cu6iQUPe.js → kanban-definition-HUTT4EX6-Bkdm46eX-DTFDSi-q.js} +11 -11
- package/dist/assets/{katex-C_9rYNVQ-c9h6sBOB.js → katex-DImtHWbS-DFcUm9x_.js} +3 -3
- package/dist/assets/{lib-D3KO7fnC.js → lib-B_JkwnLc.js} +1 -1
- package/dist/assets/line-Dy_Qlcgo-BvVwixna.js +1 -0
- package/dist/assets/{linear-lrGinF5_-CbzZdnNy.js → linear-DVIrZqkl-BqPv05Mj.js} +1 -1
- package/dist/assets/{marp-ssHueLtD.js → marp-CJ77SRyX.js} +1 -1
- package/dist/assets/{mermaid-parser.core-DxEa8E3F-CTOK7EeF.js → mermaid-parser.core-C1baW7NG-Dj6SGVcg.js} +25 -25
- package/dist/assets/mermaid.core-C09q4TmN-CQr7RToO.js +11 -0
- package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-BAQB7j75.js → mindmap-definition-LN4V7U3C-bfYIhPQ0-NMXPL0-o.js} +26 -26
- package/dist/assets/{ordinal-B9_Llu10-CU85fhaB.js → ordinal-pAvPVbFU-C5B2S93B.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-CC7His0F-BKbhzjd3.js +1 -0
- package/dist/assets/{path-COivZ1Gw-C5riojK2.js → path-CciMXxPa-COt_16Va.js} +1 -1
- package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-HDlX2gZc.js → pegDiagram-2B236MQR-CZQOKUMC-Bt2XR6XH.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-CCrcq2sL-Cfoiz5iJ.js +1 -0
- package/dist/assets/pieDiagram-ENE6RG2P-DiVKPdHE-omZF6wmG.js +39 -0
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-C0w-ELAc.js → quadrantDiagram-ABIIQ3AL-CbPY3kXI-Ht-Z8Sgk.js} +4 -4
- package/dist/assets/radar-I7S5WNFK-C5TUrpHf-jAv7AZpj.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-CGuuFEd8-CFDyeEw0.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-DuoCSFZY-fEpM8K-S.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-ig-DG9e_-Cx72Vzdn.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-BkBgpyvu-CKKHVc4W.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-CaMY_GQ1.js → railroadDiagram-RFXS5EU6-CFRaQzr0-Kv59_1Cv.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-DWHcMDxg.js → requirementDiagram-TGXJPOKE-BiIDduje-BsRM6WGB.js} +4 -4
- package/dist/assets/{rough.esm-wVmwlXu7-CSKSodPl.js → rough.esm-CRiMXkOs-By172zw-.js} +1 -1
- package/dist/assets/sankeyDiagram-HTMAVEWB-DcK87A5Y-CguIeyOy.js +40 -0
- package/dist/assets/sequenceDiagram-DBY2YBRQ-DgEQYcvc-BnGAucBY.js +162 -0
- package/dist/assets/src-CvsWqFln-Tc7ZSrRz.js +1 -0
- package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-BcuA2rt-.js → stateDiagram-2N3HPSRC-Bg7EXtW2-CRiZWlcc.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BjhArXP_-AXmUyvh8.js +1 -0
- package/dist/assets/swimlanes-5IMT3BWC-CKORDKr0-xywRB2SF.js +2 -0
- package/dist/assets/swimlanesDiagram-G3AALYLV-CyvIfl1o-Bwyhyqg5.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-_przvOVq.js → timeline-definition-FHXFAJF6-BM3Iky2r-BI3SQ-2t.js} +8 -8
- package/dist/assets/treeView-QDETBFTQ-CqfT0I1z-BeSX09kM.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CiJkiGWo-CnEhaOCI.js +1 -0
- package/dist/assets/vennDiagram-L72KCM5P-VNmAhEFP-gQY1yv0O.js +34 -0
- package/dist/assets/wardley-OPB4EBWU-D1tuVHAV-D845peIK.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-CCl9h9eK.js → wardleyDiagram-EHGQE667-BkHQpyFm-DWf6AbGr.js} +2 -2
- package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-B6LaWKwe.js → xychartDiagram-FW5EYKEG-Yz-8aLqK-Bmt5kHk3.js} +4 -4
- package/dist/index.html +2 -2
- package/package.json +3 -3
- package/server/agents/antigravity-mcp.ts +9 -41
- package/server/agents/antigravity-sessions.ts +5 -5
- package/server/agents/git-exclude.ts +29 -0
- package/server/agents/grok-args.ts +56 -0
- package/server/agents/grok-mcp.ts +138 -0
- package/server/agents/grok-session.ts +42 -0
- package/server/agents/grok.ts +9 -0
- package/server/agents/gui-mcp-bridge.ts +30 -0
- package/server/agents/registry.ts +2 -0
- package/server/agents/types.ts +1 -1
- package/server/backends/remoteHost/terminalScreen.ts +1 -1
- package/server/config/app-config.ts +98 -10
- package/server/config/config-body.ts +1 -0
- package/server/config/config-routes.ts +26 -9
- package/server/config/config-schema.ts +85 -1
- package/server/config/dir-config.ts +132 -26
- package/server/config/dir-icon-detect.ts +119 -0
- package/server/config/dir-icon.ts +79 -0
- package/server/config/header-config.ts +9 -1
- package/server/config/header-context.ts +5 -16
- package/server/config/header-resolve.ts +1 -1
- package/server/config/repo-json.ts +81 -0
- package/server/config/worktree-dir-config.ts +38 -12
- package/server/config/worktree-env-log.ts +115 -0
- package/server/config/worktree-env.ts +299 -0
- package/server/config/worktree-task.ts +22 -0
- package/server/files/open-dir.ts +53 -15
- package/server/files/pick-file.ts +186 -31
- package/server/files/ps-quote.ts +7 -0
- package/server/files/win-folder-dialog.ts +25 -4
- package/server/files/wsl.ts +49 -0
- package/server/git/worktree-routes.ts +7 -1
- package/server/git/worktrees.ts +33 -32
- package/server/index.ts +9 -0
- package/server/infra/canonical-path.ts +36 -0
- package/server/infra/mulmoterminal-home.ts +10 -0
- package/server/mcp/gui-call-history.ts +6 -4
- package/server/routes/app-routes.ts +3 -0
- package/server/routes/dir-routes.ts +31 -1
- package/server/routes/plugin-routes.ts +32 -20
- package/server/routes/routeParams.ts +2 -1
- package/server/routes/session-routes.ts +7 -2
- package/server/routes/terminal-ws-path.ts +2 -1
- package/server/routes/tool-routes.ts +2 -2
- package/server/routes/ws-routes.ts +170 -69
- package/server/session/{antigravity-conversations.ts → agent-conversations.ts} +19 -17
- package/server/session/background-chat.ts +2 -1
- package/server/session/custom-agent-command.ts +49 -0
- package/server/session/custom-agent-log.ts +60 -0
- package/server/session/dir-session.ts +1 -1
- package/server/session/lifecycle.ts +3 -0
- package/server/session/mcp-config.ts +17 -4
- package/server/session/pty-spawn.ts +18 -2
- package/server/session/registry.ts +103 -42
- package/server/session/session-reads.ts +14 -4
- package/server/session/session-settings.ts +0 -9
- package/server/session/spawn-antigravity.ts +13 -2
- package/server/session/spawn-claude.ts +97 -11
- package/server/session/spawn-codex.ts +8 -6
- package/server/session/spawn-deps.ts +2 -0
- package/server/session/spawn-grok.ts +76 -0
- package/server/session/spawn-shell.ts +5 -5
- package/server/session/spawners.ts +2 -0
- package/server/session/worktree-session-limit.ts +2 -1
- package/server/skills/mulmoterminal-config/SKILL.md +95 -9
- package/server/skills/mulmoterminal-dirs/SKILL.md +152 -8
- package/server/skills/mulmoterminal-keys/SKILL.md +5 -2
- package/server/skills/mulmoterminal-model/SKILL.md +126 -6
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-DRANrMDe.js +0 -1
- package/dist/assets/architectureDiagram-ZJ3FMSHR-3nWA91tG-BONcd9-U.js +0 -36
- package/dist/assets/c4Diagram-LMCZKHZV-C0LAqQso-B5vNwdXA.js +0 -10
- package/dist/assets/channel-7wUqSdoX-qLt0VyIe.js +0 -1
- package/dist/assets/chunk-52WLFC77-BJ-ss3Xr-CId6SWzh.js +0 -10
- package/dist/assets/chunk-EX3LRPZG-DLS6FBN1-DyLWbVhu.js +0 -231
- package/dist/assets/chunk-ICXQ74PX-DwgHBX_g-CS9xzHHA.js +0 -2
- package/dist/assets/chunk-PUDLZKDR-DcrWQRYh-CZwJPYWX.js +0 -156
- package/dist/assets/chunk-RYQCIY6F-CmHwCv5h-CR7mRx_v.js +0 -1
- package/dist/assets/chunk-WYO6CB5R-BlzOfotS-B-BAzlIB.js +0 -127
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-ClENF_Zt.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-ClENF_Zt.js +0 -1
- package/dist/assets/cose-bilkent-JH36ORCC-DDu6UbVg-DcuGhn7q.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-Xw6wbIlg.js +0 -1
- package/dist/assets/cynefinDiagram-TSTJHNR4-DHA9iPo--CsvHy5QQ.js +0 -62
- package/dist/assets/cytoscape.esm-CncKIM2s-CEEOau9_.js +0 -321
- package/dist/assets/dagre-CF0ZwvCl-CSPF5CVP.js +0 -1
- package/dist/assets/defaultLocale-DUNguUWs-BbDo_yJX.js +0 -1
- package/dist/assets/diagram-NH7WQ7WH-CXJCYvY--DMS7vR7_.js +0 -24
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-CDRF15wA.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-nMfF1thc.js +0 -1
- package/dist/assets/ganttDiagram-NO4QXBWP-DQZvdFo1-t6QKW7r_.js +0 -292
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-BpVP-1CD.js +0 -1
- package/dist/assets/gitGraphDiagram-IHSO6WYX-C2ovBouh-8CB_sdi8.js +0 -106
- package/dist/assets/index-BfZe-OnL.js +0 -614
- package/dist/assets/index-slI4QJ8P.css +0 -1
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-ugP0JbLq.js +0 -1
- package/dist/assets/init-DEsX3bhM-D6jRqBbL.js +0 -1
- package/dist/assets/line-D7ziSjKi-rKNlfotQ.js +0 -1
- package/dist/assets/mermaid.core-V0OYwIz3-ikGsVp05.js +0 -11
- package/dist/assets/packet-7NZHBO7P-CQI3flND-ChXy_hCK.js +0 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-DeFim35k.js +0 -1
- package/dist/assets/pieDiagram-ENE6RG2P-B_fBBS-2-C7-OBdMS.js +0 -39
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-D-Y4D0Ax.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-tvzxXql8.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-CMCeKqcK.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-CM7KSH9R.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-Ds4sbE-D.js +0 -1
- package/dist/assets/sankeyDiagram-HTMAVEWB-DlutaXDv-BXstTNJK.js +0 -40
- package/dist/assets/sequenceDiagram-DBY2YBRQ-BOLgtggH-DNq_A2us.js +0 -162
- package/dist/assets/src-Brzfja-q-ch1VcTiG.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-CngDb_q-.js +0 -1
- package/dist/assets/swimlanes-5IMT3BWC-CYjtALQH-CFAIsGQE.js +0 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-ktA_ajC9.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-BVr9yA0C.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-dwog38AV.js +0 -1
- package/dist/assets/vennDiagram-L72KCM5P-CdKAHoek-nDlMmrar.js +0 -34
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-BcHLy1xv.js +0 -1
- package/server/session/launcher-gui-mcp.ts +0 -126
package/README.md
CHANGED
|
@@ -227,6 +227,7 @@ Needs **Node ≥ 22.9**, plus these CLIs on your `PATH`:
|
|
|
227
227
|
| Optional | `codex` | [Codex sessions](#agents-claude--codex) in a cell, alongside Claude | `npm i -g @openai/codex` |
|
|
228
228
|
| Optional | `ffmpeg` | video rendering from the [mulmo-script panel](#wiki-collections--the-gui-panel) (its plugin ships enabled) | `brew install ffmpeg` · `sudo apt install ffmpeg` · `sudo dnf install ffmpeg` |
|
|
229
229
|
| Optional | `ollama` | [`claude-ollama`](https://receptron.github.io/mulmoterminal/guide/en/claude-ollama.html) — Claude Code against a fully local model | [ollama.com/download](https://ollama.com/download) |
|
|
230
|
+
| Linux only | a file dialog | the **Choose a folder / Insert a file path** buttons, which open an OS dialog on the machine the server runs on. macOS and Windows have one built in; **WSL** uses the Windows one over interop and needs nothing installed. A Linux desktop needs one of these — without any, the buttons say so and you type the path instead (#1447) | `sudo apt install zenity` · `sudo dnf install zenity` · `kdialog`, `qarma` and `yad` also work |
|
|
230
231
|
|
|
231
232
|
The server starts without any of the non-required rows; you just lose that row's feature,
|
|
232
233
|
and the header/panel for it says so. `git` and `gh` are marked required because losing them
|
|
@@ -343,8 +344,8 @@ The launcher detects it and prints the exact, OS-appropriate removal command; ru
|
|
|
343
344
|
┌──────────────────────────────────────┐ ┌─────────────────────────────────────────────┐
|
|
344
345
|
│ Browser (Vue 3 + xterm.js) │ │ Server (Express + Node) │
|
|
345
346
|
│ │ │ │
|
|
346
|
-
│
|
|
347
|
-
│ ▲ refetch on any push
|
|
347
|
+
│ App.vue ──────subscribe("sessions")──┼──SIO───►│ socket.io /ws/pubsub ── publish ──┐ │
|
|
348
|
+
│ ▲ refetch on any push (favicon) │ │ │ │
|
|
348
349
|
│ └──── GET /api/sessions ─────────┼──HTTP──►│ Express /api/sessions │ │
|
|
349
350
|
│ │ │ /api/hook ◄──curl── hooks │ │
|
|
350
351
|
│ Terminal.vue ── ws JSON msgs ────────┼──WS────►│ ws /ws ──► node-pty ─► `claude`──hooks┘
|
|
@@ -353,7 +354,8 @@ The launcher detects it and prints the exact, OS-appropriate removal command; ru
|
|
|
353
354
|
```
|
|
354
355
|
|
|
355
356
|
- **Terminal I/O** flows over a raw WebSocket (`/ws`), one PTY per session.
|
|
356
|
-
- **Session list** is fetched over HTTP (`/api/sessions`).
|
|
357
|
+
- **Session list** is fetched over HTTP (`/api/sessions`) — by `App.vue` for the tab favicon,
|
|
358
|
+
and by an empty cell's launch form (`?cwd=`) for its resume rows.
|
|
357
359
|
- **Live activity** is pushed over a Socket.IO pub/sub channel (`/ws/pubsub`);
|
|
358
360
|
the server learns of activity from **Claude hooks** that POST to `/api/hook`.
|
|
359
361
|
- **Other terminals** run on their own raw WebSockets: **Codex** sessions on `/ws/codex`,
|
|
@@ -385,12 +387,12 @@ SDK; we drive the real interactive CLI and relay its TTY over the WebSocket.
|
|
|
385
387
|
|
|
386
388
|
---
|
|
387
389
|
|
|
388
|
-
## Agents: Claude, Codex &
|
|
390
|
+
## Agents: Claude, Codex, Antigravity & Grok
|
|
389
391
|
|
|
390
392
|
MulmoTerminal drives **interactive coding-agent CLIs**, not just Claude. An
|
|
391
393
|
`AgentAdapter` seam abstracts the per-agent bits (which binary to spawn, how it resumes)
|
|
392
|
-
so the PTY, grid, persistence, and GUI-panel plumbing stay shared.
|
|
393
|
-
today — **Claude Code** (the default), **Codex**,
|
|
394
|
+
so the PTY, grid, persistence, and GUI-panel plumbing stay shared. Four adapters ship
|
|
395
|
+
today — **Claude Code** (the default), **Codex**, **Antigravity** (`agy`), and **Grok**.
|
|
394
396
|
|
|
395
397
|
- **Claude** — spawned as `claude` (override with `CLAUDE_BIN`). The server passes
|
|
396
398
|
`--session-id <uuid>`, so it knows the live session's id even before its transcript
|
|
@@ -403,18 +405,22 @@ today — **Claude Code** (the default), **Codex**, and **Antigravity** (`agy`).
|
|
|
403
405
|
cockpit roster next to Claude's. Because Codex only mints its rollout id **after** the first
|
|
404
406
|
turn, the server watches `~/.codex/sessions/**/rollout-*.jsonl` (home overridable via
|
|
405
407
|
`CODEX_HOME`) and maps the new rollout to the session — attributed only when it's
|
|
406
|
-
unambiguous, never by "newest wins".
|
|
407
|
-
|
|
408
|
+
unambiguous, never by "newest wins". That mapping is appended to
|
|
409
|
+
`~/.mulmoterminal/codex-rollouts.jsonl`, so a conversation is still resumable after the server
|
|
410
|
+
restarts — without it a session whose tmux is also gone came back as a fresh codex. Resume
|
|
411
|
+
reattaches a live PTY, adopts a surviving tmux session, or cold-resumes the rollout id.
|
|
408
412
|
- **Antigravity** — spawned as `agy` (override with `ANTIGRAVITY_BIN`; `ANTIGRAVITY_MODEL` sets
|
|
409
413
|
`--model`). Antigravity runs on its own WebSocket (`/ws/antigravity`). Like Codex it mints its
|
|
410
414
|
own conversation id, so the server watches `~/.gemini/antigravity-cli/brain/` (home overridable
|
|
411
415
|
via `ANTIGRAVITY_HOME`) for the directory the new conversation creates — attributed only when
|
|
412
416
|
unambiguous — and cold-resumes it with `--conversation <id>`. That mapping is appended to
|
|
413
417
|
`~/.mulmoterminal/antigravity-conversations.jsonl`, so a conversation is still resumable after
|
|
414
|
-
the server restarts.
|
|
418
|
+
the server restarts — the same log Codex keeps, in the same format.
|
|
415
419
|
|
|
416
|
-
Its **GUI tools work differently**, because `agy` takes no MCP flag: it
|
|
417
|
-
`.agents/mcp_config.json` in the working directory.
|
|
420
|
+
Its **GUI tools work differently — in the workspace too**, because `agy` takes no MCP flag: it
|
|
421
|
+
reads its servers from `.agents/mcp_config.json` in the working directory. So it never gets the
|
|
422
|
+
workspace's "every tool automatically"; register what it needs with the Canvas switches, wherever
|
|
423
|
+
it runs (see [MCP server ids](#mcp-server-ids-why-a-workspace-cell-and-a-project-cell-disagree)). MulmoTerminal writes that file from the
|
|
418
424
|
directory's [Canvas switches](#wiki-collections--the-gui-panel) — the same switches Claude's cells read — so
|
|
419
425
|
one switch serves every agent, and rewrites it whenever a switch flips or an agy session starts.
|
|
420
426
|
Servers in it that MulmoTerminal did not write are left alone, the file is removed once no group
|
|
@@ -424,9 +430,29 @@ today — **Claude Code** (the default), **Codex**, and **Antigravity** (`agy`).
|
|
|
424
430
|
per directory and shared by every session running there — and reaches the bridge through the agy
|
|
425
431
|
process's own environment instead.
|
|
426
432
|
|
|
427
|
-
**
|
|
428
|
-
|
|
429
|
-
Antigravity
|
|
433
|
+
- **Grok** — spawned as `grok` (override with `GROK_BIN`; `GROK_MODEL` sets `--model`), on its own
|
|
434
|
+
WebSocket (`/ws/grok`). It resumes the way **Claude** does rather than the way Codex and
|
|
435
|
+
Antigravity do: `grok --session-id <uuid>` starts a conversation under an id MulmoTerminal minted,
|
|
436
|
+
so there is no watcher, no attribution guess, and no mapping log — the id the browser holds is
|
|
437
|
+
grok's own. A reconnect passes `--resume <id>` instead, but only once a conversation by that name
|
|
438
|
+
exists on disk: grok writes one under `~/.grok/sessions/<url-encoded cwd>/<id>/` (home overridable
|
|
439
|
+
via `GROK_HOME`) after the first turn, and re-using a `--session-id` that already exists is a hard
|
|
440
|
+
error, which is why the two flags are never sent together.
|
|
441
|
+
|
|
442
|
+
Its **GUI tools work like Antigravity's — in the workspace too** and for the same reason: `grok`
|
|
443
|
+
takes no MCP flag, so it never gets the workspace's "every tool automatically" either, and
|
|
444
|
+
MulmoTerminal registers the bridge in `.grok/config.toml` (grok's project-scope config) from the
|
|
445
|
+
directory's [Canvas switches](#wiki-collections--the-gui-panel), wherever it runs (see
|
|
446
|
+
[MCP server ids](#mcp-server-ids-why-a-workspace-cell-and-a-project-cell-disagree)). That file is TOML and yours, so —
|
|
447
|
+
unlike agy's JSON — MulmoTerminal never rewrites it directly: it drives `grok mcp add -s project` /
|
|
448
|
+
`grok mcp remove -s project`, and only for the server ids it wrote itself. Nothing else in the file
|
|
449
|
+
is touched, a directory already in the right state runs no command at all, and the file is added to
|
|
450
|
+
`.git/info/exclude` only when MulmoTerminal created it. As with agy, the **session id is never
|
|
451
|
+
written into that file** — it reaches the bridge through the grok process's own environment.
|
|
452
|
+
|
|
453
|
+
**Choosing an agent.** Each grid cell's launch form carries the **Agent Picker** — a
|
|
454
|
+
**Claude / Codex / Antigravity / Grok / Shell** toggle — and the Collections browser a **Claude /
|
|
455
|
+
Codex / Antigravity / Grok** one (your choice is remembered).
|
|
430
456
|
**Shell** is not an agent: it runs your OS default shell (`$SHELL`, or `/bin/sh`) in the
|
|
431
457
|
chosen directory, with nothing to install and nothing to configure. It starts a launcher
|
|
432
458
|
cell, so it has no model, no MCP registration, and no worktree — those rows disappear
|
|
@@ -520,6 +546,9 @@ the `claude` / `codex` sessions themselves.
|
|
|
520
546
|
| `ANTIGRAVITY_BIN` | `agy` | The Antigravity CLI binary to spawn. |
|
|
521
547
|
| `ANTIGRAVITY_MODEL` | agy default | Model passed to Antigravity as `--model` (unset = agy's own default). |
|
|
522
548
|
| `ANTIGRAVITY_HOME` | `~/.gemini/antigravity-cli` | Antigravity home directory containing session brain storage. |
|
|
549
|
+
| `GROK_BIN` | `grok` | The Grok CLI binary to spawn. |
|
|
550
|
+
| `GROK_MODEL` | grok default | Model passed to Grok as `--model` (unset = grok's own default). |
|
|
551
|
+
| `GROK_HOME` | `~/.grok` | Grok home directory containing its per-directory session store. |
|
|
523
552
|
| `MULMOTERMINAL_HOME` | `~/.mulmoterminal` | Root for managed **git worktrees**. |
|
|
524
553
|
| `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. |
|
|
525
554
|
| `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. |
|
|
@@ -543,7 +572,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
543
572
|
|
|
544
573
|

|
|
545
574
|
|
|
546
|
-
*Open it from the ⚙ button in the toolbar. Pick a **theme**, set the **terminal font size** and **scroll speed**, set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells,
|
|
575
|
+
*Open it from the ⚙ button in the toolbar. Pick a **theme**, set the **terminal font size**, **font** and **scroll speed**, set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells, register your own **MCP servers**, and turn on the switches for what this app writes on your behalf — **issue work comments**, the **PR clone footer**, the **closing summary**, the **decision digest**, the **dev worklog** — no need to hand-edit the config file. Four settings stay with their skill because a form would be the wrong tool for them (`keymap`, `themes`, `providers`, `buttons`/`chips`); Settings shows what each is doing now and launches that skill. Note that **theme, font size and scroll speed are stored per browser** (they're display preferences, so a phone and a desktop keep their own); the rest live in `~/.mulmoterminal/config.json` and are shared by every client.*
|
|
547
576
|
|
|
548
577
|
| Field | Meaning |
|
|
549
578
|
| ------------ | ------- |
|
|
@@ -552,24 +581,27 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
552
581
|
| `soundKinds` | Which moments beep — see [Notification sounds](#notification-sounds). Defaults to `["finished","waiting"]`; the other kinds are opt-in. |
|
|
553
582
|
| `sounds` | Per-kind sound: `{ "waiting": "preset:coin" }`. A `preset:<id>` reference or an absolute path; a kind with no entry falls back to `soundFile`. |
|
|
554
583
|
| `prRepos` | `owner/repo` entries whose open PRs/issues the cross-repo **PRs & Issues** view aggregates, using whichever CLI the host needs — your own `gh` or `glab` login, so no token is stored here. An entry may name its host — `gitlab.com/group/project` is read with `glab`, and work can be started on it, commented on and turned into a merge request. A host that is neither shows a row saying so. |
|
|
555
|
-
| `gitlabHosts` | Hosts that run a **self-hosted GitLab**, e.g. `["gitlab.example.com"]`. Nothing in a URL says which forge a host runs, so declaring it is what lets `prRepos` entries on that host be read with `glab` — everything gitlab.com can do, it can do. Needs `glab auth login --hostname <host>`.
|
|
584
|
+
| `gitlabHosts` | Hosts that run a **self-hosted GitLab**, e.g. `["gitlab.example.com"]`. Nothing in a URL says which forge a host runs, so declaring it is what lets `prRepos` entries on that host be read with `glab` — everything gitlab.com can do, it can do. Needs `glab auth login --hostname <host>`. Editable in Settings → **GitHub and GitLab**; either way it takes effect on the next server start. |
|
|
556
585
|
| `repoDirs` | `{ "owner/repo": "/abs/path" }` — which local clone work on a repo starts in, when you keep several side by side. Only the *choice* is stored; which clones exist is re-derived from `cwdPresets` on every read, and an entry that no longer names a clone of that repo is ignored. |
|
|
557
|
-
| `launchers` | `{ label, command }` entries offered in a grid cell's launcher besides the agents — any interactive command. A plain shell needs no entry: the
|
|
586
|
+
| `launchers` | `{ label, command }` entries offered in a grid cell's launcher besides the agents — any interactive command. A plain shell needs no entry: the Agent Picker's **Shell** option opens `$SHELL` unconfigured. |
|
|
587
|
+
| `customAgents` | `{ id, label, agent, command }` entries offered in the **Agent Picker** — your own way of starting Claude Code (`ollama launch claude --model … --`, a wrapper script). Unlike a launcher, Claude Code's own argv is **appended** to `command`, so the cell is a real session: resume, cost, context, GUI tools. `agent` says which agent's arguments to append and is required (`"claude"` is the only value today); `command` must stop taking arguments where Claude Code's begin — hence the trailing `--` above. Up to 8. |
|
|
558
588
|
| `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. |
|
|
559
|
-
| `userMcpServers` | `{ id, url }` HTTP MCP servers merged into the `--mcp-config` of the Claude sessions that carry the full GUI MCP — a cell whose working directory is the **workspace**, and a session the server starts itself (the phone, a scheduled task) unless it asks for a grid cell's shape, as an issue's seed session does (`issueSpawnOptions`). A cell in a project directory
|
|
589
|
+
| `userMcpServers` | `{ id, url }` HTTP MCP servers merged into the `--mcp-config` of the **Claude** sessions that carry the full GUI MCP (codex is handed the GUI server alone, `codexGuiMcpServers`) — a cell whose working directory is the **workspace**, and a session the server starts itself (the phone, a scheduled task) unless it asks for a grid cell's shape, as an issue's seed session does (`issueSpawnOptions`). A cell in a project directory does not get this merge; the MCP config the user wrote is read either way. Takes effect on the next session. |
|
|
560
590
|
| `buttons` | Header action buttons — see [Header buttons](#header-buttons). Omit to keep the defaults; set to replace them. |
|
|
561
|
-
| `chips` | Header info chips (`dir` / `git` / `work` / `diff` / `ctx` / `usage` / `status` / `tools`, or custom text). Omit to keep the default set; `[]` hides all built-ins. `work` shows which PR / issue the cell is on (`#977 → #966`) and clears itself when the PR merges — see the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#work-chip). |
|
|
591
|
+
| `chips` | Header info chips (`dir` / `git` / `work` / `diff` / `ctx` / `usage` / `status` / `tools` / `env`, or custom text). `env` shows what this working tree was reserved by [`worktreeEnv`](#per-directory-settings-projectmulmoterminaljson) (`:3010`, clickable) and draws nothing where none is declared. Omit to keep the default set; `[]` hides all built-ins. `work` shows which PR / issue the cell is on (`#977 → #966`) and clears itself when the PR merges — see the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#work-chip). |
|
|
562
592
|
| `pushEnabled` | `true` to send a **Web Push** to your registered devices. Off by default; only sends while the **RemoteHost** channel is connected (see below). The master switch — `pushKinds` picks which moments. |
|
|
563
593
|
| `pushKinds` | Which moments push: `"finished"` (a turn ended, ✅) and/or `"waiting"` (the agent stopped to ask — a permission prompt or a question, ❓, **once per prompt**). Omit to keep both; `[]` for none. A kind added in a later version stays off until you tick it. |
|
|
564
|
-
| `worklogEnabled` | `true` to run the built-in **dev worklog** batch (see below). Off by default (each run spawns an LLM session, so it costs tokens). |
|
|
565
|
-
| `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). |
|
|
566
|
-
| `terminalSubmit` | Which bytes Claude reads as **submit** vs **newline**: `"cr"` (default — Enter submits, Shift+Enter makes a newline) or `"esc-cr"` (for a Claude Code rebound the other way). Applies to the keyboard **and** the phone remote-view submit, for **Claude sessions only** (shell/codex keep plain Enter). See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit). |
|
|
567
|
-
| `copyOnSelect` | `true` puts a **mouse selection on the clipboard the moment it settles**, with no key pressed (the PuTTY / iTerm2 behaviour). **Off by default** — it changes the clipboard when you may only have meant to highlight something.
|
|
568
|
-
| `decisionDigest` | Keep a **Markdown digest of the decisions this project's sessions asked for**, refreshed at startup and every few hours, so an agent can read what has already been decided before asking something similar. Written to `~/.mulmoterminal/decisions/<project>.md` (never into your repository) and served to agents by the bundled `mulmoterminal-decisions` skill. **Off by default** — it is a vision-stage idea, and it writes a file that would otherwise not exist. The digest holds dated facts, never inferred rules. |
|
|
569
|
-
| `issueWorkComments` | Let a cell **comment on the issue it is working on**: **one comment**, posted when the work starts and then **edited** as the PR opens and merges (closing the issue if
|
|
570
|
-
| `prWorkdirFooter` | Ends a PR body with `work in <clone>` — the directory name of the clone the work happened in, so a PR says which of several side-by-side checkouts produced it. Applies to **both** paths that open PRs here: **⧉ Open PR** appends it to the PR it creates, and every Claude session is told to end the bodies it writes with the same line (the name is resolved by the server, so a session inside a managed worktree still names the main checkout). **On by default**; set `false` to opt out — read per PR and per session spawn, so no restart is needed
|
|
571
|
-
| `appendSystemPrompt` | Whether a spawned Claude session is asked to end a reply with a **closing summary** — what was asked, what was achieved, what was not (see [Closing summary](#closing-summary)). **On by default**; set `false` to opt out, and a directory's `.mulmoterminal.json` outranks this. Read per spawn, so no restart is needed
|
|
572
|
-
| `
|
|
594
|
+
| `worklogEnabled` | `true` to run the built-in **dev worklog** batch (see below). Off by default (each run spawns an LLM session, so it costs tokens). Editable in Settings → **Sessions and background tasks**. |
|
|
595
|
+
| `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). A stepper in the same Settings section covers the range. |
|
|
596
|
+
| `terminalSubmit` | Which bytes Claude reads as **submit** vs **newline**: `"cr"` (default — Enter submits, Shift+Enter makes a newline) or `"esc-cr"` (for a Claude Code rebound the other way). Applies to the keyboard **and** the phone remote-view submit, for **Claude sessions only** (shell/codex keep plain Enter). See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit). Settings → **Terminal keys** offers both, worded as behaviour. |
|
|
597
|
+
| `copyOnSelect` | `true` puts a **mouse selection on the clipboard the moment it settles**, with no key pressed (the PuTTY / iTerm2 behaviour). **Off by default** — it changes the clipboard when you may only have meant to highlight something. There is a checkbox in Settings → **Terminal keys**, applied at once; a hand edit of the file needs a **server restart, then a tab reload** (the server reads this file once at startup, and the browser reads the value from it on load). Composes with the `copy` keymap action rather than replacing it. Over plain `http://` the browser gives a page no clipboard access, so a fallback asks xterm to copy instead; see the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#copy-on-select). |
|
|
598
|
+
| `decisionDigest` | Keep a **Markdown digest of the decisions this project's sessions asked for**, refreshed at startup and every few hours, so an agent can read what has already been decided before asking something similar. Written to `~/.mulmoterminal/decisions/<project>.md` (never into your repository) and served to agents by the bundled `mulmoterminal-decisions` skill. **Off by default** — it is a vision-stage idea, and it writes a file that would otherwise not exist. The digest holds dated facts, never inferred rules. Settings → **Sessions and background tasks** has the switch. |
|
|
599
|
+
| `issueWorkComments` | Let a cell **comment on the issue it is working on**: **one comment**, posted when the work starts and then **edited** as the PR opens and merges (closing the issue if the forge has not already), each milestone stamped in UTC. The comment names the working **directory** it happened in — the folder name only, never the path — so a reader can tell which clone, and two terminals do not start the same issue twice. It says it came from MulmoTerminal. CI is deliberately not reported: it is on the PR already, and it flaps. **Off by default**; it writes to the forge, often on somebody else's issue. Needs the matching CLI logged in — `gh` for GitHub, `glab` for gitlab.com and any host declared in `gitlabHosts`. See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#issue-work-comments). Editable in Settings → **GitHub and GitLab**. |
|
|
600
|
+
| `prWorkdirFooter` | Ends a PR body with `work in <clone>` — the directory name of the clone the work happened in, so a PR says which of several side-by-side checkouts produced it. Applies to **both** paths that open PRs here: **⧉ Open PR** appends it to the PR it creates, and every Claude session is told to end the bodies it writes with the same line (the name is resolved by the server, so a session inside a managed worktree still names the main checkout). **On by default**; set `false` to opt out, from Settings → **GitHub and GitLab** or the file — read per PR and per session spawn, so no restart is needed, and a second MulmoTerminal beside this one sees the change too. Appending is idempotent: an existing PR never gets a second copy. |
|
|
601
|
+
| `appendSystemPrompt` | Whether a spawned Claude session is asked to end a reply with a **closing summary** — what was asked, what was achieved, what was not (see [Closing summary](#closing-summary)). **On by default**; set `false` to opt out, and a directory's `.mulmoterminal.json` outranks this. Settings → **Sessions and background tasks** has the switch. Read per spawn, so no restart is needed, though a session already running keeps what it was launched with. `true` / `false` only. |
|
|
602
|
+
| `autoDirIcon` | Whether a project that sets no `icon` shows the favicon its repository already ships (`public/favicon.svg`, `apple-touch-icon.png`, a web manifest — first hit wins, ordered by how the image survives at 14px). **On by default**; Settings → **Directory appearance** has the switch. A single project opts out with `"icon": false` in its own `.mulmoterminal.json`, which this does not override. A key that was written and got it wrong shows nothing rather than falling back — a broken setting has to look broken. `true` / `false` only. |
|
|
603
|
+
| `cockpitLines` | `{ summary, prompt, response }` — how many lines each **cockpit-roster** row shows before it clamps (default `2` / `2` / `3`, each clamped to `1`–`20`). Raising them trades how many sessions fit on screen for reading a long one in place. Three steppers in Settings → **Waiting rows**. |
|
|
604
|
+
| `fontFamily` | The **terminal font** every session renders in — a CSS font-family stack, e.g. `"'Cica', 'MS Gothic', monospace"`. Set it in Settings → **Terminal font**, applied at once; editing the file instead needs a **restart** (this config is read once at startup). Unset uses the built-in stack (JetBrains Mono / Fira Code / Menlo / Consolas, then CJK faces for Japanese, Korean and Chinese). Unlike the per-browser font **size**, this is one value for the whole host — it names fonts, and which fonts exist is a property of the machine. A directory can override it. See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#font-family). |
|
|
573
605
|
|
|
574
606
|
Every MulmoTerminal on the machine shares this one file, so an older build could save over a key a
|
|
575
607
|
newer one wrote. It doesn't: a **top-level key this version doesn't recognise is written back
|
|
@@ -681,6 +713,7 @@ malformed file is ignored.
|
|
|
681
713
|
```jsonc
|
|
682
714
|
{
|
|
683
715
|
"name": "PROD · payments", // badge shown on this directory's terminals
|
|
716
|
+
"icon": "docs/logo.png", // image on this dir's cells (path here, URL, or data:); omit to use the repo's favicon
|
|
684
717
|
"badgeColor": "#cf222e", // badge color (hex #rrggbb)
|
|
685
718
|
"headerColor": "#190a23", // cell header background (hex #rrggbb)
|
|
686
719
|
"headerTextColor": "#ffffff", // cell header text color (hex #rrggbb)
|
|
@@ -695,10 +728,46 @@ malformed file is ignored.
|
|
|
695
728
|
"orderPriority": 10, // rank in the grid's "priority" order and the launcher chips (lowest first)
|
|
696
729
|
"sound": "./.mulmoterminal/alert.mp3", // attention sound, RELATIVE to this directory
|
|
697
730
|
"sounds": { "command-failed": "preset:gong" }, // per-notification-kind override
|
|
698
|
-
"appendSystemPrompt": false
|
|
731
|
+
"appendSystemPrompt": false, // no closing summary here; omit to follow the global setting
|
|
732
|
+
"worktreeEnv": { // a port / database name of its own per git worktree
|
|
733
|
+
"PORT": { "kind": "port", "base": 3000 },
|
|
734
|
+
"DB_NAME": { "kind": "slug", "prefix": "myapp_" }
|
|
735
|
+
}
|
|
699
736
|
}
|
|
700
737
|
```
|
|
701
738
|
|
|
739
|
+
**Already have a `repo.json`?** MulmoTerminal reads it. It is an
|
|
740
|
+
[open repository-metadata format](https://receptron.github.io/mulmoterminal/repo-json.html) — one
|
|
741
|
+
small file any tool can read — and a project that ships one gets a coloured, named, icon-bearing
|
|
742
|
+
cell without knowing this app exists:
|
|
743
|
+
|
|
744
|
+
```json
|
|
745
|
+
{ "name": "diffusion-lab", "icon": "docs/logo.png", "color": "#7c3aed" }
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
One colour becomes all seven: the header is it exactly, the badge/border/dot/button/body are
|
|
749
|
+
derived from its hue, and the header text is derived for contrast. Anything this app understands
|
|
750
|
+
but the open format doesn't goes under `extensions.mulmoterminal`.
|
|
751
|
+
|
|
752
|
+
The three files layer, general to specific — **`repo.json` → `.mulmoterminal.json` →
|
|
753
|
+
`.mulmoterminal.local.json`** — replacing whatever keys the one below it set.
|
|
754
|
+
|
|
755
|
+
**Several clones of one repository?** Drop a `.mulmoterminal.local.json` beside it. It is read
|
|
756
|
+
after `.mulmoterminal.json` and **replaces whatever keys it names**, so the shared file holds what
|
|
757
|
+
the project is — name, theme, a colour — and each checkout's local file holds only what makes it
|
|
758
|
+
recognisable:
|
|
759
|
+
|
|
760
|
+
```jsonc
|
|
761
|
+
// .mulmoterminal.json — the project. Complete on its own, so one clone needs nothing else.
|
|
762
|
+
{ "name": "acme-web", "theme": "nord", "badgeColor": "#1b3479", "headerColor": "#2d4ea9", "orderPriority": 30 }
|
|
763
|
+
|
|
764
|
+
// .mulmoterminal.local.json — this checkout only. Gitignore it.
|
|
765
|
+
{ "badgeColor": "#27b4a8", "headerColor": "#4ed0c5", "orderPriority": 65 }
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Whole keys, not a deep merge: a `colors` block in the local file replaces the shared one entirely.
|
|
769
|
+
Settings → Directory settings names both files and lists which keys the local one took over.
|
|
770
|
+
|
|
702
771
|

|
|
703
772
|
|
|
704
773
|
*As cells pile up it gets hard to tell which project is which. Give each repo a **name badge** and its own colors in `.mulmoterminal.json` and they're unmistakable — `headerColor`/`badgeColor` tint the frame, while `colors` reaches all the way into the **terminal's own background and text**. (The example above dresses four repos in Mondrian / van Gogh / Picasso / Matisse palettes.)*
|
|
@@ -706,6 +775,7 @@ malformed file is ignored.
|
|
|
706
775
|
| Field | Meaning |
|
|
707
776
|
| ------------ | ------- |
|
|
708
777
|
| `name` | Label shown as a badge in the terminal/cell header. |
|
|
778
|
+
| `icon` | An **image** marking this directory — shown in the cell header, the cockpit roster, the filmstrip thumbnails and the launcher's directory chips. Either a path **relative to this directory** (an absolute path, or a `../` that escapes it, is rejected), an `http(s)://` URL, or a `data:image/…` URI. PNG / JPEG / **GIF (animated plays)** / WebP / AVIF / SVG / ICO / BMP. Not to be confused with a header **button's** `icon`, which is a Material Symbols name. **Omit it and the repository's own favicon is used** (`public/favicon.svg`, `apple-touch-icon.png`, a web manifest — see `autoDirIcon`); `false` means no icon here and stops that search. |
|
|
709
779
|
| `badgeColor` | Badge background color (`#rrggbb`); text auto-contrasts. |
|
|
710
780
|
| `headerColor` | Header **background** color (`#rrggbb`) — the grid cell's header row and the terminal's own header row (grid row 2). While a terminal is working/blocked the status tint still shows; the custom color applies when idle. |
|
|
711
781
|
| `headerTextColor` | Header **text** color (`#rrggbb`) — the dir path, title, and prompt. |
|
|
@@ -716,11 +786,12 @@ malformed file is ignored.
|
|
|
716
786
|
| `theme` | xterm palette for terminals in this directory (one of the built-in theme ids). |
|
|
717
787
|
| `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. |
|
|
718
788
|
| `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. |
|
|
719
|
-
| `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 sort by it, so a project sits in the same place on both. The one exception is the **workspace** chip, which always leads the launcher's row regardless of any rank — it is not one of the directories being ranked against each other, and it is the one place every GUI tool
|
|
789
|
+
| `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 sort by it, so a project sits in the same place on both. The one exception is the **workspace** chip, which always leads the launcher's row regardless of any rank — it is not one of the directories being ranked against each other, and it is the one place a claude or codex session reaches every GUI tool without registering anything (agy and grok get what the directory registered wherever they run — see [MCP server ids](#mcp-server-ids-why-a-workspace-cell-and-a-project-cell-disagree)). |
|
|
720
790
|
| `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. |
|
|
721
791
|
| `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. |
|
|
722
792
|
| `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. |
|
|
723
793
|
| `appendSystemPrompt` | Whether this directory's Claude sessions are asked to end a reply with a **closing summary** (see [Closing summary](#closing-summary)). Omit to follow the global `appendSystemPrompt`, which is on; `true` / `false` here outranks it. Read per spawn, so a new session in this directory picks up an edit without a restart. |
|
|
794
|
+
| `worktreeEnv` | Values every working tree of this project needs its **own** of — the port its dev server binds, the database its migrations touch. A worktree isolates files, not ports: two trees running `yarn dev` both reach for 3000 and the second one dies. Each variable is `{ "kind": "port", "base": <1024–65215> }` (a free port, `base` + a multiple of 10 — the checkout keeps `base`, its worktrees take the numbers above it) or `{ "kind": "slug", "prefix": "…" }` (a `[a-z0-9_]` name from the tree's task name, ≤ 63 chars, for a database / schema / container). Up to 16, under whatever names the project reads (`PORT`, `VITE_PORT`, …). A value is **reserved once and kept** (`~/.mulmoterminal/worktree-env.jsonl`) for as long as its declaration is unchanged, so a running dev server's port never moves under it; editing `base` re-allocates, renaming or dropping a variable releases what it held, and removing the worktree releases all of them. Set on every terminal in the directory — agent cell, Shell, launcher, Run command — and shown on the header's `env` chip, where a port is a link to `http://localhost:<port>`. MulmoTerminal hands out the name; creating the database is the project's own job. |
|
|
724
795
|
| `addDirs` | Extra directories this project's Claude sessions may read and edit — the terminal-side equivalent of opening several folders in one VS Code workspace, via Claude Code's `--add-dir`. Relative entries resolve against **this file's directory** (`"../shared-lib"`), a path that doesn't exist is dropped, max 16. Claude only: codex has no equivalent flag and ignores the key. |
|
|
725
796
|
|
|
726
797
|
**Security.** `sound` and every `sounds` entry are directory-relative paths only — absolute
|
|
@@ -778,7 +849,9 @@ An empty grid cell's launcher sets the **Working directory** by typing, by a pre
|
|
|
778
849
|
chip, or with the **📁 folder button** (a native OS folder dialog). The preset chips are
|
|
779
850
|
the directories you have launched in; the **workspace** leads them always, labelled
|
|
780
851
|
**WORKSPACE** and marked with an icon, whether or not you have ever launched there — it is
|
|
781
|
-
the one directory where a session reaches every GUI tool
|
|
852
|
+
the one directory where a claude or codex session reaches every GUI tool (agy and grok get
|
|
853
|
+
what the directory registered, there as anywhere — see
|
|
854
|
+
[MCP server ids](#mcp-server-ids-why-a-workspace-cell-and-a-project-cell-disagree)), so it is never a click you can
|
|
782
855
|
lose. It has no remove button for the same reason. It is named for its role rather than its
|
|
783
856
|
folder, because the folder name (`~/mulmoclaude` by default, or wherever `CLAUDE_CWD`
|
|
784
857
|
points) says the least interesting true thing about it; the real path is on its hover.
|
|
@@ -786,7 +859,12 @@ points) says the least interesting true thing about it; the real path is on its
|
|
|
786
859
|
**The launcher is shorter in the workspace**, because two of its choices do not apply
|
|
787
860
|
there. The per-directory **Canvas switches** are replaced by a line saying every GUI tool
|
|
788
861
|
is already available — a session there is handed the whole GUI MCP at spawn, so a switch
|
|
789
|
-
would register a group URL that then has nothing left to serve.
|
|
862
|
+
would register a group URL that then has nothing left to serve. **With Antigravity or Grok
|
|
863
|
+
picked they stay**, in the workspace as everywhere else: neither is handed anything at spawn
|
|
864
|
+
and both read their servers from the directory's file, so the switches are their only route
|
|
865
|
+
to a GUI tool and hiding them would leave the session with none (see
|
|
866
|
+
[MCP server ids](#mcp-server-ids-why-a-workspace-cell-and-a-project-cell-disagree)).
|
|
867
|
+
And the **worktree**
|
|
790
868
|
section is hidden: a worktree isolates work on one codebase onto a branch, while the
|
|
791
869
|
workspace is what a session works *from* (the shared wiki, collections and accounting
|
|
792
870
|
live there), which is precisely what a detached branch would cut it off from. Both come
|
|
@@ -803,7 +881,7 @@ different projects' scripts.
|
|
|
803
881
|
The same launcher also has an **or launch** row for your configured **launch commands**
|
|
804
882
|
— any interactive command — set in Settings (⚙) → **Launch commands** as
|
|
805
883
|
`{ label, command }` (e.g. `htop` → `htop`, `Codex` → `codex`). A plain shell needs no
|
|
806
|
-
entry here: the
|
|
884
|
+
entry here: the Agent Picker's **Shell** option already opens `$SHELL`. Unlike
|
|
807
885
|
a one-shot script, a launcher runs as a **persistent terminal in the cell's directory**:
|
|
808
886
|
it survives grid page switches and reconnects, and its dot shows running vs. exited (it
|
|
809
887
|
has no Claude hooks, so no blocked/done states).
|
|
@@ -946,16 +1024,20 @@ separate working tree that shares the repo's `.git`, so several agents can work
|
|
|
946
1024
|
repo without colliding. Worktrees live under `~/.mulmoterminal/worktrees/` (override with
|
|
947
1025
|
`MULMOTERMINAL_HOME`), and existing ones are listed below the field.
|
|
948
1026
|
|
|
949
|
-
**A worktree inherits the project's settings.**
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
`
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1027
|
+
**A worktree inherits the project's settings.** A fresh worktree used to have none — no colours,
|
|
1028
|
+
no name, no model, no grid rank. It is now given its own copy derived from the project's, written
|
|
1029
|
+
to **`.mulmoterminal.local.json`** so it layers over whatever the repository committed: `name` /
|
|
1030
|
+
`theme` / `colors` / `fontSize` / `fontFamily` / `provider` / `model` / `worktreeEnv` as written
|
|
1031
|
+
(the last is a declaration rather than a value — the worktree resolves its own values from it), the
|
|
1032
|
+
seven chrome colours **rotated 12 degrees further around the hue wheel per worktree** (so a
|
|
1033
|
+
project's trees read as a gradient; a grey like `#ffffff` has no hue to move and stays put), and
|
|
1034
|
+
`orderPriority` at the project's rank **+ 1**, so the worktree sorts directly after it. `sound` /
|
|
1035
|
+
`sounds` / `addDirs` are not carried — they name paths inside the project directory. Written only
|
|
1036
|
+
where git would **ignore what it writes**: an untracked file in a worktree's `git status` would
|
|
1037
|
+
make it count as dirty, and a dirty worktree is one MulmoTerminal refuses to remove. The local
|
|
1038
|
+
override is preferred; a repo that ignores `.mulmoterminal.json` instead (the setup this feature
|
|
1039
|
+
shipped with) still gets its colours there. A committed shared config is never written to. A local
|
|
1040
|
+
file the worktree already has is never overwritten.
|
|
959
1041
|
|
|
960
1042
|
**One worktree, one session.** A worktree is tied to a branch, so it is never started
|
|
961
1043
|
twice: a listed row **resumes** that worktree's session when it has one, and **starts** one
|
|
@@ -965,10 +1047,13 @@ the same worktree reached by pasting its path into **WORKING DIRECTORY**, or by
|
|
|
965
1047
|
chip, will not launch either — and the **server** refuses the spawn whichever client asks,
|
|
966
1048
|
so a path spelled another way (a trailing slash, a symlink) does not slip past.
|
|
967
1049
|
|
|
968
|
-
What the limit covers is an **agent**: Claude, Codex or
|
|
1050
|
+
What the limit covers is an **agent**: Claude, Codex, Antigravity or Grok, including an **OR
|
|
969
1051
|
LAUNCH** command that runs one of them. A **Shell**, and a launcher that runs anything else
|
|
970
1052
|
(`yarn dev`, `lazygit`, `htop`), stays free — a worktree an agent is working in is exactly
|
|
971
|
-
where you want those.
|
|
1053
|
+
where you want those. A project that declares `worktreeEnv` also gets **its own value per
|
|
1054
|
+
worktree** for each variable it declares there — so two dev servers given a port of their own do
|
|
1055
|
+
not both reach for 3000 (see
|
|
1056
|
+
[`worktreeEnv`](https://receptron.github.io/mulmoterminal/guide/en/config.html#worktree-env)).
|
|
972
1057
|
|
|
973
1058
|
The same holds for **OR RESUME HERE**: a session someone is holding is listed with `● open`
|
|
974
1059
|
and refused, where before it could be confirmed away — which detached whoever had it.
|
|
@@ -998,7 +1083,7 @@ Typing a task name yourself keeps the local base it has always used, with no fet
|
|
|
998
1083
|
|
|
999
1084
|

|
|
1000
1085
|
|
|
1001
|
-
*Every empty grid cell shows this launch form:
|
|
1086
|
+
*Every empty grid cell shows this launch form: pick an agent in the **Agent Picker** (**Claude / Codex / Antigravity / Grok / 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.*
|
|
1002
1087
|
|
|
1003
1088
|
A worktree cell's header carries a **diff badge** (`+<commits> ●<dirty>`); click it for a
|
|
1004
1089
|
**Changes vs `<base>`** panel (file list + patch) with actions:
|
|
@@ -1106,27 +1191,44 @@ Which route a session takes is decided by `carriesFullGuiMcp()` in
|
|
|
1106
1191
|
`server/session/mcp-config.ts` — the single view, a cell-less chat, or anything whose cwd **is**
|
|
1107
1192
|
the workspace take the first; anything in a project directory takes the second.
|
|
1108
1193
|
|
|
1109
|
-
**The workspace is agent-agnostic
|
|
1110
|
-
|
|
1111
|
-
the
|
|
1194
|
+
**The workspace is agent-agnostic for the agents that can RECEIVE a per-spawn config** — claude and
|
|
1195
|
+
codex ask the same predicate, so two terminals in the workspace reach the same tools no matter which
|
|
1196
|
+
of the two started them. **Antigravity and Grok cannot, and that is the exception you will meet
|
|
1197
|
+
first:**
|
|
1112
1198
|
|
|
1113
1199
|
| Started as | In the workspace | In a project directory |
|
|
1114
1200
|
|---|---|---|
|
|
1115
1201
|
| claude cell (including `?gui=0`) | `mt`, every tool | the directory's registered groups |
|
|
1116
1202
|
| codex cell | `mt`, every tool | the directory's registered groups |
|
|
1117
|
-
|
|
|
1118
|
-
|
|
|
1119
|
-
| launcher chip
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1203
|
+
| antigravity cell | **the directory's registered groups** — nothing registered means **no GUI tools at all** | the directory's registered groups |
|
|
1204
|
+
| grok cell | **the directory's registered groups** — nothing registered means **no GUI tools at all** | the directory's registered groups |
|
|
1205
|
+
| any launcher chip | untouched | untouched |
|
|
1206
|
+
|
|
1207
|
+
Neither takes an MCP flag: `agy` reads `.agents/mcp_config.json` and `grok` reads `.grok/config.toml`
|
|
1208
|
+
in the working directory, and a file shared by every session in a directory cannot be handed to one
|
|
1209
|
+
session and not another — so there is nothing for "this cwd is the workspace" to change. The
|
|
1210
|
+
membership is `FULL_GUI_MCP_AGENTS` in `common/guiMcpAgents.ts`, in `common/` precisely so the
|
|
1211
|
+
launcher form and the spawn cannot disagree about it
|
|
1212
|
+
([#1423](https://github.com/receptron/mulmoterminal/issues/1423)).
|
|
1213
|
+
|
|
1214
|
+
The consequence is easy to hit and hard to guess: `presentDocument` works in a project you once
|
|
1215
|
+
flipped **Canvas** on for, and is missing in the workspace where everything else is automatic. Fix it
|
|
1216
|
+
the same way anywhere — pick **Antigravity** or **Grok**, point WORKING DIRECTORY at that directory,
|
|
1217
|
+
flip the **Canvas** switch (it stays visible for both), and start a **new** session; the switch
|
|
1218
|
+
registers the directory, never a session already running. Full procedure:
|
|
1219
|
+
[Antigravity and Grok register everywhere](https://receptron.github.io/mulmoterminal/guide/en/basics.html#antigravity-gui-tools)
|
|
1220
|
+
· [日本語](https://receptron.github.io/mulmoterminal/guide/ja/basics.html#antigravity-gui-tools).
|
|
1221
|
+
|
|
1222
|
+
**A launcher chip is not an agent session — it is a command.** Whatever the command line names,
|
|
1223
|
+
it runs exactly as written: nothing is inserted, and no GUI MCP is attached. A chip running
|
|
1224
|
+
`claude` therefore reads only its directory's own `.mcp.json`, and a chip running `codex` has no
|
|
1225
|
+
Canvas. If you want a chip to reach the GUI tools, put the flags in the command yourself.
|
|
1226
|
+
|
|
1227
|
+
Earlier releases did rewrite a `claude` or `codex` chip to match the cell beside it. That was
|
|
1228
|
+
removed: a chip that silently runs something other than what it says is indistinguishable from
|
|
1229
|
+
the Agent Picker, which is the confusion the two controls exist on either side of. Use the Agent
|
|
1230
|
+
Picker for an agent session — it is also the only one of the two that gives you a resumable
|
|
1231
|
+
transcript, cost and context, and a "waiting for you" status.
|
|
1130
1232
|
|
|
1131
1233
|
**The asymmetry is deliberate.** `mt` is ours to name: nothing on disk holds it, it is
|
|
1132
1234
|
regenerated on every spawn, so it was shortened to stop paying 17 characters per tool name. The
|
|
@@ -1266,7 +1368,7 @@ quietly answering about the **default workspace** (#1151):
|
|
|
1266
1368
|
|
|
1267
1369
|
| Where | What happens |
|
|
1268
1370
|
| --- | --- |
|
|
1269
|
-
| `/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. |
|
|
1371
|
+
| `/ws`, `/ws/codex`, `/ws/antigravity`, `/ws/grok`, `/ws/launch`, `/ws/run` | The socket is closed with `{ type: "error", message }`, which the terminal shows as a red banner and does not retry. |
|
|
1270
1372
|
| 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. |
|
|
1271
1373
|
| `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/antigravity/sessions`, `/api/session/:id`, `/api/transcript/*`, `/api/cost` | `404 { error, cwd }` — a directory that is not there. |
|
|
1272
1374
|
| A `?cwd=` that cannot name a directory at all (relative, or repeated as `?cwd=a&cwd=b`) | `400 { error, cwd }`. |
|
|
@@ -1440,7 +1542,7 @@ same-origin-guarded.
|
|
|
1440
1542
|
| `POST /api/transcribe`(`/model`…) | Voice-input transcription (Whisper, macOS). |
|
|
1441
1543
|
| `POST /api/translation` | Runtime UI-string translation. |
|
|
1442
1544
|
| `GET /api/remote-host/status` · `POST /api/remote-host/{connect,disconnect}` | Companion phone-client link. Each response carries the command channel's `health` (`online` / `reconnecting` / `offline`, plus the last listener error), so the toolbar shows a dropped channel instead of the last state it happened to fetch. |
|
|
1443
|
-
| `POST /api/open-dir` · `POST /api/pick-file` | Reveal a dir in Finder/Explorer; OS file-picker → path (`{ directory: true }` opens the folder picker — used by the launcher's Working-directory 📁 button). |
|
|
1545
|
+
| `POST /api/open-dir` · `POST /api/pick-file` | Reveal a dir in Finder/Explorer; OS file-picker → path (`{ directory: true }` opens the folder picker — used by the launcher's Working-directory 📁 button). Both run on the SERVER's machine, and both answer **500 with a reason** when it has nothing to open a dialog with, rather than a silent nothing (#1447). The picker tries every dialog the host might have: macOS `osascript`, Windows PowerShell, **WSL** the Windows dialog over interop (`powershell.exe` + `wslpath`), Linux `zenity` → `kdialog` → `qarma` → `yad`. A user cancel is a 200 with `paths: []`. |
|
|
1444
1546
|
| `POST /api/session/:id/drop` | A dropped file whose path the browser withheld. **Raw bytes**, not JSON, under the file's own content type (base64 in JSON would cap real files near 18 MB, and a dropped `.json` would be parsed as a document); the original name rides percent-encoded in `x-drop-filename` and is used for its **suffix only**. Answers `{ path }` — absolute, inside the private per-session directory the session was granted at launch. 110 MiB cap; 404 for a session this server isn't running. |
|
|
1445
1547
|
|
|
1446
1548
|
The phone itself uses **none** of these routes — it reaches the host over Firestore command
|
|
@@ -1756,7 +1858,8 @@ server/
|
|
|
1756
1858
|
git/ git, GitHub (gh) and GitLab (glab) + worktrees: git-status.ts, gitRemote.ts,
|
|
1757
1859
|
gh.ts, prs.ts, issues.ts, pr-for-branch.ts, worktrees.ts, worktree-*.ts
|
|
1758
1860
|
files/ files-browse.ts (contained tree read/write), pick-file.ts,
|
|
1759
|
-
open-dir.ts,
|
|
1861
|
+
open-dir.ts, wsl.ts (interop detection + wslpath),
|
|
1862
|
+
scripts.ts (Run-menu script.json loader)
|
|
1760
1863
|
infra/ process/transport/misc: tmux.ts, tmux-routes.ts,
|
|
1761
1864
|
pubsub.ts (socket.io /ws/pubsub), spa-fallback.ts, host-tools.ts,
|
|
1762
1865
|
plugins-registry.ts, web-push.ts, install-bundled-skills.ts, accounting-tool.ts
|
|
@@ -1767,16 +1870,15 @@ server/
|
|
|
1767
1870
|
-header, -keys, -model, -notify, -bug-report, -decisions
|
|
1768
1871
|
fix-pty-perms.js postinstall: fixes node-pty binary permissions
|
|
1769
1872
|
src/
|
|
1770
|
-
App.vue Layout; owns the
|
|
1873
|
+
App.vue Layout; owns the grid, the overlays and the tab favicon
|
|
1771
1874
|
router/ Vue Router routes (/, /terminals, /collections,
|
|
1772
1875
|
/accounting, /prs, /files, /wiki, …)
|
|
1773
1876
|
components/
|
|
1774
|
-
Sidebar.vue, SessionTabBar.vue session list + tab bar (pub/sub driven)
|
|
1775
1877
|
Terminal.vue xterm.js terminal; /ws, /ws/codex, /ws/run
|
|
1776
1878
|
AppToolbar.vue shared header + toolbar buttons
|
|
1777
1879
|
GridView.vue, TerminalGrid.vue, TerminalCell.vue, CommandCell.vue, LauncherCell.vue
|
|
1778
|
-
CellLaunchForm.vue what an EMPTY cell shows:
|
|
1779
|
-
scripts / worktrees / tool groups
|
|
1880
|
+
CellLaunchForm.vue what an EMPTY cell shows: Agent Picker + dir +
|
|
1881
|
+
resume / scripts / worktrees / tool groups
|
|
1780
1882
|
GuiPanel.vue, PluginFrame.vue GUI panel (Canvas) + Shadow-DOM plugin host
|
|
1781
1883
|
FilesOverlay.vue file browser + CodeMirror editor
|
|
1782
1884
|
GitBranchChip.vue, ModelContextBadge.vue header chips / badges
|
package/bin/mulmoterminal.js
CHANGED
|
@@ -9,6 +9,7 @@ import { execSync, spawn } from "node:child_process";
|
|
|
9
9
|
import { existsSync, statSync } from "node:fs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createServer } from "node:net";
|
|
12
|
+
import { release } from "node:os";
|
|
12
13
|
import { dirname, join, resolve } from "node:path";
|
|
13
14
|
import { createInterface } from "node:readline";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
@@ -106,6 +107,40 @@ const PATH_TOOLS = [
|
|
|
106
107
|
{ cmd: "ollama", versionArg: "--version", required: false, why: "a fully local model via claude-ollama", hint: "https://ollama.com/download" },
|
|
107
108
|
];
|
|
108
109
|
|
|
110
|
+
// The variables WSL exports into a login shell, then the kernel that names itself
|
|
111
|
+
// (`…-microsoft-standard-WSL2`, `…-Microsoft` on WSL1) for the processes those variables never
|
|
112
|
+
// reach. Mirrors `isWsl()` in server/files/wsl.ts, which this file cannot import: bin runs as
|
|
113
|
+
// plain JS, the server runs through tsx. Keep the two in step.
|
|
114
|
+
function isWslHost() {
|
|
115
|
+
if (process.platform !== "linux") return false;
|
|
116
|
+
return Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) || /microsoft/i.test(release());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// The file dialog is the one requirement that differs per host, so it is checked per host rather
|
|
120
|
+
// than listed above: macOS and Windows have one built in and can't be missing it. WSL reaches the
|
|
121
|
+
// Windows dialog over interop, and a Linux desktop has whichever toolkit it was installed with —
|
|
122
|
+
// zenity's absence there is what makes the picker look broken (#1447).
|
|
123
|
+
function fileDialogTool() {
|
|
124
|
+
if (process.platform === "darwin" || process.platform === "win32") return null;
|
|
125
|
+
const why = "the file / folder picker";
|
|
126
|
+
if (isWslHost()) {
|
|
127
|
+
return {
|
|
128
|
+
cmd: "powershell.exe",
|
|
129
|
+
versionArg: "-NoProfile -Command exit",
|
|
130
|
+
required: false,
|
|
131
|
+
why: `${why}, using the Windows dialog`,
|
|
132
|
+
hint: "enable WSL interop (wsl.conf), or: sudo apt install zenity",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
cmd: "zenity",
|
|
137
|
+
versionArg: "--version",
|
|
138
|
+
required: false,
|
|
139
|
+
why: `${why} (kdialog / qarma / yad also work)`,
|
|
140
|
+
hint: "sudo apt install zenity · sudo dnf install zenity",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
109
144
|
function toolCheckLine({ cmd, versionArg, required, why, hint }) {
|
|
110
145
|
if (hasCommand(cmd, versionArg)) return ` ✓ ${cmd} — ${why}`;
|
|
111
146
|
const head = required ? ` ✗ ${cmd} — not found, needed for ${why}` : ` ○ ${cmd} — optional (${why})`;
|
|
@@ -139,7 +174,7 @@ async function runInit(initArgs) {
|
|
|
139
174
|
console.log(" → npm install -g @anthropic-ai/claude-code (then run `claude` and log in)");
|
|
140
175
|
}
|
|
141
176
|
|
|
142
|
-
PATH_TOOLS.forEach((tool) => console.log(toolCheckLine(tool)));
|
|
177
|
+
[...PATH_TOOLS, fileDialogTool()].filter(Boolean).forEach((tool) => console.log(toolCheckLine(tool)));
|
|
143
178
|
|
|
144
179
|
// Config half: derive working-dir presets from Claude history + write config.json.
|
|
145
180
|
console.log("");
|
|
@@ -188,9 +223,13 @@ function runGoogle(googleArgs) {
|
|
|
188
223
|
});
|
|
189
224
|
}
|
|
190
225
|
|
|
226
|
+
// WSL has no Linux browser to open (and often no `xdg-open` either), so the URL goes to the
|
|
227
|
+
// Windows shell — `start` through cmd.exe, which is what opens the user's real browser. It exits
|
|
228
|
+
// 0, unlike `explorer.exe`, so a failure here still means a failure.
|
|
191
229
|
function pickOpenCommand() {
|
|
192
230
|
if (process.platform === "darwin") return "open";
|
|
193
231
|
if (process.platform === "win32") return "start";
|
|
232
|
+
if (isWslHost()) return 'cmd.exe /c start ""';
|
|
194
233
|
return "xdg-open";
|
|
195
234
|
}
|
|
196
235
|
|