Skip to content

Every session, every machine

Every machine you reach keeps its own sessions. alto ls --all asks them all at once and lists every session in one place, each named by its machine and id.

Terminal window
alto ls --all
studio, this one (2)
studio:3 working claude sanford/alto ~/dev/alto 2h
studio:7 idle shell - ~ 5m
win (2)
win:2 needs-you claude sanford/alto C:\dev\alto 40m
win:4 done cargo sanford/web C:\dev\web 1h
gpu-1, not answering (No answer. It may be asleep or offline); last seen 12m ago (1)
gpu-1:1 working? codex sanford/train /home/agent/train 3d

Each line is a session’s address, what it’s doing, its agent or program, the git repository its folder is in (named by its remote, as owner/name), its folder and how long it has run.

A machine that doesn’t answer doesn’t hold up the others. It keeps the sessions it listed last, marked as last seen and with a ? after their state, because they may have moved on since.

⌘P (Ctrl⇧Space on Linux and Windows), or Shell → Go to Session…, brings up the same list over the tab you’re in: every running session on every machine, those needing you first, then the most recent; then a new tab on each machine, and in each pool.

Typing narrows it. Each word must match something shown (the agent or program, the tab’s title, the machine, the folder, the repository), or be a selector word as for --in below: win claude, repo:alto, status:needs-you os:windows.

Keys Does
↑ ↓, Page Up Page Down Move through the list
Enter Go to the session’s tab, or open it in a new tab
⌘Enter Open it beside the pane you’re in, as a split
Esc Close the list

The mouse works too: the pointer marks a row, a click opens it (⌘-click as a split), and the wheel scrolls.

It’s drawn in the terminal’s own font and size, and it never waits on the network: the window keeps every machine’s sessions as last asked (every 5 seconds, every 2 while the list is open). A machine that isn’t answering keeps its sessions in the list, dimmed, “last seen 4m ago”.

⌘⇧J (Ctrl⇧J) goes straight to the next session needing you, wherever it is; again for the one after.

In the menu beside +, each machine’s › lists its running sessions to open, as well as its profiles.

alto fleet on a terminal opens a full-screen view of your whole fleet, to look over and act on: in groups that fold, filtered as you type, several at once. ⌘⇧L (Ctrl⇧L) opens it in a tab of its own; it runs the same over ssh or in any other terminal. Piped, or with --list or --json, alto fleet prints the counts instead.

It has six tabs, along the top with their counts; 1 to 6 or Tab move between them, and alto fleet --tab NAME opens on one:

  1. Overview, where it opens: a box for each of what needs you (the agents asking, and what they ask), anything wrong with its fix (a machine not answering, one on an older build, remote access off), your fleet (every session in one bar by state, each machine with its sessions as a mark apiece, the pools and the devices with access) and what happened lately. Enter on a line goes there: a machine opens its sessions.
  2. Needs you: every session waiting for a person, oldest first, with what it asks. Answer from the list: . then a allows (the agent’s highlighted choice), x denies, for one or every one selected.
  3. Sessions: everything running, grouped by machine to start with. A working agent that says how far along it is shows a bar.
  4. Machines: each with its state, its sessions, how full it is (where it takes only so many), its system and its build (an older one marked ↑).
  5. Devices: every device with access to this one, grouped by how it gets in; block, let back in or forget them, or show a code to pair someone else’s.
  6. Activity: what happened on every machine, newest first.

How the fleet is doing shows at a glance in the same marks everywhere: ● needs you in yellow, ○ working in cyan (it breathes while the agent works), ✓ done in green, · idle, and a machine not answering in red. Beside a session’s state, ⏸ says agents can’t type there (paused, or waiting for someone to watch) and ◉ with a number says how many are attached to it. Each list sits in a box with how it’s grouped and the filter on its top edge and how many it shows on its bottom; each group’s heading counts its sessions, and they hang from it on a line, so it’s plain which machine each is on. A machine not answering keeps what it last said, its rows dimmed and its heading saying when it was last seen. The keys that matter just then are along the bottom.

The row you’re on, and what’s selected (marked ▍ on the left), are shaded from your terminal’s own colors, so the view suits any theme.

Everything works by keyboard and by mouse:

