Run multiple AI coding agents side by side. Switch between them instantly.
tmux + fzf powered. Works with Claude Code, Codex CLI, and Gemini CLI.
Quick Start · Browser · Orchestration · Reference
AI coding agents work best with focused context. Real work often needs several of them at once — one debugging tests, another implementing a feature, a third reviewing a diff. Switching between terminal tabs and losing track of what's running where slows you down.
am gives you a single interface to launch, browse, and manage agent sessions. Each session is a persistent tmux split — agent on top, shell on the bottom — so you can check on any agent, send it new instructions, or hand it off to a teammate without losing state.
It works equally well whether you are driving from the keyboard or whether another agent is dispatching workers programmatically.
# Install dependencies (see full list below)
brew install tmux fzf jq # macOS
# sudo apt install tmux fzf jq # Debian/Ubuntu
# Install am
git clone https://github.com/ehudt/agent-manager.git
cd agent-manager
./scripts/install.sh
# Launch your first session
am new ~/my-projectThat's it. You're in a tmux session with Claude running in the top pane and a shell in the bottom. Press Prefix + d to detach, or Prefix + s to browse all your sessions.
| Dependency | Minimum | Install |
|---|---|---|
| bash | 4.0+ | Ships with most Linux distros. macOS: brew install bash |
| tmux | 3.0+ | brew install tmux / apt install tmux / pacman -S tmux |
| fzf | 0.40+ | brew install fzf / apt install fzf / pacman -S fzf |
| jq | 1.6+ | brew install jq / apt install jq / pacman -S jq |
| git | any | Required for worktree isolation and branch display |
| zoxide | any | Frecent directory ranking in the session creation form |
One-liner install for all dependencies:
# macOS (Homebrew)
brew install bash tmux fzf jq zoxide
# Debian / Ubuntu
sudo apt install tmux fzf jq zoxide
# Fedora
sudo dnf install tmux fzf jq zoxide
# Arch Linux
sudo pacman -S tmux fzf jq zoxideAt least one AI coding agent must be installed:
| Agent | Install |
|---|---|
| Claude Code | npm install -g @anthropic-ai/claude-code |
| Codex CLI | npm install -g @openai/codex |
| Gemini CLI | npm install -g @anthropic-ai/gemini-cli |
git clone https://github.com/ehudt/agent-manager.git
cd agent-manager
./scripts/install.shThis symlinks am into ~/.local/bin and sets up the dedicated tmux configuration. The installer will:
- Add
~/.local/binto your PATH in.zshrcor.bashrcif needed - Generate a dedicated tmux config at
~/.agent-manager/tmux.confwith am-specific keybindings (your personal~/.tmux.confis unaffected)
# Install options
./scripts/install.sh --yes # Non-interactive (accept all)
./scripts/install.sh --no-shell # Skip shell rc updates
./scripts/install.sh --prefix /usr/local/bin # Custom install path
./scripts/install.sh --copy # Copy files instead of symlinkam version
am helpam new ~/code/myproject # New Claude session in a directory
am new -t codex ~/code/project # Use Codex instead
am new -t gemini ~/code/project # Use Gemini instead
am new -n "fix auth bug" . # Session with a task description
am new --yolo ~/code/myproject # Permissive mode (agent-specific flags)
am new -w ~/code/myproject # Isolate changes in a git worktreeRunning am new with no arguments opens an interactive form where you pick a directory, agent type, and mode:
Run am (or am list) to open the fzf-powered session browser:
┌───────────────────────────────────────────────────────────────────────────┐
│ Agent Sessions | Enter:attach Ctrl-N:new Ctrl-X:kill Ctrl-R:refresh │
├───────────────────────────────────────────────────────────────────────────┤
│ > myapp/feature/auth [claude] Fix user auth (5m ago) │
│ myproject/main [claude] (2h ago) │
│ tools/dev [gemini] Refactor build system (1d ago) │
├───────────────────────────────────────────────────────────────────────────┤
│ Preview: │
│ Directory: ~/code/myapp │
│ Branch: feature/auth │
│ Agent: claude | Running: 2h 15m | Last active: 5m ago │
│ ────────────────────────────────── │
│ > Reading src/auth/handler.ts... │
│ > I'll implement the OAuth flow using... │
└───────────────────────────────────────────────────────────────────────────┘
| Key | Action |
|---|---|
Enter |
Attach to selected session |
Ctrl-N |
Create new session |
Ctrl-X |
Kill selected session |
Ctrl-R |
Refresh session list |
Ctrl-P |
Toggle preview panel |
Esc |
Exit |
Each session is a tmux split pane — agent on top, shell on the bottom, both in the same working directory:
┌─────────────────────────────────────┐
│ Agent (Claude/Codex/Gemini) │ ← top pane
│ │
├─────────────────────────────────────┤
│ Shell │ ← bottom pane, same directory
└─────────────────────────────────────┘
Sessions run on a dedicated tmux socket (agent-manager), so am keybindings don't interfere with your regular tmux setup.
| Key | Action |
|---|---|
Prefix + a |
Switch to last used am session |
Prefix + n |
Open new-session popup |
Prefix + s |
Open am browser popup |
Prefix + x |
Kill current session and switch to next |
Prefix + d |
Detach from session |
Prefix ↑/↓ |
Switch between agent and shell panes |
Check on a session without attaching to it:
am peek am-abc123 # Snapshot of agent pane
am peek --pane shell am-abc123 # Snapshot of shell pane
am peek --follow am-abc123 # Stream agent output in real timeam is designed to be driven by other agents. An orchestrator agent can spawn workers, send them tasks, wait for completion, and inspect results — all through the CLI.
# 1. Launch a worker with a self-contained prompt
session=$(printf 'Run the test suite in tests/ and fix any failures.
The tests use pytest. Commit fixes individually with descriptive messages.
' | am new --detach --print-session ~/repo)
# 2. Wait until the agent finishes its turn
am wait "$session"
# 3. Check results
am peek --json "$session" | jq -r '.lines[-10:][]'
# 4. Send a follow-up (waits until agent is ready)
am send --wait "$session" "Now update the changelog"
# 5. Clean up or hand off
am kill "$session" # or: am attach "$session"s1=$(printf 'Run backend tests\n' | am new --detach --print-session ~/repo)
s2=$(printf 'Run frontend tests\n' | am new --detach --print-session ~/repo)
am wait --state idle,dead "$s1"
am wait --state idle,dead "$s2"
am peek --json "$s1" | jq -r '.lines[-5:][]'
am peek --json "$s2" | jq -r '.lines[-5:][]'am events "$session" | while IFS= read -r line; do
state=$(printf '%s' "$line" | jq -r '.to // .state')
printf 'State: %s\n' "$state"
done| State | Meaning |
|---|---|
starting |
Session created, agent not yet running |
running |
Agent is actively executing |
waiting_input |
Agent finished its turn, ready for next prompt |
waiting_permission |
Agent is blocked on a permission prompt |
idle |
Agent process exited cleanly |
dead |
Agent process crashed or session gone |
am ships with an orchestration skill for Claude Code at skills/am-orchestration/SKILL.md. When installed, Claude Code agents can automatically use am to dispatch and manage worker sessions.
Experimental feature — not a security boundary. The sandbox reduces the blast radius of permissive (
--yolo) agent runs by placing them inside a Docker container. It is not designed to contain a determined adversary. The project directory is bind-mounted read-write, host credentials are shared into the container, and Docker itself requires root-equivalent access on the host. Treat the sandbox as a safety net for accidental damage, not as isolation from malicious code. The sandbox API and defaults may change in future releases.
Sandbox mode is used when --yolo and --sandbox are both set (or when yolo + sandbox are the saved defaults). The container is created per-session and removed when the session is killed.
am new --yolo --sandbox ~/project # Explicit sandbox
am new --yolo ~/project # Also sandboxed if `sandbox=true` in config
am sandbox ls # List sandbox containers
am sandbox prune # Remove stopped containers
am sandbox rebuild --restart # Rebuild image, restart running containersEach sandbox is a Docker container running Ubuntu 24.04 with a long-lived init process (tail -f /dev/null). The agent and shell panes connect via docker exec — there is no SSH overhead for the default workflow.
The entrypoint aligns the container's user identity (username, UID, GID, home directory) with the host user, so file ownership on bind-mounted directories is consistent.
Pre-installed packages:
| Category | Packages |
|---|---|
| Shell & editors | zsh (with Pure prompt), vim-nox, tmux |
| Dev tools | git, build-essential, python3 + venv, Node.js 22, npm |
| Python tooling | uv, ipython, shell-gpt |
| Search & nav | ripgrep, fzf, zoxide, bat, eza |
| Network | curl, wget, openssh-server, iproute2, dnsutils, iputils-ping |
| Testing | Playwright + Chromium |
| AI agents | Claude Code, Codex CLI |
| Optional | Tailscale (for remote SSH access) |
The container sees a mix of read-write and read-only bind mounts from the host:
| Host path | Container path | Mode | Purpose |
|---|---|---|---|
| Project directory | Same absolute path | rw | Working directory — agent reads and writes code here |
~/.claude.json |
~/.claude.json |
rw | Claude auth tokens and settings (needs write for token refresh) |
~/.claude/ |
~/.claude/ |
rw | Claude data directory |
~/.codex/config.toml |
~/.codex/config.toml |
rw | Codex configuration |
~/.local/bin/claude |
Same path | ro | Claude native binary (only if native install detected) |
~/.local/share/claude/versions/ |
Same path | ro | Claude native version artifacts |
~/.codex/auth.json |
~/.codex/auth.json |
ro | Codex authentication |
~/.ssh/ |
~/.ssh/ |
ro | SSH keys for git operations |
~/.gitconfig |
~/.gitconfig |
ro | Git identity and aliases |
~/.zshrc, ~/.vimrc, ~/.tmux.conf |
Same paths | ro | Shell, editor, and tmux preferences |
~/code/tools/ |
Same path | ro | Shared tools directory (only if it exists on host) |
Important: The project directory is mounted read-write. Any changes the agent makes to files in that directory are immediately visible on the host and vice versa.
By default, the container shares your host credentials. For stronger isolation, initialize a dedicated sandbox identity:
am sandbox identity initThis creates ~/.sb/ with:
- A dedicated SSH keypair (
~/.sb/ssh/id_ed25519) — add to GitHub/GitLab as a deploy key - Copies of
~/.claude.json,~/.claude/, and~/.codex/config/auth
When ~/.sb/ exists, am mounts sandbox-specific credentials instead of host-global ones. This lets you scope sandbox access (e.g., read-only deploy keys) without affecting your main development environment.
| Resource | Default | Environment variable |
|---|---|---|
| Memory | 4 GB | SB_MEMORY_LIMIT |
| CPUs | 2.0 | SB_CPUS_LIMIT |
| PIDs | 512 | SB_PIDS_LIMIT |
By default, the container runs with:
--cap-drop=ALL— all Linux capabilities dropped, then onlyCHOWN,DAC_OVERRIDE, andFOWNERare added back (required by the entrypoint for user alignment)--security-opt no-new-privileges:true— prevents privilege escalation inside the container- No passwordless sudo —
sudois blocked unless you explicitly setSB_UNSAFE_ROOT=1
Optional hardening flags (set in ~/.agent-manager/sandbox.env):
| Variable | Default | Effect |
|---|---|---|
SB_UNSAFE_ROOT |
0 |
1 = enable passwordless sudo (for apt install, etc.) |
SB_READ_ONLY_ROOTFS |
0 |
1 = mount root filesystem read-only (/tmp, /run remain writable) |
SB_ENABLE_TAILSCALE |
1 |
0 = disable Tailscale networking |
SB_FORWARD_SSH_AGENT |
0 |
1 = forward host SSH agent socket into the container |
ENABLE_SSH |
0 |
1 = start sshd inside the container |
TS_AUTHKEY |
(unset) | Tailscale auth key for automatic VPN join |
When SB_ENABLE_TAILSCALE=1 and TS_AUTHKEY is set, each container joins your Tailscale network with its session name as the hostname. With TS_ENABLE_SSH=1 (default), you can SSH directly into any sandbox:
ssh youruser@am-abc123 # From any device on your tailnetThis is useful for accessing sandbox sessions from a phone, tablet, or another machine.
Run agents in a git worktree to isolate changes from your main working tree:
am new -w ~/project # Auto-named worktree
am new -w my-feature ~/project # Named worktreeClaude sessions are automatically titled from the first user message. A background process extracts the message and sends it to Claude Haiku to generate a short title (e.g., "Fix auth login bug") that appears in the session browser.
Sessions are logged to ~/.agent-manager/history.jsonl. The directory picker annotates paths with recent session history — agent type, task, and age — so you can see what you were working on last.
am config # Show current defaults
am config set agent codex # Default to Codex
am config set yolo true # Default to permissive mode
am config set logs true # Enable pane log streaming
am config get agent # Read a single valuePrecedence: CLI flag > environment variable > saved config > built-in default.
| Agent | Command | --yolo maps to |
|---|---|---|
claude |
claude |
--dangerously-skip-permissions |
codex |
codex |
--yolo |
gemini |
gemini |
--yolo |
Unknown agent types are passed through as the command name, so am new -t aider . will try to run aider.
| Command | Aliases | Description |
|---|---|---|
am |
am list, am ls |
Open interactive session browser |
am new [dir] |
create, n |
Create new agent session |
am send <session> [prompt] |
Send a prompt to a running session | |
am peek <session> |
Snapshot or follow a session's pane output | |
am wait <session> |
Block until agent reaches a target state | |
am events <session> |
Stream state-change events as JSONL | |
am interrupt <session> |
Send Ctrl-C to the agent pane | |
am attach <session> |
a |
Attach to a session |
am kill <session> |
rm, k |
Kill a session |
am kill --all |
Kill all sessions | |
am info <session> |
i |
Show session details |
am status |
s |
Summary of all sessions |
am status --json |
Machine-readable session data | |
am list --json |
All sessions as JSON | |
am config |
Show or change saved defaults | |
am sandbox <cmd> |
sb |
Manage sandbox containers |
am <path> |
Shortcut for am new <path> |
|
am help |
-h |
Show help |
am version |
-v |
Show version |
~/.agent-manager/
├── config.json # Saved defaults (agent, yolo, log streaming)
├── sessions.json # Live session metadata registry
├── history.jsonl # Persistent session history (survives cleanup)
└── tmux.conf # Generated tmux config for am sessions
./tests/test_all.sh # Run the test suite
./tests/perf_test.sh # Standalone latency benchmark for am list-internal
bash -n lib/*.sh am # Syntax checkTests require tmux, fzf, and jq.
tests/perf_test.sh is not part of test_all.sh; it is a manual benchmark and should not create persistent resources.
MIT — see LICENSE.
