quil
v1.87.0 github
Skip the tour
─ tour ─
TERMINAL MULTIPLEXER · AI CODING AGENTS

Quil: one terminal for all your AI agents.

This page is one Quil session. Scroll and it plays: agents side by side, an agent that runs the others, a remote GPU box, a sandbox, the everything-at-once view, and a reboot.

That was the tour. It was all one Quil session.

Every screen above is drawn the way Quil draws it. Open source under Apache 2.0, for Linux, macOS and native Windows.

$curl -sSfL https://raw.githubusercontent.com/artyomsv/quil/master/scripts/install.sh | sh
The tour, in words

Six things people do with Quil

The tour above is one Quil session. Here is the same story as text, with the keys and the docs for each step.

01
AGENTS, SIDE BY SIDE

Run Claude Code, Codex and OpenCode at once.

Split a pane with Alt+Shift+H (side by side) or Alt+Shift+V (stacked) and type claude, codex or opencode in the new shell. Quil turns the pane into that agent's pane, so it tracks the session and can resume it later.

For an agent that needs its own checkout, open the pane with Ctrl+N and pick Worktree → + new branch…. The agent works on its own branch in its own folder, and the other agents never see its edits.

claude
typed in a shell
Ctrl+N
new pane
Alt+Shift+V
split
02
AN AGENT THAT RUNS THE OTHERS

Let one agent hand out the work.

Add Quil to your AI client as an MCP server; the command is quil mcp. The agent can then list panes, read their output, open new panes and agents, and hand out work with delegate_task.

Open a whole team at once from the command palette (Alt+Shift+P → New from template). When a delegated task ends, Quil types a [quil task …] line into the orchestrator's prompt, so it knows without polling.

Alt+Shift+P
New from template
delegate_task
one of 36 MCP tools
03
THE WORK RUNS ELSEWHERE

Close the laptop. The agents keep going.

quil --remote <host> runs the Quil daemon on the server and only the screen on your laptop. It travels over ssh, so no port is opened. Close the lid and the panes keep working; when the link comes back, Quil reconnects on its own.

Or keep your local projects and add the server as one more project (Alt+Shift+N → Remote (ssh)). Quil installs itself on the host when needed, and every folder picker reads the server's disk, not yours.

Alt+Shift+N
Remote (ssh)
quil --remote gpu01
whole session
04
AN AGENT IN A BOX

Skip permissions. Inside a box.

Tick Run in a Docker container in an agent's setup dialog. The pane runs in its own container that mounts the checkout, with the repository's history, hooks and config read-only, no --privileged and no added capabilities.

Quil publishes no image: you build it once on your machine with scripts/sandbox-image.sh. Closing the pane removes the container; the commits it made stay in your repository.

Run in a Docker container
setup dialog
scripts/sandbox-image.sh
build the image
05
TOO MUCH GOING ON

See who needs you. Find anything.

The sidebar lists every project and what each agent is doing: ▲ waiting on you, a spinner while it works, ✓ when it finished while you were elsewhere. Alt+Shift+A jumps to the agent that has waited longest.

Alt+Shift+P opens the command palette. It finds actions, tabs and projects, and it also searches the scrollback of every loaded pane; Enter goes to the match. Alt+N opens the notification center.

Alt+Shift+A
waiting longest
Alt+Shift+P
search everything
06
THE WORKSPACE REMEMBERS

Reboot. Nothing lost.

Quil keeps the workspace on disk under ~/.quil while you work. After a restart, run quil: tabs, splits, working folders, pane notes and the last screen of every pane come back, and each agent resumes its own conversation (claude --resume, codex resume, opencode --session).

Panes on a remote host never stopped at all; Quil attaches to them again. Leave yourself a note on any pane with Alt+E; it survives the restart too.

quil
after any restart
Alt+E
pane notes
Install

Two binaries: quil and quild

Linux, macOS and native Windows. No WSL, no account. quil is the screen; quild is the daemon that keeps the panes alive.

Linux and macOS

$ curl -sSfL https://raw.githubusercontent.com/artyomsv/quil/master/scripts/install.sh | sh

Checks the SHA-256 and installs both binaries to ~/.local/bin. Put that folder on your PATH.

Windows

  1. Download quil-windows-amd64.zip from the latest release.
  2. Extract quil.exe and quild.exe to a folder on your PATH, for example %LOCALAPPDATA%\Programs\Quil\.
  3. Open Windows Terminal and run quil.

With Go 1.25 or newer

$ go install github.com/artyomsv/quil/cmd/quil@latest
$ go install github.com/artyomsv/quil/cmd/quild@latest

Check it

$ quil version
$ quild version

Both must print the same version.

Every install option, uninstall and building from source: docs/installation.md ↗

What else is in the box

Everything Quil does, one line each

