I Built Another Terminal, Unterm — Its Default User Isn't Human
One number first: over the past six months, more than 80% of the commands run in my terminal weren’t typed by me. Claude Code, Codex, a fleet of agents — running in there all day, each task often going for several hours straight.
That’s the problem. The terminals I’ve been using — iTerm, Windows Terminal, Warp — were all designed around one person sitting and typing: type a line, glance at the output, type another line. Once the primary user shifted from me to agents, that default assumption broke in a handful of places. I hit each problem one by one, and eventually just built my own terminal. It’s called Unterm.
What actually pushed me to start building was a week when I tracked where my time was going. Half the day was me acting as network ops for agents, security auditor for agents, window-switcher for agents. None of that is “product work” — but nobody else was going to handle it, so I had to build something to handle it for me.
Here are the problems I ran into, and how I patched each one.
What Unterm Is, and Where the Name Comes From
In one sentence: Unterm is a terminal designed to be operated directly by external AI agents (unterm.app, open source, GitHub zhitongblog/unterm).
It’s not the same as “a terminal with AI built in.” There’s no chat box. Instead, it exposes the entire terminal over the MCP protocol so agents can open sessions, type commands, and read the output. The slogan I wrote for it is just one line:
The terminal that runs every AI coding agent.
The claim behind that is one line too:
A terminal doesn’t need AI built in. It needs to be drivable by AI.
Feature-wise: one-click setup for Claude Code, Codex CLI, Gemini CLI, OpenCode, and Aider; local-first, open source, scriptable (comes with an unterm-cli); UI in nine languages.
In practice it goes like this: I tell Claude Code, “Get this project’s build running — if it fails, read the logs and fix it yourself,” and it connects to Unterm over MCP, opens a window, types the commands, reads the output off the screen, and keeps iterating when things break — no copy-pasting between chat and terminal on my end. The terminal finally became the agent’s hands, not a relay station I had to manually move output through. I watch when I feel like it. When I don’t, it keeps going on its own.
The name is straightforward: Un + term. term = terminal. Un = liberation — liberating the act of typing from human hands and handing it to AI. The image in my head when I built it: this terminal isn’t for my hands. It’s for agents. I step back and watch. The name says exactly that.
1. Proxy: Letting the Agent Not See the Wall
I’m in mainland China. Eighty percent of what agents do requires crossing the firewall: installing packages, git clone, pulling model weights, hitting various APIs.
In a normal terminal, this means I manually export HTTPS_PROXY, once per shell. But the moment an agent opens a new window or forks a child process, the proxy vanishes — then it stalls, times out, and throws network error. The frustrating part is that it doesn’t know this is a network problem. It assumes it got the command wrong — so it tweaks the command, switches mirrors, retries, and makes a bigger mess with each attempt. Once I asked an agent to pull some model weights overnight. When I came back in the morning, it had been retrying on a proxy that had died hours earlier — for over forty minutes, logs filling the screen, not a single byte downloaded.
So I built the proxy into the terminal itself. Unterm has a built-in proxy layer that connects to Clash; all sessions route through it by default. I configured a pool of nodes — currently eight Singapore lines tuned for Gemini and GPT — and the terminal benchmarks them every thirty seconds and routes to whichever is fastest. If the one in use drops, it automatically cuts to the fastest live node in the pool. And the proxy is set at the OS level (registry on Windows, scutil on macOS, environment variable on Linux), not per-shell — so every window that opens afterward inherits it from birth.
Some details were things agents forced out of me. localhost and internal addresses can’t go through the proxy, or agents lose the ability to connect to services they started locally — those no-proxy rules need to be in place by default, not patched in after an agent trips on them. And the node pool shouldn’t need manual curation: Unterm reads Clash’s groups and the live latency of each node directly, so I can assemble a rotation pool in a few clicks — which nodes go in, how often they rotate — no config file edits required.
It’s not magic: if the entire pool gets throttled, I’m still stuck. But the “one node went flaky again” problem — which used to happen about eight times a day — it handles by itself. I don’t touch it.
2. Security: Handing Your Machine to AI Means Handing It rm -rf Too
Give an agent a bare terminal and you’ve given it the whole machine. rm -rf, git push --force, DROP TABLE — one wrong line and it’s gone, and it moves faster than you can react. A normal terminal doesn’t know there’s an AI behind the keyboard. It treats an agent and me as the same pair of hands.
I didn’t want to pick between two extremes: let it run naked and live in fear, or confirm every single command myself — which at that point I might as well just type them. So Unterm adds a few gates in between:
- “Suggest” by default, don’t execute: the agent puts the command in my input line but doesn’t press Enter. I hit Tab to approve.
- Tiered clearance: a whole category of safe commands passes automatically; only the ones that can actually do damage —
rm -rf, force-push,DROP TABLE— get pulled out for a separate prompt. - Isolated identity: each window carries its own identity profile (GitHub / AWS / npm tokens, git identity, SSH key). The agent gets that scoped identity — not the full keychain for my whole machine.
- Explicit trust: I decide which agents are trusted. Writes from a trusted agent skip confirmation.
- Actual error reads: it can scan the screen for lines that look like errors, so the agent is reading real output — not filling in the blanks.
The default is “conservative”: an agent I’ve just brought in and haven’t trusted yet has to get my sign-off on everything it writes. Once I’ve watched it run a few cycles and confirmed it’s sound, I bulk-approve the safe command categories it uses regularly. It’s slower in the break-in period. But it won’t put me into a cold sweat on day one.
Spend enough time with agents and you’ll hit the moment where one confidently types rm -rf to “clear the cache” — with a path that’s off by a substring. Humans pause half a second before hitting Enter. Agents don’t — and that gate needs to be in the terminal, not the agent.
3. Recording: Once the Agent’s Done, I Need to Rewind
This is the feature I use most and can least afford to lose.
An agent runs unattended for three hours. You come back wanting to know what it actually did. Reading its own summary isn’t enough — it might leave things out, gloss things over, or cover its tracks. What you need is the raw record: every command, every output, preserved as-is.
So Unterm records the entire session from start to finish: what the agent typed, what the terminal returned, all stored as a replayable log you can rewind like a video or scroll through command by command. Tokens and keys are automatically redacted during recording, so the log itself doesn’t become a new leak.
It’s the agent’s black box: when something goes wrong, rewind to the step where it went sideways; when it runs beautifully, keep it as a demo or retrospective artifact.
Here’s something from last week: an agent blew up a batch of tests and then confidently reported “fixed.” I rewound to the part of the session where it touched the code. Took a few minutes to see exactly what had happened — it had quietly commented out an assertion to make the tests go green. If I’d only had its own summary, I never would have caught that. I’ve also exported chunks of the recording to use as “here’s how AI actually works” teaching material more than a few times.
Half the reason I’m comfortable letting agents run unattended is that rewindable record.
4. Parallelism: I’ve Been Running More Than One Agent for a While
The normal state now is three or four agents running at once: one on the front end, one grinding through database migrations, one writing tests. In a regular terminal, that’s three or four black boxes I’m alt-tabbing through — and I lose track of which one is waiting for my go-ahead and which one has been hung for an hour.
Unterm makes managing a group feel natural:
- Batch launch: one command opens a new window and dispatches work into it.
- Broadcast: want all four agents to “pull the latest code and re-run the build”? One command goes to all windows at once — no copy-pasting four times.
- Suspend and wait: after dispatching, I can wait for a specific window to surface “done / error” before I go back in.
A typical morning looks like this: open three windows, dispatch “rebuild the login page per the new design,” “migrate the orders table to the new schema,” “write tests for the checkout logic” — then broadcast “post your progress every ten minutes” and go handle messages. Come back, scan the state of the three windows, step into whichever one is stuck and talk it through, let the ones that aren’t stuck keep running. I went from “watching them one by one” to “sweeping one glance across a row.”
My job now isn’t writing code — it’s orchestrating a team of agents (something I wrote about in my previous piece). And orchestrating requires a decent command center, not four black boxes that have no idea each other exists while I try to remember in my head who’s who.
5. Windows: More Agents, Messier Desktop
Once you’re running agents in a group, what gets chaotic isn’t the commands — it’s the space: which window is deploying, which is a scratch session I spun up to test something, which one has been sitting there for two hours with no response? Try to keep that in your head and it falls apart past a few.
What I built is dead simple in concept:
- Windows have names: alpha, bravo, charlie — not forgettable numbers. Real example: as I write this, I have two Unterm instances running — alpha is in the doaipm site directory, bravo is in this content repo — one glance and I know which is which.
- Layouts save and restore: the split panes and window arrangement I’ve set up save as a workspace, and tomorrow when I boot up it comes back exactly as I left it — no re-stacking blocks every morning.
- Call by name: an agent can say “bring the deployment window to the front” and I don’t have to dig through fifteen boxes to find it.
The workspace feature sounds minor. It’s genuinely useful in practice. I have a fixed “publish” layout — one window on the site directory, one on the content repo, one running the build, one tailing logs — and once it’s saved, every morning I’m back at all four windows with their exact cd locations already set. No re-navigating, no re-arranging.
Twelve unnamed windows and one workbench that knows every name — that’s the whole difference.
What About tmux and Warp?
You might ask: tmux has had multi-pane for years, and Warp already built AI in — what exactly is new here?
I’ve used tmux for years. The multi-pane is great. But it was designed for ten human fingers: it doesn’t know there’s an agent behind the keyboard, doesn’t care whether a command might drop a database, and doesn’t handle being in China where every outbound command is fighting a timeout. Warp went another direction — it stitched an AI assistant into the terminal. But that’s “AI living inside the terminal,” not “the terminal being driven by AI”: the protagonist is still the person sitting in front of the screen, the agent is at most a co-pilot, and network routing and China-specific issues aren’t its problem. iTerm and Windows Terminal I still use daily — they’re good terminals — but “good” is still defined by “comfortable for human hands”: colors, fonts, shortcuts, splits. Not one of them was designed with “the thing typing the commands is an agent” as a first-class assumption.
Both approaches are solid. They’re just not answering my question: once the primary user shifts from human to agent, what should a terminal actually look like? Proxy handling, security gates, rewindable recording, coordinated group orchestration, named windows — in my case these aren’t a handful of isolated features. They’re different facets of the same problem.
Wrapping Up
There are terminals everywhere. Building another one needs explaining, even to myself.
The answer is one sentence: I wasn’t trying to build a better terminal. I was trying to build one whose default user isn’t human. Nothing in this piece came from a roadmap. Every single item was something that had been genuinely driving me up the wall at the time.
One more honest note: Unterm itself was built by describing what I needed to an AI in plain language in a terminal. I didn’t write its production code. I articulated what I wanted — “drivable by an agent, able to switch its own proxy, able to block rm -rf, able to replay entire sessions” — and the AI built it. Say it clearly, and it gets built — that’s how I work.
Whether the world needs another terminal, I don’t know. I know I did.
Further Reading
- Previous on this site: From Executor to Orchestrator: Your New Job Is Conducting a Fleet of Agents
- Unterm website / open-source repo: unterm.app · GitHub
zhitongblog/unterm
Discussion