Vectrum
Sign In
465 lines · 33.7 KB
# Vectrum Code

A native [T3 Code](https://github.com/pingdotgg/t3code)-style desktop client for coding
agents, built with [GPUI](https://gpui.rs) and [GPUI Kit](https://github.com/longbridge/gpui-kit).
It drives [Claude Code](https://docs.claude.com/en/docs/claude-code),
[Pi](https://github.com/earendil-works/pi), [jcode](https://github.com/1jehuang/jcode) and
[Empryo](https://empryo.com/).

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.
Empryo threads talk to `empryo --headless --chat --events`, Empryo's headless chat: a message
per line on stdin, its events as JSONL on stdout, and a session the thread names
(`--session-id`) and resumes (`--session`).

The layout and workflow follow T3 Code. The look follows [MetalUI](https://metalui.dev/), the
"soft hardware" design system [vectrum.cc](https://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.

![Vectrum Code in Bone](docs/screenshot.png)

![Vectrum Code in Graphite](docs/screenshot-graphite.png)

## Features

- A sidebar laid out like T3 Code's inbox: unsent drafts, pinned and active threads, then
  the **Working**, **Snoozed** and **Settled** shelves. Each card shows the project, its
  branch or worktree, running processes, the pull request, the agent, and a status
  (Working with a timer, Approval, Input, Limited, Failed, Woke, Done) or how long ago it
  was active; hovering swaps that for **Snooze** and **Settle**, and a hover card sums
  the thread up. Search covers titles and conversations (↑ ↓ Enter, Esc clears), and the
  folder button filters the sidebar to one project. Drag the sidebar's edge to resize it.
- Settle a thread to stop its process and file it under **Settled**. Its transcript stays,
  and the next message resumes the conversation. Settling, snoozing, unpinning, archiving
  and discarding a draft leave a "Settled 1 thread, Ctrl+Z to undo" notice for 5 seconds.
- T3 Code's thread menu, on a right click in the sidebar or a click on the title in the
  chat header: New thread on *branch*, Pin, Settle, Snooze (in 1 or 3 hours, this
  evening, tomorrow, next week, or a custom date, time or duration), Wake, Rename,
  Regenerate title, Mark unread, Filter by *project*, Auto-settle behavior, Copy (path,
  branch, thread ID), Project settings, Archive and Delete. Ctrl+click or Shift+click picks
  several threads for one menu; a double-click renames in place.
- A command palette (Ctrl+K) with T3 Code's actions (new thread in a project or without
  one, copy the thread ID or PR link, restart the agent, add a project, change the theme or
  appearance, usage, settings) and recent threads; typing also finds projects, settings
  and threads, and a leading `>` searches actions only.
- The legacy sidebar (Settings → *Legacy sidebar*): threads in per-project trees, with
  T3's sort menu (projects by last message, creation or by hand, threads by last message
  or creation, how many show) and project menu (Rename, Group into..., Copy Path, Project
  settings, Remove).
- A new thread opens on a start screen, "What should we build in *project*?", with the
  project as a picker (No project, the projects, Add project) and a way to start without a
  project ("What should we work on?").
- The composer is laid out like T3 Code's. Its footer holds the model, reasoning and access
  controls (they lose their labels, then fold into **…**, when the composer is narrow), the
  attach button and the send button, which turns into stop, steer or queue while the agent
  works. Its placeholder says what it takes: a message, your own answer to a question,
  feedback on a plan.
- Under a new thread's composer hangs T3's strip: the machine, the workspace (**Current
  checkout**, **New worktree** or **Previous worktree**) and the branch, with a searchable
  branch picker (current, worktree, remote and default branches, **Create new ref**, and
  **Start from origin** for a new worktree). Settings → Appearance → Composer context keeps
  it there once the thread has started.
- A model picker across providers (Ctrl+Shift+M): a rail with **Favorites** and each provider,
  Claude's models (current ones, plus legacy models folded away), every model Pi's configured
  providers offer, every model route jcode's logins and provider profiles make available, and
  every model of the providers Empryo has a key or sign-in for (`empryo --list-models`).
  Star models to keep them under Favorites, search across all of them, pick the first nine
  with Ctrl+1–9, and switch providers with Ctrl+Shift+↑/↓. On a new thread, picking another
  provider's model starts a thread of that kind in its place; once a thread has started,
  other providers' models are greyed out, as in T3 Code. `/model` opens the picker too.
- A reasoning menu (Ctrl+Shift+E): Low to Max (as the model supports, its default marked),
  Claude's Ultracode and Ultrathink, and Fast Mode on models that have it. Pi threads list Pi's thinking levels;
  jcode threads offer effort levels on its Anthropic, OpenAI and OpenRouter routes; Empryo
  threads offer Low, Medium and High (`--effort`). All of it switches live; an Empryo thread
  restarts Empryo with the new flags once its turn is done, resuming its session.
- An access menu (Ctrl+Shift+A): Supervised, Auto-accept edits, Auto or Full access. Pi,
  jcode and Empryo have no approval prompts, so their threads always run with full access
  (Empryo's `--mode auto`). With plan mode turned on (Settings → General → Legacy features), a
  Build/Plan toggle and Shift+Tab switch Claude, or Empryo (`--mode plan`), into plan mode and
  back.
- Finished turns fold their tool calls and interim messages into one **Worked for 12s** row
  above the answer. Click it to unfold.
- Answers can carry live diagrams and UI, written as plain fenced code blocks. ` ```mermaid `
  draws a diagram in the theme's colors. ` ```openui ` ([OpenUI Lang](https://www.openui.com/))
  draws native cards, stats, tables, charts, tabs and forms; their state stays put while
  you scroll, and buttons and form submissions reach the agent as `[UI]` messages.
  ` ```svg ` draws the image. ` ```html ` gets a **Preview** button that opens the page in the
  thread's browser panel. Every block keeps **Copy** and **Show source**. Agents are told
  how to write them; Settings → General → *Inline diagrams and UI* turns it all off.
- Under each turn that touched files, **N changed files +a −r** with a folder tree and
  per-file counts. Click a file, or **Open diff**, to read the diff in the app. The app
  snapshots the working tree through a private git index, so your staging area is never
  touched.
- Streaming Markdown with a copy button on every code block, and tool calls as
  collapsible cards with their input and output.
- What the agent waits on docks on top of the composer, as in T3 Code: an approval (the
  command or file, **Decline**, **Approve**, and **Cancel** or **Always allow this session**
  under **…**), Claude's questions (one at a time; pick with a click or 1–9, a single choice
  moves on by itself, tick several on multi-select ones, or type your own answer in the
  composer; **Previous**, **Next question**, **Submit answers**; fold or dismiss it), a Pi
  extension's confirm or choice, or Claude's plan (**Plan ready**, then **Implement**,
  **Implement in a new thread**, or type feedback and **Refine**). The transcript keeps what
  you answered.
- Docked there too: queued messages (drag to reorder, edit in the composer with Alt+↑, send
  now as a steer, remove), work left running in the background ("Running: …" with **Stop**),
  a usage limit, and the agent's task list with its progress.
- Ctrl+S puts the prompt aside; a **Stash** tab beside the composer brings it back.
- Attachments: images are sent inline, other files by path. Pasting an image (a screenshot,
  say) or files copied in a file manager attaches them. `@` 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.
- A project panel: the machine, **Open in** your editor (Zed, VS Code, Cursor, VSCodium,
  Sublime Text, Kate or the file manager, whichever are installed), the branch with a
  switcher, **Commit** (asks the agent to commit, or commit and push), the size of the
  uncommitted changes, and the thread's cost.
- A right panel, as T3 Code has it: its button in the top bar (Ctrl+Alt+B) opens "Open a
  surface", which lists the subagents, the browser, a device and a terminal, keyed S, B, D
  and T. A
  terminal there holds the thread's terminals instead of the drawer under the conversation.
- A subagents panel (Ctrl+Alt+S, or S in the right panel), in the project panel's
  place: every subagent the thread started, the working ones first with what each is
  doing right now (its latest tool call and progress summary), how long it has run, its
  kind, tool uses and tokens, then the finished and failed ones. Every working one has a Stop button. Click one to jump to its call in the conversation. While the
  panel is closed, a light on the right panel's button says subagents are working. It covers Claude's
  Task and Agent calls; Pi's [`subagent` extension](https://github.com/earendil-works/pi/tree/main/packages/coding-agent/examples/extensions/subagent)
  (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.
- App-owned subagents, as T3 Code has them: every agent gets `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.
- The Genome map, for Empryo threads (the DNA button in the sidebar's corner, or G in the
  right panel): [Empryo's Genome](https://empryo.com/docs/concepts/genome), the ranked map of
  files, symbols and imports Empryo keeps of a checkout in `.empryo/genome.db`. A graph of the
  48 highest ranked files, sized by rank and colored by folder, with a line for each import,
  and the files ranked below it with how many files import each (`→12`) and what changed in
  the last two days. A file opens to its symbols, exported ones first with their signatures,
  and lights up with its imports in the graph. It follows the thread to its worktree and
  reloads as Empryo updates the map. For a thread on another device, that device reads its
  Genome and sends it over, and again only when Empryo changed it.
- Background tasks: when a command, monitor or subagent Claude left running finishes, the
  transcript says so and Claude picks the work back up and reports back on its own. A
  long-running command can be sent to the background from its tool card, like Ctrl+B in
  Claude Code.
- A browser the agent and you share (Ctrl+Shift+E, or the globe in the top bar), on
  Vercel's [agent-browser](https://github.com/vercel-labs/agent-browser): each thread has
  its own headless Chrome session, which its agent drives with the `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.
- A Device panel, as T3 Code's Device hub (D in the right panel): iOS Simulators and
  Android Emulators live next to the thread, streamed by Expo's
  [expo-device-hub](https://github.com/expo/expo-device-hub). First use walks through
  three steps (the device hub, simulator support, agent access); nothing installs or
  starts before you agree. Pick a running device to watch it, or **Start** a stopped one.
  Each device opens in a tab of its own (**+** for another; double-click or right-click a
  tab to rename it), and only the tab showing streams. The screen takes taps, drags and
  typing; the rail beside it has Home, Back and Recents on Android, rotation, dark mode,
  text size, a screenshot, **Float device over chat** (a small window you drag by its
  handle and resize from any edge) and **Power off device**. The **Tools** drawer shows the
  foreground app and switches appearance, text size, accessibility settings, orientation
  and network, overlays the accessibility element frames, sets a fake location and grants
  or revokes app permissions, each value read back from the device. Android video is
  H.264, decoded in the app with OpenH264; iOS comes as MJPEG. Agents get `device_list`,
  `device_open`, `device_screenshot` and `device_close`, and drive devices with the
  [agent-device](https://github.com/callstack/agent-device) CLI through a launcher the app
  puts on their PATH. A device an agent opens floats over the chat (or opens in the right
  panel with **Auto-show floating preview** off), and its activity shows in the timeline.
  Both tools are npm packages the app installs at pinned versions into its data folder and
  runs with your Node; quitting stops them, while simulators and emulators keep running.
  A thread on another device uses that device's simulators and emulators: its Device panel
  lists, starts and controls them over the remote connection, and the video and touches go
  through the other device's remote server, which passes only the stream routes on to its
  hub, and only with its key.
- A terminal drawer under the conversation, like T3 Code's (Ctrl+J, or the panel toggle in
  the chat header): each thread has its own terminals, which start in the thread's folder
  (its worktree when it has one), on the device the thread runs on. Drag the drawer's top edge to resize it. Split a terminal
  side by side (Ctrl+D) or stacked (Ctrl+Shift+D), up to four per group, or open another
  (Ctrl+N while a terminal has focus); with several, a sidebar lists the groups. Close one
  with Ctrl+W or the trash button (after a confirmation); a terminal whose shell exits
  closes on its own. Right-click for Add to chat (puts the selection in the composer), Copy,
  Paste, Select all and Jump to latest; Ctrl+Shift+C / Ctrl+Shift+V copy and paste.
- Interrupt with Esc or the stop button. Messages sent while the agent works are queued.
- Crash recovery: if a process dies, the next message restarts it and resumes the session.
- Agents run only while they're needed: a new thread's agent starts once you begin typing,
  and an idle thread's agent stops after 15 minutes without activity (Settings → General →
  **Stop idle agents**; 0 keeps them running) to give its memory back. The open thread's
  agent stays up, and the next message resumes the conversation in a new one.
- Threads are saved as you go and come back after a restart, settled ones included; the next
  message resumes the conversation. Optionally, threads a crash or restart interrupted
  continue on their own.
- New threads can start in a git worktree of their own, made like T3 Code's when the first
  message is sent: a temporary `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.
- Thread titles written by a small model from the first message (Settings → General → Text generation).
- Pin threads to the top, snooze them until later, rename them, archive them to Settings →
  Archive. Working threads can fold into a Working shelf until they need you.
- When a usage limit stops a thread, it can snooze until the limit resets and continue then.
  Idle threads, and worktree threads whose pull request merged, can settle on their own.
- Send while the agent works to steer it, or queue the message for after the turn (and
  Ctrl+Enter for the other one). Queued messages wait above the composer.
- A `/` menu with Claude's commands and skills (`$` for skills alone), docked on the composer.
- Diffs with hidden whitespace changes, foldable files, line numbers, and a stacked or
  side-by-side layout.
- Remote connections, like T3 Code's environments: turn on **Allow remote connections** on
  one device, and paste its pairing code (address and key) under Settings → Connections on
  another, or scan the QR code shown next to it with a phone. The host's projects and threads appear in the sidebar tagged with its name; open,
  continue or start threads there and they run on the host. Their terminal drawer (Ctrl+J)
  runs its shells on the host too, in the thread's folder there. The first time a device connects
  with settings that differ from yours, a dialog asks whether to give it your settings or
  leave its own as they are. The connection is a WebSocket guarded by the key but not
  encrypted, so use it on trusted networks or over a VPN.
- Updates from the releases on [vectrum.cc](https://vectrum.cc/vectrum/code/releases), as in
  T3 Code: the button in the sidebar's corner checks for a new version (every few hours on its
  own, unless Settings → General → **Automatic update checks** is off), then downloads it, then
  restarts into it, and only when you click; hovering it lists what changed. The AppImage
  replaces itself, and only with a download whose checksum is in the release's `SHA256SUMS`
  and whose sums carry the release key's Ed25519 signature. Run elsewhere, the button opens
  the release page instead. With this app up to date, the same button shows **Update N
  servers** for connected devices on an older version: each downloads and checks the release
  itself, replaces its AppImage and restarts, and the connection comes back on the new version.
- An Android companion app ([`android/`](android/README.md)), in Kotlin and Jetpack Compose:
  a remote client that looks like the desktop, in the same themes. Pair it by scanning the QR
  code under Settings → Connections; the sidebar, threads, composer and its pickers,
  approvals and questions, diffs, settings, terminals and the Device panel work from the
  phone, on the desktop's agents and files.
- A settings view (Ctrl+, or **Settings** in the sidebar), laid out like T3 Code's: a
  search field over the pages (press `/` 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.
  - **General**: New threads (model and reasoning, permissions, workspace, submodules);
    Organization (project grouping and order, auto-resume and snooze for usage-limited
    threads, the Working section, auto-settling merged and inactive threads); Behavior
    (thread notifications off, as notifications, as sound or both, in-app notifications, time
    format, response streaming by token, paragraph or whole turn, turn folding and costs, diff
    whitespace, file state and Stacked or Split layout, proactive panels, skills in the `/`
    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, and a close
    confirmation that Ctrl+Q, Alt+F4 and the window's close button go through); 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).
  - **Appearance**: System, Light or Dark (System follows the desktop), and a card per theme
    family whose swatches set the light and the dark theme: Vectrum (Bone and Graphite); Tokyo
    Night (Day, Night, Storm); Catppuccin (Latte, Mocha, Macchiato, Frappé); GitHub (Light,
    Dark, Dark Dimmed); One; Nord; Gruvbox; Rosé Pine (Dawn, Main, Moon); Solarized; and
    Dracula. Then diff colors (red and green, or blue and orange), composer context, chat
    width and thread cards.
  - **Keybindings**: the app's shortcuts in T3 Code's groups, searchable, read from the live
    keymap, with when each applies.
  - **Providers**: every agent instance (several Claude, Pi or jcode instances side by side,
    say two accounts) in a list with its status and a switch, and an editor for the selected
    one: display name and accent color, account, binary, home folder, Claude's auto-compact
    threshold, launch arguments, environment variables (sensitive ones are kept in a
    `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.
  - **Integrations**: whether agents may use the shared browser, whether an agent's device
    floats over the chat, the device hub, simulator support and agent device access (each
    tool's running, required and installed versions), and the editor "Open in" uses.
  - **Source Control**: the git and GitHub CLI found on this machine, and how worktree
    branches are named (a static prefix, a prefix the model picks, or your instructions).
  - **Storage**: where new worktrees go, and the app's folders.
  - **Connections**: remote connections to and from other devices.
  - **Archive**: settled threads by project, with Unarchive (and Delete on right-click).
- **Usage** (the chart button at the sidebar's foot, or Ctrl+U) opens on the main sheet: cost,
  tokens and plan limits over the past day, week, month or quarter, read from Claude Code's and
  Pi's local logs and any usage hubs, by provider, model and day.

## Requirements

- Rust (edition 2024)
- The `claude` CLI, logged in (`claude` once interactively)
- Optional: [Pi](https://github.com/earendil-works/pi) for Pi threads
  (`npm install -g @earendil-works/pi-coding-agent`, then log in to a provider with `pi`)
- Optional: [jcode](https://github.com/1jehuang/jcode) for jcode threads
  (`curl -fsSL https://jcode.sh/install | bash`, then `jcode login`)
- Optional: [Empryo](https://empryo.com/) for Empryo threads
  (`curl -fsSL https://empryo.com/install.sh | bash`, then `empryo --login claude` or another
  account, or `empryo --set-key <provider> <key>`). Add it in Settings → Providers → Add provider.
- Optional: agent-browser for the browser panel
  (`npm install -g agent-browser && agent-browser install`)
- Optional, for the Device panel: Node.js 22 or newer with npm, and the Android SDK
  (Platform-Tools, Emulator, Command-line Tools (latest)) with a virtual device made in
  Android Studio's Device Manager, or Xcode on macOS for iOS Simulators. The SDK is found in
  its standard locations or through `ANDROID_HOME`.
- GPUI's Linux system libraries: Vulkan, Wayland/X11, xkbcommon, fontconfig, freetype
  (on Arch/CachyOS: `vulkan-icd-loader` plus your GPU's Vulkan driver, `wayland`,
  `libxkbcommon-x11`, `libxcb`, `fontconfig`, `freetype2`)

## Run

```sh
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 |

### Environment

- `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`.
- `VECTRUM_CODE_EMPRYO_BIN`: path to the `empryo` binary. Otherwise it's looked up on `PATH`,
  then in `~/.empryo/bin` and `~/.local/bin`.
- `VECTRUM_UPDATE_URL`: where to look for updates instead of `https://vectrum.cc`, such as a
  test server. Releases from there still need the release key's signature.

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 and release

`./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, `CHANGES.md` (the commit subjects since the last release,
which the update button lists), `SHA256SUMS` and `SHA256SUMS.sig`, which publishes the
release. The signature is made with the Ed25519 key in `VECTRUM_SIGNING_KEY` or
`~/.config/vectrum/release-signing-key.pem`, whose public half must be in `RELEASE_KEYS` in
`src/update.rs`; installed apps only update to releases it signed, so keep it safe. 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.

### Android

`cd android && ./gradlew :app:assembleDebug` builds the companion app into
`android/app/build/outputs/apk/debug/` (`assembleRelease` for a shrunk build, signed with the
debug key until there's an upload key). It needs JDK 21 and the Android SDK (platform 37) in
`ANDROID_HOME` or `android/local.properties`. The themes come from the desktop's palettes:
after changing them, `cargo test export_android_themes -- --ignored` rewrites
`android/app/src/main/assets/themes.json`.

## Layout

- `src/claude.rs`: spawns and owns the agent process, `claude`, `pi`, `jcode api --stdio` or
  `empryo --headless --chat`
  (stdin writer and stdout/stderr reader threads).
- `src/session.rs`: per-thread state. Folds Claude's stream-json, Pi's RPC, jcode's
  harness API and Empryo's headless 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/genome.rs`: reads Empryo's Genome (`.empryo/genome.db`) and lays out its graph.
- `src/git.rs`: branch and change stats, working-tree snapshots, and diffs.
- `src/settings.rs`: the saved settings file, cached in memory.
- `src/update.rs`: the app's updater: release checks, signature and checksum checks, the
  staged download beside the AppImage, and the restart.
- `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`).

## Not yet supported

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. Empryo threads don't get the app's subagent and device tools (Empryo
loads MCP servers from its config files only), take images by path rather than inline, and
can't be steered mid-turn: a message sent while Empryo works becomes its next turn.