mulmoterminal 4.3.0 → 4.4.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 +45 -18
- package/common/workComment.ts +141 -14
- package/common/workCommentFailure.ts +16 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-Bdog-CiH.js → abnfDiagram-VRR7QNED-RjiYivmv-C43gBAG2.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-DRANrMDe.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-DY8jZRJO.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-BONcd9-U.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CpOQ2oxU.js → blockDiagram-677ZJIJ3-BPuAJQRW-CM2STruI.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-CWAcQCwl.js → c4Diagram-LMCZKHZV-C0LAqQso-B5vNwdXA.js} +1 -1
- package/dist/assets/channel-7wUqSdoX-qLt0VyIe.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-BzcHZdCO.js → chunk-32BRIVSS-BRrYpgtb-CoxhWbB9.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-Qq7iypNS.js → chunk-52WLFC77-BJ-ss3Xr-CId6SWzh.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-DU94jTaQ.js → chunk-C7G6YPKG-BZEucKEL-P5M5Yvpn.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-B8t9ONXm.js → chunk-EX3LRPZG-DLS6FBN1-DyLWbVhu.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-72qiJJFi.js → chunk-FWX5IMBZ-DHLSFw1H-DWcCl8Yk.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-Dh_S4m7L.js → chunk-HOUHSVGY-Bhlt8hXJ-vKYnv8Bj.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-i4M4RWjR.js → chunk-ICXQ74PX-DwgHBX_g-CS9xzHHA.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-ePkETRCM.js → chunk-MOJQB5TN-CMZRaeqt-gTPiZssc.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-BMPSaD1n.js → chunk-OGEWGWER-8Qy4a8b5-C9Osj4JZ.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-CUfUNRZU.js → chunk-PUDLZKDR-DcrWQRYh-CZwJPYWX.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-vRCdUFJt.js → chunk-Q4XR5HBZ-ZXVGkG8Z-Cco8LyAc.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-BPzQ8JEc.js → chunk-V7JOEXUC-DmGdheTX-CL2oW-GL.js} +1 -1
- package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-DDJgABqC.js → chunk-VAUOI2AC-CS9QJ4yz-DhyLweXm.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-DZS-oJom.js → chunk-VR4S4FIN-C6a91eNY-kS-cvTnu.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-DMj-dLn7.js → chunk-WYO6CB5R-BlzOfotS-B-BAzlIB.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-tmcZjNpX.js → chunk-ZGVPDNZ5-BYwxNFTK-B_5ITunz.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-ClENF_Zt.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-ClENF_Zt.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-Xw6wbIlg.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--BpiB89n2.js → cynefinDiagram-TSTJHNR4-DHA9iPo--CsvHy5QQ.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-Ddms1XzR.js → dagre-VKFMJZFB--oJKqXBZ-B_Vz2rxF.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-BAHWVmAY.js → diagram-FQU43EPY-CcJCB9bG-3PKZ2Ka3.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-DdaYFG9H.js → diagram-G47NLZAW-C_o-WGG1-BMfEdJEI.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--CHgaNPap.js → diagram-NH7WQ7WH-CXJCYvY--DMS7vR7_.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-C7QgoRtB.js → diagram-OA4YK3LP-BMzeJ87A-yVWZgC-A.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-D_93NKqo-DK7fOC2g.js → diagram-WEI45ONY-D_93NKqo-JY2oY6TV.js} +1 -1
- package/dist/assets/{dist-BvnEqyXN.js → dist-B5vu6eeE.js} +1 -1
- package/dist/assets/{dist-1yVb2v37.js → dist-By4s_hax.js} +1 -1
- package/dist/assets/{dist-N43w-1Uv.js → dist-CAS06ruL.js} +1 -1
- package/dist/assets/{dist-B8QpBddG.js → dist-cuEw-vsy.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-CVGfPNmr.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-CSODjPeQ.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-DY1yINPc.js → erDiagram-Q63AITRT-CiABnA0s-DMaAYLYB.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-CDRF15wA.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-nMfF1thc.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-BFu7XeTn.js → ganttDiagram-NO4QXBWP-DQZvdFo1-t6QKW7r_.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-BpVP-1CD.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-CVNiO1pR.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-8CB_sdi8.js} +1 -1
- package/dist/assets/index-BfZe-OnL.js +614 -0
- package/dist/assets/index-slI4QJ8P.css +1 -0
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-ugP0JbLq.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-C93AGObv.js → infoDiagram-FWYZ7A6U-7UnoB5AP-DCV7LBpV.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-B2Dn2jAL.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-DbEPqoeF.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-DQk5r-7H.js → journeyDiagram-5HDEW3XC-c5xIah9o-YWImF3-J.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-1TZVG0gD.js → kanban-definition-HUTT4EX6-Cnt6loYD-Cu6iQUPe.js} +1 -1
- package/dist/assets/{lib-JfWkHMZA.js → lib-D3KO7fnC.js} +1 -1
- package/dist/assets/{line-D7ziSjKi-DhbbwYBs.js → line-D7ziSjKi-rKNlfotQ.js} +1 -1
- package/dist/assets/{marp-BfY_ww9b.js → marp-ssHueLtD.js} +1 -1
- package/dist/assets/{mermaid-parser.core-DxEa8E3F-D6oVwac4.js → mermaid-parser.core-DxEa8E3F-CTOK7EeF.js} +2 -2
- package/dist/assets/{mermaid.core-V0OYwIz3-B6uMFLWJ.js → mermaid.core-V0OYwIz3-ikGsVp05.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-B1FJ1HVs.js → mindmap-definition-LN4V7U3C-CexN3O6L-BAQB7j75.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-ChXy_hCK.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW--wr3I5uW.js → pegDiagram-2B236MQR-BiM4G0cW-HDlX2gZc.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-DeFim35k.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-_2feGczD.js → pieDiagram-ENE6RG2P-B_fBBS-2-C7-OBdMS.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-BrtQISoY.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-C0w-ELAc.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-D-Y4D0Ax.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-tvzxXql8.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-CMCeKqcK.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-CM7KSH9R.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-Ds4sbE-D.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-Cl00PZJ3.js → railroadDiagram-RFXS5EU6--lNIlG64-CaMY_GQ1.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-BBU6_YVY.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-DWHcMDxg.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-DDG1vVfm.js → sankeyDiagram-HTMAVEWB-DlutaXDv-BXstTNJK.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-BLRc-c61.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-DNq_A2us.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-IElGdQ_H.js → stateDiagram-2N3HPSRC-jf_dXUEU-BcuA2rt-.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-CngDb_q-.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-DWw64pD1.js → swimlanes-5IMT3BWC-CYjtALQH-CFAIsGQE.js} +1 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-ktA_ajC9.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-CmkjOn4R.js → timeline-definition-FHXFAJF6-DYJ4oUm8-_przvOVq.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-BVr9yA0C.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-dwog38AV.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-Ch4eXyRT.js → vennDiagram-L72KCM5P-CdKAHoek-nDlMmrar.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-BcHLy1xv.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-B2NgeiNl.js → wardleyDiagram-EHGQE667-BF6c4_CW-CCl9h9eK.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-B544fj9E.js → xychartDiagram-FW5EYKEG-CGiKngj7-B6LaWKwe.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +8 -8
- package/server/agents/claude-args.ts +23 -14
- package/server/config/header-config.ts +18 -11
- package/server/git/forge-failure.ts +42 -0
- package/server/git/gh.ts +6 -1
- package/server/git/glab-items.ts +17 -3
- package/server/git/glab.ts +22 -1
- package/server/git/issue-work.ts +2 -15
- package/server/git/work-comment.ts +225 -60
- package/server/infra/jsonl-file.ts +96 -2
- package/server/infra/serialize-per-key.ts +27 -0
- package/server/mcp/broker.ts +6 -3
- package/server/mcp/tool-gate.ts +20 -1
- package/server/routes/dir-routes.ts +11 -4
- package/server/routes/mcp-routes.ts +11 -4
- package/server/routes/ws-routes.ts +25 -8
- package/server/session/all-tools-log.ts +54 -0
- package/server/session/cost.ts +60 -19
- package/server/session/decision-scan.ts +38 -19
- package/server/session/decisions.ts +61 -60
- package/server/session/file-cache.ts +47 -18
- package/server/session/header-hook.ts +10 -2
- package/server/session/issue-spawn-options.ts +2 -2
- package/server/session/launcher-gui-mcp.ts +25 -10
- package/server/session/mcp-config.ts +9 -5
- package/server/session/registry.ts +66 -7
- package/server/session/session-reads.ts +163 -64
- package/server/session/spawn-claude.ts +13 -6
- package/server/session/spawn-codex.ts +9 -4
- package/server/session/summary-scan.ts +82 -47
- package/server/session/transcript-fold.ts +89 -0
- package/server/session/transcript-sidecar.ts +136 -0
- package/server/session/transcript.ts +70 -38
- package/server/skills/mulmoterminal-header/SKILL.md +27 -12
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-rsT_LNkQ.js +0 -1
- package/dist/assets/channel-7wUqSdoX-C_3NKRG1.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-I_77uR8C.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-I_77uR8C.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-BASJNDQ6.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-DYbmMHSK.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DxbRE59B.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-DDSyOJ1L.js +0 -1
- package/dist/assets/index-C-UrTIoI.css +0 -1
- package/dist/assets/index-CHZiol4M.js +0 -614
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-bM6jK96q.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-M2i4X2qk.js +0 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-Bd_XkMs5.js +0 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-C3vBieyU.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-CGQcIWsR.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-jWYezWwF.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-Csc9w5bS.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-D5bhRRBY.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-KNFxI9rB.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-CjVzBocH.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-Db61iIhG.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-DX1IC1yH.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-IFR8KKBh.js +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MulmoTerminal
|
|
2
2
|
|
|
3
3
|
**Run multiple Claude Code and Codex sessions in parallel — and see which one needs you.**
|
|
4
4
|
|
|
@@ -34,6 +34,10 @@ ping to your phone when a task finishes. One `npx` command, no Electron, no conf
|
|
|
34
34
|
npx mulmoterminal@latest # starts on http://localhost:34567 and opens your browser
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Built by **[receptron](https://github.com/receptron)** — **[Satoshi Nakajima](https://x.com/snakajima)**,
|
|
38
|
+
software architect for **Windows 95** at Microsoft, and **[Isamu Arimoto](https://github.com/isamu)**;
|
|
39
|
+
the same two behind **[GraphAI](https://github.com/receptron/graphai)**. [More ↓](#who-builds-this)
|
|
40
|
+
|
|
37
41
|
> **Something looks wrong?** Type `/mulmoterminal-bug-report` in any MulmoTerminal session. The
|
|
38
42
|
> bundled skill hears the symptom out, checks your **real** config, schema and version to see
|
|
39
43
|
> whether the behaviour is configuration or by design, searches the existing issues — and only
|
|
@@ -393,7 +397,7 @@ today — **Claude Code** (the default), **Codex**, and **Antigravity** (`agy`).
|
|
|
393
397
|
file exists, and injects activity hooks per spawn (see
|
|
394
398
|
[Claude hook injection](#claude-hook-injection)) plus the
|
|
395
399
|
[closing summary](#closing-summary) instruction.
|
|
396
|
-
The **whole** GUI MCP (`--mcp-config
|
|
400
|
+
The **whole** GUI MCP (`--mcp-config`, on one all-tools URL) goes only to a session that is not a grid cell, or to a grid cell whose cwd IS the workspace — `claimFullGuiMcp` in `server/session/registry.ts`, which is what gives a workspace cell the tools the single view had before 4.0.0 removed it. That equivalence is about what the session *carries*: a workspace cell is still a grid cell in every other respect. A cell in a project directory attaches none of ours, so its GUI tools come from whichever [Canvas switches](#wiki-collections--the-gui-panel) are registered for it. **Either way, Claude Code loads your own MCP servers normally** — the directory's local scope, any `.mcp.json` up the tree, your global ones and your claude.ai connectors. It did not always: `--strict-mcp-config` used to ride along with `--mcp-config`, which hid all of that from the very sessions meant to be the most capable ([#1338](https://github.com/receptron/mulmoterminal/issues/1338), [#1385](https://github.com/receptron/mulmoterminal/issues/1385)).
|
|
397
401
|
- **Codex** — spawned as `codex` (override with `CODEX_BIN`; `CODEX_MODEL` sets
|
|
398
402
|
`--model`). Codex runs on its own WebSocket (`/ws/codex`) and its sessions appear in the
|
|
399
403
|
cockpit roster next to Claude's. Because Codex only mints its rollout id **after** the first
|
|
@@ -562,7 +566,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
562
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). |
|
|
563
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. No Settings UI: edit the file and reload the tab. 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). |
|
|
564
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. |
|
|
565
|
-
| `issueWorkComments` | Let a cell **comment on the issue it is working on**:
|
|
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 GitHub 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. When it **cannot** write — no `gh`, not logged in, or a login without write access — the cell says so next to the work chip rather than doing nothing silently; the work is unaffected either way. **Off by default**; it writes to GitHub, often on somebody else's issue. Needs `gh` logged in **with write access**. See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#issue-work-comments). |
|
|
566
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 (there is no Settings control for it). Appending is idempotent: an existing PR never gets a second copy. |
|
|
567
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 (there is no Settings control for it), though a session already running keeps what it was launched with. `true` / `false` only. |
|
|
568
572
|
| `fontFamily` | The **terminal font** every session renders in — a CSS font-family stack, e.g. `"'Cica', 'MS Gothic', monospace"`. No Settings UI: edit the file, then **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). |
|
|
@@ -772,15 +776,17 @@ In dev, open the Vite URL; its proxy forwards `/ws`, `/ws/pubsub`, and `/api` to
|
|
|
772
776
|
|
|
773
777
|
An empty grid cell's launcher sets the **Working directory** by typing, by a preset
|
|
774
778
|
chip, or with the **📁 folder button** (a native OS folder dialog). The preset chips are
|
|
775
|
-
the directories you have launched in; the **workspace** leads them always,
|
|
776
|
-
icon, whether or not you have ever launched there — it is
|
|
777
|
-
session reaches every GUI tool, so it is never a click you can
|
|
778
|
-
button for the same reason.
|
|
779
|
+
the directories you have launched in; the **workspace** leads them always, labelled
|
|
780
|
+
**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, so it is never a click you can
|
|
782
|
+
lose. It has no remove button for the same reason. It is named for its role rather than its
|
|
783
|
+
folder, because the folder name (`~/mulmoclaude` by default, or wherever `CLAUDE_CWD`
|
|
784
|
+
points) says the least interesting true thing about it; the real path is on its hover.
|
|
779
785
|
|
|
780
786
|
**The launcher is shorter in the workspace**, because two of its choices do not apply
|
|
781
787
|
there. The per-directory **Canvas switches** are replaced by a line saying every GUI tool
|
|
782
|
-
is already available — a session there is handed the whole GUI MCP at spawn, so
|
|
783
|
-
would
|
|
788
|
+
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. And the **worktree**
|
|
784
790
|
section is hidden: a worktree isolates work on one codebase onto a branch, while the
|
|
785
791
|
workspace is what a session works *from* (the shared wiki, collections and accounting
|
|
786
792
|
live there), which is precisely what a detached branch would cut it off from. Both come
|
|
@@ -970,6 +976,14 @@ and refused, where before it could be confirmed away — which detached whoever
|
|
|
970
976
|
`mulmoterminal` process on this machine: the server answers from its own PTY table plus
|
|
971
977
|
tmux, not from what one page can see.
|
|
972
978
|
|
|
979
|
+
**Change the directory and those lists empty immediately**, replaced by a single
|
|
980
|
+
`Loading this directory's sessions, worktrees and scripts…` row until the new ones arrive.
|
|
981
|
+
Everything the launch form offers below the field — **OR RESUME HERE**, the worktrees and
|
|
982
|
+
**OR RUN A SCRIPT** — belongs to the directory it was read for, and reading it costs a
|
|
983
|
+
debounce plus a round trip. Rows left standing through that wait would be the previous
|
|
984
|
+
directory's, listed under the new directory's name, and clicking one resumes exactly the
|
|
985
|
+
session it offers.
|
|
986
|
+
|
|
973
987
|
A worktree started **from an issue** gets an `issue/<N>-<slug>` branch instead. The number
|
|
974
988
|
in the name is what later tells the app which issue the work belongs to: the ⧉ Open PR
|
|
975
989
|
button puts `Fixes #<N>` in the PR body, and the branch chip, the issue work comment and
|
|
@@ -1108,11 +1122,11 @@ A chip is a command line the user wrote, so the injection is a **rewrite of thei
|
|
|
1108
1122
|
deliberately narrow: only a bare `claude` or `codex` is recognised, and anything else — a wrapper
|
|
1109
1123
|
script, `FOO=1 claude` — is passed through unchanged.
|
|
1110
1124
|
|
|
1111
|
-
A `claude` chip in the workspace is
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1125
|
+
A `claude` chip in the workspace is handed the all-tools URL, same as the cell, and like the cell it
|
|
1126
|
+
**does not** isolate the session: your own MCP servers and claude.ai connectors load as usual. Where
|
|
1127
|
+
a directory also registered per-group URLs in its `.mcp.json`, those groups stand down for that
|
|
1128
|
+
session rather than serving a second copy of the same tools — the session already reaches all of
|
|
1129
|
+
them under `mt`.
|
|
1116
1130
|
|
|
1117
1131
|
**The asymmetry is deliberate.** `mt` is ours to name: nothing on disk holds it, it is
|
|
1118
1132
|
regenerated on every spawn, so it was shortened to stop paying 17 characters per tool name. The
|
|
@@ -1802,10 +1816,23 @@ and `fetch` are mocked so the tests run without a server.
|
|
|
1802
1816
|
|
|
1803
1817
|
## Who builds this
|
|
1804
1818
|
|
|
1805
|
-
MulmoTerminal is built by
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1819
|
+
MulmoTerminal is built by **[receptron](https://github.com/receptron)** —
|
|
1820
|
+
**[Satoshi Nakajima](https://x.com/snakajima)** and **[Isamu Arimoto](https://github.com/isamu)**.
|
|
1821
|
+
|
|
1822
|
+
Satoshi was the software architect for **Windows 95**, **Windows 98** and **Internet Explorer
|
|
1823
|
+
3.0 / 4.0** at Microsoft, later founded UIEvolution / Xevo, and still builds from Seattle.
|
|
1824
|
+
|
|
1825
|
+
The two have shipped open source together since 2015, and the core of each venture has been
|
|
1826
|
+
public every time:
|
|
1827
|
+
|
|
1828
|
+
| | |
|
|
1829
|
+
|---|---|
|
|
1830
|
+
| **[VideoShader](https://github.com/snakajima/videoshader)** (2015) | GPU video processing for iOS, built at Veemob |
|
|
1831
|
+
| **[Swipe](https://github.com/swipe-org/swipe)** (2015) | an animation runtime that made manga move on phones |
|
|
1832
|
+
| **[OwnPlate](https://github.com/Nakajima-Foundation/ownplate)** (2020) | takeout ordering for restaurants during COVID, run at the Singularity Society and launched with ITOCHU |
|
|
1833
|
+
| **[SlashGPT](https://github.com/receptron/SlashGPT)** (2023) · **[GraphAI](https://github.com/receptron/graphai)** (2024) · **[MulmoCast](https://github.com/receptron/mulmocast-cli)** (2025) | LLM agents, declarative dataflow, AI video |
|
|
1834
|
+
|
|
1835
|
+
MulmoTerminal is the seventh.
|
|
1809
1836
|
|
|
1810
1837
|
It exists because we run several coding agents every day and kept losing track of which one was
|
|
1811
1838
|
waiting on us. Everything here was built for that, then kept because it worked. MIT licensed.
|
package/common/workComment.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
// The
|
|
2
|
-
// Pure, and shared, because the
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// The comment MulmoTerminal leaves on an issue: ONE per (issue, clone), edited as the work moves
|
|
2
|
+
// (#979, #1369). Pure, and shared, because the comment is its own storage — the server renders the
|
|
3
|
+
// body, reads it back on the next milestone, and appends to what it parsed. A mismatch between the
|
|
4
|
+
// two halves would either lose the earlier milestones or post a second comment on every poll.
|
|
5
|
+
import { GITHUB_REPO } from "./githubRepo.js";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// A milestone worth telling the issue about. NOT every state the cell passes through: CI is on the
|
|
8
|
+
// pull request already, and it flaps, so it is deliberately absent.
|
|
9
|
+
export type WorkCommentKind = "start" | "pr" | "merged";
|
|
10
|
+
|
|
11
|
+
const WORK_COMMENT_KINDS: readonly WorkCommentKind[] = ["start", "pr", "merged"];
|
|
12
|
+
|
|
13
|
+
export const isWorkCommentKind = (v: unknown): v is WorkCommentKind => WORK_COMMENT_KINDS.some((kind) => kind === v);
|
|
14
|
+
|
|
15
|
+
/** One milestone. `pr` is the pull request the milestone is about, null when there is none. */
|
|
16
|
+
export interface WorkEvent {
|
|
17
|
+
kind: WorkCommentKind;
|
|
18
|
+
/** `YYYY-MM-DD HH:MM UTC`, from the SERVER's clock — see formatWorkTime. */
|
|
19
|
+
at: string;
|
|
20
|
+
pr: number | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Only the two kinds that were ever written as a marker: `start` identifies the comment (see
|
|
24
|
+
// workAnchorMarker) and `merged` is the second comment older builds left, which is read back so an
|
|
25
|
+
// issue upgraded mid-flight does not get told about the same merge twice.
|
|
26
|
+
type WorkMarkerKind = "start" | "merged";
|
|
7
27
|
|
|
8
28
|
// An HTML comment, so it is invisible in the rendered issue but survives a round-trip through the
|
|
9
29
|
// GitHub API. Keyed by kind AND directory: the same issue worked on from a second clone is a
|
|
@@ -14,10 +34,15 @@ export type WorkCommentKind = "start" | "merged";
|
|
|
14
34
|
// issue as text (Codex review). Encoding also keeps a newline or a backtick in a path from
|
|
15
35
|
// reshaping the comment. Ordinary names encode to themselves, so markers already posted still
|
|
16
36
|
// match.
|
|
17
|
-
export function workCommentMarker(kind:
|
|
37
|
+
export function workCommentMarker(kind: WorkMarkerKind, dir: string): string {
|
|
18
38
|
return `<!-- mulmoterminal:work:${kind} dir=${encodeURIComponent(dir)} -->`;
|
|
19
39
|
}
|
|
20
40
|
|
|
41
|
+
/** The marker that IDENTIFIES this clone's comment, so later milestones can find it and edit it.
|
|
42
|
+
* Still spelled `:start` — that is what builds before #1369 wrote, and reusing it is what lets an
|
|
43
|
+
* already-posted comment be adopted instead of duplicated. */
|
|
44
|
+
export const workAnchorMarker = (dir: string): string => workCommentMarker("start", dir);
|
|
45
|
+
|
|
21
46
|
// The directory a comment names. The BASENAME only: the point is "which of my clones", and a full
|
|
22
47
|
// path on a public issue leaks the machine's layout (and, on a work machine, project names).
|
|
23
48
|
export function workCommentDirLabel(cwd: string): string {
|
|
@@ -27,18 +52,120 @@ export function workCommentDirLabel(cwd: string): string {
|
|
|
27
52
|
return parts[parts.length - 1] ?? cwd;
|
|
28
53
|
}
|
|
29
54
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
// Minutes, UTC, spelled out. A reader of the issue is asking "is this claim still alive" — the
|
|
56
|
+
// answer needs a date more than it needs seconds, and a fixed zone means two clones in two
|
|
57
|
+
// timezones write comparable lines.
|
|
58
|
+
export const formatWorkTime = (at: Date): string => `${at.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
59
|
+
|
|
60
|
+
const TIME = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2} UTC$/;
|
|
61
|
+
|
|
62
|
+
// Says who wrote it, in the rendered comment rather than only in the marker: this lands on other
|
|
63
|
+
// people's issues, and a reader should not have to guess which tool is claiming their issue.
|
|
64
|
+
const SIGNATURE = `<sub>posted by [MulmoTerminal](https://github.com/${GITHUB_REPO})</sub>`;
|
|
65
|
+
|
|
66
|
+
// A directory name may legally contain newlines and control characters, and the name is written
|
|
67
|
+
// into the headline as-is. The marker below is percent-encoded, but a newline here would put a
|
|
68
|
+
// line of the user's choosing into the body — including one that reads back as a milestone.
|
|
69
|
+
const displayDir = (dir: string): string =>
|
|
70
|
+
dir
|
|
71
|
+
.replace(/[\p{Cc}\p{Cf}]/gu, " ")
|
|
72
|
+
.replace(/\s+/g, " ")
|
|
73
|
+
.trim();
|
|
74
|
+
|
|
75
|
+
// The widest number a milestone line can carry. Render and parse have to agree on it: a line this
|
|
76
|
+
// side wrote and the parser could not read would vanish from the comment on the NEXT edit, taking
|
|
77
|
+
// its milestone with it (Codex review). Ten digits is far more than any forge issues, and the
|
|
78
|
+
// pattern below reads exactly that back.
|
|
79
|
+
const MAX_LINE_PR = 9_999_999_999;
|
|
80
|
+
|
|
81
|
+
const writablePr = (pr: number | null): pr is number => pr !== null && Number.isSafeInteger(pr) && pr > 0 && pr <= MAX_LINE_PR;
|
|
82
|
+
|
|
83
|
+
// The wording of the two states, unchanged from the build that wrote one comment per state: an
|
|
84
|
+
// issue that already carries "Working on this in `x`." must not read differently after an edit.
|
|
85
|
+
function headline(dir: string, events: readonly WorkEvent[]): string {
|
|
86
|
+
const shown = displayDir(dir);
|
|
87
|
+
const merged = events.find((event) => event.kind === "merged");
|
|
88
|
+
if (!merged) return `Working on this in \`${shown}\`.`;
|
|
89
|
+
// The same bound as the line: the headline is not parsed, but naming a number the line beneath
|
|
90
|
+
// it had to drop would make the comment contradict itself.
|
|
91
|
+
const where = writablePr(merged.pr) ? `Merged in #${merged.pr}.` : "Merged.";
|
|
92
|
+
return `${where} Work done in \`${shown}\`.`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Null for an event that cannot be written as a line the parser would read back — a PR milestone
|
|
96
|
+
// with no number says nothing anyway.
|
|
97
|
+
function eventLine(event: WorkEvent): string | null {
|
|
98
|
+
if (event.kind === "start") return `- started — ${event.at}`;
|
|
99
|
+
if (event.kind === "pr") return writablePr(event.pr) ? `- PR #${event.pr} — ${event.at}` : null;
|
|
100
|
+
return writablePr(event.pr) ? `- merged in #${event.pr} — ${event.at}` : `- merged — ${event.at}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isLine = (line: string | null): line is string => line !== null;
|
|
104
|
+
|
|
105
|
+
/** The whole comment body for a clone's milestones so far. Rendered from scratch every time, so
|
|
106
|
+
* the body is a function of the events and nothing else. */
|
|
107
|
+
export function renderWorkComment(dir: string, events: readonly WorkEvent[]): string {
|
|
108
|
+
const lines = events.map(eventLine).filter(isLine);
|
|
109
|
+
return [headline(dir, events), "", ...lines, "", SIGNATURE, "", workAnchorMarker(dir)].join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// MAX_LINE_PR, spelled as digits — the parse half of the same bound. It stops a run of digits out
|
|
113
|
+
// of an edited body from reaching the parser as something that would print as `1e+20`, and it
|
|
114
|
+
// keeps `\d*` from sitting inside an optional group, which `security/detect-unsafe-regex` reads as
|
|
115
|
+
// a backtracking risk (measured as linear here, but a bound is a better answer than an exception).
|
|
116
|
+
const PR_DIGITS = "[1-9]\\d{0,9}";
|
|
117
|
+
const START_LINE = /^- started — (.+)$/;
|
|
118
|
+
const PR_LINE = new RegExp(`^- PR #(${PR_DIGITS}) — (.+)$`);
|
|
119
|
+
const MERGED_LINE = new RegExp(`^- merged(?: in #(${PR_DIGITS}))? — (.+)$`);
|
|
120
|
+
|
|
121
|
+
// Strict about the timestamp on purpose. The body is editable by anyone reading the issue, and
|
|
122
|
+
// whatever this returns is written straight back into the next edit — so a line that is not
|
|
123
|
+
// exactly what render wrote is dropped rather than echoed.
|
|
124
|
+
const timed = (kind: WorkCommentKind, pr: number | null, at: string | undefined): WorkEvent | null =>
|
|
125
|
+
at !== undefined && TIME.test(at) ? { kind, at, pr } : null;
|
|
126
|
+
|
|
127
|
+
// `Number` is exact for anything the patterns above admit, so there is no second check here: a
|
|
128
|
+
// line whose number is longer than that does not match at all, and is dropped like every other
|
|
129
|
+
// line that is not exactly what render wrote.
|
|
130
|
+
const prNumber = (digits: string | undefined): number | null => (digits === undefined ? null : Number(digits));
|
|
131
|
+
|
|
132
|
+
function parseEventLine(line: string): WorkEvent | null {
|
|
133
|
+
const started = START_LINE.exec(line);
|
|
134
|
+
if (started) return timed("start", null, started[1]);
|
|
135
|
+
const pr = PR_LINE.exec(line);
|
|
136
|
+
if (pr) return timed("pr", prNumber(pr[1]), pr[2]);
|
|
137
|
+
const merged = MERGED_LINE.exec(line);
|
|
138
|
+
// A merge with no number is a line render writes and reads back, so null is an answer here
|
|
139
|
+
// rather than a failure.
|
|
140
|
+
return merged ? timed("merged", prNumber(merged[1]), merged[2]) : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** The milestones a comment body records, in the order it lists them. Empty for a comment written
|
|
144
|
+
* before #1369 — those carry the marker and the headline but no lines, and the caller supplies
|
|
145
|
+
* the start from the comment's own creation time. */
|
|
146
|
+
export const parseWorkEvents = (body: string): WorkEvent[] =>
|
|
147
|
+
body
|
|
148
|
+
.split("\n")
|
|
149
|
+
// trimEnd, not trim: a body round-tripped through the forge can come back CRLF, and `\r` at the
|
|
150
|
+
// end of the timestamp would fail the strict check above.
|
|
151
|
+
.map((line) => parseEventLine(line.trimEnd()))
|
|
152
|
+
.filter((event): event is WorkEvent => event !== null);
|
|
153
|
+
|
|
154
|
+
/** `events` with `event` added, or null when the comment already records it — which is the normal
|
|
155
|
+
* answer, because the caller asks on every poll of every open tab. */
|
|
156
|
+
export function withWorkEvent(events: readonly WorkEvent[], event: WorkEvent): WorkEvent[] | null {
|
|
157
|
+
// A PR milestone is its number, and a number the line cannot carry is not a milestone this
|
|
158
|
+
// comment can record — refused here rather than added and then silently dropped by render.
|
|
159
|
+
if (event.kind === "pr" && !writablePr(event.pr)) return null;
|
|
160
|
+
// Matched on the PR number too: a second pull request for the same issue from the same clone —
|
|
161
|
+
// the first one closed unmerged, say — is a new milestone, not a repeat of the old one.
|
|
162
|
+
if (events.some((known) => known.kind === event.kind && known.pr === event.pr)) return null;
|
|
163
|
+
return [...events, event];
|
|
37
164
|
}
|
|
38
165
|
|
|
39
166
|
// Has this exact comment already been left? Matching on the marker rather than the prose means an
|
|
40
167
|
// edited comment, or a change to the wording above, still counts as "already said".
|
|
41
|
-
export function alreadyCommented(bodies: readonly string[], kind:
|
|
168
|
+
export function alreadyCommented(bodies: readonly string[], kind: WorkMarkerKind, dir: string): boolean {
|
|
42
169
|
const marker = workCommentMarker(kind, dir);
|
|
43
170
|
return bodies.some((body) => typeof body === "string" && body.includes(marker));
|
|
44
171
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Why MulmoTerminal could not write its work comment (#1369). Shared, because the server decides
|
|
2
|
+
// it from `gh`'s exit and the UI turns it into a sentence — a second copy of the list on the client
|
|
3
|
+
// would drift into wording a server can never send.
|
|
4
|
+
//
|
|
5
|
+
// Not commenting stays a normal outcome: the work carries on either way. What this adds is the
|
|
6
|
+
// REASON, which used to exist nowhere — a read-only `gh` login made the feature do nothing at all,
|
|
7
|
+
// with the same silence as having it switched off.
|
|
8
|
+
|
|
9
|
+
/** `permission` is the one this exists for: logged in, but not allowed to write on that repo. */
|
|
10
|
+
export type WorkCommentFailure = "cli-missing" | "auth" | "permission" | "unknown";
|
|
11
|
+
|
|
12
|
+
const FAILURES: readonly WorkCommentFailure[] = ["cli-missing", "auth", "permission", "unknown"];
|
|
13
|
+
|
|
14
|
+
// The value arrives over the wire, so a build skew (an older server naming a cause this one has
|
|
15
|
+
// never heard of) has to read as "no cause" rather than reach the UI's wording switch.
|
|
16
|
+
export const isWorkCommentFailure = (v: unknown): v is WorkCommentFailure => FAILURES.some((failure) => failure === v);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-
|
|
1
|
+
import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-CTOK7EeF.js";import"./chunk-WYO6CB5R-BlzOfotS-B-BAzlIB.js";import"./chunk-VAUOI2AC-CS9QJ4yz-DhyLweXm.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-gTPiZssc.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as e}from"./mermaid-parser.core-DxEa8E3F-CTOK7EeF.js";export{e as createArchitectureServices};
|