Skip to content

Releases: receptron/mulmoterminal

mulmoterminal@4.16.0

Choose a tag to compare

@isamu isamu released this 03 Sep 10:17

Highlights

A deck in an ordinary repository can now be opened

4.15.0 shipped the ⊞ Mulmo menu and the file tree's Open in the Canvas, and both only worked under the workspace MulmoTerminal was started in. A deck anywhere else was found and then correctly refused — the app could see it and could not serve it.

  • #1958 — every directory your launcher has saved is now a stories root, registered at startup beside the workspace. Nothing to configure: a directory qualifies by being one you already launch cells in.

    The list is read once, at startup, so a repository opened for the very first time needs a restart before its decks show. That is the deliberate half of the trade: registering later means rebuilding the plugin's server ops, and that one object also holds the in-flight movie and PDF generation state — a cell opened during a render would have discarded it.

The Tools pane stops being a dead end

  • #1967No GUI plugin tools enabled. said the one thing you could already see. It now names where tools come from and how to switch a group on: the switches are on an empty cell's launch form, and a session reads the registration when it starts. Either half alone sends you looking for a control that is not on screen.

    It also stops claiming things it does not know — an empty list from a failed request, an unreadable response, or a request still in flight is no longer reported as a confirmed empty configuration, so the pane will not send you off to reconfigure a folder that is fine.

  • #1969 — switching cells kept the previous session's tool list, and its "this agent reports no hooks" note, until the new answer arrived. The list now carries the session it describes, while a same-session refresh still keeps the answer it has.

  • #1960 — the mulmoterminal-shared-app skill used to stop and point you at the launcher's tool-group switch, which is hard to find if you have never seen it. It now hands the agent the claude mcp add command that switch runs, so the agent registers the group itself and the only thing left for you is reopening the cell.

Fixes

  • #1955 — a collection's custom view could 404 in the Canvas. Staged authoring keeps views/*.html in the staging directory alone, and the host's "is this a managed workspace" predicate was asking whether the path was ~/mulmoclaude — so this server's own workspace lost staging entirely.
  • #1959@mulmoclaude/core 4.6.0, which decides staged-ness per collection slug rather than per root. Without it a stale staged view could win over a committed one in a collection that was imported rather than staged.

Setup guide: 4.16.0 — Decks in an ordinary repository · 日本語

npm: mulmoterminal@4.16.0


