canopy is a terminal UI for developers running multiple AI coding agents in parallel. One view. All your agents.
- Why canopy?
- Demo
- Requirements
- Install
- Usage
- Keybinds
- How it works
- Config
- Editor Integration
- Contributing
- License
Each worktree gets a dedicated tmux session so your agent runs in a real terminal with full PTY support. Canopy sits above all of them: monitor every agent at a glance and drop in when you need to.
Managing 3–5 parallel AI agents across separate terminals is chaotic. You have no overview of what's running, you're constantly switching windows, and there's no signal for when an agent needs your input. Canopy gives you one persistent view: see every agent's state, catch anything waiting for you, and drop in when needed.
- tmux (any modern version)
- An AI coding agent on your PATH (e.g. Claude Code, Codex, Aider)
- A git repository
Quick install (Linux & macOS):
curl -fsSL https://raw.githubusercontent.com/isacssw/canopy/main/install.sh | shHomebrew:
brew install isacssw/tap/canopyWith Go:
go install github.com/isacssw/canopy/cmd/canopy@latestRun from anywhere inside your git repo:
canopyFirst run will open a short setup wizard to configure your agent command and give it a name.
canopy --help # show keybinds and usage
canopy --version # print versionConfig is saved to ~/.config/canopy/config.json.
| Key | Action |
|---|---|
↑/↓ or j/k |
Navigate worktrees |
n |
Create new worktree (prompts for branch + base) |
r |
Run agent in selected worktree (shows picker if multiple agents are configured) |
a |
Attach to the live tmux session (full interactive agent) |
x |
Kill running agent |
d |
View git diff for selected worktree |
D |
Delete worktree (confirm with y, then 5-second undo window) |
u / esc |
Cancel a pending delete during the countdown |
i |
Send input to agent when it's waiting |
R |
Refresh worktree list |
e |
Open selected diff file in your editor at the changed line |
? |
Toggle keybindings help overlay |
q |
Quit |
Click any worktree in the left panel to select it. Use the scroll wheel to scroll the output panel.
Press a to drop into the agent's tmux session and interact with it directly. Canopy suspends while you're attached. Press Ctrl+b d to detach and return to canopy.
Nested tmux sessions: If you're already running inside a tmux session, canopy opens a nested session. To detach from the inner (canopy) session, press
Ctrl+btwice thend. The firstCtrl+bis consumed by your outer session, so you need a second one to reach the inner session. To avoid the double-prefix, set"tmux_prefix": "C-Space"(or any other key) in your config — canopy's sessions will use that prefix instead.
| Icon | State | Meaning |
|---|---|---|
○ |
idle | No agent running |
● |
running | Agent active |
⚠ |
waiting | Agent needs your input, press i or a |
✓ |
done | Agent finished cleanly |
✗ |
error | Agent exited with error |
When an agent transitions to waiting, canopy marks it with a yellow ● badge in the list and emits a terminal bell, so you notice even when working in another window. The badge clears when you navigate to that worktree.
The status bar shows a live summary of agent counts (e.g. 1 running · 2 waiting) on the right side.
With idle_timeout_secs set, any agent that has produced no new output for that many seconds is automatically promoted from running to waiting. This is a useful fallback for agents that don't emit standard input-prompt patterns. Set to 0 (the default) to disable.
When you press r, canopy creates a detached tmux session named canopy_<repo-hash>_<branch> and launches your agent command inside it. The output panel shows a live snapshot of the tmux pane, refreshed every 500ms.
Session names include a short hash of the repo root, so branches with the same name across different repos never collide.
Agents keep running after you quit canopy. They're just tmux sessions. You can reattach at any time with tmux attach -t <session-name> or by reopening canopy and pressing a.
~/.config/canopy/config.json:
{
"agents": [
{ "name": "claude", "command": "claude" },
{ "name": "claude-yolo", "command": "claude --dangerously-skip-permissions" },
{ "name": "codex", "command": "codex" }
],
"left_panel_width": 38,
"theme": "github-dark",
"output_colors": "adaptive",
"idle_timeout_secs": 0,
"tmux_prefix": "C-Space"
}With a single agent configured, pressing r starts it immediately. With two or more, a picker appears so you can choose which profile to run.
| Field | Default | Description |
|---|---|---|
agents |
— | List of agent profiles. Each entry has a name (label shown in the picker) and a command (executable + flags). |
agent_command |
— | Legacy. Single agent command, used when agents is not set. Existing configs continue to work without any changes. |
left_panel_width |
38 |
Width of the worktree list panel in columns. Minimum 20. Omit to use the default. |
theme |
"github-dark" |
UI colour theme. Options: "github-dark", "nord", "catppuccin", "light". Omit or leave empty for the default. |
output_colors |
theme-dependent | Agent output colour handling. Options: "preserve" to keep raw ANSI colours from tmux, "plain" to strip ANSI and render output with the current theme text colour, or "adaptive" to remap ANSI colours into the active theme palette. Defaults to "adaptive" for the light theme and "preserve" otherwise. |
idle_timeout_secs |
0 |
Seconds of no new agent output before status is promoted from running to waiting. 0 disables the timeout. Useful for non-Claude agents that don't emit standard input-prompt patterns. |
tmux_prefix |
— | Custom prefix key for canopy's tmux sessions (e.g. "C-Space", "C-a"). Avoids double-prefix when running inside an existing tmux. Omit to use tmux's default (C-b). |
| Name | Description |
|---|---|
github-dark |
Default, GitHub dark palette |
nord |
Nord palette, cool blues and greens |
catppuccin |
Catppuccin Mocha, pastel dark theme |
light |
Light terminal palette |
Pressing D on a worktree asks for confirmation. After pressing y, canopy starts a 5-second countdown displayed in the status bar (Deleting "branch" in 5s… [u]ndo). Press u, esc, or n at any point during the countdown to cancel. The delete only executes when the timer reaches zero.
For Neovim users, see canopy.nvim for setup instructions and integration.
Set EDITOR=code or EDITOR=cursor and press e from diff view. Canopy opens the selected file at the first changed hunk line in your GUI editor.
See CONTRIBUTING.md. Contributions, bug reports, and feature requests are welcome.
MIT, see LICENSE