Persistence

  • Reboot-proof sessions — Workspaces survive full host reboots. Type quil after a restart and everything snaps back.
    • Continuous snapshot of tabs, panes, layout, and working directories to ~/.quil/workspace.json.
    • Ghost buffers render the last 500 lines of each pane instantly while shells re-initialise. Large buffers are sent in 8 KB chunks with 2 ms yield between each to prevent input starvation.
    • Pane split tree is serialised to JSON and restored on reconnect — same horizontal/vertical nesting, same ratios.
    • Target boot-to-productive time: under 30 seconds.
  • Live CWD tracking — Pane borders show the shell's current directory in real time — no config, no manual hooks.
    • Auto-injects OSC 7 hooks into bash, zsh, and PowerShell at spawn time.
    • Fish emits OSC 7 natively, no injection needed.
    • The directory shown on the pane border updates on every cd, pushd, and popd.
  • Remote daemon over SSHbeta — quil --remote gpu01 puts the panes on another machine. They keep running when the laptop sleeps — the TUI is only a viewer.
    • No network port is opened on the remote host. Quil runs ssh -T <host> "quil --stdio" and speaks its normal protocol over that one channel — so a bastion behind ProxyJump, a Tailscale or WireGuard address, and a box on the public internet all work with no extra setup.
    • The server does not need Quil installed first. Point --remote at a bare machine and it offers to install one, then attaches. Your laptop downloads the release for the *remote's* platform, verifies the checksum locally, and pushes it over the SSH connection you already have — so a cluster node with no route to GitHub is provisioned just as easily as one with. Linux and macOS, amd64 and arm64; nothing is installed without an explicit yes.
    • The destination goes to ssh verbatim, so your ~/.ssh/config keeps working unchanged: Host aliases, ProxyJump, ControlMaster, per-host keys, hardware tokens, and SSH certificates all apply.
    • Quil forces off what the remote never needs — agent forwarding, X11, port forwarding, local-command execution — and bounds both ends of the connection's life so a dead host fails in seconds rather than hanging. Host-key policy is left to your config, because forcing it could only weaken it.
    • Every command that manages a daemon's lifecycle refuses under --remote rather than silently acting on your laptop, and the status bar carries [remote <host>] so the machine you are driving is never ambiguous.
    • A dropped link is a pause, not an ending. Close the lid, lose wifi, switch networks — an amber bar names the host, counts the attempts, and shows what ssh actually said, while retries back off from half a second to at most thirty and never stop. When the host answers again the panes come back with their contents intact, because nothing ever stopped running.
    • Keystrokes are dropped while the link is down rather than queued: a key typed at a dead connection would otherwise arrive minutes later in a live agent session, answering a question that had already moved on. ctrl+q stays live throughout, for a host that is not coming back.
    • Dialogs that browse a filesystem read the server's disk, not yours. The working-directory picker, ~, relative paths, drive and root listings, and git-repository discovery all ask the daemon — before that, Alt+G could report no repository in a directory where the agent in that very pane answered git status with the branch name. Names and paths coming back from a host you may not control are stripped of terminal escapes and of the invisible characters that reverse text direction, so a folder name cannot scramble the dialog or read as something other than what it is.
    • Kube contexts, plugin availability and the recent-directories list follow the same rule: they describe the server. The recent list is kept per host, and whether a remembered directory still exists is asked of the daemon — checking it locally dropped every server path, so the list rendered empty, which is indistinguishable from a feature nobody had used.
    • Full-screen tools run on the remote the same as locally. k9s, lazysql and lazygit open against the server's clusters, databases and repositories — an SSH connection carries no terminal type, and without one those tools exit within milliseconds, which reads as a pane that crashed rather than a missing variable. Panes that keep no scrollback, such as OpenCode, are asked to repaint when you reattach instead of showing an empty rectangle in front of a process that never stopped running.
    • Beta limits: plugin definitions still come from your local machine, the daemon checks which tools are installed only at startup and on plugin reload (so a tool installed on the server mid-session stays greyed until then), and the remote must be Linux or macOS — a running Windows executable cannot be replaced in place, which makes the upgrade half of provisioning impossible there.
  • Several machines in one windowbeta — A project belongs to the daemon that holds its files — so projects from your laptop and from a build host sit side by side in the same sidebar, in one TUI process.
    • quil --remote <host> binds a whole TUI to one daemon. This does not: the client holds connections to the local daemon and any number of remote ones at once, and their projects appear as siblings in the same sidebar. Working against a laptop and a GPU box is no longer two terminals.
    • Add a host without relaunching. Tick Remote (ssh) in the New Project dialog, give a user and host, and press Enter on the Host row — Quil dials it and then browses *that* machine's filesystem for the root directory. A root directory lives on exactly one machine, so connecting before submitting is the order that makes sense.
    • A host you can't attach to is provisioned from that dialog rather than from a shell: no Quil there installs it, and a daemon older than your client is upgraded — which stops that daemon and respawns its panes from the saved workspace, so the status line says so while it runs. Each is attempted once per host per session. The one case it won't fix is a remote daemon *newer* than your client: pushing your own build would downgrade a machine other people may share, so it names the client upgrade instead.
    • The dialog's one message line is coloured by what it means, not by the fact that something was reported: red only when the host cannot be reached at all, amber while a dial or an install is under way, green once the link is up. Provisioning a machine used to arrive in the same red as a failure, which read as an error that then somehow succeeded.
    • One host holds one project. A daemon must have at least one tab and a tab must belong to a project, so a host always arrives already holding one — naming a project there renames that one rather than adding a second, and whatever was already running on the machine ends up under the name you chose. The local daemon is unaffected: it holds as many projects as you like.
    • A host that already has several folds them into the one you name. Press Enter and the form states what it will do — how many projects the host has, what the result is called, how many tabs move, and that nothing is closed — and a second Enter does it; editing the name in between re-describes instead of acting on what you moved away from. Every tab moves onto the surviving project and the emptied records are dropped, so no pane or running command is touched. That is the way to tidy a host connected before this rule existed, where reconnecting and re-creating the project that seemed to vanish left another row behind each time.
    • The fold leaves the surviving project's root directory alone. The dialog fills that field in by itself as soon as the directory listing arrives, so it usually holds wherever the daemon happens to start rather than anywhere you chose — writing that over a root you had picked would be a change nobody asked for. Rename moves one, and it opens with the project's own root already in the field.
    • The host is remembered under [[destinations]] and attached at every launch. Disconnect it from the sidebar's right-click menu and it stops nothing on the far end — the remote daemon keeps every pane alive, and reconnecting restores the same workspace. That is worth knowing before you read a missing row as a deletion: the projects come back on the next connect.
    • Each destination carries its own reconnect state: its own backoff ladder, its own amber banner naming the host and what ssh actually said. One daemon dying no longer ends the session — the others stay live and interactive.
    • Beta limits worth knowing before you rely on it: a destination unreachable at *launch* never starts a reconnect ladder (no connection means no reader, so nothing reports a loss — relaunch to pick it up), background destinations dial non-interactively so a first-time host key must be accepted once with ssh <dest> or quil remote setup <dest> first, plugin availability is a single registry fed by whichever daemon answered last, and the recent-directories list is one per client rather than one per host.

