voidlink

# ◇ VoidLink ### The keyboard-driven Git workbench that runs entirely on your machine. Editor, terminal, and a Graphite-grade Git suite in one native window — with optional AI that uses **your own CLI**. No cloud. No API keys. No telemetry.

VoidLink screenshot

VoidLink screenshot

┌──────────────────────────────────────────────────────────────────────┐
│  ◇ voidlink            feature/auth ↑2   ✓ clean              ⌘K       │
├────────────┬─────────────────────────────────────────┬───────────────┤
│ ▾ src/     │  auth.rs                          ● dirty │ ▾ Stack       │
│   auth.rs  │  ─────────────────────────────────────── │  ● feature/ui │
│   lib.rs   │   1  use crate::session::Token;           │  │ feature/auth│
│ ▾ git/     │   2  pub fn verify(t: &Token) -> bool {   │  └ main       │
│   diff.rs  │   3 +    if t.is_expired() { return … }   │               │
│            │   4      t.signature_valid()              │ Restack ↻     │
│            │   5  }                                    │ Submit ↑      │
├────────────┴─────────────────────────────────────────┴───────────────┤
│  $ ▏                                                    bash · ⌃\ split │
└──────────────────────────────────────────────────────────────────────┘

Most “AI dev tools” ship a model client, ask for your API key, and phone home. VoidLink takes the opposite stance:


Features

🗂 Editor & files

▸ Terminal

⎇ Git suite

A near-complete Git client built directly on libgit2:

Area What you get
Working tree Status, stage / unstage / stage-all, commit, amend, undo last commit
Hunk-level Stage, discard, and apply individual hunks from the diff view
Branches List, create, switch, rename, delete · MRU branch switcher
History Commit log, working-tree diff, ref-to-ref diff, split diff renderer
Sync Fetch, pull, push (SSH agent or GITHUB_TOKEN)
Rewrite Merge, rebase, cherry-pick, revert — each with continue / abort
Reset Soft / mixed / hard reset to any ref
Stash Save, list, show, apply, pop, drop
Tags Create, delete, push
Worktrees Create / list / remove isolated worktrees, open a terminal in any
Remotes Add, remove, rename, set URL

⫶ Stacked PRs

Graphite-style stacked branches, built in:

⚔ Conflict resolution

⎄ Branch compare

🤖 AI — bring your own CLI

No embedded model, no API key, no telemetry. Configure a shell command in Settings → AI and VoidLink pipes context to it:

# example Settings → AI commands
claude --no-tools -p "Write a concise git commit message for this diff:"
ollama run llama3.2

⌘ Command-driven workflow


Tech stack

Layer Technology Role
Desktop shell Tauri 2 Native window, JS ↔ Rust IPC, custom titlebar
Core logic Rust Git, PTY, filesystem, CLI bridge — all heavy work
Frontend SolidJS + TypeScript Fine-grained reactive UI in the WebView
Styling Tailwind CSS 4 + lucide-solid + Geist Utilities, icons, type
Editor Monaco Code editing with workers per language
Markdown marked + DOMPurify Sanitized live preview
Terminal portable-pty (Rust) + xterm.js Real PTY, canvas terminal
Git git2 (vendored libgit2) Git ops without a system git binary
HTTP reqwest (blocking, rustls) GitHub REST for stacked-PR submit
AI BYO-CLI bridge Shells out to your local generative-text CLI

No embedded LLM client. No database. No backend service.


Quick start

Prerequisites

# Install frontend dependencies
cd frontend && npm install && cd ..

# Run the desktop app (macOS)
cargo tauri dev

# Run on Linux / Wayland
WAYLAND_DISPLAY="" cargo tauri dev

Frontend-only (no native window, for UI work):

cd frontend && npm run dev   # http://localhost:5173

Configuration

VoidLink needs zero environment variables to run. A couple of optional knobs:

Variable Used by Description
GITHUB_TOKEN push / pull / stack submit PAT with repo scope, used as an SSH-agent fallback for HTTPS auth. Never stored on disk.

AI commands are configured in-app under Settings → AI (commit-draft and agent command templates) — not via environment variables.


Building

cargo tauri build          # native installer in src-tauri/target/release/bundle

Tests

cd src-tauri && cargo test   # Rust unit tests (git engine, parsing, …)
cd frontend  && npm run lint # TypeScript / ESLint

Project structure

voidlink/
├── frontend/                 # SolidJS + Vite + TypeScript
│   └── src/
│       ├── App.tsx           # Root: workspace + tab orchestration
│       ├── api/              # IPC wrappers (invoke → Tauri command)
│       ├── commands/         # Command palette, file finder, keybindings,
│       │                     #   AI commit, agent, secret scan, snapshots, toasts
│       ├── components/
│       │   ├── editor/       # Monaco controller + blame overlay
│       │   ├── terminal/     # xterm.js pane
│       │   ├── preview/      # Markdown preview
│       │   ├── files/        # File tree
│       │   ├── git/          # Git suite: diff, sidebar, stack, compare, conflict
│       │   └── layout/       # App shell, titlebar, status bar, surfaces
│       └── store/            # Reactive stores: layout, settings, theme
└── src-tauri/                # Rust (Tauri desktop shell)
    └── src/
        ├── lib.rs            # Command registration + global state
        ├── fs/               # Filesystem commands
        └── git/              # libgit2-backed engine
            ├── repo, branch, status, staging, diff, push, fetch …
            ├── merge, rebase, pick (cherry-pick), reset, stash, tag …
            ├── worktree, conflict, blame, compare, apply_hunk, discard …
            ├── cli, ai_commit, agent     # BYO-CLI AI bridge
            └── stack/                     # stacked-PR discovery, restack, submit

License

MIT