A native T3 Code-style desktop client for coding agents, built with GPUI and GPUI Kit. It drives Claude Code, Pi and jcode.
Every thread runs its own agent process in the background. Claude Code threads talk to the
claude CLI over the stream-json protocol the Claude Agent SDKs use
(--input-format/--output-format stream-json plus the stdio control channel). Pi threads
talk to pi --mode rpc over its JSONL RPC protocol. jcode threads talk to
jcode api --stdio, a bridge to jcode's versioned harness API that starts jcode's shared
daemon if it isn't running, so jcode threads also show up in jcode's own session list.
The layout and workflow follow T3 Code. The look follows MetalUI, the "soft hardware" design system vectrum.cc is built on: the conversation sits on a raised sheet, buttons are caps that sink when pressed, code and tool output sit in inset wells, and labels are engraved in mono caps. It comes in MetalUI's two colorways, Bone and Graphite, plus popular color schemes rebuilt in the same style: Tokyo Night, Catppuccin, GitHub, One, Nord, Gruvbox, Rosé Pine, Solarized and Dracula.


> searches actions only./model opens the picker too.@ lists the thread folder's
files and folders to mention. An optional ring (Settings → General → Legacy features)
shows how full the context window is, with the numbers and Compact context on hover.subagent extension
(a row per task, single, parallel or chained); and jcode's swarm, whose agents keep
working in sessions of their own after the turn ends: the app checks on them every few
seconds through jcode's session list, and reads what each last said.delegate_task,
task_status, task_cancel and orchestrator_capabilities, with instructions for
them. A delegated task runs in a subagent thread in the parent's folder, on the
parent's agent and model or any other agent and model the app has (Claude, Pi or
jcode). The subagents panel shows it working and opens its thread. The thread is
left out of the sidebar, and its header leads back to the parent. When the task
finishes, its answer goes to the call waiting on it (mode: "wait") or reaches the
parent's agent as a message the transcript doesn't show, which wakes it. Nothing to install: Claude reaches the
tools through an MCP server the app runs on loopback, with a token per thread.
Pi reaches them through an extension the app writes and loads with --extension;
before every run, that extension also switches off Pi's other subagent tools.
jcode gets them as session tools over its API, with its swarm and schedule
tools turned off. Subagents can't delegate tasks of their own.agent-browser CLI
while the panel shows it live. Click, scroll and type in the page yourself, or go to a
URL. When the agent opens a page while the panel is closed, a light on the globe says so.vectrum/<8 hex> branch checked out in a folder named after
it (under ~/.local/share/vectrum-code/worktrees/<project>, or the worktree location in
Settings → Storage), optionally from origin's copy of the base branch, with submodules
filled in. A setup card shows each stage (with Work locally and Cancel), and the
branch is then renamed after the first message (Settings → Source Control → Worktree
branch naming: a prefix plus a few words, a semantic prefix like feat/, or your own
instructions). Ctrl+Shift+L starts in the project's previous worktree instead./ menu with Claude's commands and skills ($ for skills alone), docked on the composer./ to search, ↑/↓ and Enter to jump to a setting,
which scrolls into view and pulses), a "Settings / Page" breadcrumb, and every setting as
a row with its description, its control and, once changed, a button that resets it.
Restore device defaults on General lists what it will reset before it does.
General, Integrations, Source Control and Storage open with T3 Code's "Applying settings
for All projects across All environments": the settings that shape threads (new
thread defaults, auto-settle, worktrees and their cleanup, branch naming, writing style,
text generation, browser access) belong to the machine the threads run on, so you can edit
this device's, one connected device's, or every connected device's at once. A Mixed tag
shows where they disagree (hover for each one's value), and picking a value applies it to
all of them; offline devices keep theirs. Pick a project to override settings for it on the
selected devices: overridden rows say Override, and their reset button returns them to
the environment's value. Device preferences, such as appearance and confirmations, ignore
the selection.
/
menu, the rich text composer, collapsing on scroll, the send shortcut (Enter, Ctrl+Enter
for multiline prompts, or Ctrl+Enter always), follow-up behavior, update checks,
continuing after restarts, background activity); Projects & threads; Confirmations
(unpin, archive, delete, and the quit shortcut: Direct, Hold or Double press); the text
generation model; the version; Diagnostics (CPU, memory and every process the app
started, with interrupt and kill buttons) and the open source licenses; and the folded
Legacy features (plan mode, the context window ring, the per-project sidebar).secrets.json only you can read), and its models: favorites, which ones the picker
shows, their order, and custom models. Add provider walks through kind, identity and
configuration. Below: usage hubs (CLIProxyAPI servers) and the health check interval.claude CLI, logged in (claude once interactively)npm install -g @earendil-works/pi-coding-agent, then log in to a provider with pi)curl -fsSL https://jcode.sh/install | bash, then jcode login)npm install -g agent-browser && agent-browser install)vulkan-icd-loader plus your GPU's Vulkan driver, wayland,
libxkbcommon-x11, libxcb, fontconfig, freetype2)cargo run --release -- /path/to/project # defaults to the current directory
The shortcuts follow T3 Code's default keymap.
| Shortcut | Action |
|---|---|
| Enter / Shift+Enter | Send / new line |
| Esc | Interrupt Claude |
| Ctrl+K | Command palette |
| Ctrl+N (or Ctrl+Shift+O) | New thread (picks the project when there are several) |
| Ctrl+Shift+N | New thread in the current project |
| Ctrl+Alt+N | New thread without a project |
| Ctrl+Shift+[ / Ctrl+Shift+] | Previous / next thread |
| Ctrl+1–9 | Jump to a sidebar thread (hold Ctrl to see the numbers) |
| Ctrl+Shift+S | Settle the thread, or un-settle it |
| Ctrl+Shift+P | Pin or unpin the thread |
| Ctrl+Z | Undo the last settle, snooze, unpin or archive (while the notice shows) |
| Ctrl+Shift+C | Copy the thread's PR link, or its ID |
| Ctrl+U | Usage |
| Ctrl+Alt+A / Ctrl+Alt+Shift+A | Change the theme / cycle System, Light and Dark |
| Ctrl+Shift+M / E / A | Model picker / reasoning / access |
| Ctrl+Shift+G / X / H | Branch / workspace / machine (new thread) |
| Ctrl+Shift+L | Start in the previous worktree |
| Ctrl+S | Stash the prompt, or open the stash |
| Alt+↑ | Edit the last queued message |
| Ctrl+F | Find in thread |
| Ctrl+B | Show or hide the sidebar |
| Ctrl+J | Show or hide the terminal drawer |
| Ctrl+D / Ctrl+Shift+D | Split the terminal side by side / stacked (terminal focused) |
| Ctrl+N / Ctrl+W | New terminal / close the terminal (terminal focused) |
| Ctrl+Shift+C / Ctrl+Shift+V | Copy / paste in the terminal |
| Ctrl+Alt+B | Show or hide the right panel: Open a surface |
| Ctrl+Shift+B | Show or hide the thread details (project panel) |
| Ctrl+Alt+S | Show or hide the subagents panel |
| Ctrl+O | Add a project folder |
| Ctrl+, | Open or close settings |
| Ctrl+Q | Quit |
VECTRUM_CODE_CLAUDE_BIN: path to the claude binary. Otherwise it's looked up on PATH,
then ~/.local/bin and ~/.claude/local.VECTRUM_CODE_PI_BIN: path to the pi binary. Otherwise it's looked up on PATH, then in
~/.local/bin, ~/.npm-global/bin, ~/.bun/bin, ~/.volta/bin and nvm's Node versions.VECTRUM_CODE_JCODE_BIN: path to the jcode binary. Otherwise it's looked up on PATH,
then in ~/.local/bin and ~/.cargo/bin.Favorite models and everything in the settings view are saved in
~/.config/vectrum-code/settings.json. The environment variables above win over the binary
paths set there for each agent's first instance. Threads and the project list live in
~/.local/share/vectrum-code (threads/, state.json); threads started without a project run
in its scratch folder.
./build.sh builds the release binary and an AppImage into build/. It builds in an Ubuntu
24.04 container, so the binary needs only glibc 2.39 and runs on Ubuntu 24.04, Fedora 40,
Debian 13 and newer; that needs Docker (directly, through sudo, or as DOCKER="…"), and
the image, target dir and cargo cache are kept in target/ubuntu-24.04. --native builds with
the host toolchain instead, for the host's glibc or newer. -v 0.1.2 first sets the
version (in Cargo.toml and Cargo.lock, committed as "Version 0.1.2"), and --release (or
-r) publishes the build on Vectrum: it tags the commit with the version, pushes the tag, and
uploads the AppImage, the binary and SHA256SUMS, which publishes the release. So
./build.sh -v 0.1.2 -r makes and publishes 0.1.2. Releasing needs a clean working tree, a
version not tagged yet, and a read & write API token in VECTRUM_TOKEN or
~/.config/vectrum/token; the version commit stays on your branch until you push it.
VECTRUM_URL (https://vectrum.cc) and VECTRUM_REPO (from the origin remote) pick where
it goes.
src/claude.rs: spawns and owns the agent process, claude, pi or jcode api --stdio
(stdin writer and stdout/stderr reader threads).src/session.rs: per-thread state. Folds Claude's stream-json, Pi's RPC and jcode's
harness API events into chat items, handles permission requests, control requests and
extension dialogs, and snapshots each turn's changes.src/models.rs: the model catalog across providers, and favorites.src/git.rs: branch and change stats, working-tree snapshots, and diffs.src/settings.rs: the saved settings file, cached in memory.src/terminal.rs: a shell in a pseudo-terminal, emulated by alacritty_terminal, and the
bytes keys and pastes send to it.src/ui/terminal.rs: one terminal's view: the grid in the mono font, cursor, selection,
scrollback and its right-click menu.src/ui/chat.rs: start screen, transcript (with turn folding), tool cards, permission bar
and composer.src/ui/pickers.rs: the model, reasoning and access pickers.src/ui/settings/: the settings view (mod.rs: nav, search, top bar), the row and
section building blocks (widgets.rs), the search catalog (search.rs) and a file per
page.assets/licenses.tsv: the crates the app is built from, with their licenses, for the
Open source licenses page (regenerate it from cargo metadata after changing
dependencies).src/ui/changes.rs: the per-turn change overview and the diff view.src/ui/workspace.rs: window root: thread sidebar, top bar and project panel, plus the
git and editor helpers behind it. Its submodules hold the sidebar (sidebar.rs, and the
legacy layout in sidebar/legacy.rs), the thread menus, snoozing, confirmations and undo
(actions.rs), the chat header (header.rs), the command palette (palette.rs) and the
terminal drawer (terminal_drawer.rs).src/ui/icons.rs: the extra Lucide icons the app embeds.src/ui/metal.rs: the MetalUI layer. Colorway tokens and shadow recipes copied from
MetalUI's CSS, the theme mapping for the stock widgets, and the primitives (caps, wells,
keycaps, status LEDs, segmented switch).src/ui/themes.rs: the color schemes, the palette builder that turns each into a full
MetalUI palette, the light/dark/system selection, and the theme families.assets/fonts/: Geist, Geist Mono and Geist Pixel, embedded in the binary
(SIL Open Font License, see assets/fonts/OFL.txt).There is no UI for Pi extensions that ask for free text, code blocks have no syntax highlighting, and T3 Code's project scripts aren't here yet. The terminal drawer doesn't report mouse clicks to full-screen programs (only the wheel), open links, or show T3's popup after a selection. jcode models can't write thread titles (every jcode run is saved as a session), and jcode's side panel, applets and background-task progress aren't shown.