Orchestrate parallel AI coding agents from a single web UI.
vibe-station lets you spawn multiple AI coding agents (Claude Code, Cursor, OpenCode, Gemini CLI) working simultaneously on isolated git branches — each with its own worktree, terminal, and file preview — all managed from your browser.
Instead of juggling tmux tabs and editor windows, you get a unified interface where every agent runs in its own branch, streams its output live, and can be messaged, paused, or replicated with a single command.
- Parallel agents — run Claude Code, Cursor, OpenCode, and Gemini CLI side-by-side on separate branches
- Isolated worktrees — each agent gets its own
git worktreecheckout, so they never conflict - Live terminal streaming — watch agents work in real time, send messages mid-task
- File preview — browse the working tree, view diffs, render markdown and Mermaid diagrams
- CLI + web UI —
vstfor scripting and automation, browser UI for interactive oversight
Mobile
- Node.js ≥ 20
- pnpm ≥ 9 —
npm install -g pnpm - tmux —
brew install tmux/apt install tmux - git ≥ 2.5 (worktree support)
- At least one AI CLI installed: Claude Code, Cursor, OpenCode, or Gemini CLI
# Clone the repo
git clone https://github.com/your-org/vibe-station.git
cd vibe-station
# Install dependencies
pnpm install
# Build everything (CLI + web)
pnpm build
# Link the CLI globally so `vst` is available anywhere
pnpm link --global
# — or add cli/dist to your PATHVerify it works:
vst --version
vst doctor # checks tmux, git, and installed AI CLIsThe daemon manages all state, worktrees, and tmux sessions. It runs locally on localhost:7421.
vst daemon startIt starts in the background and writes its port + PID to ~/.vibe-station/config.json. Any vst command will also auto-start the daemon if it isn't running.
Point vibe-station at a git repository:
vst project add /path/to/your/repo --name=my-appThe --name flag sets the project ID you'll use in other commands. If omitted, it's inferred from the directory name.
A mode pairs an AI CLI with an optional system context. You need at least one before spawning agents.
vst mode add --name="Claude Coder" --cli=claude --context="You are an expert TypeScript engineer."Supported --cli values: claude, cursor, opencode, gemini.
List your modes:
vst mode lsA worktree is an isolated branch + agent session combo. Creating one atomically checks out a new branch and spawns an agent on it.
vst worktree create my-app \
--branch=feat/my-feature \
--mode=<mode-id> \
--prompt="Implement the user authentication flow described in docs/auth.md"The --prompt is the initial task handed to the agent. The --mode ID comes from vst mode ls.
vst open my-appThis opens your browser to the vibe-station UI where you can watch all agents, browse files, and send follow-up messages.
A project is a registered git repository. All worktrees for a project are created inside ~/.vibe-station/projects/<project-id>/worktrees/.
vst project ls
vst project info my-app
vst project rm my-appA worktree is an isolated git worktree checkout on its own branch. It's the unit of parallel work — one feature, one bug fix, one experiment. Each worktree is completely independent: agents in different worktrees work on different branches and can never overwrite each other's files.
When you create a worktree, vibe-station automatically creates a main session on it and starts your agent. The worktree and its main session are always created together.
vst worktree ls --project=my-app
vst worktree info <worktree-id>
vst worktree rm <worktree-id> # removes the branch, worktree, and all its sessionsA session is a running process inside a worktree — either an AI agent or a plain terminal. Every worktree starts with one session and you can add more as needed. Think of them as tabs that all share the same branch and file system.
The main session (slot m) is created automatically with the worktree. It runs your primary agent and cannot be removed — it lives as long as the worktree does.
Additional sessions can be agents or terminals, and can be added or removed freely:
# Add a second agent (e.g. to write tests while the main agent writes code)
vst session create <worktree-id> --type=agent --mode=<mode-id> --prompt="Write tests for the auth module"
# Add a plain terminal — no AI, just a shell in the worktree
vst session create <worktree-id> --type=terminalSession slots are named m (main agent), a2, a3 (extra agents), t1, t2 (terminals).
vst session ls --worktree=<worktree-id>
vst session info <session-id>
vst session kill <session-id> # any session except the main slot
vst session attach <session-id> # drop into the raw tmux sessionWorktree vs session in short: a worktree is the isolated branch + directory; sessions are the processes running inside it. One worktree, many sessions.
Modes define how agents are configured. Each mode binds an AI CLI to an optional context string that gets prepended to every agent's system prompt.
vst mode ls
vst mode add --name="Reviewer" --cli=claude --context="You review code for correctness and clarity."
vst mode rm <mode-id> # blocked if sessions are using itYou can have up to 10 modes.
Once a session is running, send it a follow-up message:
# Inline message
vst send <session-id> "Add error handling for the network timeout case"
# From a file
vst send <session-id> --file=./instructions.md
# Wait for the agent to go idle before returning
vst send <session-id> "Refactor the data layer" --waitStream an agent's output to your terminal:
# Show last 100 lines
vst session output <session-id> --lines=100
# Follow live (like tail -f)
vst session output <session-id> --followCheck overall status across all your projects:
vst status
vst status --project=my-app --jsonSessions move through these states:
| State | Meaning |
|---|---|
not_started |
Spawned but not yet launched |
working |
Agent is actively processing |
idle |
Agent is waiting for input |
done |
Agent has completed its task |
exited |
tmux session died (can be resumed) |
If the daemon restarts or tmux dies, sessions can be restored:
vst session restore <session-id>Claude Code sessions resume with full conversation history. Cursor sessions restart fresh (API limitation). OpenCode sessions resume if a session ID was captured.
The UI is organized around a workspace layout:
- Left sidebar — project and worktree navigator; create/delete worktrees here
- Terminal panel — live streaming output for the selected session; send messages inline
- File tree — browse files in the active worktree
- Preview panel — render files (markdown, diagrams, code), view diffs
Tabs at the top of the terminal panel correspond to sessions (m, a2, t1, etc.). Click to switch between them or use the + button to add new sessions.
To view a diff of changes an agent made:
- Select a file in the file tree
- Click the Diff toggle in the preview panel
- Choose
local(working tree vs HEAD) orbranch(vs base branch)
The UI collapses to a single-column layout on phones — the kanban becomes a stacked list, and the workspace stacks the markdown preview above the agent terminal so you can read a plan and watch the agent execute it without switching tabs.
Drop an AGENTS.md file in your project root (or .vibe-station/rules.md) to inject project-specific instructions into every agent spawned for that project:
# AGENTS.md
- Always write tests for new functions
- Use the existing logger from src/lib/logger.ts
- Never modify migration files directlyvibe-station reads this file at spawn time and includes it in the agent's system prompt.
vibe-station stores all its state in ~/.vibe-station/:
~/.vibe-station/
├── config.json # daemon port, PID
├── modes.json # your configured modes
├── logs/
│ └── daemon.log
└── projects/
└── <project-id>/
├── manifest.json # worktrees + sessions for this project
└── worktrees/
└── <worktree-id>/ # git worktree checkout
The daemon manages this automatically. You shouldn't need to edit these files manually.
flowchart LR
user([👤 You])
subgraph clients [Clients]
direction TB
cli["vst CLI<br/>scripting + automation"]
webui["Web UI<br/>React + Vite"]
end
subgraph daemon ["Daemon — localhost:7421"]
direction TB
rest["REST<br/>/projects · /worktrees · /sessions"]
ws["WebSocket<br/>terminal stream + lifecycle events"]
state[("~/.vibe-station<br/>manifest.json · modes.json")]
end
subgraph runtime ["Per-worktree runtime"]
direction TB
tmux["tmux session"]
pty["PTY"]
agent["claude / cursor / opencode / gemini"]
repo[("git worktree<br/>isolated branch")]
end
user --> cli
user --> webui
cli -->|HTTP| rest
webui -->|HTTP| rest
webui <-->|WebSocket| ws
rest --- state
rest -->|spawn / kill| tmux
ws -.->|attach| tmux
tmux --> pty --> agent
agent --> repo
- CLI and Web UI are thin clients — every action (create worktree, send message, kill session) is an HTTP call to the daemon.
- The daemon is the only stateful component. It owns the manifest, spawns agents into tmux, and broadcasts terminal output + lifecycle changes over WebSocket.
- Each worktree is an isolated
git worktreecheckout on its own branch, with one or more tmux sessions running an AI CLI inside it. Agents in different worktrees never see each other. - Persistence: tmux sessions outlive the daemon, so a daemon restart reattaches without losing agent state. Claude sessions additionally resume their conversation history via
claude --resume.
# Start the dev server (hot reload for the web UI)
pnpm dev # http://localhost:5173
# Build everything
pnpm build
# Run tests
pnpm test
# Type checking
pnpm typecheck
# Lint
pnpm lintThe project is a monorepo with three sibling directories at the root:
web-ui/— React 19 + Vite frontend (@vibestation/web)cli/—vstCLI binary (@vibestation/cli)daemon/— Fastify HTTP server + PTY/tmux management (TypeScript source only — compiled intocli/dist/daemon/via a source symlink, not a separate package)
cli/src/daemon is a symlink to ../../daemon/src, so a single tsc in cli/ compiles both the CLI commands and the daemon in one pass. The daemon runs as a detached child process spawned by vst daemon start — it is not imported as a module.
Windows: Git requires symlink support (
git config core.symlinks true+ Developer Mode enabled) forcli/src/daemonto clone correctly. Without it the build will fail. Linux and macOS work out of the box.
Editor tip: Open daemon source via
cli/src/daemon/(the symlink path) rather thandaemon/src/directly — TypeScript's project context andgo-to-definitionare anchored to thecli/tsconfig, so the symlink path gives you full IDE support.
vst project add | rm | ls | info
vst worktree create | rm | ls | info
vst session create | kill | ls | info | attach | restore | output
vst mode add | rm | ls
vst send <session-id> [message] [--file] [--wait]
vst status [--project] [--json]
vst open [target]
vst daemon start | stop | restart | status
vst doctor
vst completion <bash|zsh|fish>
Run vst <command> --help for full options on any subcommand.
vst doctor is your first stop — it checks for tmux, git, and AI CLIs.
Daemon not starting:
vst daemon status
cat ~/.vibe-station/logs/daemon.logOrphaned worktrees after a crash:
vst doctor # detects and offers to clean up orphansPort conflict (7421 already in use):
The daemon auto-picks the next free port. Check ~/.vibe-station/config.json for the actual port in use.
Claude sessions not resuming:
Make sure you're on Claude Code ≥ 1.x with the --resume flag available. Run claude --version to check.
vibe-station is inspired by agent-orchestrator, emdash, and claudecodeui — but built from the ground up to be extremely lightweight and minimal. No cloud, no accounts, no platform. Just a local daemon, a browser tab, and your agents.
- Zero dependencies at runtime — a single
vst daemon startis all it takes; no Docker, no databases, no external services - Minimal UI, zero noise — the interface is stripped to what matters: watching agents work, reading their output, and navigating the files they produce
- Built for reading plans — the preview pane renders markdown with full GFM support and Mermaid diagram rendering, so agent-written specs and plans are readable without leaving the UI
- Live file tree — the file tree updates in real time as agents create, rename, and delete files; no manual refresh needed
- Agent-agnostic — Claude Code, Cursor, OpenCode, and Gemini CLI all plug in identically; swap or mix them per worktree without changing anything else
- Sessions outlive the daemon — tmux-backed sessions survive restarts; Claude sessions even resume their full conversation history




