We Need a New Terminal

I use terminals quite a bit in my work, they're crazy useful. Yet, from years of working in big tech I've come to realize that tons of developers largely don't use them. They'd rather use web apps that are less powerful and more difficult to build. With some changes to how terminals work I think we can flip this and unlock productivity gains for many more developers (and spend less time building web apps). But first, a quick primer on terminals. It's easy to overlook what a terminal actually is. Its not just a text box for stdout.

You should think of the terminal like a web browser and the program its running a server. There is bidirectional communication over stdout/stdin with its own protocol and api. For example, when you press the down arrow your terminal sends the request ESC [ B over stdin and a program like less would respond by scrolling the view. In the opposite direction, a program can print a request like , to stdout which asks the terminal to send mouse events as they happen in a similiar format.

These are "control sequences". They don't get printed like normal text, they are the APIs available to terminal applications. They let you query terminal dimensions, move the cursor to any cell on the screen, set the "scrolling region" (think of a spreadsheet with sticky column headers), copy and paste rectangles, set OS window properties, and even draw images (see awrit a terminal web browser).

Terminal apps are GUIs

For years I thought of the terminal as just the way to interact with programs if you aren't building a GUI, but the reality is the terminal is a platform for building GUIs.

^ An example terminal GUI (or "TUI") using the kitty images protocol.

Terminals make a different set of trade-offs than web browsers. Saliently, they trade visual expressiveness for simplicity, but more importantly terminals are built for composability.

Just take a look at a command like this:

$ my-cli --all-jobs | tee -p /tmp/out | head -n10

Suppose my-cli is a tool for querying service deployments. The first 10 lines will be shown but all the output will be available in /tmp/out.

Now imagine instead of building my-cli to interact with our jobs we built a web app. This feature, optionally streaming output to a file with a configurably sized preview, might take an engineering team months to implement! How will the preview look? How does selecting a file fit into the existing UI? What protocol will be used for streaming? Is the server going to be able to support it without a giant refactor? How will you send a preview of the data to the GUI while streaming everything to a file?

That's so many more problems to solve! Why do we do this to ourselves? At least in my experience, developers waste time building web based tools for eachother. All the design trade-offs terminals make perfectly suit developer tools. The terminal is an application platform for developers.

Why aren't we leveraging terminal apps more?

TL;DR its because terminals can feel old and unwelcoming. The web feels modern and friendly. Even the most modern terminals lag behind current development environment standards.

Its text editor, but a bad one

If you think about it, the terminal is a text editor focused on conveniently writing and running one line scripts. However, it delegates the work of being a text editor to the shell its connected too!

This made sense back in the day, but in a desktop environment it a little silly. Bash is a horrible text editor: multi-line editing sucks, background processes can spew text into your input buffer, and there's no support for highlighting, formatting, or LSPs. Worst of all, for remote connections your keystrokes are at the mercy of your ping. I want to be able to write out a thought and then submit it while on a train from NYC to DC.

You need like 11 of them

I usually find myself opening a terminal for each thing I'm doing: one for vcs, one for building/running each binary relevant to the project, one for editing configs, one for building/canarying configs, one for the db, one for running that backfill, and so on.

The ergonomics of all wrangling all these terminals is awful. Plus, every time I open a new window I have to re-setup what should be shared context ( ssh && cd $dir ). This is what terminal mulitplexers were created to solve, but terminal multiplexers are a bad idea . Terminals should just support "multiplexing" features.

Shell history isn't enough

When I'm sitting down and opening my 11 terminals every morning, I'm sorta building a temporary UI. Each day its a little bit different but it usually follows one of a couple different workflows, like building a feature for a service or debugging an issue. Why do I have to rebuild these UIs every day? Why can't I save them for later or even share them with teammates?

No GUI primitives

Speaking of UI's, the only way to build one in the terminal seems to be to depend on some huge library. No one wants that. Look at golang's go tool pprof or the fish shell's fish_config. Both are cli tools that launch a web server just so they can draw you a UI. Neither of these UIs are anything fancy, just some text, some rectangles, and a few buttons. Terminal should support this kinda thing, out-of-the-box, no problem.

Also think of all the LLM chatbots that support rendering markdown. Our terminals should be able to do that too.

Don't go crazy with this, we don't want browser level complexity. Trade off stylability for simplicity and utility. Just like five elements that you can print and then listen for updates to over stdin would be enough. See DECKER below.

A Prototype

So what should terminals look like? Sometimes the best design doc is a prototype. This is a neovim plugin I wrote in Lua called justtxt .

This is what I use for more complicated workflows. Above I've contrived an example where I've got to query some table for requests made after a certain time, do some parsing on the ids, and then test the result of running validator on them (presumably after changing it).

justtxt exposes a command JustTxtRun that looks at your cursor position and buffer, selects either the current line or the current block (between two special comments), writes that to a temp file, runs it, and streams the output back into the buffer. It also supports a special comment #!! $whatever-shell-commands-here that lets you specify how the file gets run. !! just gets replaced by the name of the temp file. In the video I use this to save the full output to a file and show a preview in the buffer.

I like the UX of this a lot. I can fit all my work in one buffer so I don't have to flip between windows. I can edit everything with vim. I can easily re-run things and fix mistakes without building up a long confusing shell history. I can save everything to a file and repeat this workflow later.

Its far from perfect though. Running in neovim means the implementation is trivial but it also significantly limits what I can do. Commands are not run connected to a terminal, which means some things just don't work. Fixing this would make justxt a terminal multiplexer, which is a bad idea . Command output is included in your history, which basically means you can't use undo/redo. It's difficult/impossible to get neovim to syntax highlight and attach LSPs for different languages in the same buffer. At 2:08 in the video I have to open a separate buffer to remove the column headers from one of my outputs. I wish I could just edit the output in my current buffer. Also, its pretty much out of the question to build any GUI primitives into this.

A Vision

Finally, the moment you've been waiting for. I'll try to put into the fewest possible words what a modern terminal should be like.
  • Its built from the ground up, for maximum flexibility and performance. No web tech.

  • It has a nice learning curve. Beginners should be productive with it quickly and gradually unlock more productivity gains as they become experts.

  • Its open source and built on open protocols that multiple terminals can implement.

  • Its a backwards-compatible superset of a regular terminal. You can ignore all the extra stuff and rely on muscle memory.

  • The buffer where you write commands is a fully fledged text editor.

  • Any text can be run as code in any language.

  • Running programs have a "regular" terminal that is embeded inline with the text. This doesn't disrupt the "normal" text environment. You can navigate the cursor through the terminal, code, and unrelated text like normal.

  • You can run many commands and have many terminals running simultaneously in the same buffer.

  • After a program exits its stdout/stderr is available inline as open files. Output retains rich properties (color, style, background images). Rerunning erases the old output.

  • Its easy to pipe the output of one program into another.

  • Programs can print GUI components to stdout and then listen for interactions over stdin. You can quickly implement simple GUIs like a form or a timeseries viewer without requiring a huge graphics library.

  • Almost nothing about the experience changes if you're connected to a remote machine.

  • The tool is ai friendly. Interacting with an llm is just "running" some text with #!llm.

Who knows when I'll have time to build something like this or if anyone else will have the motivation. At the very least writing my ideas out like this has made them more concrete.

Addendum: Existing Tools

Here are some cool existing tools for more inspiration. None of them are close enough to the tool I want, but they all have elements of it.

ACME

I first heard of ACME several years ago through this video . Its what made me realize the current interaction model that all terminals use isn't the only way to do things. They way the entire editor is runnable, how you can pipe anything in the editor through a command, how everything integrates together yet is so loosely coupled, is really inspiring. Its whole approach is 100% spot on. However, you can hardly call ACME modern. It was written for Plan 9, the never-to-be successor to linux. It leans hard into using the mouse and has no modal editing at all.

Modern Terminals

Modern terminals like Kitty do solve some of the problems I mention. Kitty was the first to support the Kitty graphics protocol, which lets you draw images almost as easily as just printing the bytes. Kitty also has ssh integration called Truly convenient SSH which automatically reuses ssh connections (among other things). Another terminal, Alacrity, supports a vi mode that lets you temporarily treat the scrollback buffer as a vim buffer, thats definitely nice.

Warp Terminal

Warp is trying to be a modern Terminal. I like the intent but not the end result. It feels like a freemium web app that leans too hard into AI. Plus its closed source.

However, it does have some good ideas. As far as I can tell, it is handling text editing itself instead of delegating it to a shell. Its aware of what is a command and what is output and groups the interface into "blocks". It also has built in support for "workflows" and "notebooks" that allow you to build executable runbooks or documentation for single commands. Unfortunately, last time I checked these features are effectively paywalled.

Modern shells (zsh, fish)

Modern shells like zsh and fish, and even modern bash, have some nice quality of life improvements. In all of these you can enable vi-mode and write commands with vim like keybindings or trigger the current input to be opened with $EDITOR. In fish you can write multiline commands with shift-enter. I extensively use substring history search in both bash and fish.

DECKER

This thing is actually pretty cool. Love the aesthetic too. Its a tool for creating interactive multimedia documents. It runs as a native app using SDL or in the browser, try it out below. I think this is a great example of exposing a simple set of GUI elements that allow users to quickly compose UIs.

Jupyter Notebooks

Python notebooks are obviously an inspiration for my vim plugin. Love the idea, but it can't just be for Python and it can't just be in the browser.

Emacs

I feel like Emacs is an obligatory mention here since ideologically it does set out to solve most of what I ask for. I could never get into emacs though. Vim's modal editing is just much better.