mulmoterminal 5.7.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.
Files changed (159) hide show
  1. package/README.md +7 -5
  2. package/bin/agent-install-guides.d.ts +10 -0
  3. package/bin/agent-install-guides.js +23 -0
  4. package/bin/agent-install-guides.json +9 -0
  5. package/bin/default-agent.js +5 -0
  6. package/common/agentAccounts.ts +61 -0
  7. package/common/agentAvailability.ts +50 -0
  8. package/dist/assets/{abnfDiagram-O67JEVCF-CYOfx7Yo.js → abnfDiagram-O67JEVCF-D_iilXNw.js} +1 -1
  9. package/dist/assets/architecture-7GRP2DOG-Dg93bZQr.js +1 -0
  10. package/dist/assets/{architectureDiagram-NJMV4G6O-Dm0h1sLC.js → architectureDiagram-NJMV4G6O-Dg6KqOfp.js} +1 -1
  11. package/dist/assets/{blockDiagram-BEXU5L5S-cC6WXel_.js → blockDiagram-BEXU5L5S-OpoBgS45.js} +1 -1
  12. package/dist/assets/{c4Diagram-YGBWAQC7-BqW75dss.js → c4Diagram-YGBWAQC7-BiPq7eyT.js} +1 -1
  13. package/dist/assets/channel-sVN0rzvb.js +1 -0
  14. package/dist/assets/{chunk-3FUC2YCW-DTVDYTX9.js → chunk-3FUC2YCW-Dl-tZ8u_.js} +1 -1
  15. package/dist/assets/{chunk-5DYCD2WN-Dg5McYUR.js → chunk-5DYCD2WN-QqDSMpGG.js} +1 -1
  16. package/dist/assets/{chunk-742MDFTN-BEAKMPzi.js → chunk-742MDFTN-CqyQ7H3U.js} +1 -1
  17. package/dist/assets/{chunk-7INBJB4K-C0yNpk2a.js → chunk-7INBJB4K-oUksIXIF.js} +1 -1
  18. package/dist/assets/{chunk-7M6MHVWA-D-sFy2uQ.js → chunk-7M6MHVWA-3CzQM4w0.js} +1 -1
  19. package/dist/assets/{chunk-7PRAP22T-B14Ti70Y.js → chunk-7PRAP22T-CE61sscF.js} +1 -1
  20. package/dist/assets/{chunk-GTNCS2PH-GehvsYOa.js → chunk-GTNCS2PH-DNCVJdqM.js} +1 -1
  21. package/dist/assets/{chunk-GWA4HPMP-BulkiEZu.js → chunk-GWA4HPMP-C99nmf_n.js} +1 -1
  22. package/dist/assets/{chunk-MBY4JIJT-tfx8juF1.js → chunk-MBY4JIJT-BBf_ea3Q.js} +1 -1
  23. package/dist/assets/{chunk-NETBCI7D-C47K-ltk.js → chunk-NETBCI7D-B6BfdVQJ.js} +1 -1
  24. package/dist/assets/{chunk-O7XYJQB3-BCjP9Rta.js → chunk-O7XYJQB3-CIi6yVqu.js} +1 -1
  25. package/dist/assets/{chunk-UA2S7LBM-BpEzQRku.js → chunk-UA2S7LBM-Bz2c68AY.js} +1 -1
  26. package/dist/assets/{chunk-WEXAMYUT-D2vVHs-s.js → chunk-WEXAMYUT-BXAdivY_.js} +1 -1
  27. package/dist/assets/{chunk-Z7XXMR3K-DrJC9r6c.js → chunk-Z7XXMR3K-8Rs1gJ7F.js} +1 -1
  28. package/dist/assets/{chunk-ZIGJFQKS-Cqa6msm6.js → chunk-ZIGJFQKS-BWGwelQ1.js} +1 -1
  29. package/dist/assets/{classDiagram-v2-NBCMYWYE-D69sDeij.js → classDiagram-v2-NBCMYWYE-DY_MTXmS.js} +1 -1
  30. package/dist/assets/{cynefin-OW5HDTMX-D9hRYnKO.js → cynefin-OW5HDTMX-JgQjEw_j.js} +1 -1
  31. package/dist/assets/{cynefinDiagram-VND7K2PF-DldT_nhD.js → cynefinDiagram-VND7K2PF-wv67JXTw.js} +1 -1
  32. package/dist/assets/{dagre-6A5THRUB-BlyXIart.js → dagre-6A5THRUB-BvNFYL64.js} +1 -1
  33. package/dist/assets/{diagram-22UHCM2B-CjYyg7-_.js → diagram-22UHCM2B-B5N0YRLw.js} +1 -1
  34. package/dist/assets/{diagram-3UASUU5V-D8pdVWk4.js → diagram-3UASUU5V-TS_HXEIA.js} +1 -1
  35. package/dist/assets/{diagram-ATOU4E4O-DxMrJkE8.js → diagram-ATOU4E4O-BD30PaAy.js} +1 -1
  36. package/dist/assets/{diagram-CDSNMT55-BrsVpho9.js → diagram-CDSNMT55-HypZAcSs.js} +1 -1
  37. package/dist/assets/{diagram-MLGK6HIB-CzfhH-bY.js → diagram-MLGK6HIB-DiAvCgLd.js} +1 -1
  38. package/dist/assets/{diagram-MPIPVDR6-BGHdoFkl.js → diagram-MPIPVDR6-CHEz46M8.js} +1 -1
  39. package/dist/assets/{dist-C5oyTNeI.js → dist-AkGY6ZSy.js} +1 -1
  40. package/dist/assets/{dist-CJNuReym.js → dist-C9XikeN4.js} +1 -1
  41. package/dist/assets/{dist-DEUFvoe-.js → dist-MgQJa3eo.js} +1 -1
  42. package/dist/assets/{dist-CQ590Pt6.js → dist-vsU0ZzUu.js} +1 -1
  43. package/dist/assets/{ebnfDiagram-ZINNZB2B-DCg9UZ81.js → ebnfDiagram-ZINNZB2B-B0rm83Mm.js} +1 -1
  44. package/dist/assets/{elk-276RUBZZ-C18yctnQ.js → elk-276RUBZZ-DBnezDNx.js} +1 -1
  45. package/dist/assets/{erDiagram-OPXOYQCR-Dq5BOJPw.js → erDiagram-OPXOYQCR-B0J0EfnS.js} +1 -1
  46. package/dist/assets/eventmodeling-NTZA5JFV-DbceiIgH.js +1 -0
  47. package/dist/assets/flowDiagram-KWPJA3E3-D6NP_RWp.js +1 -0
  48. package/dist/assets/{ganttDiagram-FUAMR5RP-BSrhA7Pa.js → ganttDiagram-FUAMR5RP-s2fe_9jO.js} +1 -1
  49. package/dist/assets/{gitGraph-4MIJSDKK-CvczhabF.js → gitGraph-4MIJSDKK-lEGh6K06.js} +1 -1
  50. package/dist/assets/{gitGraphDiagram-X574FWY7-29kAFfTL.js → gitGraphDiagram-X574FWY7-Cq2zmfXz.js} +1 -1
  51. package/dist/assets/{index-yIGIp5mP.js → index-BOnHUooX.js} +342 -342
  52. package/dist/assets/index-Dj3DfXLX.css +1 -0
  53. package/dist/assets/{info-A6RAGUB7-5Vfn18aA.js → info-A6RAGUB7-B251CDaE.js} +1 -1
  54. package/dist/assets/{infoDiagram-VRGFBTTK-C0xjRn68.js → infoDiagram-VRGFBTTK-CNb22cdU.js} +1 -1
  55. package/dist/assets/{ishikawaDiagram-OU5B5YK6-BNyfWBfD.js → ishikawaDiagram-OU5B5YK6-Bc56Q6j0.js} +1 -1
  56. package/dist/assets/{journeyDiagram-ZHPQQLJL-Bgoyolap.js → journeyDiagram-ZHPQQLJL-DU3ty_Ym.js} +1 -1
  57. package/dist/assets/{kanban-definition-PNTS6WVX-B_T0w2yS.js → kanban-definition-PNTS6WVX-suSodKMp.js} +1 -1
  58. package/dist/assets/{line-BrXN-7YZ.js → line-wPejaanq.js} +1 -1
  59. package/dist/assets/{mermaid-parser.core-Co2Ckrb2.js → mermaid-parser.core-C9FaUx4X.js} +3 -3
  60. package/dist/assets/{mermaid.core-D_YUclq0.js → mermaid.core-C86wCgRX.js} +4 -4
  61. package/dist/assets/{mindmap-definition-NLK3R4M7-fNbg5oGy.js → mindmap-definition-NLK3R4M7-aMqJ9xqk.js} +1 -1
  62. package/dist/assets/{packet-AYTQ26CC-CMQpjVs5.js → packet-AYTQ26CC-Bxv-7rY-.js} +1 -1
  63. package/dist/assets/{pegDiagram-GJSIUBJH-Be59j7k7.js → pegDiagram-GJSIUBJH-BGaHoPsU.js} +1 -1
  64. package/dist/assets/{pie-WAS4IAKB-BXlA1T4A.js → pie-WAS4IAKB-DFaAilpQ.js} +1 -1
  65. package/dist/assets/{pieDiagram-5QR66LMP-DNRFkJyr.js → pieDiagram-5QR66LMP-DmTVwWEd.js} +1 -1
  66. package/dist/assets/{quadrantDiagram-O4NWA36T-C0i5FRZ5.js → quadrantDiagram-O4NWA36T-C1Owo31s.js} +1 -1
  67. package/dist/assets/{radar-RG4KPBEZ-2KY2iAu1.js → radar-RG4KPBEZ-Dqu9x4Ut.js} +1 -1
  68. package/dist/assets/{railroad-74A4TZTK-C_GgKCcI.js → railroad-74A4TZTK-HplRxWGm.js} +1 -1
  69. package/dist/assets/railroad-abnf-HS5TGJTU-BOb3Tiah.js +1 -0
  70. package/dist/assets/railroad-ebnf-LZEXJU2U-CXOVCobD.js +1 -0
  71. package/dist/assets/railroad-peg-WCYAUIDC-VNpERTPV.js +1 -0
  72. package/dist/assets/{railroadDiagram-XR7U4H2S-fr0qrDLC.js → railroadDiagram-XR7U4H2S-D2iw36xG.js} +1 -1
  73. package/dist/assets/{requirementDiagram-PLB6GJNP-B2KHZAiT.js → requirementDiagram-PLB6GJNP-7GXrMj1G.js} +1 -1
  74. package/dist/assets/{sankeyDiagram-IPEJSGJF-BemGIYLY.js → sankeyDiagram-IPEJSGJF-BsfsDc82.js} +1 -1
  75. package/dist/assets/{sequenceDiagram-PO4LG4MO-CiH1vDbp.js → sequenceDiagram-PO4LG4MO-B1zk_5Kq.js} +1 -1
  76. package/dist/assets/{stateDiagram-v2-GCMORJYK-DmIHn8Nr.js → stateDiagram-v2-GCMORJYK-PLJGV3Hj.js} +1 -1
  77. package/dist/assets/{swimlanes-2SLR337P-BmsAt5p2.js → swimlanes-2SLR337P-DqbwuF2B.js} +1 -1
  78. package/dist/assets/swimlanesDiagram-TC7HE7FX-D7vXnQUD.js +8 -0
  79. package/dist/assets/{timeline-definition-EJHVYXUP-CyNooLGe.js → timeline-definition-EJHVYXUP-gX8g7kfI.js} +1 -1
  80. package/dist/assets/{treeView-Q6P3EWNA-o7bobygY.js → treeView-Q6P3EWNA-XFsSBFi8.js} +1 -1
  81. package/dist/assets/{treemap-WGGIJYW6-CZmVz7iJ.js → treemap-WGGIJYW6-BXAiy-Rl.js} +1 -1
  82. package/dist/assets/{usecaseDiagram-POWQR4AR-Bmltb1jH.js → usecaseDiagram-POWQR4AR-CpUW4ta2.js} +1 -1
  83. package/dist/assets/{vennDiagram-UO4OBE2U-dgKx3GjX.js → vennDiagram-UO4OBE2U-QE3iOuoX.js} +1 -1
  84. package/dist/assets/{wardley-WFR3VGLG-C2KTqHOy.js → wardley-WFR3VGLG-D50Fyi96.js} +1 -1
  85. package/dist/assets/{wardleyDiagram-VNRHLVJA-BUIrJNs3.js → wardleyDiagram-VNRHLVJA-CFbMITn7.js} +1 -1
  86. package/dist/assets/{xychartDiagram-PMCCYNJV-B1S0Fgu_.js → xychartDiagram-PMCCYNJV-CZ-dSSQj.js} +1 -1
  87. package/dist/index.html +2 -2
  88. package/package.json +1 -1
  89. package/server/agents/account-rate-limits.ts +145 -0
  90. package/server/agents/agent-availability.ts +22 -0
  91. package/server/agents/agent-homes.ts +44 -0
  92. package/server/agents/agent-session-title.ts +2 -2
  93. package/server/agents/antigravity-session.ts +2 -2
  94. package/server/agents/codex-rollout.spec.ts +30 -2
  95. package/server/agents/codex-rollout.ts +6 -6
  96. package/server/agents/codex-session.ts +2 -3
  97. package/server/agents/codex-skills.ts +2 -3
  98. package/server/agents/copilot-hooks-file.ts +2 -2
  99. package/server/agents/cursor-hooks-file.ts +2 -2
  100. package/server/agents/grok-session.ts +2 -2
  101. package/server/agents/muse-session.ts +2 -2
  102. package/server/agents/probe-transcript.ts +3 -2
  103. package/server/agents/rate-limit-persist.ts +13 -1
  104. package/server/agents/rate-limit-probe.ts +3 -1
  105. package/server/agents/rate-limit-routes.ts +21 -4
  106. package/server/agents/rate-limit-service.ts +32 -3
  107. package/server/agents/statusline.ts +10 -2
  108. package/server/backends/collections.ts +2 -2
  109. package/server/backends/remoteHost/getFeed.spec.ts +1 -1
  110. package/server/backends/remoteHost/handlers/deps.ts +5 -4
  111. package/server/backends/remoteHost/handlers/issueWork.ts +12 -6
  112. package/server/backends/remoteHost/handlers.spec.ts +1 -1
  113. package/server/backends/remoteHost/hostBindings.ts +11 -8
  114. package/server/backends/remoteHost/issueWork.spec.ts +35 -13
  115. package/server/backends/workspaceSetup.ts +2 -2
  116. package/server/cli-init.ts +2 -1
  117. package/server/config/app-config.ts +34 -0
  118. package/server/config/config-body.ts +1 -0
  119. package/server/config/config-routes.ts +13 -0
  120. package/server/config/config-schema.ts +10 -0
  121. package/server/git/issue-work.ts +40 -10
  122. package/server/index.ts +33 -0
  123. package/server/infra/gui-mcp-registration.ts +2 -7
  124. package/server/infra/install-bundled-skills.ts +12 -5
  125. package/server/routes/agent-availability-routes.ts +13 -0
  126. package/server/routes/app-routes.ts +8 -1
  127. package/server/routes/issue-work-routes.ts +12 -18
  128. package/server/routes/session-routes.ts +28 -14
  129. package/server/routes/ws-routes.ts +15 -5
  130. package/server/session/account-log.ts +46 -0
  131. package/server/session/account-mcp.ts +22 -0
  132. package/server/session/account-sessions.ts +48 -0
  133. package/server/session/agent-badges.ts +2 -2
  134. package/server/session/cleared-transcripts.ts +2 -2
  135. package/server/session/cost.ts +20 -6
  136. package/server/session/decision-scan.ts +7 -2
  137. package/server/session/dir-session.ts +2 -5
  138. package/server/session/issue-session-spawn.ts +94 -0
  139. package/server/session/mcp-config.ts +23 -0
  140. package/server/session/project-dir.ts +21 -4
  141. package/server/session/resumable-sessions.ts +2 -4
  142. package/server/session/session-home.ts +221 -0
  143. package/server/session/session-reads.ts +39 -23
  144. package/server/session/session-title.ts +2 -3
  145. package/server/session/spawn-claude.ts +14 -5
  146. package/server/session/spawn-codex.ts +9 -4
  147. package/server/session/survivor-agent-guard.ts +2 -3
  148. package/server/session/transcript-view-read.ts +4 -5
  149. package/server/session/types.ts +7 -0
  150. package/server/skills/mulmoterminal-model/SKILL.md +62 -2
  151. package/dist/assets/architecture-7GRP2DOG-Dijgh_EH.js +0 -1
  152. package/dist/assets/channel-DHp9c6le.js +0 -1
  153. package/dist/assets/eventmodeling-NTZA5JFV-CDYLpUt9.js +0 -1
  154. package/dist/assets/flowDiagram-KWPJA3E3-h1ikP3h8.js +0 -1
  155. package/dist/assets/index-ugC-gYB8.css +0 -1
  156. package/dist/assets/railroad-abnf-HS5TGJTU-Cf7XV4TE.js +0 -1
  157. package/dist/assets/railroad-ebnf-LZEXJU2U-CKRQohBA.js +0 -1
  158. package/dist/assets/railroad-peg-WCYAUIDC-CjbG--HC.js +0 -1
  159. package/dist/assets/swimlanesDiagram-TC7HE7FX-DvhbaIot.js +0 -8
