termden
A workspace and harness control surface for terminal coding agents.
Agent-facing conventions, commands and gotchas live in AGENTS.md.
termden is one borderless GPUI window: a Hyprland-flavoured panel on the left
listing workspaces and their harnesses, and a dwindle-tiled terminal canvas on
the right. A workspace is a named view over a folder (two workspaces may point
at the same folder, or at subfolders). A harness is a real terminal process β
Claude Code, opencode, pi, or any command.
Every harness is a real terminal: each runs in a session on a private tmux
server (tmux -L termden, session td-<harness id>) and is rendered through
alacritty_terminal. Closing or reloading termden only detaches; reopening
reattaches. Workspaces, layouts and focus persist in
$XDG_STATE_HOME/termden/state.json. The mock fleet lives behind --demo.
Install
|
The script installs termden and termdenctl into ~/.local/bin, plus a launcher entry
and icon; no root and no Rust toolchain. Linux x86_64 (Wayland or X11, glibc
2.35+). It also needs tmux, a Vulkan driver, xdg-open, and the fonts Noto Sans,
JetBrainsMono Nerd Font and "Material Symbols Rounded"; the installer lists
whatever is missing. sh install.sh --uninstall removes it again.
On Arch (AUR):
With Rust, without compiling (cargo-binstall fetches the release binary):
With Rust (1.90+), building from source:
This compiles C and C++ code from GPUI's dependencies, so it needs a C/C++ toolchain and a few dev packages first:
- Arch:
sudo pacman -S --needed base-devel pkgconf libxkbcommon-x11 wayland vulkan-icd-loader freetype2 fontconfig - Debian/Ubuntu:
sudo apt install build-essential pkg-config clang libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libvulkan-dev libfontconfig-dev libfreetype-dev libxcb1-dev libxcb-xkb-dev - Fedora:
sudo dnf install gcc gcc-c++ clang pkgconf-pkg-config libxkbcommon-x11-devel wayland-devel vulkan-loader-devel fontconfig-devel freetype-devel libxcb-devel
failed to find tool "c++" means the C++ compiler is missing (build-essential,
gcc-c++ or base-devel). linking with cc failed with cannot find -lxyz
in its notes means that library's dev package is missing (on Debian/Ubuntu
-lxcb-xkb is libxcb-xkb-dev, -lxkbcommon-x11 is libxkbcommon-x11-dev). Updates arrive the same way as below (or re-run the command).
Updates: termden checks GitHub for a new release every few hours. When one
is out, the sidebar footer shows Update to vX.Y.Z; click it (or run
termdenctl update). The new binary is swapped in and termden reloads itself
in place, and every tile keeps running. Re-running the installer works too.
Launching termden again while it runs focuses the existing window (Hyprland).
Build from source
&&
Source builds never self-update (rebuild instead; see below).
Developing termden inside termden
dev.sh runs termden under Subsecond hot-patching, in the background (log in
$XDG_STATE_HOME/termden/dx.log; point your launcher at it). Saving a source
file patches the running app in a second or so: the window and every tile
stay. Edits that cannot be patched (struct fields, startup code,
termden-core) make dx rebuild and relaunch termden; harnesses live in tmux
and reattach, including the agent editing the code. The first build takes
several minutes and keeps its own artifacts under target/.
Without dx, run a plain build (cargo build -p termden && target/debug/termden): it watches its own binary and execs into each rebuild,
closing the window briefly. ctrl+shift+r reloads by hand.
termdenctl talks to the running app over $XDG_RUNTIME_DIR/termden/app.sock.
Every harness has it on PATH and knows its own id ($TERMDEN_HARNESS, or
just self). Harnesses and workspaces are named by id or name:
|||||| | | | |
Build errors land in dx.log. Agents can drive and read
tiles but cannot move, resize or zoom them; layout is yours.
Shortcuts (ctrl+shift + β¦)
| key | action |
|---|---|
T |
new shell (+ on a workspace picks an agent) |
arrows / hjkl |
focus the neighbouring tile |
Z or double-click a header |
zoom the tile to the whole canvas |
W |
close the tile (press twice; kills its process) |
PgUp / PgDn |
previous / next workspace |
V |
paste (drag in a tile selects; release copies) |
+ - 0 |
terminal text size |
R |
reload termden |
Drag a tile header to split or swap; drag the gap between tiles to resize. Enter on an exited tile restarts it.
Releasing
- Bump
versionin the rootCargo.toml, and thetermden-coreversion in[workspace.dependencies]to match; commit. - Tag and push:
git tag v0.2.0 && git push origin v0.2.0..github/workflows/release.ymltests, builds on Ubuntu 22.04 and publishestermden-x86_64-linux.tar.gz; installed copies offer it in the footer. - crates.io:
cargo publish --workspace --exclude termden-daemon. - AUR: in
packaging/aur/termden-bin, setpkgver, runupdpkgsumsandmakepkg --printsrcinfo > .SRCINFO, then push both to the AUR repo.
Headless screenshots
No display needed, and it never touches your session:
It builds the app, starts a private Weston compositor on the headless backend
(kiosk shell, no panel or decorations), runs termden against the software
Vulkan driver, and captures the output with weston-screenshooter. Default
output: target/termden-headless.png.
GPUI's own Application::headless() is not enough: on Linux it deliberately
refuses to open windows, so there is nothing to capture. The script uses a
virtual compositor instead. It also generates a transparent Xcursor theme so
the host's cursor art cannot leak into captures.
Requires weston, weston-screenshooter, python3, and ideally the lavapipe
Vulkan ICD (lvp_icd.json).
What works right now
-
Live harnesses of every kind, added with
+on a workspace orctrl+shift+t; close (confirmed), restart and zoom on each tile. -
Resizable splits, keyboard focus movement, adjustable terminal text size.
-
Mouse, scroll and selection via tmux; drag to copy,
ctrl+shift+vpastes. -
--demo: three mock workspaces, eight harnesses, live subagent rows. -
Click a workspace to activate it, the chevron to collapse it.
-
Click a harness (panel or tile) to focus its tile with the workspace accent.
-
Drag a tile by its header: edge zones split, centre swaps. The dwindle tree mutates for real, in memory.
-
The settings popup (gear in the panel header, or ctrl+shift+,) switches Catppuccin Mocha β Everforest, sets text size, lists shortcuts, and persists the choice to
~/.config/termden/config.toml. -
Tiles resize their tmux session; typing goes to the focused tile.
tmux -L termden lslists them;tmux -L termden kill-serverresets them. -
Borderless window; drag the logo/wordmark to move it.
-
Ctrl+click an http(s) link in a tile to open it in your browser.
Not wired yet: agent adapters (status comes from terminal activity) and the daemon.
Agents
Claude Code and opencode tiles started by termden get a termden MCP server
(per-process config; your global settings are untouched):
termden_agents,termden_read_screen: see the other tiles.termden_send_mail,termden_read_mail: mailboxes. An idle agent gets a one-lineπ¬ sender: previewβ¦typed into its prompt, never over your draft.termden_set_name|tag|pronouns|title: identity shown in the sidebar.termden_ask_user_choice: shows you 2-8 options (with images) on a local web page in termden's colours. The agent posts the link; Ctrl+click it, pick, and the answer is mailed back. The page lives on127.0.0.1behind a random token kept beside the control socket.
Configuration
$XDG_CONFIG_HOME/termden/config.toml (defaults shown):
= "catppuccin-mocha" # or "everforest"
= "Noto Sans"
= "JetBrainsMono Nerd Font"
= 16.0 # terminal text; ctrl+shift +/-/0
= 268.0
= 12.0
= 12.0
The settings popup writes the same file. Icons use the system font "Material Symbols Rounded".
Layout
crates/
core/ domain types, dwindle tree, config β no GPUI dependency
app/ package `termden`: the app, plus `termdenctl` (src/bin) for
humans and agents; GPUI front end
theme.rs palette + fonts, Catppuccin and Everforest
mock.rs the demo fleet (`--demo`)
state.rs app state, sessions, input, frame ticker
actions.rs workspace/harness CRUD, persistence, hot reload
control.rs control socket for `termdenctl`
pty.rs tmux client PTY, alacritty Term, key map
panel.rs workspace/harness/subagent tree
canvas.rs canvas rect + dwindle geometry
tile.rs tile chrome and drop zones
terminal.rs terminal grid renderer + demo buffers
widgets.rs dots, badges, pills, spinner
daemon/ stub β will own tmux sessions, PTYs, socket, MCP
core holds no GPUI types so the daemon, CLI and MCP server can reuse the same
model and layout tree.
Design decisions
- Terminal strategy: daemon-owned tmux sessions on a private server
(
tmux -L termden), rendered by an embedded terminal surface.kitty attachremains available for the real thing. Wayland cannot embed a foreign kitty window inside a GPUI window, so this is the closest honest equivalent. - GPUI 0.2.2 only.
gpui-component0.9 is built ongpui-pre0.3.11, a separate pinned Zed snapshot. The design system here is intentionally local. - Messaging: hybrid. Messages land in a per-agent mailbox; an idle harness gets a short notice and the agent pulls the full text through an MCP tool.
- Agent permissions: agents may spawn/kill harnesses in their own workspace, create workspaces under an allowed root, and message anyone. They may not move or resize tiles.
- Themes: palette plus metrics are resolved once at startup; switching rebuilds the theme from config.
Hyprland
The window declares app_id = "termden" and requests client-side decorations
with no titlebar, so it is already borderless. Optional rice:
windowrulev2 = rounding 12, class:^(termden)$
windowrulev2 = noblur, class:^(termden)$
The app paints its own 12px outer gap, panel radius and tile borders.
Roadmap
- Terminal + daemon β
portable-pty+alacritty_terminal, tmux on a private socket, reattach after restart,window-size latestfor kitty attach. - Adapters β Claude Code launched with our
--settingshooks; opencode with our config and SSE events. Subagent trees, unread counts, status. - Control plane β JSON-RPC over
$XDG_RUNTIME_DIR/termden/sock,termdenctl, MCP tools, hybrid mailbox. - Polish β command palette, persistence, optional translucent/blurred window background.
License
GPL-3.0-or-later. See LICENSE.