Introducing teru: An AI-First Terminal Emulator
Why I built a terminal emulator from scratch in Zig — native Claude Code agent protocol, CPU SIMD rendering, 1.3MB binary, no GPU.
Introducing teru: An AI-First Terminal Emulator
I built teru because my workflow was broken, and no existing terminal could fix it.
I was running Claude Code with agent teams — multiple AI agents working in parallel, each with its own scope, each spawning subprocesses. The agents were brilliant. The terminal situation was a disaster.
The problem
Every time Claude spawned an agent team, tmux would fragment into unusable pieces. Four or five agents meant four or five new panes, and tmux had no idea which pane belonged to which team, which agents were waiting on others, or which had finished. I was manually reorganizing layouts constantly.
Worse: I was managing three config layers simultaneously. tmux.conf for the multiplexer. Shell scripts for workspace assignment. Xmonad keybindings for window management. None of them composed. None of them understood what the agents were doing.
The terminal emulator saw bytes. The tiling manager saw windows. tmux saw panes. Not one of them understood process relationships, AI agent state, or why some panes were blocked on others.
The solution
I wanted a single tool that understood the whole picture. One binary that knew:
- Which pane is running which agent
- What its current task is
- Whether it succeeded, failed, or is still running
- How it relates to the other agents (parent-child, team membership)
And I wanted it to receive Claude Code’s spawn commands directly, without a tmux adapter layer.
teru is that tool.
What I built
teru is a terminal emulator, multiplexer, and tiling manager in a single 1.3MB binary. Written in Zig 0.16. CPU SIMD rendering via @Vector. No GPU, no OpenGL, no EGL. Frame times under 50μs.
AI-native architecture
teru implements three layers of AI integration:
CustomPaneBackend protocol. Claude Code has a protocol for terminal backends (issue #26572). When Claude detects teru, it sends JSON-RPC to teru’s Unix socket instead of shelling out to tmux. teru creates the pane, adds it to the process graph, and auto-assigns it to a workspace by team group name.
# Claude Code sends to teru's socket:
{"method": "spawn", "params": {"argv": ["claude", "--agent", "backend-dev"], "metadata": {"group": "team-temporal"}}}
# teru creates a pane in the team-temporal workspace
MCP server. teru exposes six tools over a Unix socket: teru_list_panes, teru_read_output, teru_get_graph, teru_send_input, teru_create_pane, teru_broadcast. Multiple Claude Code instances can connect and query each other’s terminal state.
OSC 9999 agent protocol. Any process can self-declare as an AI agent by emitting escape sequences:
printf '\e]9999;agent:start;name=backend-dev;group=team-temporal\a'
printf '\e]9999;agent:status;progress=0.6;task=Building API\a'
printf '\e]9999;agent:stop;exit=success\a'
teru tracks agents in a process DAG and colors pane borders in real time: cyan for running, green for done, red for failed.
Process graph
Every process spawned inside teru gets tracked in a directed acyclic graph with parent-child relationships and agent metadata. You can query it via MCP, watch status in pane borders, or correlate tool activity to specific output lines.
This is the visibility primitive I was missing. “Which agents are running, what are they doing, which ones failed” — queryable in milliseconds.
Built-in multiplexer
teru’s multiplexer is part of the same event loop as the renderer. No separate tmux server. No IPC overhead. Four tiling layouts (master-stack, grid, monocle, floating), nine workspaces, Ctrl+Space prefix keys.
Agent teams auto-organize into dedicated workspaces when they declare a group name. The status bar shows live counts: [3 running] [1 done] [0 failed].
No GPU by design
I benchmarked the GPU path early. For terminal rendering — text on a character grid — the GPU is idle 99.9% of the time. The actual work is: look up glyph in atlas → alpha-blend onto pixel buffer. SIMD @Vector blitting on CPU is faster for this workload and works everywhere.
// SIMD alpha blending for 4-pixel chunks
const fg_vec: @Vector(4, f32) = .{ fr, fg, fb, fa };
const bg_vec: @Vector(4, f32) = .{ br, bg, bb, 1.0 };
const blended = fg_vec * @splat(alpha) + bg_vec * @splat(1.0 - alpha);
The 1.3MB release binary works in VMs, SSH sessions, containers, and cheap VPS instances with no GPU. No Mesa version conflicts, no GPU driver headaches.
Compressed scrollback
Traditional terminals store scrollback as a grid of character cells — each cell is a Unicode codepoint plus 16 bytes of attributes. A 50,000-line scrollback (80 columns) costs ~640MB.
teru stores the raw VT byte stream with keyframe/delta compression. A keyframe is a full grid snapshot at regular intervals. Deltas are the raw VT bytes between keyframes — typically much smaller than the rendered output. A 50,000-line scrollback that costs ~150MB in a traditional terminal costs ~3–7MB in teru: a 20–50x improvement.
Technical details
- ~16,000 lines of Zig, 250 inline tests
- Pure XCB for X11 (hand-declared externs, no Xlib)
- Wayland via xdg-shell + wl_shm
- stb_truetype for fonts (vendored, no FreeType/fontconfig)
- Three runtime deps: libxcb, libxkbcommon, libwayland-client
- VT100/xterm state machine with SIMD fast-path for ASCII
- Config file:
~/.config/teru/teru.conf, 14 key-value pairs - macOS (AppKit) and Windows (Win32) platform stubs compile but aren’t functional yet
Current status
v0.1.4. Works as my daily driver on X11.
What’s missing: full Unicode (emoji, CJK), shell integration scripts, detach/attach daemon mode (planned v0.2.0), Wayland keyboard from compositor keymap FD, plugin system.
What works well: everything in the AI integration layer — CustomPaneBackend, MCP server, OSC 9999, hook listener (16 event types). That’s the part I built teru for. It’s stable.
Available now
# Arch Linux
paru -S teru
# Build from source
git clone https://codeberg.org/ng/teru.git
cd teru && make release && sudo make install
See the docs for configuration and keybinding reference, and the roadmap for what’s coming.
I’d welcome feedback on the architecture — especially the agent protocol design. The space of AI-native terminal primitives is wide open. I think there are better abstractions than what we have today, and teru is my attempt at defining them.