package/README.md CHANGED
@@ -723,9 +723,9 @@ the `claude` / `codex` sessions themselves.
723
723
  | `CURSOR_BIN` | `cursor-agent` | The Cursor CLI binary to spawn. |
724
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. |
725
725
  | `MULMOTERMINAL_HOME` | `~/.mulmoterminal` | Root for managed **git worktrees**. |
726
- | `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. |
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. |
727
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. |
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/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. |
729
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. |
730
730
  | `WAIT_REAP_GRACE_MS` | `1800000` | How long a **waiting** background session is kept before it's auto-reaped (`0` or negative = never). |
731
731
 
@@ -777,6 +777,7 @@ The Settings modal (the gear button) persists per-user UI choices to `~/.mulmote
777
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. |
778
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. |
779
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. |
780
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. |
781
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. |
782
783
  | `buttons` | Header action buttons — see [Header buttons](#header-buttons). Omit to keep the defaults; set to replace them. |
@@ -1782,7 +1783,7 @@ newest first, including freshly-created sessions that aren't yet written to disk
1782
1783
  }
1783
1784
  ```
1784
1785
 
1785
- - Sessions are read from `~/.claude/projects/<encoded CLAUDE_CWD>/*.jsonl` and
1786
+ - Sessions are read from `<claude config home>/projects/<encoded CLAUDE_CWD>/*.jsonl` (`CLAUDE_CONFIG_DIR`, default `~/.claude`) and
1786
1787
  merged with in-memory sessions started this run but not yet persisted (those
1787
1788
  have `title: "New session"` and `mtime` = creation time).
1788
1789
  - Sorted by `mtime` descending and capped at the **50** most recent. Files are
@@ -1955,7 +1956,8 @@ same-origin-guarded.
1955
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). |
1956
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`. |
1957
1958
  | `GET /api/repo-dirs` | Which saved directories clone which GitHub repo, ordered, with the recorded choice per repo. |
1958
- | `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 as a draft. |
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. |
1959
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. |
1960
1962
 
1961
1963
  **Workspace views**
@@ -2259,7 +2261,7 @@ Codex sessions are unaffected — the CLI has no equivalent flag.
2259
2261
  ## Session discovery & titles
2260
2262
 
2261
2263
  Claude stores each project's sessions as JSONL files under
2262
- `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl`, where the absolute `cwd`
2264
+ `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl` (under `CLAUDE_CONFIG_DIR` instead of `~/.claude` when that is set), where the absolute `cwd`
2263
2265
  has its `/` and `.` characters replaced with `-` (e.g.
2264
2266
  `/Users/you/proj` → `-Users-you-proj`).
2265
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
+ }
@@ -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-Co2Ckrb2.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CllyRBe0.js";import"./chunk-O7XYJQB3-BCjP9Rta.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-GehvsYOa.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-D_YUclq0.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};
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};