Mimir

A Linux-native terminal multiplexer built for AI coding agents. Think cmux but for Linux, written in Rust and GTK4. Right now it is growing its own terminal emulator from the ground up.

Rust+ GTK4 / VTE4
6/7native terminal phases shipped
123lib tests green
privaterepo, for now
Mimir running fullscreen with two panes: the left pane shows Mimir's own git log with the native terminal phase commits, the right pane is running cargo test
real screenshot · Mimir showing its own git log, two panesyes that is a Gear 5 sticker
In Norse myth Mimir is the keeper of wisdom Odin consults. Fitting name for the cockpit I spend my whole day inside.

What it is

Mimir is a multiplexer designed around how AI coding agents actually work. Agent-driven panes, scrollback the agent can query as text, workspaces per project, and my browser agents (Blackreach, Huginn) running as panes inside the same window. I build it session by session and use it daily. It is a real tool, not a weekend demo.

Probing instead of guessing

A rendering bug was breaking TUIs like deepseek-tui inside Mimir panes. The lazy fix would have been to patch the symptom. Instead I sent a DECRQM query from inside a pane and proved the real cause: VTE reports synchronized output, mode 2026, as permanently unsupported. Mimir now handles it correctly. As far as I can tell it is the only VTE-based multiplexer that does. That debug session became a house rule: no fix until a probe proves the cause.

The native terminal stack

The bigger arc is making Mimir own its whole terminal stack instead of being a wrapper around VTE. Staged migration, no rewrite. VTE stays the default until the native backend earns parity. Turns out Mimir already owned the bottom of the stack, so this was migration work, not greenfield.

child process (bash, nvim, an agent...) │ raw bytes ▼ pty.rs ───────── Mimir's own pty. fork/exec, async-signal-safe ▼ SyncParser ───── every byte flows through a pure, 15-test parser ▼ first. sync frames handled before rendering TerminalBackend trait boundary. no VTE types allowed past here │ ├─▶ VteBackend ──── the incumbent. still the default │ └─▶ native core ─── the replacement, phase by phase ├─ parser.rs incremental VT500 state machine ├─ state.rs cursor quirks, SGR truecolor, alt screen ├─ grid.rs scrollback, BCE, CJK/emoji widths ├─ render.rs own pixels via cairo/pango └─ input.rs keys→bytes, bracketed paste, mod params verified by: 12 golden fixtures + 9 VTE-as-oracle diff tests + xvfb screenshot tests that caught a real reverse-video bug

Six phases shipped so far:

Why the testing setup matters. Writing a terminal emulator is hard. Proving it is correct is harder. Using VTE itself as a differential oracle against my implementation means every disagreement is either my bug or a documented VTE quirk. So far: three VTE serialization quirks found, zero unexplained mismatches.

What's next

Phase 7 is the query-reply path: DA, cursor position, pixel size. That last one is why fastfetch currently prints a warning inside native panes, and I knew about it before fastfetch told me. After that it is scrollback, search, and selection parity, then the native backend can take the default.


// private
The repo is private while the native terminal matures. If you work on terminal emulators, GTK, or agent tooling and want to talk shop, reach out.