{"slug": "gitgui-a-real-git-gui-inside-cmux-next-to-pi", "title": "gitgui: a real git GUI inside cmux, next to Pi", "summary": "Developer Antonello F. released gitgui, a Rust binary that renders a full git GUI—commit graph, staged file list, inline diffs, and hunk buttons—inside terminal panes using the kitty graphics protocol, enabling mouse-driven git operations alongside AI agents like Pi in cmux. The tool, available on GitHub, supports macOS and Linux, sends frames via POSIX shared memory locally (rasterizing a 1600×1000 release build in about 6 ms on a Mac), and offers a JSON-lines socket for external tools to query status, stage paths, or capture screenshots.", "body_md": "# gitgui: a real git GUI inside cmux, next to Pi\n\nI 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.\n\nPi runs `git status`\n\nand `git diff`\n\nfine. 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`\n\noutput. I wanted Sourcetree in the same cmux grid as the agent.\n\nSo 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.\n\nThis is my daily layout:\n\nPi 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 = 😍\n\n## Why stay inside the terminal?\n\ncmux 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.\n\nlazygit 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.\n\n[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.\n\nThree steps:\n\n- Draw the UI into an RGBA framebuffer with egui\n- Send the framebuffer as a kitty graphics image each frame\n- Map kitty keyboard and SGR mouse events into egui input\n\nYour 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.\n\nYou 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`\n\nCLI, and a JSON-lines socket so Pi or Cursor queries status, selects commits, stages paths, or saves a PNG screenshot.\n\nKeys are single letters and Ctrl combinations terminals do not steal. cmux keeps Cmd+*. Quit with q or Ctrl+C.\n\n## Install\n\nYou need macOS or Linux and a kitty-graphics terminal.\n\nQuick start:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/antonellof/gitgui/main/scripts/install.sh | bash\n```\n\nThat one-liner works on public repos. Private repo? Clone with `gh`\n\nand run the script locally:\n\n```\ngh repo clone antonellof/gitgui\ncd gitgui\nbash scripts/install.sh\n```\n\nThe script pulls a release binary when GitHub has one. Otherwise it builds from source with cargo (Rust 1.95+). Success looks like:\n\n```\ninstalled: ~/.local/bin/gitgui\n\nrun in a kitty-graphics terminal (cmux, Ghostty, kitty):\n  gitgui                  open repo in current directory\n  gitgui /path/to/repo    open a specific repo\n  gitgui --split right .  open in a new terminal split\n\nquit with q or Ctrl+C\n```\n\nCheck terminal support:\n\n```\ngitgui --probe\n```\n\n## cmux + Pi + gitgui\n\nSame workspace as the trio post. One extra split.\n\nPane A, Pi:\n\n```\ncd ~/Projects/my-app\npi\n```\n\nPane B, gitgui, from the agent pane:\n\n```\ngitgui --split right .\n```\n\ncmux runs `new-split right`\n\nand `send`\n\nwith the gitgui command. Or split by hand and type `gitgui`\n\nin the new pane.\n\nGive your agent the skill file:\n\n```\nmkdir -p ~/.cursor/skills/gitgui\nln -sf ~/path/to/gitgui/skill/SKILL.md ~/.cursor/skills/gitgui/SKILL.md\n```\n\nDrive the GUI from the shell:\n\n```\ngitgui ls\ngitgui action '{\"cmd\":\"status\"}'\ngitgui action '{\"cmd\":\"stage\",\"paths\":[\"src/main.rs\"]}'\ngitgui action '{\"cmd\":\"screenshot\",\"path\":\"/tmp/frame.png\"}'\n```\n\nRun `action`\n\nfrom 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.\n\nA 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`\n\nand you type the commit message yourself.\n\n## Inside the binary\n\nOne Rust process. Three threads.\n\nstdin reader blocks on terminal bytes and feeds a parser for kitty keys, SGR mouse, paste, resize.\n\nmain loop runs egui, tessellates meshes, rasterizes triangles, encodes kitty graphics.\n\ngit worker uses libgit2 for reads and index writes. fetch, pull, and push shell out to `git`\n\nso your credential helper and SSH agent stay untouched.\n\nThe UI reads an immutable RepoSnapshot. The worker swaps in a new snapshot after each command. Rendering never calls git.\n\negui 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).\n\n## One refactor, start to finish\n\nPi 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.\n\nBefore gitgui: `git diff`\n\nin the shell for small diffs, Alt-Tab to Fork for a graph, lazygit when you want speed over pixels.\n\nNow: `gitgui --split right .`\n\nonce 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.\n\n## What v1 skips\n\ncmux and Ghostty are the main targets. kitty works. tmux and Zellij need graphics passthrough and fail today.\n\nGhostty on macOS has no stable split-from-child API. `gitgui --split`\n\nprints a keybind hint and runs in the current pane when needed.\n\nMerge conflict UI is out of scope for v1. Use git on the CLI or your desktop client for conflicts.\n\nIf you already run cmux and Pi, gitgui is the git pane I wished existed. Install it, split right, keep the graph beside the harness.\n\ngitgui: [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/).", "url": "https://wpnews.pro/news/gitgui-a-real-git-gui-inside-cmux-next-to-pi", "canonical_source": "https://www.fratepietro.com/2026/gitgui-terminal-git-gui-cmux-pi/", "published_at": "2026-09-02 22:00:00+00:00", "updated_at": "2026-09-03 10:52:33.407213+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Antonello F.", "gitgui", "cmux", "Pi", "GitHub", "egui", "kitty graphics protocol", "Ghostty"], "alternates": {"html": "https://wpnews.pro/news/gitgui-a-real-git-gui-inside-cmux-next-to-pi", "markdown": "https://wpnews.pro/news/gitgui-a-real-git-gui-inside-cmux-next-to-pi.md", "text": "https://wpnews.pro/news/gitgui-a-real-git-gui-inside-cmux-next-to-pi.txt", "jsonld": "https://wpnews.pro/news/gitgui-a-real-git-gui-inside-cmux-next-to-pi.jsonld"}}