What's Changed

  • fix(collections): staged なカスタムビューを、このサーバ自身のワークスペースでも読む (#1925) by @isamu in #1955
  • feat(mulmoscript): 起動時に登録する stories root を複数にして、一般的なリポジトリのデッキも開けるようにする (#1951) by @isamu in #1958
  • chore(deps): @mulmoclaude/core 4.6.0 — staged view の判定が slug 単位になる (#1957) by @isamu in #1959
  • docs(skills): tool group の登録コマンドを skill に書き、ランチャー任せをやめる (#1931) by @isamu in #1960
  • feat(tools): Tools ペインの空状態に、ツールグループの有効化手段を書く (#1966) by @isamu in #1967
  • fix(tools): セル切り替え中に前のセッションのツール一覧が出たままになるのを直す (#1968) by @isamu in #1969

Full Changelog: 4.15.0...4.16.0

mulmoterminal@4.15.0

Choose a tag to compare

@isamu isamu released this 02 Sep 21:48

Highlights

Open a mulmoScript deck in the Canvas — four PRs, one story

A presentation deck kept in a repository could only be opened by asking the agent, one turn at a time. It can now be opened directly, from three places, and the deck stays the file in your repository — editing it in the Canvas edits that file.

  • #1924 — right-click a row in the file tree. The row menu gained Open in the Canvas, offered for the files a plugin can actually render. It works in any cell with a Canvas beside it, a plain shell cell included.
  • #1934 — decks anywhere under the workspace. The mulmoScript plugin served stories from artifacts/stories alone, so a deck kept beside the code it is about could not be opened at all. The workspace subtree is now a registered stories root, which is what lets a deck live in the repository. Two decks of the same name in different roots stay two separate Canvas cards.
  • #1942 — a refusal now says why. A click the server refused looked exactly like a dead button. The reason — the deck was deleted, the workspace moved since startup — is shown in the Files pane, announced through a live region so a screen reader gets it too.
  • #1950 — the ⊞ Mulmo header menu. A dropdown beside Run and Skill, listing this project's decks. Picking one shows it in the Canvas without a turn spent asking the agent: no tokens, and it works while the agent is busy. Two sources and no search of your disk — the workspace's artifacts/stories, plus the paths you list in a new per-project decks key. A search was built first and deleted: in a real workspace it found 250 decks, of which 217 were a checked-out repository's test fixtures.
// <your-project>/.mulmoterminal.json
{ "decks": ["decks/launch.json", "docs/talks/retro.json"] }

Collections: see and change which agent a chat starts

  • #1940 — the agent a collection's chat launches is global and persistent, but was visible in one dropdown only. Set it to something other than Claude once and every chat started from a screen without that dropdown used it — which produced a real "why is Meta Muse starting?" report that was configuration, not a bug. A shared picker now appears in the in-cell Collections pane header and in Settings' skill-launch dialog, and stays out of the way while the value is claude.
  • #1946 — the chat modal covers the screen and blurs what is behind it, so the picker was invisible at the one moment it is needed. It now fills the footer slot the plugin opened for it (@mulmoclaude/collection-plugin 4.6.0).

Fixes

  • #1952 — reloading a tab with a custom theme left the terminal's own background on Midnight's dark blue while the rest of the chrome took the custom colours. The xterm palette is repainted when the config arrives, rather than decided at mount from a theme list that is still empty.

Under the hood

  • #1936@mulmoclaude/mulmoscript-plugin 4.5.1, which stops a spurious warning on startup.
  • #1937 — the guide's header page split into an introduction and a reference, with when and the substitution variables corrected against the implementation.
  • #1947, #1954 — two test fixes: the string a seeded spawn hands each agent is pinned, and a Windows CI check that measured elapsed time now asks whether a retry happened instead.
  • #1949 — dependency update.

Setup guide: 4.15.0 — Show a deck from your repository · 日本語

npm: mulmoterminal@4.15.0


What's Changed

  • feat(files): ツリーの行を右クリックして、そのファイルを Canvas で開く (#1923) by @isamu in #1924
  • fix(sharedApp): publish が「匿名に公開」と言うのは public.enabled が立っているときだけ (#1926) by @snakajima in #1930
  • feat(mulmoscript): ワークスペース以下に置いたデッキを Canvas で開いて編集する (#1933) by @isamu in #1934
  • feat(collections): 誰がどのコレクションに触れるかをペインで示す by @snakajima in #1935
  • chore: @mulmoclaude/mulmoscript-plugin 4.5.1 — 起動時の誤った警告が消える by @isamu in #1936
  • docs(guide): ヘッダーのページを入門とリファレンスに分割し、when と変数を実装に揃える (#1928) by @isamu in #1937
  • feat(collections): 起動エージェントを、ドロップダウンが見えない画面でも見せて変えられるようにする (#1938) by @isamu in #1940
  • feat(collections): 「チャットを開始」ダイアログに起動エージェントのピッカーを出す (#1945) by @isamu in #1946
  • fix(files): Canvas で開けなかった理由をペインに出す (#1941) by @isamu in #1942
  • test(server): seeded spawn が各エージェントに渡す文字列を固定する (#1939 は再現せず) by @isamu in #1947
  • fix(theme): 設定到着後に xterm のパレットを塗り直す (#1943) by @isamu in #1952
  • fix(test): Windows CI で落ちる config-lock の経過時間予算を、retry の有無に置き換える (#1953) by @isamu in #1954
  • update by @isamu in #1949
  • feat(header): [Mulmo ▾] でリポジトリのデッキを Canvas に出す(宣言ベース) (#1948) by @isamu in #1950

Full Changelog: 4.14.0...4.15.0

mulmoterminal@4.14.0

Choose a tag to compare

@isamu isamu released this 29 Aug 23:36

Highlights

  • Restart an agent without leaving the cell (#1918, #1920) — same cell, same directory, same conversation. Opt-in on two surfaces: a header button ("run": "action" with "action": "restart") and the terminal-restart keyboard action. Neither ships in the defaults, because a restart costs a resume: the conversation is read back from its transcript, and it acts immediately, even mid-turn.
  • Shared apps: ownRead (#1917) — @receptron/sharedapp 0.35.0 lets a view receive only the rows the viewer wrote, instead of every row plus a per-row ownership check. The magazine template's decision 10 records when to reach for it and what it costs (limit, and catching a duplicate slug before the row is sent).
  • A PORT left behind in the tmux server no longer steals this app's port (#1919, #1921) — 4.11.0 stopped the launcher handing MulmoTerminal's own port to every cell, but could not reach the tmux server, which outlives every restart. Startup now clears a PORT that equals the port this server is listening on, and a tmux server MulmoTerminal creates is never started carrying it. A PORT you set yourself is untouched.

Setup guide: Restart an agent without leaving the cell — written at release time (日本語). The full per-PR detail is in docs/ChangeLog.md.

Restart the agent in the cell you are looking at (#1918, #1920)

Changing an MCP server, a plugin or ~/.mulmoterminal/config.json left the agent already running in a cell with the old view of the world, and the way back was to close the cell, pick the directory and agent again in the launcher, and find the conversation under or resume here.

{ "id": "restart", "icon": "restart_alt", "label": "Restart the agent", "run": "action", "action": "restart" }

The shape is decided by tmux. Reconnecting to a live tmux session only attaches — nothing is re-read, because nothing is re-started — so a restart reaps the tmux session and lets the next connection start a fresh --resume. No server route was added: the mechanism is the close button's existing POST /api/session/:id/terminate, awaited, then a retarget onto the same session id. Reversing those two steps attaches to a session that has not been reaped yet, which looks exactly like a restart that did nothing, so the ordering lives as a pure function pinned by two specs that both go red when it is swapped. codex, antigravity, grok and muse cells take the same path.

Shared apps: ownRead limits a participant's page to their own rows (#1917)

The declaration is not a permission — it makes the page simpler, dropping the owner test and the third "still deciding" state. The magazine template's own declaration is deliberately unchanged: while a handful of people are writing, handing the whole collection to every page is what lets the app say "that URL name is taken" before the article is sent.

A PORT left behind in tmux (#1919, #1921)

Measuring the reported case turned up a second route that was live in 4.13.0 and is not in the issue: PORT=<n> mulmoterminal leaves that value in the server's own environment, so when no tmux server is running yet, the first spawn's client creates one carrying it — and a tmux server keeps the environment it was started with for life.

Both are closed by one rule: the only PORT taken away is the one this server is listening on. Anything else under that name stays, because nothing in the tmux environment distinguishes a leftover from a value the user set — PORT=3000 mulmoterminal --port 34601 still puts the user's 3000 in the cells, and a value admitted that way survives a later restart carrying no PORT. Verified end to end with an isolated server and a real pane reading its own environment back.

Documentation

The header guide said ~/.mulmoterminal/config.json buttons apply to "every terminal". They apply to agent cells: a launcher chip, a Shell cell or a Run command shows no custom buttons, because that terminal is the user's own command line. Corrected in both languages — found while taking the release screenshot.

Dependencies (#1922)

@google/genai 2.19.0, zod 4.5.4, puppeteer 25.9.0, sharp 0.35.4, knip 6.33.0.

📦 npm: mulmoterminal@4.14.0


What's Changed

  • feat: sharedapp 0.35.0 — 参加者のページを自分の行に絞る ownRead by @snakajima in #1917
  • feat: セルの中でエージェントを再起動する — 設定ボタンとキーボードショートカット (#1918) by @isamu in #1920
  • fix(tmux): mulmoterminal 自身の PORT を tmux 経由でセルに配らない (#1919) by @isamu in #1921
  • update by @isamu in #1922

Full Changelog: 4.13.0...4.14.0

4.13.0

Choose a tag to compare

@isamu isamu released this 29 Aug 11:39

Highlights

Browsing files stops taking the whole screen, a file's path can be put where you are typing, every keyboard shortcut is finally listed, and a splitter drag no longer gets swallowed by an embedded preview.

  • "Browse files in the app" opens the pane, not the full-screen view (#1910, #1911) — the path menu now opens the file pane beside the enlarged terminal, enlarging the cell first if the grid is tiled. A header button you wrote yourself ("run": "open" with open.files) still opens the full-screen view, deliberately: a button can carry any path, the pane can only be rooted at the enlarged terminal's directory.
  • Right-click a tree row to insert its path at the terminal's cursor (#1859, #1912) — Insert relative path / Insert absolute path, landing in the sentence you are already typing.
  • Settings lists every keyboard shortcut, bound or not (#1858, #1892, #1894) — read-only on purpose; keymap is written by the mulmoterminal-keys skill, reached from a Set up shortcuts… button that says what the agent does that the screen cannot. Action labels are translated.
  • A splitter drag is no longer eaten by an iframe (#1899, #1907) — the drag now captures the pointer, so events reach the separator whatever the cursor passes over. Three more endings were closed with it: pointercancel, lostpointercapture, and a second press on the same bar. Worth recording: the separator tracks the pointer 1:1, so a slow drag never reaches the iframe at all — it takes a fast one, a width already at its floor, or a preview sitting flush against the bar.
  • The server serves ::1 as well as 127.0.0.1 (#1893, #1903) — localhost resolves to both, and the address the launcher printed was not always the one the browser reached.

Smaller fixes

  • Copy button on a rendered code block (#1895, #1908) — CopyCodeBlock dropped the class it was passed, so the button lost its styling under a fragment root.
  • Collision warning names the right winner (#1901, #1906) — with copy and a send binding on one keystroke, copy acts only while text is selected, so send is what fires when nothing is.
  • Header buttons and chips replace, not merge (#1896, #1904) — the config API replaces the whole array; the mulmoterminal-header skill said "partial merge" and users lost buttons they already had.
  • questionPaneEnabled is documented (#1897, #1905) — it existed in the UI, the skills and the coverage test, and in no guide page.
  • browserUrl stays on localhost for two reasons (#1900, #1909) — both written down, with what to do when sign-in will not complete.
  • Two grid specs stopped making real requests (#1913, #1916) — they mounted components without mocking fetch; the guard is now a shared helper.

Setup guide: Files beside the terminal, and a path you can insert (日本語) — written at release time, with screenshots.

📦 npm: mulmoterminal@4.13.0


What's Changed

  • feat: キー列送信を「知っている人しか辿り着けない」状態から出す (#1858) by @isamu in #1892
  • i18n: キーボードショートカット一覧のアクションのラベルを翻訳する (#1894) by @isamu in #1902
  • fix: -header skill の指示どおりに書くと既存の buttons / chips が消える (#1896) by @isamu in #1904
  • fix: copy と send がキーで衝突したとき、勝者は 1 つに決まらない (#1901) by @isamu in #1906
  • docs: questionPaneEnabled を設定ガイドに書く (#1897) by @isamu in #1905
  • fix(server): サーバ自身が ::1 も serve して、localhost の宛先を一意にする (#1893) by @isamu in #1903
  • fix(ui): CopyCodeBlock が渡された class をボタンに届ける (#1895) by @isamu in #1908
  • docs(cli): browserUrl が localhost 固定な理由は 2 つある —— Firebase の承認済みドメイン (#1900) by @isamu in #1909
  • feat: パスメニューの "Browse files in the app" は全画面ではなく右ペインで開く (#1910) by @isamu in #1911
  • feat: ファイルペインのツリー行を右クリックしてパスをターミナルに挿入する (#1859) by @isamu in #1912
  • fix: capture the pointer while dragging a splitter (#1899) by @isamu in #1907
  • test: gridOpenFiles の spec が実リクエストを 3 本出していたので mock する by @isamu in #1913
  • feat: 9 本目のテンプレート magazine — 記事ごとの URL を持つ共有アプリ by @snakajima in #1914
  • test: 残り 2 つの grid spec の mock 漏れを塞ぎ、ガードを共有ヘルパに切り出す by @isamu in #1916

Full Changelog: 4.12.0...4.13.0

mulmoterminal@4.12.0

Choose a tag to compare

@isamu isamu released this 28 Aug 11:25

Highlights

Released the same day as 4.11.0. A fix for an upgrade that came up with an empty grid, one launch form that opens beside whatever you are looking at, a storyboard edit that was silently doing nothing, and OpenRouter prices that had drifted from the catalog.

Setup guide: The launch panel, and getting your grid back (日本語)

The browser opens at localhost again, so your grid comes back (#1889, #1891)

4.11.0 started opening http://127.0.0.1:<port> instead of http://localhost:<port>. Nothing errored, and the app came up empty: the grid layout, the theme, the font size and eleven other preferences live in localStorage, which browsers partition by origin, so a different hostname is a different drawer. Sessions were all still running in tmux and nothing on screen connected the two.

If you upgraded to 4.11.0 and your grid was blank, this restores it — the layout was never deleted, only filed under the other hostname, and that is the hostname the launcher opens again.

One thing to know if you rebuilt your grid during 4.11.0: that newer arrangement is on http://127.0.0.1:<port> and stays there. Open that address directly to get it back. The app cannot fetch it for you — measured on Chrome, a page cannot read another origin's localStorage even through an embedded frame, and requestStorageAccess() does not change that.

The port fixes from 4.11.0 are untouched. The second-instance guard still probes the address the server will bind, and the readiness check still polls the address the server reports binding — what changed back is only the URL handed to the browser, which is a name your saved state is filed under rather than a claim about which socket answered.

localhost is now used only when the launcher has checked that nothing else can answer to it. localhost resolves to [::1] as well as 127.0.0.1, and the startup probe never asked about the first of those — so before opening anything, the launcher tries to take [::1]:<port> too. If something else already holds it, the browser goes to the address that was actually checked, and the banner says so and tells you where your layout is filed. Closing the remaining window is tracked as #1893.

If you set MULMOTERMINAL_HOST to reach the app from another machine, the banner now names that address on its own line.

One launch form, opening beside what you are looking at (#1867, #1890)

The launch form used to be a cell: pressing + appended an empty square at the end of the grid, so the thing you were starting appeared as far from your attention as it could get, and in the zoomed views it had nowhere to appear at all.

It now opens as an overlay at the right edge of the stage, the same way in every view mode — tiled grid, cockpit roster, filmstrip.

  • The toolbar + opens it on the workspace directory.
  • Every terminal's header gets a + that opens it on that terminal's directory — starting a second agent where you already are, without retyping a path.
  • terminal-new in the keymap now opens the panel; a new terminal-new-here action is the header +. terminal-new-adjacent is unchanged — still a shell in this directory, immediately, with no form.

While the panel is open it owns the keyboard, so Escape closes it and typing goes to the form rather than to the terminal underneath. Closing it returns focus to whatever opened it.

presentMulmoScript replaced a beat and reported success without writing anything (#1880, #1886)

Calling presentMulmoScript with filePath + beatIndex + beat — the documented way to replace a single beat — returned a normal 200 with the usual script payload and left the file unchanged. Nothing in the response told the caller apart from a plain re-display, so an agent editing a storyboard would report the edit as done.

Two causes, both in the tool's argument handling: the allowlist dropped beatIndex and beat before the package ever saw them, so the request arrived looking like a re-display; and even once written, script-changed was not published, so a canvas already showing that file kept the old content. New files looked fine because the response opens the file fresh — only in-place edits were affected.

OpenRouter prices in the model picker match the catalog again (#1849, #1887)

pricePerMTok is documented as the provider's published price, and 16 of the 27 OpenRouter presets had drifted from openrouter.ai/api/v1/models, some by more than 2x in either direction — gpt-5.6-luna read 1.0/6.0 against an actual 0.2/1.2, deepseek-v4-pro read half its real price. One contextLength was double the real window (nemotron-3-ultra, 524288 → 262144).

All 27 now agree with the catalog exactly, read on 2026-08-28 and checked programmatically. The frontier presets are ordered cheapest-first again, which the price refresh had scrambled.

📦 npm: mulmoterminal@4.12.0


What's Changed

  • fix: refresh OpenRouter prices in modelPresets from the catalog (#1849) by @isamu in #1887
  • fix: presentMulmoScript のビート差し替えが黙って無視される (#1880) by @isamu in #1886
  • feat: open the launch form as a right-side panel, in every view mode (#1867) by @isamu in #1890
  • fix(cli): ブラウザに渡す URL を localhost に戻す —— 4.11.0 で grid 配置が旧 origin に置き去りになっていた (#1889) by @isamu in #1891

Full Changelog: 4.11.0...4.12.0

mulmoterminal@4.11.0

Choose a tag to compare

@isamu isamu released this 28 Aug 04:34

Highlights

A way to stop the server that works from anywhere, an update badge that can actually appear, three port bugs that had been quietly breaking local sessions, the first code for Campaign Mode, and shared-app templates that teach a look rather than leaving grey boxes.

Setup guide: Stopping it, and the ports it takes — written at release time. (日本語)

mulmoterminal stop, and a Quit button in the browser (#1820, #1823, #1824, #1825)

There was no way to stop a running server other than finding the terminal you started it in. npx mulmoterminal@latest opens a browser, so that terminal is often the one window you are not looking at — and closing the tab leaves the server running.

  • The process says its own namemulmoterminal :<port>, in both launcher and server, so ps and pkill have something to find. The port is in the name because overwriting argv erases --port and the install path, which were previously the only way to identify the process (#1823).
  • mulmoterminal stop works from any terminal (#1824). No searching was needed: the server already registers its pid under ~/.mulmoterminal/instances/ (#1061). It sends SIGTERM so the server's own handler runs — the same shutdown Ctrl+C performs — and a new GET /api/instance lets the pid be confirmed before signalling, rather than trusting a stale file.
  • Settings → Quit MulmoTerminal, and POST /api/shutdown behind it (#1825). MulmoClaude had already answered the same need, so the route name and shape were taken from there rather than invented.

The update badge can now appear at all (#1821, #1826)

The version check ran once, at boot. Under npx mulmoterminal@latest the running version is the registry's latest at that moment, so the only check that ever ran could structurally only return "you are current". The comparison logic was never broken — what was missing was a second look. It now repeats every three hours.

Three port bugs, all of them local sessions failing to reach the server

  • A widened bind cut the server off from its own sessions (#1834, #1838). Setting MULMOTERMINAL_HOST to a specific address made hooks fail on every tool call and left the GUI MCP stuck on "still connecting". The server now also listens on loopback when the primary bind does not serve it.
  • PORT was ignored, and the error message told you to use it (#1857, #1861, #1873). PORT=34601 npx mulmoterminal started on 34567. The same line was wrong in the other direction too — the launcher exported its own PORT to every cell, so a dev server started there tried to bind MulmoTerminal's port. The chosen port now travels as an argument, which argv does not pass to child processes.
  • The second-instance guard never fired on a default install (#1876, #1877). The launcher probed the :: wildcard while the server binds 127.0.0.1, and a bind only collides with the same address — so a held port read as free, a doomed server was started, and the readiness check printed "ready" over a 200 from the other server. The launcher no longer guesses which address a port means: the child reports what the kernel bound, over IPC, and every address a launch needs is checked before spawning.

Behaviour change worth knowing: the launcher may now refuse a port it used to accept, and the URL it prints is the address it checked (http://127.0.0.1:34567) rather than localhost, which resolves to either family. The setup guide covers what to do.

Campaign Mode, first code (#1815, #1816, #1817, #1837, #1841, #1845)

Groundwork for running a queue of tasks unattended, unwired: design notes under future/, a turn-correlation rule moved to common/, a task state machine, a durable append-only record, and a claim registry for path exclusivity.

The deck editor moved, the canvas keeps up with the LLM, and formulas render (#1846, #1853, #1854, #1855, #1884)

@mulmocast/deck-web was deprecated in favour of @mulmocast/beat-editor, and the mulmocast dependencies moved to the 2.x line. More visibly: an LLM rewriting a MulmoScript now updates the canvas you already have open (#1854) — previously you had to close and reopen — every beat type is editable, and a single beat can be replaced without resending the whole script (#1855). TeX formulas render in a presentDocument preview (#1884), matching what the Marp deck side already did.

Shared apps

The templates each ship a real stylesheet at a hue of their own, so generated pages arrive styled rather than as grey boxes. A project board no longer lets a registration be mistaken for a claim (#1814) — pressing 「これをやります」 before registering used to leave people believing they had taken the work. todo-board has been removed; project-board does everything it did.

Documentation

Launch demo videos embedded in the README and both guides (#1827, #1828) with a poster frame (#1842, #1847), placed after the getting-started block with a lead-in (#1839, #1840). The README's opening was reordered so the first two screens answer "what is this" and "does it work" (#1860), with the GIF ahead of the video because GitHub does not autoplay attached video (#1862).

Dependency updates: #1809, #1865, #1883.

📦 npm: mulmoterminal@4.11.0


What's Changed

  • update by @isamu in #1809
  • One parent in the preview: the pane stops differing from the live page by @snakajima in #1810
  • The pane sends a public page's move, and one vocabulary explains every refusal by @snakajima in #1811
  • Take sharedapp 0.20.0, and give the staff page its delete by @snakajima in #1813
  • refactor: ターン相関の純関数を common/ へ(#1815 PR0) by @isamu in #1817
  • A template for the board that has a roster and an owner's desk by @snakajima in #1814
  • テンプレート 7 本が、自分の紙の色を宣言する by @snakajima in #1818
  • docs: Campaign Mode の設計メモ(汎用パイプラインと grid への載せ方) by @isamu in #1816
  • 名前の無い担当行ができる穴を塞ぐ(project-board) by @snakajima in #1819
  • feat: プロセスに mulmoterminal と名乗らせる (#1820) by @isamu in #1823
  • feat: ブラウザから MulmoTerminal を止められるようにする (#1820) by @isamu in #1825
  • feat: mulmoterminal stop — どのターミナルからでも止められるようにする (#1820) by @isamu in #1824
  • feat(update-check): 稼働中も更新チェックを回す — npx @latest 起動だと badge が出る瞬間が来なかった by @isamu in #1826
  • 共有アプリのテンプレートが、見た目とフローまで教える by @snakajima in #1829
  • sharedapp 0.22.0 — メンバー自身の取り下げ by @snakajima in #1830
  • Bump @receptron/sharedapp to 0.23.0 by @snakajima in #1831
  • Stream a watching page's records into the preview pane by @snakajima in #1833
  • feat: プレビューもビューが宣言した件数上限に従う by @snakajima in #1835
  • docs(readme): embed the launch demo videos (en/ja) by @ystknsh in #1828
  • feat(campaign): タスクの状態機械(#1815 PR1a) by @isamu in #1837
  • fix: bind を広げても自分のセッションが届くよう loopback でも listen する (#1834) by @isamu in #1838
  • 追記だけのフィードを 7 つ目のテンプレートに加える by @snakajima in #1836
  • docs(guide): move the launch demo below the getting-started block and give it a lead-in by @ystknsh in #1840
  • feat: 共有アプリに参加する側の汎用ツール useSharedApp(S0-S2) by @snakajima in #1843
  • feat(campaign): キャンペーンの永続記録(#1815 PR1b) by @isamu in #1841
  • feat: 共有アプリの変化を待つ watch / unwatch by @snakajima in #1844
  • chore: mulmocast 系の依存を 2.x ラインへ揃える by @isamu in #1846
  • docs(guide): give the launch demo video a poster frame (#1842) by @ystknsh in #1847
  • docs: 共有アプリの人向けガイド(英語)と、プレビュー操作の記述を実物に合わせる by @snakajima in #1848
  • feat(campaign): パスの排他が決めること(#1815 PR1c) by @isamu in #1845
  • fix: 再 publish の暗転と、publish のゲートを名乗る check by @snakajima in #1850
  • feat: 公開したアプリが、そこに座る agent に「持ち場」を宣言できるようにする by @snakajima in #1851
  • chore: デッキエディタを @mulmocast/beat-editor へ移行する by @isamu in #1853
  • docs: 親の状態で子の create を止める refIn と、agent に owner を渡す取引 by @snakajima in #1852
  • feat: LLM がスクリプトを書き換えたらキャンバスが自動更新されるようにする by @isamu in #1854
  • chore: mulmoscript-plugin 4.4.0 を取り込む(全 beat 種別が編集可能に) by @isamu in #1855
  • docs: put the fold to work for Show HN by @snakajima in #1860
  • docs: put the gif before the video in the Demo section by @isamu in #1862
  • feat(shared-app): a...
Read more

mulmoterminal@4.10.1

Choose a tag to compare

@isamu isamu released this 19 Aug 23:16

Highlights

Two fixes you can see, a shared-app preview whose desk actually moves records, an identity that carries no address, and a test suite with no time budgets left in it.

Setup guide: A scrollbar attached to nothing, and prompts you can copy (日本語)

A zoomed cell grew a scrollbar attached to nothing (#1762, #1807)

Enlarging a cell from the roster could leave it with a scrollbar that scrolled nothing: the thumb dragged independently of the terminal, the wheel still reached the agent, and it never went away until the next resize.

xterm stops drawing a cell that is off-screen, and a resize arriving while it is paused updates the row count immediately but defers the renderer's dimensions. The viewport builds its scroll range from both, so it ends up with the pre-zoom canvas height and the post-zoom content length — a scroll range that does not exist. On the alternate screen (a full-screen TUI redraws in place) nothing ever calls _sync() again, so it stays wrong. The roster hit it every time, because that cell was parked at left: -99999px until the moment it was enlarged.

Now the fit is held while the cell is off-screen and run inside the delivery that brings it back, so xterm's own paused resize is flushed in the same batch. The rule is a pure function (src/composables/terminalFitGate.ts), and the size handed to the PTY was measured as identical with and without the fix, in every view state. This is also an upstream xterm.js defect — a paused resize is deferred and the viewport is never re-synced.

The prompts pane could be read but not copied (#1806)

A prompt row in the right panel was one big <button>, and a browser makes a button's contents unselectable — so dragging across your own prompt started no selection at all. The button is now just the header row (time plus the expand chevron, with aria-expanded intact), and the prompt text sits outside it as selectable text. A click that ends a drag-selection no longer collapses the row, which would have thrown the selection away; that rule is a tested pure function (src/components/textSelected.ts).

Shared apps (#1797, #1800, #1801, #1802, #1804, #1805, #1808)

  • The pane's desk performs what its buttons say (#1802). Opening a member or participant page in the Collections pane drew the desk's controls and then failed every one of them with read-only — the member parent was never handed a perform port. transition / assign / withdraw now run, and the decision is made on the server, before the write, by the same readIntentMessage mulmoserver uses on /m/ and /p/: the author owns the app, so deferring to the deployed rules would let the preview succeed at operations the page on screen forbids. The ask carries which page it came from, writes go out as a batch (rules read the second half of a pair with getAfter()), and headless preview runs still do not perform anything.
  • Identity without an address: uidField (#1805, #1808). An app that spends its document id on exclusivity — a shared TODO board where the claim's id is the task's id — could only identify a person by emailField, which publishes an address next to a name on a world-readable row. uidField says the same thing with no address: the host validates the field exists and is a string, keeps it out of the public form (the box is writable and every value written to it is refused), and fills it from the session on write. A sixth template, todo-board, ships with it. It was published as protocol: "2.0.0" and walked back to no version at all (#1808), after measuring that an old reader already refuses the projection and that nothing reads a minor difference; the invariant that makes that safe is now pinned in publicForm.spec.ts.
  • A submission that cannot form an ID is refused by name (#1800). auth.uid+field with an empty field returned "<uid>_" — a valid document id — so two different claims by one person collapsed onto one document. missingIdField() now asks before recordId() and names the empty field. With sharedapp 0.16 a whitespace-only answer counts as missing (stored values are still never trimmed), and submit.stampField is passed to writableFields() so the shared function also knows which fields a visitor never chooses.
  • The live-poll desk closes the current question before opening the next (#1801). The audience page shows the lowest-order question that is open, and the template's desk had only per-row toggles — so opening the next question left the previous one open and the audience did not move, while the transition returned success and the row read "open". The desk now does it in two ordered writes and stops if the close is refused. SKILL.md gained the general form: transitions can only judge one record, so an invariant across records is executed by the page.
  • Which screen the page shows is decided from the data (#1804). Two published-survey bugs — a cancelled submission showing results, and a returning visitor being shown the form again — had one cause: the page decided from what happened in this tab. Render from onState only; cancelled is a third outcome, not a failure (four templates were reporting "could not send" to someone who pressed cancel).
  • The audience page absorbs a refusal it cannot avoid (#1797). A sandboxed page has no localStorage, sessionStorage or cookie — all three raise SecurityError — so after a reload it cannot know it already voted, and viewer.mine cannot answer for a composite id. It now states the refusal and stops instead of inviting another press, and does not count a cancel or a double-press as one.

Tests (#1737, #1796, #1798, #1799)

  • Waits are named conditions, not time budgets (#1796, #1798). test_windows failed on a different spec every run. Counting the hops one spec actually needed showed 1 required against 1 provided — zero slack — so one extra await anywhere upstream read an array before the answer landed. All 39 waits in the touched specs are now named conditions, the settle() helper is gone, and three more specs carrying the same 300ms budget were fixed with them.
  • The suite opens no sockets (#1737, #1799). Every request(app) was a listener on an ephemeral port — invisible to a grep listen( — and under load they failed with socket hang up. All 23 specs moved to light-my-request in-memory injection through an adapter that returns supertest's shape, so not one assertion was rewritten to prove a transport change. The two places where the adapter differs (empty body, top-level array) are pinned by spec, supertest is out of the dependencies, and remoteHost/routes.spec.ts went from 13 listeners to 1 to 0.

npm: mulmoterminal@4.10.1


What's Changed

  • ライブ投票のテンプレート: 押す前に親へ訊き、それでも来た拒否は受け止める by @snakajima in #1797
  • 共有アプリ: sharedapp 0.16.0 への bump と、ID を作れない投稿の拒否 by @snakajima in #1800
  • skill(shared-app): ライブ投票の司会卓が、次を出す前に今の設問を締める by @snakajima in #1801
  • ペインの進行卓が、押したボタンの通りにレコードを動かす by @snakajima in #1802
  • test: 時間の予算ではなく事象を待つ — test_windows が実行ごとに別の spec で落ちる (#1796) by @isamu in #1798
  • test: supertest を捨てて、ソケットを一本も張らない (#1737) by @isamu in #1799
  • docs(shared-app): 画面の切り替えはデータから決める by @snakajima in #1804
  • feat(shared app): 住所を持たない本人性をホストに通す(uidField) by @snakajima in #1805
  • fix: 画面外のセルをリサイズしない — ズーム時の実体のないスクロールバー (#1762) by @isamu in #1807
  • fix(prompts): プロンプト一覧を範囲選択できるようにする by @isamu in #1806
  • fix(shared app): uidField に版を割り当てない(sharedapp 0.18.0) by @snakajima in #1808

Full Changelog: 4.10.0...4.10.1

mulmoterminal@4.10.0

Choose a tag to compare

@isamu isamu released this 18 Aug 20:22

Highlights

  • The machine's own load, in the grid header (#1786, #1791)load 334% sits beside the 5h / 7d usage windows, as a percentage of this host's cores. 100% means every core has work queued and one more cell slows the ones already running (amber); 200% means everything is waiting for a core (red). Hover for the raw 1 / 5 / 15-minute figures, the core count and the multiplier. Polled every 10s through a new GET /api/load, never showing a reading older than 60 seconds. A host that keeps no load average shows nothing rather than 0% — Windows returns [0, 0, 0], and an idle machine must not look like an unmeasurable one. Turn it off with "showLoadAverage": false or Settings → Grid header read-outs.
  • The collections pane has one toolbar (#1784) — the bar used to be drawn only in a directory that declared a shared app, so it appeared and disappeared as you moved between cells. It is always there now, with expand and close in it, and a preview opens by default.
  • A Claude session can be read from the phone (#1751, #1776) — the remote terminal could only show the current screen, because claude runs on the alternate screen and capture-pane returns the 30–41 rows of the pane. The host now answers getTerminalTranscript from claude's own transcript, so the phone client can page back through the conversation.

Shared apps

  • #1764, #1770 — the headless preview now runs the same @receptron/sharedapp/view modules (viewBridge / memberBridge / portChannel / publicViewSrcdoc) that the pane and mulmoserver's public page run, so what an LLM is shown and what a person sees differ only in the chrome around them. 0.9.0 marks a submit dispatched from a click, which is what the headless write path was waiting for.
  • #1790sharedapp 0.11.0: views[].live is declarable and checked. The host bumps before the subscriber because the schema is .strict() — an author cannot even parse an app.json with live until it does.
  • #1794sharedapp 0.12.0: a projection carries protocol, the publish contract's version, so a document says which rules it was written under.
  • #1793 — the deployment/capabilities gate was dropped. It asked "what can this deployment serve?" when the question that matters is "which contract is the document in front of me written to", and that one is answered by the document itself, at the moment it is drawn.
  • #1774, #1792, #1795 — templates that say who reads the answers: the form-shaped templates ship all three views (public / intake / owner) rather than one public page with nowhere to read what came back, and a live-poll template was added and made anonymous.
  • #1781aid lifecycle: "it is stuck, let me delete the aid" is exactly what an agent reaches for, and publishing then silently minted a second app. Publish refuses instead, and a partial state is handed the steps that recover it.

Fixes

  • #1769, #1772 — a cell's title is read from the transcript, not generated. The claude -p session that existed only to write a heading had run git restore and git push origin main in the working repository.
  • #1771 — the launcher no longer comes up empty. The saved-directory chips, the WORKSPACE chip and a dozen global settings all come from one GET /api/config issued once at boot; a backend still restarting answered nothing and the row stayed blank until a reload. The request retries.
  • #1777, #1782 — Codex's "OR RESUME HERE" lists your own conversations again: the cwd filter ran after the overall scan limit, so subagent rollouts filled the list and every entry read Codex session.
  • #1758, #1775 — under yarn dev, HTML outside artifacts/ opens in the Canvas again. The Vite dev proxy had no /htmlfile entry, so the SPA catch-all answered with the app's own index.html and the sandboxed iframe (origin null) failed on CORS.

Docs

  • #1767 — the 4.9.0 guide no longer describes shared-app work that did not ship in it.
  • #1780 — the primary material that had misled four external reviews was corrected.

Setup guide: The machine's own load (日本語)

📦 npm: mulmoterminal@4.10.0


What's Changed

  • check がレコードも見る/datetime は壁時計であることを書く (#1763) by @snakajima in #1765
  • feat(shared-app): ヘッドレスプレビューを、ペインと同じものにする (P6) by @snakajima in #1764
  • chore: core 4.3.0 — putItems が返すようになった lint を skill に書く by @snakajima in #1768
  • docs: 4.9.0 のガイドから共有アプリを外す by @isamu in #1767
  • fix: retry /api/config so a restarting backend stops emptying the launcher by @snakajima in #1771
  • chore(shared-app): sharedapp 0.9.0 に上げ、クリック印をランタイムから受け取る by @snakajima in #1770
  • 共有アプリ: 「誰が結果を見るのか」を手順にして、フォーム型のテンプレートを足す by @snakajima in #1774
  • fix(dev): Vite dev proxy に /htmlfile を足す (#1758) by @isamu in #1775
  • fix: セル見出しを claude -p ではなくトランスクリプトから読む (#1769) by @isamu in #1772
  • docs: 外部レビューを 4 本とも誤らせた一次資料を直す by @snakajima in #1780
  • feat: claude の transcript を phone に返す getTerminalTranscript (#1751) by @isamu in #1776
  • fix(sharedApp): aid のライフサイクル — publish が黙って 2 つ目のアプリを mint するのを止め、partial に復旧手順を渡す by @snakajima in #1781
  • collections ペイン: ツールバーを 1 本にまとめ、expand / close とプレビュー既定を入れる by @snakajima in #1784
  • fix(codex): resume 一覧の cwd フィルタを全体上限より先に走らせる (#1777) by @isamu in #1782
  • 共有アプリ: sharedapp 0.11.0 への bump と、live のページについて preview が言えないこと by @snakajima in #1790
  • skill(shared-app): ライブ投票のテンプレートを足す(英語) by @snakajima in #1792
  • chore(shared-app): deployment/capabilities の門番を外す by @snakajima in #1793
  • sharedapp 0.12.0 に上げ、テンプレートが契約の版を名乗るようにする by @snakajima in #1794
  • 共有アプリ: ライブ投票のテンプレートを anonymous にする by @snakajima in #1795
  • feat: グリッドヘッダに load average を出す — 「今もう1本立てて良いか」が画面に無い (#1786) by @isamu in #1791

Full Changelog: 4.9.0...4.10.0

4.9.0

Choose a tag to compare

@isamu isamu released this 17 Aug 08:26

Highlights

  • A pane for the prompts you sent (#1748, #1749) — with several cells running it stops being obvious which one you told what. The cell header shows only the latest prompt, the Activity timeline shows what the agent ran, and the roster row shows what the agent said. None of them is a list of what you asked for. Press the forum button on an enlarged cell to get one: newest first, read-only, expands a long prompt in place, and keeps up by itself if left open. Claude and Codex.

    It reads what you typed~/.claude/history.jsonl, not the transcript — and that is measured, not preference. A prompt sent while a turn is running never appears in the transcript as a user record (it lands as queue-operation / attachment), while text a skill injects does. A pane built on the transcript would drop the interruptions you are most likely to forget giving, and add text nobody typed.

Three things the review changed after the plan was written, each measured rather than reasoned:

  • /compact does not reissue the session id. A comment said it did and a chain of id-mapping was built on it. Measured across every transcript on the development machine — 95 compacted, 61 with prompts after — the id was identical in all of them. The chain was deleted and the comment corrected.
  • After /clear, prompts from before the clear are not shown. The header, the AI title and the latest reply all blank on a clear; this pane alone was still showing the finished conversation. The boundary is drawn on time, not the id, because whether /clear reissues the id could not be measured and an id-based implementation fails toward a permanently empty pane.
  • That boundary is persisted. Both the time and claude's id at the moment of the clear were in memory and lost on a restart — losing the time resurrects the erased conversation, losing the id empties the pane. Both moved onto the mark that already records the clear.

Also in this release

  • The prompts pane resumes instead of re-reading (#1750, #1753). ~/.claude/history.jsonl is 8 MB on the development machine and was scanned end to end on every poll. It now resumes from where it stopped, reading only what was appended, and everything a scan reads comes from one file handle rather than a path re-resolved per read — a path can land on two different files across two reads, and a reader that folds a range and then checks something else about "the file" has no way to say the two saw the same one.
  • The dev server no longer restarts forever on a port collision (#1735, #1736). A port already in use made it exit, which made it restart, which made it exit again. It reports the collision and stops.
  • The Windows PTY specs get a 60-second timeout (#1740, #1741). They drive a real PTY, and the Windows runner is slow enough that the default was the thing failing, not the code.
  • collection-plugin is on 4.2.0 (#1754), and a recorded timestamp is no longer editable from the pane — it is the server's record of when a row was written, not a field.

Plans and docs

#1755 records the design for reading a Claude transcript on the phone — the terminal screen cannot answer it, because claude runs in the alternate screen where tmux keeps no scrollback (measured: alt=1 hist=0 on 11 of 12 live sessions).

Release pages in the guide now use a nav_order that descends as the version rises (oldest = 10000000). Shipping a release used to renumber all fifty pages in both languages; it is now one new file per language and nothing else moves.

📖 Setup guide: The prompts pane (日本語)

📦 npm: mulmoterminal@4.9.0

4.8.5

Choose a tag to compare

@isamu isamu released this 15 Aug 07:46

Highlights

Windows: Shell cells start again

A Shell cell died the instant it launched on Windows, while Claude and Codex cells in the same window were fine. $SHELL is an executable path, and Git for Windows sets it to C:\Program Files\Git\usr\bin\bash.exe — that reached PowerShell as a command string and was split at the first space, so C:\Program was reported as an unknown command (#1717, #1718).

Quoting alone would not have fixed it: in PowerShell a quoted path is a string expression, so the path is echoed and no shell starts — a terminal that opens onto nothing, which is worse than an error. The follow-up removes the parser instead of satisfying it: a shell we chose is now handed to the PTY as a program, and only a launcher chip — a command line the user wrote — still goes through a shell (#1720, #1724).

tmux 3.7: every new session no longer dies after a worktree is deleted

On tmux 3.7, deleting a git worktree could leave the tmux server holding that directory, after which every new session on the host died at startup while existing ones carried on — and restarting MulmoTerminal did not help, because the broken thing was the tmux server (#1725, reported by @michiof).

tmux 3.7 moves the server's working directory to the client's on every new-session and never restores it, and spawn_pane() guards its chdir on getcwd(). Once that directory is gone the guard skips the chdir entirely, so a later pane ignores -c and starts in the deleted path — where a program that calls getcwd() as it starts dies immediately. MulmoTerminal now starts the tmux client from a directory it never deletes; the pane still opens where the cell asked. Reported upstream as tmux/tmux#5473. tmux 3.6 and earlier were never affected.

Remote host: Google sign-in no longer loses the credential

firebase is pinned to 12.16.0. @firebase/auth 1.13.4 treats visibilityState === 'hidden' as page teardown, so signing in through a popup — which backgrounds the opener — throws while persisting the credential to IndexedDB. The network call succeeds and nothing is saved. The upstream fix is merged but not yet on npm, so pinning is the only route today (#1731, firebase-js-sdk#10264).

Shared apps: see the participants' screen before publishing

A shared app's view is written by an LLM, and until now authors published it without ever running it. There is now a preview that runs it on the author's own machine, in a frame no looser than the published one (#1707). Slot refill in the meeting-room template moves onto putItems's itemsFile, with @mulmoclaude/core at 4.1.1 (#1726).

Skills turned off stay off

A skill set to "off" in skillOverrides still appeared in the Skill menu and in GET /api/skills (#1698, #1714).

Also in this release

  • Issue templates.github/ISSUE_TEMPLATE/ now carries a bug report and a "problem or use case" form. The second asks what you were trying to do and what got in the way, rather than for a feature: a proposal narrows the answer to the one already thought of, and when a proposal is turned down the problem it came from is thrown away with it (#1715, #1716).
  • Windows CI runs before mergeyarn test on Windows now runs on every pull request that touches anything other than docs. Fifteen issues had been found only after landing on main (#1721, #1723).
  • Two flaky specs fixedSharedAppPreview was missing a settle phase and failed only on the slower Windows runner (#1719, #1722); sharedAppPreviewRoutes opened a real TCP listener per request and failed 2 runs in 6 under full-suite load, returning another server's HTML rather than timing out (#1729, #1730).

📦 npm: mulmoterminal@4.8.5


What's Changed

  • feat(shared-app): 公開する前に、手元で参加者の画面を動かす by @snakajima in #1707
  • fix(skills): claude が off にした Skill をメニューに出さない (#1698) by @isamu in #1714
  • chore: issue テンプレートを置く(バグ報告 / 課題・ユースケース、英語) by @isamu in #1716
  • fix: Windows で Shell セルが起動しない — 空白を含む shell パスを PowerShell が分割していた (#1717) by @isamu in #1718
  • fix: windows-daily の SharedAppPreview を直す — 待ちのフェーズが 1 つ足りない (#1719) by @isamu in #1722
  • refactor: 自分で決めたシェルは argv で渡す — Windows だけ、理由付きで非対称に (#1720) by @isamu in #1724
  • ci: Windows のゲートを PR 前にも置く — yarn test だけを全 PR で回す (#1721) by @isamu in #1723
  • feat(shared-app): 枠の補充を itemsFile に載せる(+ core 4.1.1) by @snakajima in #1726
  • fix: tmux クライアントを消えうるディレクトリから起動しない — tmux 3.7 の回帰への回避 (#1725) by @isamu in #1727
  • fix(test): ルート spec が実ソケットを立てるのをやめる — フルスイートで 2/6 → 0/10 (#1729) by @isamu in #1730
  • fix(remote-host): firebase を 12.16.0 にピン留めする — mulmoclaude #2912 の伝播漏れ (#1731) by @isamu in #1732

Full Changelog: 4.8.4...4.8.5