Interaction

  • Projects, and a sidebar that watches all of them — Group tabs into named projects with their own root directory. The left sidebar shows every project's agents at once — so one that finished or got stuck while you were elsewhere is visible from where you already are.
    • A project owns its tabs and remembers which one you left it on. Ctrl+T files a new tab into the project you're on, and switching project switches the whole tab bar. Alt+Shift+N creates one, Alt+P fuzzy-finds, Alt+O bounces between the last two, Alt+Shift+←/→ cycles.
    • The sidebar (Alt+Shift+S) is a reserved left column, not an overlay. Each project row carries a roll-up of its agents — ⠹N working, ▲N blocked waiting on you — and those counts keep updating for projects in the background, which is the entire point.
    • Under the active project, each pane gets its own glyph: ⠹ working — the same spinner the tab bar and the pane border run, so "still going" reads the same wherever you look (with ⋯N outstanding subagents), ▲ blocked on you and the tool it's asking about, ○ idle, ✓ finished while you weren't looking. Click any row to jump there.
    • Blocked is a separate state from finished. The agents' hook events always carried the distinction — Notification, PermissionRequest, permission.ask — but the old UI only needed "mark unseen", so they were collapsed into one. A permission prompt and a completed turn now look different, because they need different things from you.
    • Alt+Shift+A jumps to whichever agent has been blocked longest, anywhere in the workspace, and cycles on repeated presses. Oldest-first rather than sidebar order: with several agents running, the one waiting longest is the one costing you time.
    • Each pane row also shows the checkout it sits in — branch, wt for a linked worktree, ↑N/↓N against upstream. Refreshed on a background ticker and cached per checkout, so ten panes in one repository cost one git invocation. git status --porcelain is deliberately not among them: it's the one call that can take seconds on a large repo without fsmonitor. A probe that doesn't answer keeps its last value and marks it stale rather than guessing.
    • Too many projects for one column? Put them in named groups — right-click a project → Move to group… A group can mix local and remote projects. Click its header to collapse it to one row that still carries its projects' badges (and the project you are in); drag headers to reorder groups, and drag a project onto a header or back onto PROJECTS to move it in or out. Groups are kept on this machine, in project-groups.json.
    • The row under the pointer is shaded, the one you are dragging stands out, and while a project is dragged the group it would land in turns green — in the PANES section too, where pointing at a tab shades its whole block.
    • Existing workspaces migrate on first load into a single project named Default, tab order preserved. No prompt, no data loss, nothing to opt into.
  • Typed panes — Terminals are not all the same. Quil understands pane types and gives each one context-aware behaviour — including a per-spawn setup dialog with directory browser and runtime checkboxes.
    • Eleven built-in pane types: Terminal, Terminal (keeps content on squeeze), Claude Code, OpenCode (beta), Codex, SSH, Stripe, lazygit, hunk, k9s, lazysql.
    • Each type has its own resume strategy, error handler, and status line.
    • Pane setup dialog (opt-in via plugin TOML): a directory browser pre-filled with the active pane's CWD plus one checkbox per declared [[command.toggles]] entry. claude-code uses both — picks up the project's .claude/ context automatically and offers a Dangerously skip permissions toggle for unattended runs.
    • The directory step remembers where you've been: the last five folders you actually opened a pane in are offered as a one-keystroke quick pick (deleted ones filtered out), and for git-aware pane types the repositories discovered near the active pane take priority. Browse… always drops to the full picker.
    • Toggle state rides through the existing InstanceArgs IPC field and survives daemon restarts; no IPC schema changes.
    • User-definable additional types via TOML plugin files in ~/.quil/plugins/.
  • tmux-style splits with spatial navigation — Arbitrarily nested horizontal and vertical splits with mouse hit-testing AND directional Alt+Arrow pane navigation that picks the closest neighbour, not the next leaf in the tree.
    • Binary split tree, each split with its own direction and ratio.
    • Click any pane to focus it; the scroll wheel traverses terminal history.
    • Over a pane running something that handles its own mouse input — claude-code, opencode, codex, vim, htop, lazygit — the wheel scrolls that program's viewport instead. Those apps run on the alternate screen and never fill Quil's scrollback, so the wheel is forwarded to them as the mouse sequence they expect. The daemon is what detects this, so it stays correct even when you reattach to a session that was already running. A sideways wheel or trackpad swipe is ignored there rather than read as a scroll down.
    • Click the scrollbar to jump the thumb; click-and-drag scrolls continuously. The hit zone is three cells wide so off-by-one clicks register as scroll instead of text selection.
    • Spatial pane navigation: Alt+Left/Right/Up/Down focuses the closest neighbour in that direction. Three tie-breakers (gap, perpendicular overlap, perpendicular center distance) match tmux/vim/iTerm muscle memory.
    • Drag any tab in the tab bar to reorder it — intermediate tabs slide one slot at a time. A click without motion still switches tabs. The active tab is prefixed with * so it's visible at a glance even when colored.
    • More tabs than fit? The mouse wheel over the tab bar scrolls the strip without switching tabs, and «N / N» say how many tabs are hidden on each side.
    • Tab and Shift+Tab are deliberately NOT bound globally — they fall through to the PTY so shell completion and Claude Code's mode-cycling work naturally. Splits live on Alt+Shift+H / Alt+Shift+V to keep Alt+V free for Claude Code's image paste.
    • Focus mode (Ctrl+E) expands the active pane full-screen while others keep running in the background.
  • Keybindings that tell you when they're broken — Every key resolves to a named action, and anything Quil can't honour — a duplicate, a collision with a built-in, an unparseable spec — shows up as a warning in the shortcuts dialog instead of failing silently.
    • Rebind any of 66 actions in ~/.quil/bindings.toml. Comma-separate alternatives in one field ("pane.rename" = "alt+f2,alt+shift+r") when a key is unreliable on one platform — macOS eats most F-keys unless you enable standard function keys.
    • A binding that can't fire now says so. F1 → Shortcuts puts a warning row at the top naming the key, which action won, and what will never run: duplicates, a key another action resolves first, collisions with built-ins (F1, Ctrl+N, Alt+1-9, the paste aliases, the text-selection chords), unparseable specs, and unknown action names.
    • One bad line costs you one binding, not your keymap — a spec that fails to parse falls back to that action's default and leaves the other 65 alone.
    • Spelling is normalized: modifier names fold and reorder (Ctrl+Shift+A = shift+ctrl+a), named keys alias (escape/esc, pgdn/pagedown, meta/super). Single-character keys keep their case, because on macOS with Option-as-Meta alt+m and alt+M are genuinely different presses.
    • The shortcuts dialog is generated from the action table rather than hand-written, so it can't drift from what the keys actually do — the hand-maintained version had quietly lost seven of eight project bindings.
    • Multi-key sequences ("ctrl+b c") and two presets: preset = "tmux" in bindings.toml switches to a tmux-style prefix keymap, and preset = "default" switches back.
    • Some actions ship unbound, for you to give a key: the six tab layouts (tab.layout_even, tab.layout_columns, tab.layout_rows, tab.layout_grid, tab.layout_main, tab.layout_spiral) and the two project-group actions (project.group_toggle, project.groups_collapse_all).
  • Mouse drag-resize splits — Grab any border between panes and drag — the split follows your mouse, every nested pane keeps its minimum size, and the child processes see exactly one resize when you let go.
    • Click-and-drag any split border; the affected panes show a highlight while the drag is active.
    • Works on arbitrarily nested layouts: the drag is clamped so every pane in both subtrees keeps the 10×4 minimum — not just the two panes touching the border.
    • The grab zone is wider than the drawn line, and the drawn line always wins over the scrollbar where they overlap — no pixel hunting.
    • PTY resize and layout persistence fire once, on mouse release — mid-drag the borders move locally, so TUI apps (claude-code, vim, htop) never see resize churn and never repaint mid-drag.
    • The new ratios ride the existing workspace snapshot, so a drag-resized layout survives daemon restarts and reboots.
    • Companion pane type — Terminal (keeps content on squeeze): the same shell on an AI-pane-style window-sized canvas, for log tails and watch loops where content survival matters more than width-perfect formatting.
  • Command palette + content search — Alt+Shift+P opens a fuzzy-find launcher for every action and every pane — and as you type it also searches the scrollback of all your panes.
    • Type a fragment of the intent (split, restart, backend) and the list filters live by fuzzy score; Enter runs the highlighted command, Esc closes.
    • Entries are grouped under section headers — Go to pane, Tabs, Pane, System — with navigation first (jumping to a pane or tab is the most common reason to open it); headers disappear once you type. Panes are listed by tab.pane index and type so duplicates are easy to tell apart.
    • Content search is built in — no separate mode. Start typing and, alongside the filtered commands, a Found in panes section lists every pane whose scrollback contains your text (case-insensitive), with a match count and a preview of the most recent hit. Arrow to a match and press Enter to jump straight to that pane. Great for 'which pane had that error / URL / container id?'
    • It searches each pane's loaded output buffer and never wakes a dormant pane, so it's fast even across many tabs. Because Quil restores panes lazily, a pane you haven't opened yet this session may not show up in results until you visit it.
    • Every command dispatches into the same handler its keybinding uses — a launcher, not a second code path — and each row shows its shortcut, so the palette teaches the bindings as you use it. Rows that don't apply grey out (input history without an AI pane, lazygit without the binary). Configurable via command_palette; Ctrl+Shift+P is opt-in since many terminals intercept it.
  • Right-click pane menu — Right-click any pane for a per-pane action menu — history, focus, notes, lazygit, rename, move to another tab, mute, attention pin, restart, close — no keybinding memorization required.
    • Right-click a pane (or press Alt+A for the active pane) to open a popup targeting the pane under the cursor — its border lights up so there's never doubt which pane the actions will hit, and the menu header shows the pane's name.
    • Thirteen actions in three groups: view (input history, enter/exit focus mode, notes, lazygit, hunk), pane settings (rename, move to tab, mute notifications, mark for deletion, mark attention, clear attention), and destructive (restart, close — both keep their confirmation dialogs).
    • Hover highlights the row under the mouse; arrow keys / j / k navigate; unavailable actions grey out (input history without an AI pane, lazygit or hunk without the binary installed, move to tab with nowhere to move to).
    • Mark attention pins a purple border and ◆ that survive focusing the pane — a manual "don't let me forget this one" flag, cleared only by unmarking.
    • Right-click with a text selection active still copies it — the menu only opens on a plain right-click.
  • Rearrange tabs and panes, still running — Move a pane to another tab, a tab to another project, or re-lay-out a whole tab in one click. Nothing restarts — every process, its history and its directory stay as they were.
    • Right-click a tab — in the tab bar or in the sidebar — for Rename tab, Set color… (a list of the colours, each painted in its own colour), Layout… and Move to project…. Right-clicking does not switch to the tab.
    • Move to project… sends the tab, panes and all, to another project on the same machine. You stay in the project you were in.
    • Move to tab… on the pane menu sends one pane to another tab on the same machine. It splits the last pane there, alternating direction, so moved panes spiral in instead of forming thin columns; a tab left with no panes closes.
    • Layout… rebuilds the tab as Even out, Columns, Rows, Grid, Main + stack or Spiral. The same six are in the command palette and can be bound to keys. A layout that would make a pane too small is refused.
    • Hold Alt and drag a pane: drop it on the edge of another pane to place it on that side, on the middle to swap the two, or on a tab in the tab bar to move it there. Esc cancels.
    • A tab's new name or colour is saved at once, so it survives a daemon stopped straight afterwards.
  • Pane notes — Alt+E opens a plain-text editor beside any pane. Notes save automatically and travel with the workspace.
    • Markdown-compatible plain text, rendered as the pane loses focus.
    • 30-second debounce auto-save, Ctrl+S for explicit save.
    • Side-by-side layout so you can take notes while the pane keeps producing output.
  • Three build variants — Production, dev, and debug binaries — each self-contained with the right log level and data directory baked in at compile time.
    • quil.exe / quild.exe — production build, stripped symbols, normal log level, data in ~/.quil/.
    • quil-dev.exe / quild-dev.exe — auto dev mode (data in .quil/ next to the binary), debug logging, finds its matching quild-dev daemon. Just double-click — no --dev flag or env vars needed.
    • quil-debug.exe / quild-debug.exe — debug logging against the production data directory. Useful for diagnosing issues in the live workspace.
    • Each variant auto-starts its matching daemon. ./scripts/dev.sh build produces all 6 binaries in one Docker run.
  • Cross-platform from day one — Native Linux, macOS, and Windows support. No WSL required.
    • PTY via creack/pty on Unix, ConPTY on Windows.
    • On Windows 10, Quil bundles Microsoft's OpenConsole (MIT) and hosts panes through it. The Windows 10 inbox console re-serializes an app's incremental screen updates incorrectly — typing Hello in claude-code showed H ello. Windows 11's built-in host is unaffected and is left alone; if the bundle is missing, Quil falls back to the inbox host rather than failing to start.
    • IPC via Unix domain sockets on Linux/macOS, Named Pipes on Windows.
    • Pre-built binaries for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64.

