mulmoterminal 5.6.0 → 5.8.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 +17 -6
- package/bin/agent-install-guides.d.ts +10 -0
- package/bin/agent-install-guides.js +23 -0
- package/bin/agent-install-guides.json +9 -0
- package/bin/default-agent.js +5 -0
- package/common/agentAccounts.ts +61 -0
- package/common/agentAvailability.ts +50 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-RFfgyLoh.js → abnfDiagram-O67JEVCF-D_iilXNw.js} +1 -1
- package/dist/assets/architecture-7GRP2DOG-Dg93bZQr.js +1 -0
- package/dist/assets/{architectureDiagram-NJMV4G6O-DAIQV4rO.js → architectureDiagram-NJMV4G6O-Dg6KqOfp.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-UROY713P.js → blockDiagram-BEXU5L5S-OpoBgS45.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-DTNuYtHX.js → c4Diagram-YGBWAQC7-BiPq7eyT.js} +1 -1
- package/dist/assets/channel-sVN0rzvb.js +1 -0
- package/dist/assets/{chunk-3FUC2YCW-C-X1ZyiP.js → chunk-3FUC2YCW-Dl-tZ8u_.js} +1 -1
- package/dist/assets/{chunk-5DYCD2WN-DYzrVJ4g.js → chunk-5DYCD2WN-QqDSMpGG.js} +1 -1
- package/dist/assets/{chunk-742MDFTN-DS7U8zBF.js → chunk-742MDFTN-CqyQ7H3U.js} +1 -1
- package/dist/assets/{chunk-7INBJB4K-B1wYOj10.js → chunk-7INBJB4K-oUksIXIF.js} +1 -1
- package/dist/assets/{chunk-7M6MHVWA-BiGTObKz.js → chunk-7M6MHVWA-3CzQM4w0.js} +1 -1
- package/dist/assets/{chunk-7PRAP22T-DVtJcWUJ.js → chunk-7PRAP22T-CE61sscF.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Da0ND9eE.js → chunk-GTNCS2PH-DNCVJdqM.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-CVesnc8B.js → chunk-GWA4HPMP-C99nmf_n.js} +1 -1
- package/dist/assets/{chunk-MBY4JIJT-L9FiAeQ3.js → chunk-MBY4JIJT-BBf_ea3Q.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-CXJT9YAs.js → chunk-NETBCI7D-B6BfdVQJ.js} +1 -1
- package/dist/assets/{chunk-O7XYJQB3-Dqy2Ij3F.js → chunk-O7XYJQB3-CIi6yVqu.js} +1 -1
- package/dist/assets/{chunk-UA2S7LBM-Bfg3sSte.js → chunk-UA2S7LBM-Bz2c68AY.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-RM1WdIlv.js → chunk-WEXAMYUT-BXAdivY_.js} +1 -1
- package/dist/assets/{chunk-Z7XXMR3K-Dt-Plljg.js → chunk-Z7XXMR3K-8Rs1gJ7F.js} +1 -1
- package/dist/assets/{chunk-ZIGJFQKS-3lsPo_kX.js → chunk-ZIGJFQKS-BWGwelQ1.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-DKOdHcNv.js → classDiagram-v2-NBCMYWYE-DY_MTXmS.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-CdfEeYnn.js → cynefin-OW5HDTMX-JgQjEw_j.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-IkWvIqEb.js → cynefinDiagram-VND7K2PF-wv67JXTw.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DLgBrsyE.js → dagre-6A5THRUB-BvNFYL64.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-zk6neLfs.js → diagram-22UHCM2B-B5N0YRLw.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-C7fNd6nz.js → diagram-3UASUU5V-TS_HXEIA.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-CzZ0ybLh.js → diagram-ATOU4E4O-BD30PaAy.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DFzYQbXV.js → diagram-CDSNMT55-HypZAcSs.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-LhlSm3At.js → diagram-MLGK6HIB-DiAvCgLd.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-DGlz-Gz_.js → diagram-MPIPVDR6-CHEz46M8.js} +1 -1
- package/dist/assets/{dist-BmiKcFDk.js → dist-AkGY6ZSy.js} +1 -1
- package/dist/assets/{dist-CYyCFtex.js → dist-C9XikeN4.js} +1 -1
- package/dist/assets/{dist-CUB7R6CP.js → dist-MgQJa3eo.js} +1 -1
- package/dist/assets/{dist-DlVfafDj.js → dist-vsU0ZzUu.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BXOpXLDq.js → ebnfDiagram-ZINNZB2B-B0rm83Mm.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-COOLC29s.js → elk-276RUBZZ-DBnezDNx.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-DLW9touV.js → erDiagram-OPXOYQCR-B0J0EfnS.js} +1 -1
- package/dist/assets/eventmodeling-NTZA5JFV-DbceiIgH.js +1 -0
- package/dist/assets/flowDiagram-KWPJA3E3-D6NP_RWp.js +1 -0
- package/dist/assets/{ganttDiagram-FUAMR5RP-B-J2yKj-.js → ganttDiagram-FUAMR5RP-s2fe_9jO.js} +1 -1
- package/dist/assets/{gitGraph-4MIJSDKK-D9V7nTh3.js → gitGraph-4MIJSDKK-lEGh6K06.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-D2ydD-sr.js → gitGraphDiagram-X574FWY7-Cq2zmfXz.js} +1 -1
- package/dist/assets/{index-jqKj2Ih-.js → index-BOnHUooX.js} +342 -342
- package/dist/assets/index-Dj3DfXLX.css +1 -0
- package/dist/assets/{info-A6RAGUB7-BZamo1a2.js → info-A6RAGUB7-B251CDaE.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-1mF8GY7H.js → infoDiagram-VRGFBTTK-CNb22cdU.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-Duj77geT.js → ishikawaDiagram-OU5B5YK6-Bc56Q6j0.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-avHPhq3z.js → journeyDiagram-ZHPQQLJL-DU3ty_Ym.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-B3YcPpJl.js → kanban-definition-PNTS6WVX-suSodKMp.js} +1 -1
- package/dist/assets/{line-DoCr4bJP.js → line-wPejaanq.js} +1 -1
- package/dist/assets/{mermaid-parser.core-V1gpWY2I.js → mermaid-parser.core-C9FaUx4X.js} +3 -3
- package/dist/assets/{mermaid.core-D3e7D68E.js → mermaid.core-C86wCgRX.js} +4 -4
- package/dist/assets/{mindmap-definition-NLK3R4M7-u1klmfn5.js → mindmap-definition-NLK3R4M7-aMqJ9xqk.js} +1 -1
- package/dist/assets/{packet-AYTQ26CC-BJILPUX-.js → packet-AYTQ26CC-Bxv-7rY-.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-B1a6bBkF.js → pegDiagram-GJSIUBJH-BGaHoPsU.js} +1 -1
- package/dist/assets/{pie-WAS4IAKB-DbR_G7bn.js → pie-WAS4IAKB-DFaAilpQ.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-BQP4p421.js → pieDiagram-5QR66LMP-DmTVwWEd.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-D8utwgpm.js → quadrantDiagram-O4NWA36T-C1Owo31s.js} +1 -1
- package/dist/assets/{radar-RG4KPBEZ-BXz4Ty90.js → radar-RG4KPBEZ-Dqu9x4Ut.js} +1 -1
- package/dist/assets/{railroad-74A4TZTK-B37cvSf2.js → railroad-74A4TZTK-HplRxWGm.js} +1 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-BOb3Tiah.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-CXOVCobD.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-VNpERTPV.js +1 -0
- package/dist/assets/{railroadDiagram-XR7U4H2S-DMSskLpg.js → railroadDiagram-XR7U4H2S-D2iw36xG.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DVh6Y9kv.js → requirementDiagram-PLB6GJNP-7GXrMj1G.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-7e6ybiU1.js → sankeyDiagram-IPEJSGJF-BsfsDc82.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-MwLzuLBw.js → sequenceDiagram-PO4LG4MO-B1zk_5Kq.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-ClFmP9D_.js → stateDiagram-v2-GCMORJYK-PLJGV3Hj.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-94uTsBbN.js → swimlanes-2SLR337P-DqbwuF2B.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D7vXnQUD.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-Do5dGsUL.js → timeline-definition-EJHVYXUP-gX8g7kfI.js} +1 -1
- package/dist/assets/{treeView-Q6P3EWNA-BcUdf2cs.js → treeView-Q6P3EWNA-XFsSBFi8.js} +1 -1
- package/dist/assets/{treemap-WGGIJYW6-g4e4QV1M.js → treemap-WGGIJYW6-BXAiy-Rl.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-CwWWrct5.js → usecaseDiagram-POWQR4AR-CpUW4ta2.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CzwGLaXY.js → vennDiagram-UO4OBE2U-QE3iOuoX.js} +1 -1
- package/dist/assets/{wardley-WFR3VGLG-CwShVfwe.js → wardley-WFR3VGLG-D50Fyi96.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-BYMi6RfJ.js → wardleyDiagram-VNRHLVJA-CFbMITn7.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-yED7nk5a.js → xychartDiagram-PMCCYNJV-CZ-dSSQj.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/agents/account-rate-limits.ts +145 -0
- package/server/agents/agent-availability.ts +22 -0
- package/server/agents/agent-homes.ts +44 -0
- package/server/agents/agent-session-title.ts +2 -2
- package/server/agents/antigravity-session.ts +2 -2
- package/server/agents/codex-rollout.spec.ts +30 -2
- package/server/agents/codex-rollout.ts +6 -6
- package/server/agents/codex-session.ts +2 -3
- package/server/agents/codex-skills.ts +2 -3
- package/server/agents/copilot-hooks-file.ts +2 -2
- package/server/agents/cursor-hooks-file.ts +2 -2
- package/server/agents/grok-session.ts +2 -2
- package/server/agents/muse-session.ts +2 -2
- package/server/agents/probe-transcript.ts +3 -2
- package/server/agents/rate-limit-persist.ts +13 -1
- package/server/agents/rate-limit-probe.ts +3 -1
- package/server/agents/rate-limit-routes.ts +21 -4
- package/server/agents/rate-limit-service.ts +32 -3
- package/server/agents/statusline.ts +10 -2
- package/server/backends/collections.ts +2 -2
- package/server/backends/remoteHost/getFeed.spec.ts +1 -1
- package/server/backends/remoteHost/handlers/deps.ts +5 -4
- package/server/backends/remoteHost/handlers/issueWork.ts +12 -6
- package/server/backends/remoteHost/handlers.spec.ts +1 -1
- package/server/backends/remoteHost/hostBindings.ts +11 -8
- package/server/backends/remoteHost/hostSessionList.ts +67 -13
- package/server/backends/remoteHost/issueWork.spec.ts +35 -13
- package/server/backends/remoteHost/phoneRowText.ts +60 -0
- package/server/backends/remoteHost/terminalScreen.ts +28 -3
- package/server/backends/workspaceSetup.ts +2 -2
- package/server/cli-init.ts +2 -1
- package/server/config/app-config.ts +34 -0
- package/server/config/config-body.ts +1 -0
- package/server/config/config-routes.ts +13 -0
- package/server/config/config-schema.ts +10 -0
- package/server/git/issue-work.ts +40 -10
- package/server/index.ts +50 -1
- package/server/infra/gui-mcp-registration.ts +2 -7
- package/server/infra/install-bundled-skills.ts +12 -5
- package/server/infra/tmux.ts +27 -0
- package/server/routes/agent-availability-routes.ts +13 -0
- package/server/routes/app-routes.ts +8 -1
- package/server/routes/issue-work-routes.ts +12 -18
- package/server/routes/session-routes.ts +28 -14
- package/server/routes/ws-routes.ts +15 -5
- package/server/session/account-log.ts +46 -0
- package/server/session/account-mcp.ts +22 -0
- package/server/session/account-sessions.ts +48 -0
- package/server/session/agent-badges.ts +2 -2
- package/server/session/cleared-transcripts.ts +2 -2
- package/server/session/cost.ts +20 -6
- package/server/session/decision-scan.ts +7 -2
- package/server/session/dir-session.ts +2 -5
- package/server/session/issue-session-spawn.ts +94 -0
- package/server/session/lifecycle.ts +3 -0
- package/server/session/mcp-config.ts +23 -0
- package/server/session/pane-mode-watch.ts +72 -0
- package/server/session/project-dir.ts +21 -4
- package/server/session/pty-connection.ts +40 -14
- package/server/session/resumable-sessions.ts +2 -4
- package/server/session/session-home.ts +221 -0
- package/server/session/session-reads.ts +50 -23
- package/server/session/session-title.ts +2 -3
- package/server/session/spawn-claude.ts +14 -5
- package/server/session/spawn-codex.ts +9 -4
- package/server/session/survivor-agent-guard.ts +2 -3
- package/server/session/transcript-view-read.ts +4 -5
- package/server/session/types.ts +7 -0
- package/server/skills/mulmoterminal-model/SKILL.md +62 -2
- package/dist/assets/architecture-7GRP2DOG-D6B2cgBI.js +0 -1
- package/dist/assets/channel-btmtLVS8.js +0 -1
- package/dist/assets/eventmodeling-NTZA5JFV-DzjiXhxT.js +0 -1
- package/dist/assets/flowDiagram-KWPJA3E3-DB_pveN5.js +0 -1
- package/dist/assets/index-Ckw6w67E.css +0 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-DyVFspQu.js +0 -1
- package/dist/assets/railroad-ebnf-LZEXJU2U-DdONoAAl.js +0 -1
- package/dist/assets/railroad-peg-WCYAUIDC-B7_KDdFK.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-CpVlK8Hz.js +0 -8
package/README.md
CHANGED
|
@@ -646,6 +646,12 @@ a crash. Windows keeps the old behaviour — the launcher ends when its server d
|
|
|
646
646
|
before. An explicit close (a cell's ✕) ends the tmux session; a machine reboot does not
|
|
647
647
|
survive (tmux itself is gone). Command-cell scripts are ephemeral and not persisted.
|
|
648
648
|
|
|
649
|
+
**Scrolling back can put a pane in tmux's copy-mode.** A program that does not handle the mouse
|
|
650
|
+
itself — a shell, or Codex's normal screen — scrolls through tmux: a wheel-up or a drag enters
|
|
651
|
+
copy-mode, where keys move through the history instead of reaching the program. While a pane is
|
|
652
|
+
in it, the cell shows a **"Viewing history" banner** with a **Back to input** button; `q` works
|
|
653
|
+
too. Leaving sends nothing to the program, and keys typed right after the button are not lost.
|
|
654
|
+
|
|
649
655
|
**Installing tmux** (optional):
|
|
650
656
|
|
|
651
657
|
```bash
|
|
@@ -717,9 +723,9 @@ the `claude` / `codex` sessions themselves.
|
|
|
717
723
|
| `CURSOR_BIN` | `cursor-agent` | The Cursor CLI binary to spawn. |
|
|
718
724
|
| `CURSOR_MODEL` | cursor default | Model passed to Cursor as `--model` (unset = cursor's own default). Cursor's model names are **account-specific** and a wrong one is a hard exit — check `cursor-agent --list-models` first. |
|
|
719
725
|
| `MULMOTERMINAL_HOME` | `~/.mulmoterminal` | Root for managed **git worktrees**. |
|
|
720
|
-
| `CLAUDE_CONFIG_DIR` |
|
|
726
|
+
| `CLAUDE_CONFIG_DIR` | `~/.claude` | Claude Code's own config directory. MulmoTerminal reads Claude's transcripts (`projects/`), prompt history (`history.jsonl`) and user skills (`skills/`) from it, and `.claude.json` from **inside** it (unset: `~/.claude.json`), which is how the per-project GUI MCP registration is checked (`server/session/project-dir.ts`). Set it in the environment MulmoTerminal starts from, so it matches the one Claude runs under. |
|
|
721
727
|
| `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`), and it is what decides where MulmoTerminal's own runtime state goes — see the note under the table. Set it to the same value MulmoClaude uses. |
|
|
722
|
-
| `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. |
|
|
728
|
+
| `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's skills root (`~/.claude/skills/`, and `$CLAUDE_CONFIG_DIR/skills/` as well when that is set) and the Codex skills root on startup. |
|
|
723
729
|
| `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. |
|
|
724
730
|
| `WAIT_REAP_GRACE_MS` | `1800000` | How long a **waiting** background session is kept before it's auto-reaped (`0` or negative = never). |
|
|
725
731
|
|
|
@@ -771,6 +777,7 @@ The Settings modal (the gear button) persists per-user UI choices to `~/.mulmote
|
|
|
771
777
|
| `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. |
|
|
772
778
|
| `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. |
|
|
773
779
|
| `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. |
|
|
780
|
+
| `accounts` | **Beta** ([setup guide](docs/guide/en/accounts.md)). `{ id, label, agent, home }` entries — a second login for Claude Code (`agent: "claude"`) or Codex (`"codex"`), kept in its own config directory `home` (absolute or `~/…`). Picked per cell in the launch form's **ACCOUNT** select (the cell's header then names it); a session started on one runs with `CLAUDE_CONFIG_DIR` / `CODEX_HOME` set to it, and is bound to that account for good: resume, cost, titles and history are read from there, session lists show every account's rows, and the toolbar shows each account's 5h / 7d usage beside the default's. An account pointed at `~/.claude` / `~/.codex` itself is treated as the default login: no variable, no gauge of its own. A cell with no account gets no such variable added (whatever MulmoTerminal's own environment carries is inherited as before), so configuring none changes nothing. Up to 8. |
|
|
774
781
|
| `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` scopes a chip to session kinds — any of `SESSION_AGENTS` (`"claude"`, `"codex"`, `"antigravity"`, `"grok"`, `"muse"`, `"copilot"`, `"cursor"`, `"shell"`); omit it to offer the chip everywhere. Empty by default. |
|
|
775
782
|
| `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. |
|
|
776
783
|
| `buttons` | Header action buttons — see [Header buttons](#header-buttons). Omit to keep the defaults; set to replace them. |
|
|
@@ -1776,7 +1783,7 @@ newest first, including freshly-created sessions that aren't yet written to disk
|
|
|
1776
1783
|
}
|
|
1777
1784
|
```
|
|
1778
1785
|
|
|
1779
|
-
- Sessions are read from
|
|
1786
|
+
- Sessions are read from `<claude config home>/projects/<encoded CLAUDE_CWD>/*.jsonl` (`CLAUDE_CONFIG_DIR`, default `~/.claude`) and
|
|
1780
1787
|
merged with in-memory sessions started this run but not yet persisted (those
|
|
1781
1788
|
have `title: "New session"` and `mtime` = creation time).
|
|
1782
1789
|
- Sorted by `mtime` descending and capped at the **50** most recent. Files are
|
|
@@ -1949,7 +1956,8 @@ same-origin-guarded.
|
|
|
1949
1956
|
| `POST /api/worktrees/create` · `/remove` · `/push` · `/pr` | Create on `agent/<slug>` — or, with `issue: <N>`, on `issue/<N>-<slug>` forked from a freshly fetched `origin/<base>`; remove (managed root only), push, open a PR (`gh`, else compare URL). |
|
|
1950
1957
|
| `GET /api/prs` · `GET /api/issues` | Open PRs / issues across the configured `prRepos` — `gh` for github.com entries, `glab` for gitlab.com and any host declared in `gitlabHosts`. |
|
|
1951
1958
|
| `GET /api/repo-dirs` | Which saved directories clone which GitHub repo, ordered, with the recorded choice per repo. |
|
|
1952
|
-
| `POST /api/issues/start` | Cut an issue's worktree in one of that repo's known clones and spawn a session there, seeded with the issue
|
|
1959
|
+
| `POST /api/issues/start` | Cut an issue's worktree in one of that repo's known clones and spawn a session there, seeded with the issue. An optional `agent` (any hosted agent; absent is `claude`, anything else is a 400) picks which. A Claude seed is a draft; every other agent runs it at once. The reply names the `agent` and whether the seed runs (`seedRuns`), so the cell attaches on the right endpoint. |
|
|
1960
|
+
| `GET /api/agents/availability` | Which hosted agents this machine can start — `{ agents: [{ agent, available, reason?, installGuide? }] }`, `reason` being `missing`, `no-such-path` (an `<AGENT>_BIN` naming nothing) or `not-executable`, and `installGuide` the agent's official install page (from `bin/agent-install-guides.json`) or `null`. The same check a spawn makes before it starts, run once at server start, so an agent installed later shows after a restart. |
|
|
1953
1961
|
| `GET /api/github/star` · `POST /api/github/star` | Whether you have starred MulmoTerminal, and star it (via `gh`). `starred: null` means `gh` could not answer, and hides the button. |
|
|
1954
1962
|
|
|
1955
1963
|
**Workspace views**
|
|
@@ -2034,6 +2042,7 @@ connection (or reattach to an existing background PTY).
|
|
|
2034
2042
|
| `{ "type": "session", "id": string }` | Sent immediately on connect — the session id this socket is bound to (lets the client learn a new session's generated id). |
|
|
2035
2043
|
| `{ "type": "output", "data": string }` | PTY output to write to the terminal. On reattach, the first `output` frame is the replayed tail buffer (≤ 64 KB). |
|
|
2036
2044
|
| `{ "type": "exit", "exitCode": number, "signal": number }` | The `claude` process exited; the socket then closes. |
|
|
2045
|
+
| `{ "type": "paneMode", "inCopyMode": boolean }` | tmux-backed sessions only: whether the pane is in tmux copy-mode, where keys go to tmux instead of the program. Sent when it changes, and again to a reattached socket. |
|
|
2037
2046
|
|
|
2038
2047
|
**Client → server** (JSON text frames):
|
|
2039
2048
|
|
|
@@ -2041,6 +2050,7 @@ connection (or reattach to an existing background PTY).
|
|
|
2041
2050
|
| ------- | ------- |
|
|
2042
2051
|
| `{ "type": "input", "data": string }` | Keystrokes / bytes to write to the PTY. |
|
|
2043
2052
|
| `{ "type": "resize", "cols": number, "rows": number }` | Resize the PTY. |
|
|
2053
|
+
| `{ "type": "exitCopyMode" }` | tmux-backed sessions only: leave copy-mode (`send-keys -X cancel`). Writes nothing to the PTY. |
|
|
2044
2054
|
|
|
2045
2055
|
A non-JSON frame is written to the PTY verbatim (fallback).
|
|
2046
2056
|
|
|
@@ -2050,7 +2060,8 @@ A non-JSON frame is written to the PTY verbatim (fallback).
|
|
|
2050
2060
|
|
|
2051
2061
|
### More WebSocket endpoints
|
|
2052
2062
|
|
|
2053
|
-
The other raw WebSockets share the `/ws` frame format (`output` / `input` / `resize` / `exit
|
|
2063
|
+
The other raw WebSockets share the `/ws` frame format (`output` / `input` / `resize` / `exit`, plus
|
|
2064
|
+
`paneMode` / `exitCopyMode` for a tmux-backed session).
|
|
2054
2065
|
**Every non-Claude agent has one** — `/ws/codex`, `/ws/antigravity`, `/ws/grok`, `/ws/muse`,
|
|
2055
2066
|
`/ws/copilot`, `/ws/cursor` — and they take the same query and behave the same way; codex's is
|
|
2056
2067
|
documented here as the representative one, and the per-agent differences are the matrix in
|
|
@@ -2250,7 +2261,7 @@ Codex sessions are unaffected — the CLI has no equivalent flag.
|
|
|
2250
2261
|
## Session discovery & titles
|
|
2251
2262
|
|
|
2252
2263
|
Claude stores each project's sessions as JSONL files under
|
|
2253
|
-
`~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
|
2264
|
+
`~/.claude/projects/<encoded-cwd>/<session-id>.jsonl` (under `CLAUDE_CONFIG_DIR` instead of `~/.claude` when that is set), where the absolute `cwd`
|
|
2254
2265
|
has its `/` and `.` characters replaced with `-` (e.g.
|
|
2255
2266
|
`/Users/you/proj` → `-Users-you-proj`).
|
|
2256
2267
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Types for bin/agent-install-guides.js — hand-written for the reason every other bin/*.d.ts is: the
|
|
2
|
+
// file is plain JS because it runs before tsx does, and the specs and the server are TypeScript.
|
|
3
|
+
export interface AgentInstallGuideEntry {
|
|
4
|
+
url: string;
|
|
5
|
+
/** The date (YYYY-MM-DD) the page was last opened and confirmed to be this agent's install guide. */
|
|
6
|
+
checked: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare function agentInstallGuide(agent: string): string | null;
|
|
10
|
+
export declare const AGENT_INSTALL_GUIDES: Readonly<Record<string, AgentInstallGuideEntry>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Where each hosted agent's OFFICIAL install guide lives (#2230). The URLs themselves are data, in
|
|
2
|
+
// agent-install-guides.json beside this file — edit that, not this.
|
|
3
|
+
//
|
|
4
|
+
// Links only, never install commands, for agent-bins.js's reason: a wrong command is the one thing
|
|
5
|
+
// a stuck user will paste. Each entry is a page on the maker's own domain that gives the install
|
|
6
|
+
// steps for the command this app runs; `checked` is the date someone last opened it and confirmed
|
|
7
|
+
// that. An agent whose page cannot be confirmed is left OUT rather than guessed — its readers get
|
|
8
|
+
// no link, which is better than a wrong one.
|
|
9
|
+
//
|
|
10
|
+
// Here in bin/ because the CLI's missing-agent message runs before tsx exists; the server imports
|
|
11
|
+
// it from here (as it does agent-bins.js), and the browser reads it off /api/agents/availability.
|
|
12
|
+
import { createRequire } from "node:module";
|
|
13
|
+
|
|
14
|
+
const GUIDES = createRequire(import.meta.url)("./agent-install-guides.json");
|
|
15
|
+
|
|
16
|
+
/** The install guide for `agent`, or null when none is recorded. */
|
|
17
|
+
export function agentInstallGuide(agent) {
|
|
18
|
+
const entry = Object.prototype.hasOwnProperty.call(GUIDES, agent) ? GUIDES[agent] : null;
|
|
19
|
+
return entry !== null && typeof entry === "object" && typeof entry.url === "string" ? entry.url : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The raw table, for the spec that checks every entry's shape. */
|
|
23
|
+
export const AGENT_INSTALL_GUIDES = GUIDES;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"claude": { "url": "https://code.claude.com/docs/en/setup", "checked": "2026-09-24" },
|
|
3
|
+
"codex": { "url": "https://learn.chatgpt.com/docs/codex/cli", "checked": "2026-09-24" },
|
|
4
|
+
"antigravity": { "url": "https://antigravity.google/docs/cli/install/", "checked": "2026-09-24" },
|
|
5
|
+
"grok": { "url": "https://docs.x.ai/build/overview", "checked": "2026-09-24" },
|
|
6
|
+
"muse": { "url": "https://dev.meta.ai/docs/muse-code", "checked": "2026-09-24" },
|
|
7
|
+
"copilot": { "url": "https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli", "checked": "2026-09-24" },
|
|
8
|
+
"cursor": { "url": "https://cursor.com/docs/cli/installation", "checked": "2026-09-24" }
|
|
9
|
+
}
|
package/bin/default-agent.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// saved Claude cell as something else. This value is read when a NEW session starts, and nowhere
|
|
14
14
|
// in the path that restores an old one.
|
|
15
15
|
import { AGENT_BIN_SPEC, AGENT_INSTALL_HINT } from "./agent-bins.js";
|
|
16
|
+
import { agentInstallGuide } from "./agent-install-guides.js";
|
|
16
17
|
|
|
17
18
|
export const isKnownAgent = (value) => typeof value === "string" && Object.prototype.hasOwnProperty.call(AGENT_BIN_SPEC, value);
|
|
18
19
|
|
|
@@ -92,6 +93,10 @@ export function missingAgentMessage(gate, bin) {
|
|
|
92
93
|
lines.push(' ~/.mulmoterminal/config.json: { "defaultAgent": "codex" }');
|
|
93
94
|
lines.push(` agents: ${Object.keys(AGENT_BIN_SPEC).join(", ")}`);
|
|
94
95
|
}
|
|
96
|
+
// The maker's own install page (#2230), from the same table the Agent Picker links to — so the
|
|
97
|
+
// terminal and the browser cannot send someone to two different places for one missing agent.
|
|
98
|
+
const install = agentInstallGuide(gate.agent);
|
|
99
|
+
if (install) lines.push(`Install guide: ${install}`);
|
|
95
100
|
lines.push("");
|
|
96
101
|
lines.push(`Details: ${GUIDE.en}`);
|
|
97
102
|
lines.push(`日本語: ${GUIDE.ja}`);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// An ACCOUNT: a second login for an agent CLI, kept in its own config home (#2215).
|
|
2
|
+
//
|
|
3
|
+
// Claude Code and Codex each keep their login, transcripts and settings in one directory, and
|
|
4
|
+
// both let the environment move it (CLAUDE_CONFIG_DIR / CODEX_HOME). An account names such a
|
|
5
|
+
// directory, so a cell can run on a different subscription from the cell beside it.
|
|
6
|
+
//
|
|
7
|
+
// It is NOT a custom agent (common/customAgents.ts). A custom agent is HOW the CLI is started;
|
|
8
|
+
// an account is WHOSE login it runs under. The two combine: an `ollama launch claude` custom
|
|
9
|
+
// agent can run on the work account.
|
|
10
|
+
//
|
|
11
|
+
// Configuring none changes nothing. A cell without an account has no variable ADDED to its spawn —
|
|
12
|
+
// not even the default value, because Claude Code keys its keychain entry on the variable being
|
|
13
|
+
// SET: pointing CLAUDE_CONFIG_DIR at `~/.claude` explicitly is already a different login. What the
|
|
14
|
+
// server's own environment already carries is inherited as before.
|
|
15
|
+
import { isRecord } from "./isRecord.js";
|
|
16
|
+
|
|
17
|
+
// The CLIs whose home an account can move. Each needs the spawn to set its variable and every
|
|
18
|
+
// reader of its state to follow the session's home, so adding one is that work, not a label.
|
|
19
|
+
export const ACCOUNT_AGENTS = ["claude", "codex"] as const;
|
|
20
|
+
|
|
21
|
+
export type AccountAgent = (typeof ACCOUNT_AGENTS)[number];
|
|
22
|
+
|
|
23
|
+
export const isAccountAgent = (value: unknown): value is AccountAgent => ACCOUNT_AGENTS.some((agent) => agent === value);
|
|
24
|
+
|
|
25
|
+
export interface AgentAccount {
|
|
26
|
+
/** Stable slug. It keys the wire (`?account=`) and the per-session record, so renaming the
|
|
27
|
+
* LABEL is free while renaming this strands the sessions started on it. */
|
|
28
|
+
id: string;
|
|
29
|
+
/** What the launch form and the cell show. */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Which CLI this is a login for. */
|
|
32
|
+
agent: AccountAgent;
|
|
33
|
+
/** The config home, absolute or `~/`-relative, as written. */
|
|
34
|
+
home: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Lowercase slug, for the same reasons as a custom agent id: it travels in a query string and is
|
|
38
|
+
// compared exactly on both sides.
|
|
39
|
+
export const ACCOUNT_ID_RE = /^[a-z0-9][a-z0-9_-]{0,31}$/;
|
|
40
|
+
|
|
41
|
+
export const isAccountId = (value: unknown): value is string => typeof value === "string" && ACCOUNT_ID_RE.test(value);
|
|
42
|
+
|
|
43
|
+
// Relative homes are refused rather than resolved: the CLI would resolve one against each cell's
|
|
44
|
+
// own working directory, i.e. a different home — and login — per directory.
|
|
45
|
+
export const isAccountHome = (value: unknown): value is string =>
|
|
46
|
+
typeof value === "string" && (value.startsWith("/") || value.startsWith("~/") || /^[A-Za-z]:[\\/]/.test(value));
|
|
47
|
+
|
|
48
|
+
/** A wire/config row read back as an AgentAccount. Used by the server sanitizing config.json and
|
|
49
|
+
* by the browser filtering GET /api/config. */
|
|
50
|
+
export function isAgentAccount(row: unknown): row is AgentAccount {
|
|
51
|
+
if (!isRecord(row)) return false;
|
|
52
|
+
return isAccountId(row.id) && typeof row.label === "string" && !!row.label.trim() && isAccountAgent(row.agent) && isAccountHome(row.home);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The accounts one agent can run on, in config order — none for an agent an account cannot move. */
|
|
56
|
+
export const accountsForAgent = (accounts: readonly AgentAccount[], agent: unknown): AgentAccount[] =>
|
|
57
|
+
isAccountAgent(agent) ? accounts.filter((account) => account.agent === agent) : [];
|
|
58
|
+
|
|
59
|
+
/** What a cell or a list row calls an account: its label, or the bare id once the entry has left the
|
|
60
|
+
* config (the session still runs on it — see server/session/account-log.ts). */
|
|
61
|
+
export const accountLabel = (accounts: readonly AgentAccount[], id: string): string => accounts.find((account) => account.id === id)?.label ?? id;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Which agents this machine can start, as GET /api/agents/availability reports it (#2229). Here
|
|
2
|
+
// rather than in server/ because the launch pickers will read it to grey out an agent that cannot
|
|
3
|
+
// run, and #2230 words its install guidance from `reason`.
|
|
4
|
+
import { isRecord } from "./isRecord";
|
|
5
|
+
import { isTerminalAgent, type TerminalAgent } from "./sessionAgent";
|
|
6
|
+
|
|
7
|
+
/** Why an agent cannot be started — the kind of the spawn preflight's own diagnosis, so the advice
|
|
8
|
+
* can differ: `missing` is not installed (or not on the server's PATH), `no-such-path` is an
|
|
9
|
+
* `<AGENT>_BIN` override naming nothing, `not-executable` is a file that cannot run. */
|
|
10
|
+
export const AGENT_UNAVAILABLE_REASONS = ["missing", "no-such-path", "not-executable"] as const;
|
|
11
|
+
|
|
12
|
+
export type AgentUnavailableReason = (typeof AGENT_UNAVAILABLE_REASONS)[number];
|
|
13
|
+
|
|
14
|
+
// No `bin`: with an `<AGENT>_BIN` override it is a path on this machine, and nothing that reads
|
|
15
|
+
// this needs it — the reason already says whether an override is what failed.
|
|
16
|
+
export type AgentAvailability =
|
|
17
|
+
| { agent: TerminalAgent; available: true }
|
|
18
|
+
/** `installGuide` is the agent's official install page, or null when none is recorded (#2230). */
|
|
19
|
+
| { agent: TerminalAgent; available: false; reason: AgentUnavailableReason; installGuide: string | null };
|
|
20
|
+
|
|
21
|
+
export interface AgentAvailabilityResponse {
|
|
22
|
+
agents: AgentAvailability[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const isUnavailableReason = (value: unknown): value is AgentUnavailableReason => AGENT_UNAVAILABLE_REASONS.some((reason) => reason === value);
|
|
26
|
+
|
|
27
|
+
function parseEntry(raw: unknown): AgentAvailability | null {
|
|
28
|
+
if (!isRecord(raw) || typeof raw.agent !== "string" || !isTerminalAgent(raw.agent)) return null;
|
|
29
|
+
if (raw.available === true) return { agent: raw.agent, available: true };
|
|
30
|
+
if (raw.available !== false || !isUnavailableReason(raw.reason)) return null;
|
|
31
|
+
return { agent: raw.agent, available: false, reason: raw.reason, installGuide: httpsUrlOrNull(raw.installGuide) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Only an https link is ever rendered: this goes into an href, and the server is the one source,
|
|
35
|
+
// but the page must not be the thing that trusts it.
|
|
36
|
+
function httpsUrlOrNull(value: unknown): string | null {
|
|
37
|
+
if (typeof value !== "string") return null;
|
|
38
|
+
try {
|
|
39
|
+
return new URL(value).protocol === "https:" ? value : null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The response body as the browser receives it, entry by entry: a malformed entry is dropped, not
|
|
46
|
+
* the whole answer, and an agent with no entry reads as available — today's behaviour. */
|
|
47
|
+
export function parseAgentAvailabilityResponse(raw: unknown): AgentAvailability[] {
|
|
48
|
+
if (!isRecord(raw) || !Array.isArray(raw.agents)) return [];
|
|
49
|
+
return raw.agents.map(parseEntry).filter((entry): entry is AgentAvailability => entry !== null);
|
|
50
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-C9FaUx4X.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CllyRBe0.js";import"./chunk-O7XYJQB3-CIi6yVqu.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-DNCVJdqM.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-C86wCgRX.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(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=n(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=n(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=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-FOHPRMQF-DzxwRta7.js";import{M as e}from"./mermaid-parser.core-C9FaUx4X.js";export{e as createArchitectureServices};
|