Launch each project's AI coding agent in its own terminal, auto-tiled across every monitor
Project description
multideck
Open every project in its own terminal, launch your AI agent, and auto-tile all windows across your screens.
One command. Every tool. Every monitor.
Monitor 1 (4K @ 250%) Monitor 2 (4K @ 250%) Monitor 3 (1080p @ 175%)
+------------+------------+ +------------+------------+ +---------+---------+
| api | web | | infra | docs | | ops | ... |
| [claude] | [claude] | | [codex] | [vscode] | | [claude]| |
+------------+------------+ +------------+------------+ +---------+---------+
columns x rows per screen -- true physical pixels on every monitor
Quick Start
Available once
v1.0.0is published to PyPI. Until then, install from source.
pip install multideck # or: uv tool install multideck
multideck
On first run, multideck scans your Claude, Codex, and VS Code history, finds your recent projects, and generates a config. Run it again to launch everything.
Supported Tools
| Tool | Type | Launch command | Session resume | Multi-window |
|---|---|---|---|---|
| Claude Code | CLI agent | claude --continue |
Yes | Yes |
| Codex CLI | CLI agent | codex |
Yes | Yes |
| Cursor Agent | CLI agent | cursor-agent |
-- | -- |
| Antigravity (agy) | CLI agent | agy |
-- | -- |
| VS Code | IDE | code |
-- | -- |
| Cursor IDE | IDE | cursor |
-- | -- |
| Custom | Any | your command | -- | -- |
Add any tool by mapping a name to a shell command in settings.tools. CLI agents open in a terminal; IDE tools open via their native CLI (code, cursor).
Happy (mobile/web access)
Enable Happy to monitor and control all your AI sessions from your phone or browser with end-to-end encryption:
"settings": { "happy": true }
Requires npm install -g happy. Supported agents: Claude, Codex. Per-project override with "happy": true/false.
psmux (persistent sessions for SSH access)
Enable psmux (native Windows terminal multiplexer) so each project runs in a named session you can attach to from anywhere — SSH from your phone, another PC, or a second terminal:
"settings": { "psmux": true }
Requires psmux installed (choco install psmux or download from GitHub). When enabled, multideck creates a detached psmux session per project and opens Windows Terminal attached to it. From any SSH client: psmux attach -t project-name.
Mobile image upload (over Tailscale)
Send screenshots from your phone straight into a project's agent session:
"settings": { "psmux": true, "uploadServer": true, "uploadPort": 8033 }
multideck serve (or uploadServer: true during launch) starts a small HTTP server; multideck mobile prints the phone URL + a QR code you can install as a home-screen app (the QR code needs the optional qr extra: pip install multideck[qr]). Pick a project on the phone, upload an image, and its path is pasted into that project's session. The Alt+V hotkey (Windows) does the same for whatever md: session is focused.
This works over Tailscale: the server binds only the loopback and your machine's Tailscale IP — never the LAN wildcard — and attach/mobile/termius shell out to the tailscale CLI to resolve hosts. Devices must be on your tailnet; there is deliberately no auth token, since the bind set is the access control. To bind something else (e.g. LAN-wide), use the escape hatch: multideck serve --host 0.0.0.0.
Usage
Run multideck with no arguments for the interactive menu:
_ _ _ _ _
_ __ _ _| | |_(_)__| |___ __| |__
| ' \ || | | _| / _` / -_) _| / /
|_|_|_\_,_|_|\__|_\__,_\___\__|_\_\
v1.0.0 auto-tile your AI workspace
----------------------------------------
1 Launch & tile new windows (default)
2 Re-tile all open windows
3 Launch a group AUTOMATIONS | INTERNAL | LEAD-GEN
e Edit config
q Quit
Or skip the menu with flags:
| Command | What it does |
|---|---|
multideck |
Interactive menu. |
multideck --go |
Launch + tile new windows, no menu. |
multideck --retile-all |
Re-tile every matching window. |
multideck -g <name> |
Launch only projects in a group. |
multideck --init |
Re-scan sessions and regenerate config. |
multideck --init --base-dir <folder> |
Generate config from a folder of git repos. |
multideck --edit |
Open config in your default editor. |
multideck docs |
Print full config reference (Markdown). |
multideck doctor [--json] |
Diagnose the environment: config, env vars, agent tools on PATH, terminal, monitors, writable dirs, Tailscale, upload port. Exit 1 on any failure. |
multideck sessions |
List active psmux sessions, pick one to attach. |
multideck sessions <name> |
Attach directly to a psmux session by name. |
multideck up [--json] [-g <group>] |
Host side: ensure a persistent psmux session per project. |
multideck attach <host> |
From another PC: bring host sessions up over SSH, tile locally, Alt+V uploads. |
multideck watch |
Live table of every agent session, most-urgent first; press a row number to focus that window. |
multideck attention [-d] [--stop] |
Attention daemon: badges window titles with agent state, flashes the taskbar on needs-input/error, optional toast/ntfy push (settings.attention). Badges/flash/toast are Windows-only; ntfy push is cross-platform — see Platform support. |
multideck status [--json] |
Session + daemon health (incl. an agents state list in --json). Exit codes: 0 healthy, 1 config error, 3 degraded. |
multideck down [--all] [--server] |
Stop sessions; --all/--server also stop the upload server (and listener). |
multideck serve [--host <addr>] |
Run the mobile upload server (see below). |
multideck mobile |
Phone URL + QR code for installing the uploader as a home-screen app. |
multideck termius |
Generate an SSH config entry that opens the session picker. |
multideck hotkey |
Run the Alt+V clipboard-upload listener standalone (Windows). |
multideck config <subcommand> |
Edit config from the CLI — 14 subcommands incl. migrate; see multideck config --help. |
Platform support
Launching, tiling, and the mobile/notification plumbing run on all three OSes. A few power-user features are Windows-only because they lean on Win32 primitives with no cross-platform equivalent wired up yet. This table is the honest contract — every cell is derived from the capability probes in src/multideck/platform/, not from aspiration.
| Feature | Windows | macOS | Linux |
|---|---|---|---|
| Launch + auto-tile across monitors | Yes | Yes | Yes |
watch live fleet table |
Yes | Yes | Yes |
attention title badges + taskbar flash |
Yes | No | No |
Desktop toast (settings.attention.toast) |
Yes | No | No |
ntfy phone push (settings.attention.ntfy) |
Yes | Yes | Yes |
Persistent psmux sessions (up / sessions / attach) |
Yes | No | No |
| Global Alt+V clipboard-image hotkey | Yes | No | No |
Mobile upload server (serve / mobile) |
Yes | Yes | Yes |
Notes:
- Badges, flash, and toast are gated on
Platform.supports_attention_signals(), which returnsTrueonly inplatform/windows.py. On macOS/Linux the daemon printswindow badges/flash aren't supported on this OSand those renderers stay off. Toast additionally uses the Windows-onlywinotify([toast]extra). ntfy push is cross-platform — it is stdliburllibover HTTP — so phone notifications work on every OS. - Persistent psmux sessions and the Alt+V hotkey are gated on
supports_psmux()/supports_hotkey()(also Windows-only). Off Windows the psmux entry points raiseNotImplementedErrorand importinghotkeyraisesImportError. - The mobile upload server itself (serving the PWA over loopback + Tailscale and receiving images) runs everywhere; auto-pasting the uploaded path into a live agent session uses psmux, so that last hop is Windows-only. Likewise,
watch's table renders on every OS but its press-a-number-to-focus action uses the same Windows-only window primitives.
Where agent states come from
multideck watch, multideck attention, and multideck status --json do not poll your agents directly. They read per-session state records — working, needs-input, done, error, idle — that your coding agent writes through its lifecycle hooks: Claude Code hooks and Codex's notify hook. Until those hooks are wired, the state store stays empty and multideck watch shows an empty table.
The companion ai-agent-notifier package (same authors) installs those hooks for you across Claude Code, Codex, and Cursor:
npx ai-agent-notifier setup
The setup wizard detects your installed agents and wires the hooks; restart your agents to activate. After that, multideck watch and multideck attention -d light up as your agents change state.
Configuration
Config is stored at a platform-standard location:
- Windows:
%APPDATA%\multideck\config.json - macOS:
~/Library/Application Support/multideck/config.json - Linux:
~/.config/multideck/config.json
Or place multideck.config.json in your working directory (it is gitignored — your personal config never gets committed).
Start from the committed sample, multideck.config.example.json — it is generated from the config factory and exercises every surface (groups, remote host/remotePath, ssh, the full settings block):
{
"version": 1,
"baseDir": "C:/Users/you/projects",
"layout": { "columns": 2, "rows": 1 },
"settings": { "defaultTool": "claude", "...": "see the example file / multideck docs" },
"projects": [
{ "path": "backend/api", "group": "backend", "tool": "claude", "color": "#3b82f6" },
{ "path": "gpu-worker", "group": "infra", "host": "gpu-box.example.com", "remotePath": "/home/dev/worker", "tool": "codex" }
]
}
Configs are versioned ("version": 1). A config without a current version still loads but prints a warning until you run multideck config migrate — loading never rewrites your file; migrate is the only writer (it also persists auto-assigned project colors; those are derived deterministically from each project's title/path, so they stay the same every run even before you migrate).
Project fields
| Field | Default | Description |
|---|---|---|
path |
(required) | Absolute, or relative to baseDir. |
group |
none | Tag for group launches (-g). |
tool |
defaultTool |
claude, codex, cursor-agent, agy, vscode, cursor, or any custom tool. |
color |
derived | Terminal tab color (#rrggbb); auto-derived from the project title/path when unset. |
title |
folder name | Window title for matching. |
enabled |
true |
Set false to skip without deleting. |
happy |
inherit | Override global Happy setting for this project. |
host |
none | SSH target for remote projects. |
remotePath |
path |
Remote directory when different from path. |
windows |
none | List of window objects {"name", "tool", "command"} with per-window tool/command overrides. Legacy int / ["name1", "name2"] forms still parse. |
Multi-window sessions
Open the same project in multiple windows. windows is a list of window objects, each with optional per-window tool/command overrides:
{
"path": "api",
"windows": [
{ "name": "api" },
{ "name": "api-2" },
{ "name": "api-codex", "tool": "codex" }
]
}
name sets the window title; tool/command override the project's defaults for that window only. Windows without an override each resume the Nth most recent Claude/Codex session.
The legacy "windows": 3 and "windows": ["api", "api-2"] forms still parse and are normalized to window objects by multideck config migrate.
Remote projects
{ "host": "deploy@server", "path": "/srv/api", "tool": "claude" }
CLI agents run over SSH. VS Code/Cursor projects open via Remote-SSH.
Custom tools
"tools": {
"claude": "claude --continue",
"codex": "codex",
"cursor-agent": "cursor-agent",
"agy": "agy",
"aider": "aider --model sonnet",
"shell": "bash"
}
Testing
Run it yourself
pip install -e ".[dev]"
pytest tests/unit/ -q # fast, safe anywhere
pytest tests/e2e/ -m "e2e and not needs_ssh" # subprocess dry-runs; no SSH server needed
pytest tests/platform/ -v -m platform # real monitors/terminals — CI-grade env only
pip install build && pytest tests/dist/ -m dist # wheel -> pristine venv -> real installed entry point
python scripts/check.py # the quality gate: ruff + custom lint + ty + compileall + vulture + pytest w/ coverage
A bare pytest collects all tiers, including tests that enumerate real monitors, launch real terminals, and expect an SSH server — run those only in an environment set up like CI (.github/workflows/ci.yml). scripts/check.py is the repo's commit gate; it must pass before every commit.
Cross-platform support
| Feature | Windows | macOS | Linux |
|---|---|---|---|
| Monitor detection | ctypes Win32 | Swift/AppKit | xrandr |
| Window management | EnumWindows/MoveWindow | AppleScript | xdotool/wmctrl |
| Terminal | Windows Terminal | kitty/iTerm/Terminal.app | kitty/alacritty/gnome-terminal |
| DPI awareness | Per-Monitor V2 | Native Retina | xrandr DPI |
Install from source
git clone https://github.com/DevinoSolutions/multideck-ai-agent.git
cd multideck-ai-agent
pip install -e .
Contributing
Contributions are welcome. Please open an issue first to discuss what you'd like to change.
License
AGPL-3.0 -- Copyright (c) 2026 DevinoSolutions
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file multideck-1.0.0.tar.gz.
File metadata
- Download URL: multideck-1.0.0.tar.gz
- Upload date:
- Size: 351.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab9637cc384ffae073522da25bb0922baafbf9b10fe2d35c4bb1e5e0d4f4c863
|
|
| MD5 |
b80185f77ef2b68ed4d21ceb55faf052
|
|
| BLAKE2b-256 |
22ec7a114c0c8b24d8f23acbd094dc9fd0da190f1984b85a04ed72906abfe75e
|
Provenance
The following attestation bundles were made for multideck-1.0.0.tar.gz:
Publisher:
release.yml on DevinoSolutions/multideck-ai-agents-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
multideck-1.0.0.tar.gz -
Subject digest:
ab9637cc384ffae073522da25bb0922baafbf9b10fe2d35c4bb1e5e0d4f4c863 - Sigstore transparency entry: 2152013237
- Sigstore integration time:
-
Permalink:
DevinoSolutions/multideck-ai-agents-manager@88859c9ae0f5fb778967b470670fa2563dc2925d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/DevinoSolutions
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@88859c9ae0f5fb778967b470670fa2563dc2925d -
Trigger Event:
push
-
Statement type:
File details
Details for the file multideck-1.0.0-py3-none-any.whl.
File metadata
- Download URL: multideck-1.0.0-py3-none-any.whl
- Upload date:
- Size: 146.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e36992476609a2d8d42c71a734ab280742f36bb1eb34ce52906511704d201988
|
|
| MD5 |
90cdfeba2825fd9adbe5cb91e4f6a6e8
|
|
| BLAKE2b-256 |
6fa6e293aca39da47d1de0c0f7fb2c52cb4ebf2570ff6e4c1aa4b98d94fc1a67
|
Provenance
The following attestation bundles were made for multideck-1.0.0-py3-none-any.whl:
Publisher:
release.yml on DevinoSolutions/multideck-ai-agents-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
multideck-1.0.0-py3-none-any.whl -
Subject digest:
e36992476609a2d8d42c71a734ab280742f36bb1eb34ce52906511704d201988 - Sigstore transparency entry: 2152013311
- Sigstore integration time:
-
Permalink:
DevinoSolutions/multideck-ai-agents-manager@88859c9ae0f5fb778967b470670fa2563dc2925d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/DevinoSolutions
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@88859c9ae0f5fb778967b470670fa2563dc2925d -
Trigger Event:
push
-
Statement type: