We spent forty years escaping the command line. Then AI agents arrived, and the best-funded interface teams in software went back to a grid of monospaced characters. So what's happened?
The terminal came back beautiful
In 1984 the Macintosh promised we would never look at a blinking cursor again. In 2026, OpenAI, Anthropic, Google, and xAI all ship their flagship coding products as terminal apps. Not grudgingly, either. These are polished, animated, mouse-aware interfaces that happen to be drawn entirely out of text. Something strange happened on the way to the future, and it is worth understanding what.
A screen made of cells
A terminal gives a program almost nothing. There is no canvas, no DOM, no pixels. There is a grid of monospaced character cells, and two streams of bytes: one going out, one coming in. Print text and it simply fills the next cells in the grid.
Everything else is a trick. The trick is called an escape sequence: a run of bytes starting with the escape character that the terminal reads as a command instead of text. \x1b[5;10H moves the cursor to row 5, column 10. \x1b[31m turns the next characters red. Other sequences clear lines, hide the cursor, or make mouse clicks arrive on the input stream as encoded bytes. An app drawing itself is just a program printing instructions; those are the boxed bytes in fig 01.
A full-screen app adds two more moves. It switches the input to raw mode, so each keystroke arrives instantly instead of waiting for Enter; that is why a single j moves the cursor in vim. And it opens the alternate screen buffer, a second blank screen to draw on. Quit vim and your shell history is exactly where you left it. That is the alternate screen closing.
So every TUI framework ever written has the same job. Hold a model of the grid in memory. Turn your interface into cells. Compare that with what is already on screen, and emit the fewest escape sequences that make the two match. It is the same paint, diff, and flush loop as a game engine, or as React against the DOM. The whole craft lives in that loop, and in one cursed detail: a Japanese character is two cells wide, an emoji is anyone's guess, and every miscount shows up as a broken border.
Why we left, and what pulled us back
We left because the terminal of 1985 deserved to be left. Sixteen colors on a good day. No mouse. No images. The GUI won on fidelity, then the web won on reach, and the terminal became the boiler room: always there, never decorated.
What pulled us back was not nostalgia. It was AI agents, and three unsentimental facts about where their work happens.
First, the terminal is the one interface that already exists everywhere an agent works: every server, every SSH session, every CI box. Second, it composes. Pipes, logs, and replayable event journals are native citizens, and an agent's output is exactly that kind of material. Third, it is cheap to render. A status row updating thirty times a second costs nothing, which matters when the interesting part of your product is the model, not the chrome.
A chat web app shows you what an agent says. The terminal is where the agent's actual work already lives. Putting the interface there removes a whole layer of translation.
The hardware quietly got good
The return would have looked terrible on the terminal we abandoned. It looks good now because the substrate improved while nobody was watching.
Color came first. Two shades became sixteen, then 256, then 24-bit truecolor: 16.7 million colors per cell, standard in every serious emulator today.
Then came the protocols that fixed the old embarrassments. Synchronized output lets an app wrap a whole frame in a pair of escape sequences so the terminal paints it atomically. No flicker, no tearing. Support landed in VS Code's terminal and tmux this year, work driven in part by Claude Code's anti-flicker push. The kitty graphics protocol transmits real pixels, and Ghostty, WezTerm, Konsole, iTerm2, and Warp all speak it. Where it is missing, there is a lovely fallback: the half-block character ▀, with the foreground color as the top pixel and the background as the bottom. Two pixels per cell, on any terminal made since the Reagan administration.
Add GPU-rendered emulators like Ghostty, and beauty stopped being impossible. It just needed a reason. Millions of developers staring at agent harnesses all day turned out to be the reason.
What the AI coding tools run on
Look at what the major coding agents actually ship, and the field splits into camps.
| Tool | Stack | Notes |
|---|---|---|
| Claude Code | TS, React, forked Ink | custom reconciler, 140+ components |
| Codex CLI | Rust + ratatui | rewrote from TS + Ink in 2025 |
| Gemini CLI | TS, React 19 + forked Ink | open source |
| Grok Build | Rust + ratatui | open-sourced July 2026 |
| opencode | OpenTUI + SolidJS | was Go + Bubble Tea |
| Crush | Go + Bubble Tea v2 | the original opencode lineage |
| Aider | prompt_toolkit + Rich | a REPL by choice |
Half that chart says Ink, so Ink deserves a proper introduction. Ink is a renderer that points React at the terminal instead of the browser. You write ordinary React components with hooks and state, lay them out with flexbox through Yoga, the same layout engine React Native uses, and Ink turns the tree into styled lines of text, re-printing what changed. Its pull is simple: millions of developers already think in React, and Ink lets them keep thinking in it. Its limits are just as simple: it is pure JavaScript, line-oriented, capped around 30 frames per second, with no mouse support and no images.
Those limits explain the table's real story. The biggest Ink users all ended up forking Ink rather than using it stock; Claude Code's build carries a custom React reconciler and its own layout engine. And OpenAI threw away a working TypeScript app to rewrite Codex in Rust on ratatui. When teams this well resourced keep rebuilding the same layer by hand, the layer is missing from the ecosystem.
OpenTUI is the most direct answer to that gap, and its origin is the point. The team behind opencode (Anomaly, formerly the SST crew) started with a Go interface built on Bubble Tea, but wanted the whole product in TypeScript. Nothing in the JS world could draw fast enough, so they built their own framework and rebuilt opencode on it. OpenTUI keeps React's programming model but replaces Ink's machinery: a native rendering core written in Zig holds the screen as a buffer of cells, diffs each frame, and repaints only what changed. That buys the things Ink cannot do: no frame cap, full mouse support, real images over the kitty graphics protocol with half-block fallback. It is MIT-licensed, documented at opentui.com, runs opencode in production today, and is the closest thing the TypeScript terminal world has to a common foundation forming in real time.
The other camps are healthy too. Charm shipped Bubble Tea v2 in February, hardened inside Crush. Rust has ratatui, and Python has Textual. Nobody is starving; the fight is over which model wins the next decade of terminal software.
The parts everyone rebuilds
Here is the gap all those forks point at. Chat components exist in every ecosystem: message bubbles, spinners, markdown renderers. But a harness is not a chat. A harness needs a status row that proves the agent is alive and tells you what this turn costs. An approval prompt that gates a real action on your real machine, with the safe option first. A home screen that says which model is pointed at which screen. Nobody ships those, so every team on that chart has built them from scratch, and several built them wrong in the same off-by-one-cell ways.
A copy-paste culture is forming around the problem, in the mold of shadcn/ui: registries like termcn, TermUI, and InkUI distribute terminal component source you own and edit. Vercel's @ai-sdk/tui attacks the same space from the SDK side.
I got tired of rebuilding the harness parts too, so I decided to open source the parts I use. **agentparts* is a small library of exactly these components, built on OpenTUI: the turn status row, the approval prompt, the shell layout, the prompt box. All the width arithmetic lives in a renderer-free core that runs on plain Node. Feel free to use for your harnesses!*
So you want to build one
Start with an honest question: does this need to be full-screen at all? Aider and Goose are proof that a well-formatted REPL can carry a serious product. If your interface is a conversation with occasional structure, print good output and stop. Reach for a TUI framework when you need layout: panes, status rows, things that update in place.
If you do need one, the choice is mostly made by the language you already live in:
| You write | Reach for | Why |
|---|---|---|
| TypeScript | Ink, or OpenTUI | Ink is mature; OpenTUI adds speed, mouse, images |
| Rust | ratatui + crossterm | the default; start from ratatui/templates |
| Go | Bubble Tea v2 + Bubbles | best component library, Lip Gloss for styling |
| Python | Textual | CSS-like styling, rich widget gallery |
The beautiful part is not the framework. It is discipline about the medium:
Design a palette, then design its absence. Pick a handful of truecolor values on purpose, and decide what the interface looks like with color stripped away: plainly unstyled, never broken. Honor NO_COLOR, the environment variable users set when they want none. Get width right. Test with CJK text and emoji, and snapshot your layout at 40, 80, and 132 columns, because every TUI bug is an off-by-one-cell bug. Never flicker. Wrap each frame in synchronized output, the escape pair that tells the terminal to paint it in one go, and diff before you draw. Shed, don't squish. When the window gets narrow, drop the least important element entirely instead of truncating everything a little.
And steal the parts that already exist: the spinners, the status rows, the prompts. Bubbles has them in Go, the registries have them in TypeScript. The chrome is the least differentiated thing you will build, which is exactly why you should not build it twice.
Step back far enough and the cycle stops looking like a cycle. We did not return to the terminal we left. We brought everything back with us: React reconcilers running flexbox, CSS-like styling, component registries, GPU rendering, twenty years of interface craft applied to a character grid. The real trade was never terminal versus GUI. It is fidelity versus leverage, how rich an interface can be against how many places it can reach, and right now leverage is winning.
Good design finally reached the terminal, one escape sequence at a time.
Originally published on arthurkatcher.com.







Top comments (0)