AI integration

  • AI session resume — Claude Code conversations resume automatically after a reboot. No copy-paste, no context rebuild.
    • Each AI pane gets a UUID at creation time. On restart Quil runs claude --resume <session-id> automatically.
    • Works for any AI tool that exposes a session ID — Claude Code (production), OpenCode (beta) and Codex today, more to come.
    • For tools without a session ID, plugins can fall back to regex scraping the last state or replaying a command.
  • Pick a past session when you open a pane — New Claude Code pane, but continue yesterday's conversation. The setup dialog lists the sessions for that folder — titled with your own first prompt.
    • The Session field lists every Claude Code session recorded for the folder you picked, newest first, each row showing a relative age and the first prompt you typed in it.
    • Press i on a session for details: when it started, when it was last touched, how many prompts you typed, and the last prompt you left it on — the thing that actually identifies a conversation, and which appears nowhere else. Arrow keys re-read for the next session so you can compare candidates.
    • Beats running --resume inside the pane: you get real titles and ages without waiting for Claude to boot its own picker, and Quil knows which session the pane is on from the first instant — so restore, input history, and the model/context readout are correct immediately.
    • Sessions already open in another pane are shown greyed and cannot be picked — two claude processes on one transcript would overwrite each other's history.
    • The field stays collapsed to a single line until you Tab onto it, and the listing is only fetched when you do, so creating an ordinary fresh pane costs nothing.
    • Opt-in per pane type via [command] sessions = "claude"; picking a session spawns claude --resume <id> with your permission-mode toggles still applied.
  • Build the whole tab from a template — Some work needs four panes, not one. A template describes the panes, the layout and each one's opening prompt — pick it from the palette and Quil builds the tab.
    • A template is one tab: 1 to 8 panes of any plugin type, a layout (rows, columns, main-left, main-top, grid) and an optional starting prompt for each. Choose 'New from template' in the command palette, pick a directory, and it is built.
    • Each pane can set a model, plugin toggles by name, a subdirectory, a mute, and a prompt. Model and toggle arguments are frozen into the pane at creation, so editing the file later cannot change what an existing pane restarts with.
    • Prompts substitute {{task}} (the text you typed), {{dir}}, {{branch}} and {{panes}} — the last lists every pane in the tab with its id and type, which is what lets one pane's prompt drive the others without pasting ids by hand. Substitution is a single pass, so placeholder-like text inside your own task stays literal.
    • The directory row is the same daemon-side browser the pane setup dialog uses, so it lists the right machine when the daemon is remote. Naming a branch opens the tab in a fresh git worktree instead, with a placeholder pane spinning while git runs.
    • Three templates ship — agent-team, pair and review — and templates.toml is yours to edit at F1 → Settings → Templates. A save validates the whole document before replacing it atomically; an invalid file keeps the editor open with the reason and leaves the previous file intact.
    • Quil sets the workspace up and then gets out of the way: it does not supervise the panes, interpret their answers, or hold any state about the template afterwards. The tab that results is an ordinary tab.
    • Agents can create one too, via the create_from_template MCP tool. A template pane can opt into Quil's own MCP server without changing your global agent config — a guard rail for which tools it reaches for, not a security boundary against an agent that has a shell.
  • MCP server for AI agents — Run quil mcp and an AI agent can manage projects across remote hosts, create AI panes, and delegate work between them.
    • 36 tools exposed over the Model Context Protocol (Anthropic's open standard for AI tool use).
    • Create tabs and AI panes with named toggles, session resume, worktree and sandbox options; manage projects across configured remote hosts.
    • Delegate tasks between panes, track completion, and notify the requester when it is ready. Read output, send keys, inspect screens, watch events, and query memory use.
    • Lets any MCP-capable client (Claude Desktop, Claude Code, Cursor) reach directly into your running Quil session.
  • Clipboard image paste — Press paste on a screenshot and Quil decodes it, saves a PNG, and types the file path into the active pane — works around Claude Code's broken Windows clipboard reader.
    • Win32 DIB / DIBV5 reader handles screenshots from Snipping Tool, Win+Shift+S, and other capture apps.
    • Decodes 24bpp BI_RGB and 32bpp BI_BITFIELDS, including the all-zero-alpha promotion that catches apps which leave the alpha channel uninitialised.
    • Files land in ~/.quil/paste/quil-paste-<ts>-<rand>.png with owner-only 0o600 / 0o700 permissions and an 8-byte crypto/rand suffix so a co-tenant can't enumerate them.
    • Sidesteps the upstream Claude Code Windows clipboard image bug (anthropics/claude-code#32791) — any AI tool with file-reading tools picks the file up via the typed path.
    • Three paste keys: Ctrl+V (default), Ctrl+Alt+V, and F8. F8 is the recommended Windows trigger because Windows Terminal eats Ctrl+V before it reaches the TUI.
  • Agent work indicators — A spinner shows which AI panes are mid-turn; when one finishes or waits for your input, it stays marked green until you actually look at it.
    • Work state is derived entirely from the agent's own hook events (Claude Code hooks, OpenCode plugin bus, Codex hooks) — no output polling, no heuristics.
    • While an agent works, a spinner animates on the pane border and its tab label.
    • When a turn completes — or the agent parks on a permission prompt or a question — the pane border turns green and the tab label of a background tab turns green with it.
    • No timer: the green mark persists until you focus that exact pane (click it, Alt+Arrow onto it, or switch to its tab). With several agent panes split in one tab, the border pinpoints which one needs you.
    • A crash never shows a green mark — process exit clears the spinner without claiming the turn finished.
  • Model and context readout — The status bar shows which model an AI pane is on and how much context it has burned — without asking the agent or reading its header.
    • AI panes show the model id and context-window token count from the last completed turn, e.g. opus-4.8 · 612k ctx.
    • Deliberately tokens, not a percentage: Claude Code and OpenCode record no window size in their data, and a Claude session may be running at 200k or at 1M — a percentage would be a guess presented as a fact. Codex panes show the same segment for consistency.
    • Right after a compaction the pane reads · compacting until the next turn reports the reduced size. The transcript doesn't contain the new count yet at that moment, so showing the old one would be worse than showing nothing.
    • Rides the same hook-event stream as the work indicators — no polling, no extra IPC, and it keeps working while the pane is muted.
  • Input history — AI panes bury your prompt under a wall of output. Alt+Shift+I lists every prompt you submitted — open one full-text and copy it back.
    • Alt+Shift+I opens a per-pane list of your past prompts as 3-line previews, newest first; Enter opens the full text in a read-only viewer you can scroll and copy from.
    • Captured from the agent's own UserPromptSubmit hook, not keystroke scraping — multiline prompts, pastes, and edits are recorded exactly as submitted.
    • Persists across daemon restarts at ~/.quil/history/<pane>.jsonl (64 KiB per entry, ring-trimmed to the last 200) and is removed when the pane is destroyed.
    • Opt-in per pane type via [command] record_history = true (enabled for Claude Code and Codex); other pane types show an empty state. OpenCode support is planned.
  • Run an agent in a Docker sandboxbeta — Skip-permissions is how an agent gets useful, and how it reaches every file you own. Run the pane in a container instead — real edits, real commits, filesystem reach that stops at the checkout.
    • Turn on 'Run in a Docker container' in the pane setup dialog. The checkout is bind-mounted in, so the agent's edits and commits are real; everything outside it is simply not there. Available for Claude Code, Codex and OpenCode.
    • The mount set is the boundary — no --privileged, no --cap-add, no --network flag. The repository's .git is mounted as a mountpoint (which answers EBUSY to rename and remove) with objects, hooks, config, config.worktree, modules and worktrees pinned read-only on top: those are values host git executes, and without the pin an agent can rewrite .git/config with a core.fsmonitor the host then runs.
    • Your history cannot be destroyed from inside. New objects go to a per-pane store with your repository's own mounted read-only as an alternate, copied across every 30 seconds and again at pane close. Branch pointers stay writable — read-only refs would make committing impossible — and are recoverable via git reflog.
    • Notifications, the working spinner, input history and session resume all keep working: a Linux quild is mounted read-only into the container and the agent's hooks call it. Each pane gets its own hook spool and its own agent config directory.
    • Signing in follows each vendor's own container guidance. Claude Code offers a per-pane choice: forward CLAUDE_CODE_OAUTH_TOKEN by name (Docker reads the value, so it never enters a command line or a log; a pane with no token runs claude setup-token for you once per machine), or sign in inside the container for Fable, Remote Control and claude.ai connectors. Codex has its auth.json copied in. Quil never reads, copies, stores or refreshes a Claude credential in any mode.
    • You supply the image; Quil publishes none and pulls none. scripts/sandbox-image.sh builds one locally and then verifies it provides a non-root user, the agent binary and git. There is deliberately no default registry name — the official-looking anthropics/claude-code on Docker Hub is a security researcher's honeypot containing no Claude Code at all.
    • Documented limits: egress is not bounded (a default-deny firewall needs NET_ADMIN, which Quil does not grant), bind-mount IO on Docker Desktop is roughly 20× slower and inotify does not cross it on Windows, and repositories with submodules are unsupported.

Extensibility

  • TOML plugin system — Declare a new pane type in a single TOML file. No compilation, no restart, hot-reload on save.
    • Plugin definitions live in ~/.quil/plugins/<name>.toml.
    • Sections: [plugin], [spawn], [keys], [resume], [error], [status] — each optional.
    • Declarative config means no shell scripting footguns; the daemon validates the TOML at load time.
  • Plugin auto-upgrade — When Quil ships new plugin features, a side-by-side merge dialog lets you reconcile your config with the new defaults — no silent breakage, no lost customizations.
    • Each embedded default plugin carries a schema_version number. On startup, Quil compares your on-disk version with the shipped default.
    • If yours is older, a full-screen split view opens: your config on the left (editable), the new default on the right (read-only). Diff highlighting shows red for your custom lines and green for new additions.
    • Ctrl+C / Ctrl+V to copy lines from the default into your config, Ctrl+S to save, F5 to accept the full default. Esc is blocked — migration must be resolved before the workspace loads.
    • Multiple stale plugins get a tab bar. Each must be resolved independently.
  • Lazygit overlay — Press Alt+G to drop a full-tab git UI over any pane — pointed at the repository of whatever directory that pane is working in.
    • Alt+G toggles a per-tab lazygit overlay for the repository resolved from the active pane's working directory; press it again to hide — the process keeps running, so re-opening is instant with lazygit's UI state intact.
    • Repositories are discovered automatically near the pane (the enclosing repo plus one level down); when several are found, a picker lets you choose which to open.
    • Also available as an ordinary pane via Ctrl+N → Tools → Lazygit, where the setup dialog lists the same discovered repositories with a Browse… fallback.
    • Overlays are ephemeral — one per tab, never persisted, recreated with one keypress, and auto-destroyed when you quit lazygit. Offered only when the lazygit binary is on PATH.
  • k9s for Kubernetes — Open k9s as a pane (Ctrl+N → Tools → k9s) to drive your Kubernetes cluster, with a context picker sourced from the kubeconfig on the machine the daemon runs on.
    • k9s opens as an ordinary pane (not an overlay — it's a long-lived monitoring view you can split alongside your other panes).
    • The setup dialog lists the contexts from KUBECONFIG / ~/.kube/config on the machine the daemon runs on (current one marked) and pins the pane to your choice via --context; "Default context" uses that kubeconfig's current-context. In a remote session those are the server's clusters, not your laptop's.
    • A read-only toggle (--readonly) lets you browse a cluster with all mutating commands disabled, and a start-on-Pods toggle jumps straight to the pods view.
    • Cross-platform (Windows, macOS, Linux). Offered only when the k9s binary is on PATH — otherwise it shows greyed in Ctrl+N with a link to install it. Re-runs and reconnects on daemon restart.
  • lazysql for databases — Open lazysql as a pane (Ctrl+N → Tools → lazysql) to browse and query MySQL, PostgreSQL, SQLite, and MSSQL from its connection manager.
    • lazysql opens as an ordinary pane into its own connection manager — split it alongside your app and AI panes.
    • By design there's no Quil-side connection picker: lazysql's only launch argument is a full connection string with embedded credentials, so Quil never reads its config or injects a DSN. Credential handling stays inside lazysql (which supports ${env:VAR} substitution to keep passwords out of its config).
    • A read-only toggle (--read-only) opens a session with data modification disabled.
    • Cross-platform (Windows, macOS, Linux). Offered only when the lazysql binary is on PATH — otherwise greyed in Ctrl+N with a link to install it. Re-runs on daemon restart.

Observability

  • Notification center — Quil detects when a pane exits, errors, or goes idle and surfaces it in a dedicated sidebar.
    • Daemon-side event queue with pattern-matching idle analysis.
    • Process exit detection with exit-code extraction.
    • Optional sidebar surfaces notifications without interrupting focused work.
  • Desktop notificationsbeta — When an agent parks for input while you are in another window, Windows raises a toast. Click it and Quil is already on that project, tab and pane.
    • Fires on the same two states the project sidebar already marks: a pane parked waiting on you (▲) and a turn that finished while you were away (✓). Not on every event — the notification sidebar remains the full log.
    • For any pane you are not looking at — another tab, another project, or another application. Only the pane on screen in a focused terminal stays silent, so an agent parking in a project you are not watching still reaches you.
    • Clicking the toast routes to the exact pane via a registered quil:// handler — the same jump the attention queue (Alt+Shift+A) performs, including switching project and tab.
    • One toast per pane, so six agents finishing at once give you six independently clickable toasts rather than a storm of duplicates — a toast only fires on a state change, and a pane already showing one cannot get a second.
    • Answering a prompt withdraws its toast from Action Center, so the notification surface never keeps claiming attention you have already given.
    • Opt-in registration: quil notify setup writes a Start Menu shortcut and a quil:// handler, prints exactly what it wrote, and quil notify setup --remove is a true inverse. Nothing is written as a side effect of a config flag.
    • Toggle live from F1 → Settings or [notification.desktop] in config.toml. The Settings row reports whether registration is actually in place rather than just echoing the flag.
    • Windows only. macOS and Linux have no transport that supports click-to-route, so they are deliberately not faked.
  • Memory reporting — Per-pane memory accounting in the status bar and a collapsible breakdown dialog (F1 → Memory).
    • Daemon-side 5 s collector snapshots Go-heap (output ring buffer + ghost snapshot + plugin state) and PTY child resident memory per pane.
    • Cross-platform RSS: /proc/<pid>/status on Linux, ps -o rss= batched on Darwin, GetProcessMemoryInfo on Windows.
    • Status bar gains a mem <n> segment refreshed every 5 s; F1 → Memory opens a tab/pane tree with expand/collapse and notes-editor byte accounting.
    • Two MCP tools — get_memory_report (per-tab totals + grand total) and get_pane_memory (single-pane detail) — expose the layers for external agents.
  • Updates without leaving the app — Quil notices a new release, stages it in the background, and installs it on your say-so — no re-running the install script, no manual binary swap.
    • The daemon checks for new releases shortly after startup and once a day after that; the status bar shows ↑ v1.42.0 [ready] once one is staged, and F1 → Check for updates runs a real check on demand.
    • Applying swaps both binaries and restarts — your tabs, panes, and AI sessions come back from the workspace snapshot exactly as they do after any restart.
    • Downloads are checksum-verified before anything is swapped, and the previous binaries are kept as a backup that is restored automatically if the swap fails halfway.
    • Two settings under [update] in config.toml, both in the Settings dialog: check (look for releases) and auto (download in the background so applying is instant). Turn both off and Quil never contacts GitHub.
    • Dev and debug builds have the pipeline compiled out entirely — a release binary applied over quil-dev would strip its dev-mode wiring and silently point the next launch at your production data directory.
  • Client/daemon version handshake — Upgrade in one step. The client checks the running daemon's version on attach and self-heals when they drift.
    • TUI handshakes with the daemon before attaching. Older daemon → prompt, gracefully stop, auto-spawn the matching daemon from alongside the TUI binary.
    • Newer daemon than client → TUI refuses to attach and points to the releases page (avoids subtle protocol drift bugs).
    • Dev/debug builds and unstamped local builds skip the check.
    • Backed by a new IPC pair (MsgVersionReq/MsgVersionResp) and a shared internal/version/ package with proper semver comparison — no more lexical-ordering traps with 1.10.0 vs 1.9.0.
  • Self-healing daemon — A wedged AI pane can't freeze your workspace, and quil restart recovers anything in one command — tabs and AI sessions restored from the last snapshot.
    • quil restart stops the daemon with bounded escalation (graceful IPC shutdown with final snapshot → SIGTERM → force-kill, each tier timed out so even a deadlocked daemon can't stall it), cleans stale pid/socket files, starts fresh, and reopens the TUI.
    • quil status answers "is it actually healthy?" before you reach for restart: daemon state, pid, version, environment, uptime, and a per-tab/pane breakdown with each pane's state and memory. --json for scripts, and exit codes that distinguish healthy from not-running from wedged — so it works in a monitoring loop, not just by eye.
    • Prints the target environment first — production (~/.quil) vs dev (QUIL_HOME) — so you can never kill the wrong daemon. PID-reuse guard: a recorded PID is only signaled if it actually belongs to a quild binary.
    • Per-pane input isolation: every pane's stdin is written by its own goroutine behind a bounded queue. A process that stops reading input costs you a 'Pane not accepting input' sidebar warning on that one pane — everything else stays interactive. Alt+R restarts the stuck pane in place with its AI session resumed.
    • Liveness watchdog: if no workspace snapshot completes for 2 minutes, the daemon writes a full goroutine stack dump to quild.log — a wedge becomes a precise bug report instead of a silent freeze.
  • Leveled logging + in-app log viewer — A single [logging] level setting controls 152 existing log call sites and the new debug helpers. F1 opens read-only viewers for the client, daemon, and MCP logs.
    • internal/logger wraps Go's stdlib slog and bridges every existing log.Printf call site at info level — old and new code respect one filter.
    • Flip [logging] level = "debug" in config.toml to trace clipboard pipeline, per-key handler decisions, and Win32 image read step-by-step.
    • F1 → About → View client log / daemon log / MCP logs opens a read-only TextEditor viewing the tail (256 KB) of each file. Symlink-rejecting via os.Lstat plus a re-stat through the open handle defeats TOCTOU swap.
    • Alt+Up / Alt+Down jump the cursor by [ui] log_viewer_page_lines (default 40, configurable). The same TextEditor.ReadOnly flag is now available for any other look-but-don't-touch dialog.
    • Hot-path Debug calls pre-check slog.Enabled so the fmt.Sprintf is skipped entirely when filtered out — important for the per-keystroke trace.
    • The shared TextEditor now supports Ctrl+C (copy selection), Ctrl+Y (delete line), and Ctrl+X (cut) — used across log viewers, plugin TOML editor, pane notes, and the migration dialog.

All of it in detail: docs/features.md ↗

Pane types

A pane knows what runs in it, so it comes back the right way after a restart. Add your own type in one TOML file.

  • Terminal — The default shell pane. Runs your system shell (bash, zsh, PowerShell, fish) with OSC 7 auto-injection so pane borders display the live working directory.
    • Runs $SHELL or /bin/bash as fallback
    • OSC 7 hook auto-injection on bash / zsh / PowerShell (fish emits natively)
    • 500-line ghost buffer replay on reconnect
    • Full mouse + keyboard support
  • Terminal (keeps content on squeeze) — The same shell pane on a window-sized canvas. Squeezing the pane crops what you see instead of reflowing the shell, so long log lines survive a narrow split — the trade is that formatting assumes the wider width while the pane is narrow. Built for log tails and watch loops.
    • Window-sized canvas: the child PTY is not resized when the pane is squeezed
    • Content is cropped from the left edge by default; Alt+Shift+W switches the pane to soft wrap
    • Renders natively once the pane is at least 80 columns wide (`[display] min_native_cols`)
    • Same shell detection, OSC 7 injection, and ghost buffer as the standard Terminal pane
  • Claude Code — An AI session pane that runs Anthropic's Claude Code CLI. A setup dialog asks for the working directory (so project-specific `.claude/` context is preserved), lets you resume one of that folder's earlier sessions instead of starting fresh, and offers a `Dangerously skip permissions` checkbox for unattended runs. Sessions resume across daemon restarts.
    • Setup dialog (Ctrl+N → AI → Claude Code) browses the filesystem starting from the active pane's OSC 7 working directory. On Windows, backspace at a drive root shows all available drives for cross-drive navigation.
    • Resume picker: the same dialog lists the sessions already recorded for the folder you picked — newest first, titled with the first prompt you typed in each — so a new pane can continue yesterday's conversation instead of starting cold. Sessions open in another pane are greyed out and blocked, since two claude processes on one transcript would overwrite each other's history.
    • Press `i` on a listed session for details — when it started, when it was last touched, how many prompts you typed, and the last prompt you left it on. Arrow keys re-read for the next session, so two similar-looking conversations can be told apart without opening either.
    • `Dangerously skip permissions` checkbox is off by default; when on, the toggle args persist across daemon restarts (the resume strategy now appends ResumeArgs to InstanceArgs instead of replacing).
    • Auto-resume on daemon restart via `claude --continue`, with daemon-side `EvalSymlinks` re-resolution closing the spawn-time TOCTOU window.
    • Idle-state detection surfaces to the notification center.
    • Pairs with the Win32 clipboard image paste proxy: take a screenshot, press F8 in a Claude Code pane, and the file path is typed in for the AI to read.
    • Plugin auto-upgrade: when Quil ships a new schema version for claude-code.toml, a side-by-side merge dialog lets you reconcile your customizations with the new defaults on first launch.
  • OpenCodebeta — An AI session pane that runs [opencode](https://opencode.ai), the second production AI integration alongside Claude Code. A setup dialog asks for the working directory; sessions resume across daemon restarts to the exact conversation (not just the most recent in CWD) via a small JS plugin Quil registers through opencode's plugin runtime. The user's own opencode plugins, agents, and modes remain active — Quil's plugin is additive.
    • Setup dialog (Ctrl+N → AI → OpenCode) browses the filesystem starting from the active pane's OSC 7 working directory, same UX as Claude Code.
    • Session-id rotation tracked via opencode's first-class plugin runtime — Quil writes a small JS plugin to `$QUIL_HOME/opencodehook/` and injects it via the `OPENCODE_CONFIG_CONTENT` env var per spawn, with **zero writes** into `~/.config/opencode/`.
    • On daemon restart the pane respawns with `opencode --session <id>` so each pane reattaches to its own conversation — including any rotation from `/new`, fork, or compaction during the previous run.
    • If the recorded id is missing or fails shape validation, the pane falls back to `opencode --continue` (most-recent in CWD) — never to an empty restore.
    • `OPENCODE_CONFIG_CONTENT` merges with the user's existing opencode config, so any user-installed plugins, agents, and modes remain active inside Quil-spawned opencode panes.
    • `--pure` deliberately not exposed as a toggle: it disables external plugins (including Quil's tracker). Run opencode outside Quil if you need pure mode.
  • Codex — An AI session pane that runs [Codex](https://github.com/openai/codex), OpenAI's coding agent CLI, with the same hook-driven capabilities as Claude Code: notifications, the work-in-progress indicator, subagent tracking, the model and context-token status segment, input history, and per-pane session resume. Codex only runs hooks it trusts; Quil registers its hook per pane with a `-c hooks=…` override that carries the trust hash codex expects, so nothing under `~/.codex` is touched and no trust prompt appears.
    • Setup dialog (Ctrl+N → AI → Codex) browses the filesystem from the active pane's working directory; two mutually exclusive approval-mode toggles plus an independent web-search toggle.
    • Codex's Claude-compatible hooks (SessionStart, UserPromptSubmit, PermissionRequest, Stop, SubagentStart/Stop, compaction, a throttled PreToolUse heartbeat) feed the notification sidebar, the spinner and green/amber tab marks, and the `<model> · <n>k ctx` status segment — with **zero writes** into `~/.codex`.
    • On daemon restart the pane respawns with `codex resume <id>` so each pane reattaches to its own conversation, including a rotation from `/new` during the previous run.
    • A pane with no recorded session starts fresh — never `resume --last`, codex's most-recent-session lookup, which on restore finds the sibling pane that respawned a second earlier.
    • `Alt+Shift+I` browses the prompts submitted in the pane, captured from the `UserPromptSubmit` hook rather than keystrokes.
    • Requires the native codex binary on Windows: an npm `.cmd` shim re-parses the hook override, so Quil logs a warning there and spawns codex without hooks.
  • SSH — A persistent SSH tunnel pane. On reboot Quil re-runs the original SSH command with the same host, port, and forwarding rules.
    • Stores host, port, and forwarding arguments in workspace.json
    • Auto-reconnects on disconnect via ServerAliveInterval
    • Error handler pattern-matches `Connection refused` and `Host key verification failed`
    • Status line shows connection state + latency
  • Stripe CLI — A webhook listener pane that runs `stripe listen` with a configurable forward URL. Quil captures the webhook signing secret from the output and exposes it in the pane status line.
    • Forward URL stored per pane so the exact `stripe listen` invocation restores
    • Webhook signing secret extracted from output and surfaced in status line
    • Error handler pattern-matches common auth failures
    • Resume strategy: re-spawn with the same forward URL on reboot
  • Lazygit — A git UI pane backed by [lazygit](https://github.com/jesseduffield/lazygit). Open it as an ordinary pane from Ctrl+N → Tools, or toggle it as a full-tab overlay over any pane with Alt+G — pointed at the git repository of whatever directory the active pane is working in. Offered only when the lazygit binary is found on PATH.
    • Alt+G toggles a per-tab lazygit overlay for the repo resolved from the active pane's working directory; press it again to hide — the process keeps running, so re-opening is instant with its UI state intact.
    • discover = "git" turns the setup-dialog directory step into a repo picker: the enclosing repository plus one-level subfolders (up to ten), with a Browse… fallback to the plain directory browser.
    • When several repositories are found near the pane, a picker lets you choose which one to open.
    • Repo discovery is a pure filesystem walk that canonicalises paths and rejects UNC/device paths, so an untrusted working directory can't steer it onto a network share.
    • Overlays are ephemeral — one per tab, excluded from workspace snapshots, recreated with one keypress, and auto-destroyed when you quit lazygit (q).
  • hunk — A review-first diff viewer backed by [hunk](https://github.com/modem-dev/hunk) — built for reading what an agent just wrote. Open it as an ordinary pane from Ctrl+N → Tools, or toggle it as a full-tab review of the working tree with Alt+D, pointed at the git repository of whatever directory the active pane is in. Offered only when the hunk binary is found on PATH.
    • Alt+D toggles a per-tab review of the working tree for the repo resolved from the active pane's directory; press it again to hide, and the process keeps running so re-opening is instant.
    • Alt+G and Alt+D share one overlay slot per tab: pressing the other tool's key swaps lazygit and hunk rather than stacking them, so the outgoing tool's process ends.
    • discover = "git" turns the setup-dialog directory step into a repo picker — the enclosing repository plus one-level subfolders, with a Browse… fallback.
    • Deliberately toggle-free: instance args replace the base args at spawn, so any toggle would drop the `diff` subcommand and leave the pane on a help screen. Per-user options belong in hunk's own ~/.config/hunk/config.toml.
    • Binary-gated: greyed out in Ctrl+N with a link to the project when hunk is not installed (npm i -g hunkdiff, brew install hunk, or mise use -g hunk).
  • k9s — A Kubernetes cluster TUI backed by [k9s](https://github.com/derailed/k9s). Open it as an ordinary pane from Ctrl+N → Tools — it connects to whatever cluster your kubeconfig points at (KUBECONFIG / ~/.kube/config). Offered only when the k9s binary is found on PATH. Cross-platform: Windows, macOS, Linux.
    • Opens as a normal pane (Ctrl+N → Tools → k9s) — a long-lived monitoring view you can split alongside other panes, not an overlay.
    • discover = "kube" gives the setup dialog a context pick-list: "Default context" (your kubeconfig current-context) plus the contexts found in KUBECONFIG / ~/.kube/config, with the current one marked. The choice is pinned via --context.
    • Cluster connection comes from the standard kubeconfig resolution (KUBECONFIG env, then ~/.kube/config) — no working-directory prompt.
    • Read-only toggle appends --readonly so the pane can browse a cluster without exposing any mutating commands; a start-on-Pods toggle opens k9s directly on the pods view.
    • Binary-gated: the entry is greyed out in Ctrl+N when k9s is not installed. On daemon restart the pane re-runs k9s and reconnects (rerun strategy, no stale-frame replay).
  • lazysql — A database TUI backed by [lazysql](https://github.com/jorgerojas26/lazysql) for MySQL, PostgreSQL, SQLite, and MSSQL. Open it as an ordinary pane from Ctrl+N → Tools — it launches lazysql's own connection manager, where you select or save connections. Offered only when the lazysql binary is found on PATH. Cross-platform: Windows, macOS, Linux.
    • Opens as a normal pane (Ctrl+N → Tools → lazysql) into lazysql's connection manager — split it alongside your app and AI panes.
    • No Quil-side connection picker, by design: lazysql's only launch argument is a full connection string with embedded credentials, so Quil never reads its config or injects a DSN. Connection selection and credentials stay inside lazysql (which supports ${env:VAR} substitution to keep passwords out of its config).
    • Read-only toggle appends --read-only so you can browse a database without risking a modification.
    • Binary-gated: greyed out in Ctrl+N with a homepage link when lazysql is not installed. Re-runs on daemon restart (rerun strategy).

Write a pane type: docs/plugin-reference.md ↗

FAQ

Questions people ask

What is Quil?

Quil is a reboot-proof terminal multiplexer for developers who run complex multi-tool workflows alongside AI coding assistants. It persists your entire workspace — tabs, panes, layout, working directories, and AI session IDs — across host reboots, so typing `quil` after a restart snaps everything back in under 30 seconds.

How is Quil different from tmux or Zellij?

tmux and Zellij are terminal multiplexers — they survive network disconnects, but not a host reboot with your work still running. tmux loses the session outright; Zellij rebuilds the layout from its cache and waits for you to press ENTER on each command again. Quil brings the running workspace back. It also understands pane types (a Claude Code pane resumes differently than an SSH pane), ships an MCP server for AI agents, and tracks per-pane notes alongside your work. For a deeper comparison, see /vs/tmux or /vs/zellij.

Can I use Quil on a remote server over SSH?

Yes — `quil --remote gpu01` runs the daemon on the server and the TUI on your laptop, so the panes and AI sessions live on the remote host and keep running when you close the lid. No port is opened on the server: Quil runs `ssh -T gpu01 "quil --stdio"` and speaks its normal protocol over that one channel, so bastions behind ProxyJump, Tailscale addresses, hardware tokens, and your existing ~/.ssh/config all work unchanged. If the link drops, an amber bar names the host and reconnects on its own with backoff — nothing stopped running, so there is nothing to resume. The server does not need Quil installed first: point --remote at a bare machine and it downloads the release for the remote's platform onto your laptop, verifies the checksum there, and pushes it over the connection you already have. It is beta today — plugin definitions still come from your local machine, and remotes must be Linux or macOS.

How do I find the right pane when I have dozens open?

Press Alt+Shift+P for the command palette. It fuzzy-finds every pane and tab across the whole workspace (listed as tab.pane with the pane type, so duplicates are distinguishable) and every action, each row showing its keybinding. The same box also searches content: start typing and a `Found in panes` section lists every pane whose scrollback contains your text, with a match count and a preview of the most recent hit — press Enter on one to jump straight to that pane. That answers "which pane had that error, URL, or container id?" without hunting tab by tab. No other multiplexer searches across panes without a third-party plugin.

Which AI tools does Quil support today?

Claude Code has first-class support via the built-in Claude Code pane type, with auto-resume on daemon restart, a setup dialog that pre-fills the active pane's working directory (so the project's `.claude/` context is preserved), and a one-click `Dangerously skip permissions` toggle for unattended runs. Quil also runs an MCP server (`quil mcp`) that exposes 36 tools so any MCP-capable client can read pane output, send keystrokes, snapshot a workspace, query per-pane memory usage, create tabs and AI panes with the dialog's options, manage projects across remote hosts, and delegate work between AI panes. Any other AI tool can be wrapped in a custom TOML plugin that defines its spawn command, resume strategy, and error patterns.

Can I paste a screenshot into Claude Code on Windows?

Yes. Quil ships a Win32 clipboard image proxy that works around the upstream Claude Code Windows clipboard image bug (anthropics/claude-code#32791). Take a screenshot with Win+Shift+S, focus a Claude Code pane, and press F8 (or Ctrl+Alt+V — Windows Terminal eats Ctrl+V before it reaches the TUI). Quil decodes the clipboard DIB, saves a PNG under `~/.quil/paste/` with owner-only 0o600 permissions and a crypto/rand filename suffix, then types the absolute file path into the pane. Claude Code reads the file via its normal file-reading tools.

Does Quil work on Windows without WSL?

Yes. Quil ships a native Windows binary that uses ConPTY for pseudo-terminal support and Named Pipes for client-daemon IPC. No WSL required. Linux and macOS use creack/pty and Unix domain sockets, respectively.

Why does the Windows build include OpenConsole.exe?

On Windows 10, the built-in console host (conhost.exe) mis-renders some TUIs — Claude Code's input box, for example, shows an extra space after the first typed character. Quil fixes this automatically by bundling Microsoft's MIT-licensed OpenConsole (the same console host Windows Terminal ships) and hosting panes through it; on Windows 10 it is extracted to ~/.quil/conpty/ at first run. Windows 11's console host is already correct, so nothing is extracted there. Attribution and license text are on the /legal page and in THIRD_PARTY_LICENSES.md.

How does the reboot-proof persistence actually work?

Quil runs as a client-daemon pair. The daemon (quild) continuously snapshots workspace state to ~/.quil/workspace.json (atomic temp+rename) and maintains 500-line ghost buffers per pane as binary files under ~/.quil/buffers/. On reboot the client spawns the daemon, reads the snapshot, re-creates the pane split tree, and replays ghost buffers instantly while shells re-initialise in the background.

Can I write my own plugins?

Yes. Plugins are single TOML files in ~/.quil/plugins/<name>.toml with sections for spawn, resume, keybindings, error handlers, and status lines. No compilation, no restart, hot-reload on save. The pane types section on this page lists the built-in ones, and the plugin reference on GitHub (docs/plugin-reference.md) walks through writing your own.

What happens when I upgrade Quil and plugin configs have changed?

Quil detects when your on-disk plugin TOML has a lower schema_version than the version shipped with the new binary. Instead of silently overwriting your config, it opens a full-screen side-by-side merge dialog at startup: your config on the left (editable), the new default on the right (read-only). Lines unique to your config are highlighted red, new lines in the default are highlighted green. Copy what you need from the right, edit on the left, then Ctrl+S to save. You can also press F5 to accept the new default entirely. The dialog blocks until resolved — no risk of running with a stale config.

What if Quil hangs or a pane stops responding?

One command recovers everything: `quil restart` stops the daemon with escalating force (graceful shutdown with a final state snapshot, then SIGTERM, then force-kill — each step has a timeout, so even a fully deadlocked daemon can't resist), starts a fresh one, and reopens your tabs and panes from the last snapshot. AI panes resume their sessions. If only a single pane stops accepting input (for example an AI tool wedged mid-turn), Quil shows a 'Pane not accepting input' warning in the notification sidebar while every other pane keeps working — press Alt+R to restart just that pane in place.

Is Quil free?

Yes. Quil is open source under the Apache License 2.0. There's no hosted version, no paid tier, no telemetry. You self-host it on your own machine and it stores all state locally under ~/.quil/.

How do I install it?

On Linux or macOS: `curl -sSfL https://raw.githubusercontent.com/artyomsv/quil/master/scripts/install.sh | sh`. Go users run both `go install github.com/artyomsv/quil/cmd/quil@latest` and `go install github.com/artyomsv/quil/cmd/quild@latest` — quil needs its daemon, quild. Windows users download the .zip from the latest GitHub release. Full instructions are in the Install section of this page and in docs/installation.md on GitHub.

Can I run an AI agent in a container so it can't touch the rest of my machine?

Yes — turn on `Run in a Docker container` in the pane setup dialog and the pane runs inside a per-pane container, for Claude Code, Codex or OpenCode. Its checkout is bind-mounted in, so its edits and commits are real, but its filesystem reach stops at that checkout. The mount set is the whole boundary: no --privileged, no --cap-add, no --network flag. The repository's .git is mounted as a mountpoint with objects, hooks, config, config.worktree, modules and worktrees pinned read-only on top, because those are values host git executes. New objects go to a per-pane store with your repository's own mounted read-only, so no commit on any branch can be deleted from inside. Notifications, the working spinner, input history and session resume all keep working. You supply the image — Quil publishes none and pulls none, and scripts/sandbox-image.sh builds one locally and then verifies it. Egress is deliberately not bounded. Full guide: docs/sandbox-panes.md.