# gitgui: a real git GUI inside cmux, next to Pi

> Source: <https://www.fratepietro.com/2026/gitgui-terminal-git-gui-cmux-pi/>
> Published: 2026-09-02 22:00:00+00:00

# gitgui: a real git GUI inside cmux, next to Pi

I wrote [The Local Agent Trio: cmux + Pi + Unsloth Studio](/2026/cmux-pi-unsloth-local-glm-setup/) about splitting your local stack into cmux, inference, and Pi. Git stayed in the shell.

Pi runs `git status`

and `git diff`

fine. You still lose the commit graph, the staged file list, inline diffs, and hunk buttons when you review a refactor. I kept Alt-Tabbing to Fork or squinting at `git diff`

output. I wanted Sourcetree in the same cmux grid as the agent.

So I built [gitgui](https://github.com/antonellof/gitgui). One Rust binary paints a GUI into your terminal pane with the [kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/). Pixels, not a TUI. Graph, sidebar, diff viewer, commit box. cmux, Ghostty, kitty, WezTerm. Over SSH it sends zlib plus base64 frames.

This is my daily layout:

Pi on the left edits the repo. gitgui on the right shows branch lanes, unstaged files, and hunk buttons. One workspace. One checkout. cmux + pi + gitgui = 😍

## Why stay inside the terminal?

cmux is a pane grid. You already split shell, Pi, and sometimes a browser. Fork or GitKraken pull you out: different font, different shortcuts, another app fighting for focus.

lazygit and gitui help for quick commits. They fall short when you read a three-file refactor with colored hunks, click Stage hunk on one block, and scan merge lanes on a graph. I wanted mouse clicks and a graph without leaving the grid.

[terminal-browser](https://github.com/zenbu-labs/terminal-browser) and [terminal-code](https://github.com/zenbu-labs/terminal-code) proved the pattern: render pixels in the terminal, read pixel mouse events back. gitgui uses [egui](https://github.com/emilk/egui) instead of Chromium.

Three steps:

- Draw the UI into an RGBA framebuffer with egui
- Send the framebuffer as a kitty graphics image each frame
- Map kitty keyboard and SGR mouse events into egui input

Your terminal never prints UI text. It shows a picture. Locally frames go through POSIX shared memory. On my Mac a 1600×1000 release build rasterizes in about 6 ms.

You get a commit graph with branch lanes, a sidebar for branches/tags/stashes, unstaged and staged lists, per-hunk stage and unstage, fetch/pull/push through your existing `git`

CLI, and a JSON-lines socket so Pi or Cursor queries status, selects commits, stages paths, or saves a PNG screenshot.

Keys are single letters and Ctrl combinations terminals do not steal. cmux keeps Cmd+*. Quit with q or Ctrl+C.

## Install

You need macOS or Linux and a kitty-graphics terminal.

Quick start:

```
curl -fsSL https://raw.githubusercontent.com/antonellof/gitgui/main/scripts/install.sh | bash
```

That one-liner works on public repos. Private repo? Clone with `gh`

and run the script locally:

```
gh repo clone antonellof/gitgui
cd gitgui
bash scripts/install.sh
```

The script pulls a release binary when GitHub has one. Otherwise it builds from source with cargo (Rust 1.95+). Success looks like:

```
installed: ~/.local/bin/gitgui

run in a kitty-graphics terminal (cmux, Ghostty, kitty):
  gitgui                  open repo in current directory
  gitgui /path/to/repo    open a specific repo
  gitgui --split right .  open in a new terminal split

quit with q or Ctrl+C
```

Check terminal support:

```
gitgui --probe
```

## cmux + Pi + gitgui

Same workspace as the trio post. One extra split.

Pane A, Pi:

```
cd ~/Projects/my-app
pi
```

Pane B, gitgui, from the agent pane:

```
gitgui --split right .
```

cmux runs `new-split right`

and `send`

with the gitgui command. Or split by hand and type `gitgui`

in the new pane.

Give your agent the skill file:

```
mkdir -p ~/.cursor/skills/gitgui
ln -sf ~/path/to/gitgui/skill/SKILL.md ~/.cursor/skills/gitgui/SKILL.md
```

Drive the GUI from the shell:

```
gitgui ls
gitgui action '{"cmd":"status"}'
gitgui action '{"cmd":"stage","paths":["src/main.rs"]}'
gitgui action '{"cmd":"screenshot","path":"/tmp/frame.png"}'
```

Run `action`

from the pane where gitgui lives and it finds the instance via the controlling tty. Writes queue on a background git thread. Poll status until busy hits zero.

A typical session: Pi edits and runs tests on the left. You stage hunks and commit with Ctrl+Enter on the right. Or Pi stages through `gitgui action`

and you type the commit message yourself.

## Inside the binary

One Rust process. Three threads.

stdin reader blocks on terminal bytes and feeds a parser for kitty keys, SGR mouse, paste, resize.

main loop runs egui, tessellates meshes, rasterizes triangles, encodes kitty graphics.

git worker uses libgit2 for reads and index writes. fetch, pull, and push shell out to `git`

so your credential helper and SSH agent stay untouched.

The UI reads an immutable RepoSnapshot. The worker swaps in a new snapshot after each command. Rendering never calls git.

egui 0.36, a custom rasterizer, git2, libc for termios and shm, serde for the agent API. No Electron. No GPU backend. No tokio. Spec and protocol bytes live in [docs/SPEC.md](https://github.com/antonellof/gitgui/blob/main/docs/SPEC.md) and [docs/PROTOCOLS.md](https://github.com/antonellof/gitgui/blob/main/docs/PROTOCOLS.md).

## One refactor, start to finish

Pi touches three files. You want the graph to update, one hunk staged and one left alone, and a commit message you wrote yourself. All inside cmux.

Before gitgui: `git diff`

in the shell for small diffs, Alt-Tab to Fork for a graph, lazygit when you want speed over pixels.

Now: `gitgui --split right .`

once per session. Tab between Pi and gitgui. cmux still rings when Pi waits on you. Mouse clicks hit the right widgets. SSH sessions use the direct transport and the same UI.

## What v1 skips

cmux and Ghostty are the main targets. kitty works. tmux and Zellij need graphics passthrough and fail today.

Ghostty on macOS has no stable split-from-child API. `gitgui --split`

prints a keybind hint and runs in the current pane when needed.

Merge conflict UI is out of scope for v1. Use git on the CLI or your desktop client for conflicts.

If you already run cmux and Pi, gitgui is the git pane I wished existed. Install it, split right, keep the graph beside the harness.

gitgui: [github.com/antonellof/gitgui](https://github.com/antonellof/gitgui). cmux: [cmux.dev](https://cmux.dev). Pi: [pi.dev](https://pi.dev). Related: [The Local Agent Trio: cmux + Pi + Unsloth Studio](/2026/cmux-pi-unsloth-local-glm-setup/).
