Workspace-organized AI conversations. A GTK4/libadwaita desktop app in C.
Chats do not live in a flat list; they live in a tree of workspaces and folders, and each chat inherits its parent chain's context — backend, model, working directory, repository and project instructions.
Lunar
├── Proxy
│ ├── Implement rate limiting
│ └── Fix websocket reconnect
└── Dashboard
└── UI Rewrite
Personal
└── Dotfiles
The app does not talk to any AI API itself. It drives the coding-agent CLIs
already installed and authenticated on your machine — claude, codex, and
opencode for Cerebras — as subprocesses, streaming their JSONL output into
the UI.
For Cerebras, install OpenCode and either export
CEREBRAS_API_KEY before starting xd or run opencode auth login, choose
Cerebras, and enter your API key. xd never stores the key.
Linux, x86_64:
curl -fsSL https://github.com/RestartFU/xd/releases/download/nightly/install.sh | shThat fetches the latest nightly, puts it in ~/.local/opt/xd-nightly, adds the
command xd-nightly and an entry in the app menu. No root, no package manager,
and nothing compiled: the bundle carries its own GTK and everything under it, so
it runs anywhere with glibc.
Chats and workspaces live in ~/.local/share/xd-nightly, which is the nightly's
own — it installs beside a release rather than over it, and neither edits the
other's work. sh -s -- --uninstall takes it away again and leaves that
directory alone.
To try a pull request, there is a dev build: the same thing built from a branch,
published on every push to that pull request. It uses the nightly's paths and
installs over it on purpose, and from then on the update button and
xd serve --auto-update follow the branch rather than master. Linux only.
curl -fsSL https://github.com/RestartFU/xd/releases/download/dev/install.sh | sh -s -- --devRunning the nightly line again puts the nightly back.
Windows, x86_64:
irm https://github.com/RestartFU/xd/releases/download/nightly/install.ps1 | iexThat downloads the latest nightly MSI, verifies its SHA256 checksum, and opens the Windows installer. Approve the Windows elevation prompt. The MSI carries GTK and the rest of its runtime; MSYS2 is not required on the installed machine. It can also be downloaded directly from the nightly release.
This first Windows client supports local chats and connecting to a paired Linux daemon. Embedded terminals and hosting the daemon on Windows are not available yet. The terminal button is omitted instead of exposing a control that cannot work.
macOS 14 or newer, Apple Silicon:
curl -fsSL https://github.com/RestartFU/xd/releases/download/nightly/install-macos.sh | shmacOS needs its own native Mach-O build; the Linux ELF/glibc bundle cannot run
there. The installer verifies the download and puts the self-contained app in
~/Applications/xd-nightly.app. This first macOS build supports local chats,
the embedded terminal, and connecting to a paired Linux daemon. Hosting the
daemon on macOS is not available yet.
Docker is the only requirement. Nothing is installed on the host.
./scripts/build.sh # -> ./dist, a self-contained bundle
./scripts/test.sh # headless test suitebuild.sh produces a relocatable directory containing the binary, its whole
library closure (including the dynamic loader), GTK's support data and a
launcher. It runs on any glibc x86_64 host, including distributions with no
system GTK such as NixOS.
./dist/xd.shThe app itself is deliberately not run inside Docker: it spawns the host's
agent CLI, which needs the host's own credentials and PATH. The launcher
invokes the bundled loader with --library-path rather than exporting
LD_LIBRARY_PATH, so child processes still use host libraries.
On non-Debian hosts, fontconfig prints parse warnings on startup for files under
/usr/share/fontconfig/conf.avail: that path is compiled into the bundled
library and the host's copy is a newer format. Fonts resolve correctly; the
noise is cosmetic.
- Workspaces and folders are real directories under
~/Workspaces, nested to any depth. Each carries a.xd.jsonwith a UUID, so a folder can be renamed or moved without its chats losing track of it. - A folder refers to a repository rather than containing one. Working directory, repository, backend, model and project instructions are set per folder and inherited by everything below; instructions accumulate from the root down, everything else is overridden by the nearest folder that sets it.
- New chats pick their own working directory and can stay in that checkout,
reuse any worktree already registered with its repository, or create an
isolated, request-named worktree under
../worktrees/<repository>/<worktree-name>/<repository>/before the first message. Branches use the readable name plus a short stable suffix. - The composer shows which assistant will answer and which branch, worktree and remote it is looking at.
- Replies stream in and are rendered as Markdown. Stopping sends SIGINT first, so the CLI's own session survives and the chat can still be resumed.
Ctrl+Ksearches every message.
| Path | What lives there |
|---|---|
src/tree/ |
Workspace tree: nodes, disk scanner, sidebar |
src/chat/ |
Chat view, message rows, subprocess session |
src/backend/ |
Agent CLI argv building and JSONL parsing |
src/settings/ |
Per-folder .xd.json settings and inheritance |
src/storage/ |
SQLite: chats, messages, full-text search |
tests/ |
Headless tests, no GTK required |
Workspace folders are real directories (default ~/Workspaces), so they can be
browsed, moved and synced with ordinary tools. Chat messages live in SQLite at
~/.local/share/xd/chats.db, keyed by a stable folder UUID so renaming or
moving a folder never breaks its chats.