Install
Install a pre-built release on Linux x86_64/arm64 or macOS Apple silicon (arm64):
curl -fsSL https://raw.githubusercontent.com/bnema/vev/main/install.sh | sh
Alternatively, build and install the binary with Go:
go install github.com/bnema/vev@latest
The default UI runs entirely from the vev binary. The pre-built installer also installs optional external bar anchors, which only take effect when configured in ~/.config/vev/config.
Usage
vev create an ephemeral session
vev new <name> create a named session
vev attach <name> attach to a session (alias: a)
vev attach user@host[:session] attach to a remote daemon over SSH
vev ls list sessions
vev kill <name> kill a session (--all kills everything)
The daemon starts on first use and exits with the last session. Ephemeral sessions are numbered, survive detach, and disappear with the daemon. Named sessions persist across daemon restarts and come back with their layout, recovered terminal transcript, and allowlisted processes.
Each session has one active client. Attaching another client shows Session snatched on the prior client; press r to resume control, or q/Esc to quit that attachment. A remote snatched client that reconnects stays snatched and does not take control automatically.
Keys
No prefix key; everything is Alt.
| Key |
Action |
| Alt+Space |
command palette |
| Alt+f |
floating terminal for the current tab |
| Alt+1 .. 9 |
switch tab |
| Alt+h/j/k/l, Alt+Arrow |
focus pane |
| Alt+a |
jump to a session needing attention |
The palette does the rest: type a short code (SPR split right, CNT new tab, SSP session picker, ...) or fuzzy-search the command list. MPN (Move pane to tab) and MTB (Move tab to session) open live-destination pickers; the unbound pane consume/expel actions are discoverable there as CEL and CER. Named active and stopped sessions are fuzzy-searchable for navigation, and selecting a stopped session resumes it. Scroll up with the mouse to enter scrollback; vim keys move, v selects, y copies via OSC 52.
Remote attach
vev attach user@host[:session]
SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. vev must be installed on the remote. If your firewall only allows SSH, open the UDP range first (default 61000:61023, override with VEV_UDP_PORT_RANGE):
sudo ufw allow 61000:61023/udp
Where UDP is not an option, VEV_REMOTE_TRANSPORT=stdio keeps everything inside SSH at the cost of slower disconnect detection. Details in docs/remote-resilience.md.
Scripting
vev cmd runs control commands against a running daemon; it never starts one. For example:
vev cmd split-right
vev cmd toast -l warn "build failed"
vev cmd list-panes --json
Target a session explicitly with -s (vev cmd -s work new-tab). Inside a pane, --self targets that pane; it cannot be combined with -s. Otherwise vev uses $VEV inside a pane, then the only live session; ambiguous targets fail. Run vev cmd --help for the scriptable command list and vev cmd <command> --help for command usage.
Move the focused pane or active tab with these exact forms:
vev cmd [-s <source-session>] [--self] move-pane <destination-session> <destination-tab-id>
vev cmd [-s <source-session>] [--self] move-tab <destination-session>
Use vev cmd -s <destination-session> list-tabs to find stable destination tab IDs. Destinations must be live, but may be named or ephemeral; stopped sessions are not eligible. A moved pane is split to the right of the destination tab's focused pane and becomes that tab's internal focus without activating the tab. A moved tab is appended to the destination in the background. If the move empties the source session, its client follows the moved pane or tab, activates the destination, and snatches any active destination client.
Moving the final tiled pane out of a tab is rejected while that tab has a floating pane slot; close the floating pane or move the whole tab instead. Named-session persistence is best-effort across a move, not crash-atomic across source and destination snapshots.
Configuration
Optional file at ~/.config/vev/config, reloaded live. Themes, key bindings, palette codes, the floating terminal, status bar scripts: see docs/configuration.md. Terminal color and VT compatibility notes live in docs/terminal.md.
Development
make test # go test ./... -race
make lint # goimports check, go vet
make mocks # regenerate mocks
make demo # regenerate docs/assets/demo.gif (needs Docker and ~/.claude credentials)
source
1<h1 align="center">vev</h1>
2
3<p align="center"><em>Norwegian: to weave</em></p>
4
5<p align="center">A terminal multiplexer for Linux and macOS.<br>
6One Go binary, zero runtime dependencies, remote attach that survives bad networks.</p>
7
8<p align="center"><img src="docs/assets/demo.gif" alt="vev demo: named sessions, splits, stacked panes, floating window, agent bell notification, detach and re-attach" width="800"></p>
9
10---
11
12## Install
13
14Install a pre-built release on Linux x86_64/arm64 or macOS Apple silicon (arm64):
15
16```sh
17curl -fsSL https://raw.githubusercontent.com/bnema/vev/main/install.sh | sh
18```
19
20Alternatively, build and install the binary with Go:
21
22```sh
23go install github.com/bnema/vev@latest
24```
25
26The default UI runs entirely from the `vev` binary. The pre-built installer also installs optional external bar anchors, which only take effect when configured in `~/.config/vev/config`.
27
28## Usage
29
30```text
31vev create an ephemeral session
32vev new <name> create a named session
33vev attach <name> attach to a session (alias: a)
34vev attach user@host[:session] attach to a remote daemon over SSH
35vev ls list sessions
36vev kill <name> kill a session (--all kills everything)
37```
38
39The daemon starts on first use and exits with the last session. Ephemeral sessions are numbered, survive detach, and disappear with the daemon. Named sessions persist across daemon restarts and come back with their layout, recovered terminal transcript, and allowlisted processes.
40
41Each session has one active client. Attaching another client shows `Session snatched` on the prior client; press `r` to resume control, or `q`/Esc to quit that attachment. A remote snatched client that reconnects stays snatched and does not take control automatically.
42
43## Keys
44
45No prefix key; everything is Alt.
46
47| Key | Action |
48|---|---|
49| Alt+Space | command palette |
50| Alt+f | floating terminal for the current tab |
51| Alt+1 .. 9 | switch tab |
52| Alt+h/j/k/l, Alt+Arrow | focus pane |
53| Alt+a | jump to a session needing attention |
54
55The palette does the rest: type a short code (`SPR` split right, `CNT` new tab, `SSP` session picker, ...) or fuzzy-search the command list. `MPN` (`Move pane to tab`) and `MTB` (`Move tab to session`) open live-destination pickers; the unbound pane consume/expel actions are discoverable there as `CEL` and `CER`. Named active and stopped sessions are fuzzy-searchable for navigation, and selecting a stopped session resumes it. Scroll up with the mouse to enter scrollback; vim keys move, `v` selects, `y` copies via OSC 52.
56
57## Remote attach
58
59```sh
60vev attach user@host[:session]
61```
62
63SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. vev must be installed on the remote. If your firewall only allows SSH, open the UDP range first (default `61000:61023`, override with `VEV_UDP_PORT_RANGE`):
64
65```sh
66sudo ufw allow 61000:61023/udp
67```
68
69Where UDP is not an option, `VEV_REMOTE_TRANSPORT=stdio` keeps everything inside SSH at the cost of slower disconnect detection. Details in [docs/remote-resilience.md](docs/remote-resilience.md).
70
71## Scripting
72
73`vev cmd` runs control commands against a running daemon; it never starts one. For example:
74
75```sh
76vev cmd split-right
77vev cmd toast -l warn "build failed"
78vev cmd list-panes --json
79```
80
81Target a session explicitly with `-s` (`vev cmd -s work new-tab`). Inside a pane, `--self` targets that pane; it cannot be combined with `-s`. Otherwise vev uses `$VEV` inside a pane, then the only live session; ambiguous targets fail. Run `vev cmd --help` for the scriptable command list and `vev cmd <command> --help` for command usage.
82
83Move the focused pane or active tab with these exact forms:
84
85```text
86vev cmd [-s <source-session>] [--self] move-pane <destination-session> <destination-tab-id>
87vev cmd [-s <source-session>] [--self] move-tab <destination-session>
88```
89
90Use `vev cmd -s <destination-session> list-tabs` to find stable destination tab IDs. Destinations must be live, but may be named or ephemeral; stopped sessions are not eligible. A moved pane is split to the right of the destination tab's focused pane and becomes that tab's internal focus without activating the tab. A moved tab is appended to the destination in the background. If the move empties the source session, its client follows the moved pane or tab, activates the destination, and snatches any active destination client.
91
92Moving the final tiled pane out of a tab is rejected while that tab has a floating pane slot; close the floating pane or move the whole tab instead. Named-session persistence is best-effort across a move, not crash-atomic across source and destination snapshots.
93
94## Configuration
95
96Optional file at `~/.config/vev/config`, reloaded live. Themes, key bindings, palette codes, the floating terminal, status bar scripts: see [docs/configuration.md](docs/configuration.md). Terminal color and VT compatibility notes live in [docs/terminal.md](docs/terminal.md).
97
98## Development
99
100```sh
101make test # go test ./... -race
102make lint # goimports check, go vet
103make mocks # regenerate mocks
104make demo # regenerate docs/assets/demo.gif (needs Docker and ~/.claude credentials)
105```