To Keyboard Mouse
Move ↑ ↓ (and j k), Page Up Page Down, Home End the wheel; the pointer shades the row under it
Add or remove one Space Ctrl-click (⌘-click in Alto’s tabs)
Select a range Shift + ↑ ↓ Shift-click, or drag down the rows
Select a group Space on its heading click the heading
Select all shown Ctrl-A
Clear the selection, then the filter Esc click empty space
Fold or unfold a group ← → click its ▸ ▾
Open Enter double-click
Actions ., then a letter, or ↑ ↓ and Enter right-click
Filter type, or / click the filter
Another tab 1 to 6, Tab, Shift-Tab click its name
Group by g, then a letter, or ↑ ↓ and Enter click “Group by”
Every key ?

Typing anything that isn’t one of the list’s own keys (j k g y q . ? Space) goes to the filter, which then has every key until Enter, Esc, ↑ or ↓. The filter takes the same words as the switcher: win claude, status:idle, repo:alto. Typing on the Overview filters Sessions, so the view is the switcher too.

Opening a session attaches to it in place; detaching (Ctrl-</kbd>) comes back to the list. The actions: for sessions, open, allow or deny what they ask, fetch their branches, pause agents’ typing in them and resume it (as alto pause and resume), and end them; for a machine, a new session there, and for this one, drain and undrain; for devices, block, allow and forget, and pairing; and copying the addresses of what’s selected (y). Ending sessions asks first: y ends them; Enter or Esc keeps them running. The view doesn’t list its own session.

A session is machine:id wherever you name one:

Terminal window
alto attach win:2
alto send -s win:2 "yes" --key Enter
alto capture gpu-1:1
alto run build-2:3 -- cargo test
alto kill studio:7
alto fetch win:2 # its branch, if it was started with --repo

win:2 means the same as --on win 2. The machine is its Tailscale name, or the name it was paired as.

alto attach with no id, outside a session, offers every machine’s running sessions to pick from, newest first, with “New session” at the top.

--in takes words about the machine and about the session, all of which must match:

Terminal window
alto ls --in 'agent:claude os:windows' # Claude Code on Windows machines
alto ls --in 'status:needs-you' # everything waiting on you
alto ls --in 'repo:alto' # every session in that repository, wherever
alto ls --in 'pool:gpu status:working'
alto ls --in 'by:agent' # what agents started, rather than people
alto ls --in 'watched:no' # what nobody is attached to
Word Matches
status:STATE working, needs-you, done, idle or ended
agent:NAME the agent a session runs (claude, codex), or else its program (vim, cargo; shell for the login shell)
repo:NAME the repository’s whole name (sanford/alto) or its last part (alto)
by:WHO who started it: agent (any agent), person, or a name: an agent’s (claude), the account it was started from here, a Tailscale login (sam@example.com, or just sam), or the device it was asked from. Sessions from before this was kept match none.
watched:WHO who’s attached now: yes (anyone), no (nobody), person, agent, or a mode (observer, peer, moderator). Sessions from before this was said match none.
os:, arch:, has:, tag:, kind:, gpu, pool:, a name the session’s machine, as in Putting work where it fits
Terminal window
alto ls --all --group-by repo
alto ls --all --group-by agent
alto ls --in 'has:claude' --group-by os

Group by machine (the default), pool, kind, os, tag, repo, agent, status or none. A machine with several tags, or in several pools, puts its sessions in each.

--count counts them instead of listing them: per machine, or by any of the same keys.

Terminal window
alto ls --count # per machine: 5 sessions: 2 working, 1 needs you, 1 done
alto ls --count --group-by agent # claude 3 sessions: 3 working

--json on any of these gives:

  • machines: each with its state (online, not answering, drained or full), its kind and what it says about itself, and when it was last seen if it isn’t answering;
  • sessions: each with its address, machine, state, agent, repo, folder and last_seen, and the whole session as ls --json gives it;
  • groups: each group’s name, count and sessions’ addresses.
Terminal window
alto ls --in 'status:needs-you' --json | jq -r '.sessions[].address'

alto devices says what each machine is: OS and version, chip, CPUs, memory, GPUs, whether it’s a VM, a container or a phone, the agents and tools a session there can run, its build, and how fast it answered. alto ls --waiting lists only what needs you, on every machine, newest first.