Agent multiplexer for the terminal
Manage AI coding agents as persistent background daemons. Attach, detach, and switch between agent sessions instantly. Think tmux, but purpose-built for AI coding agents.
- Never lose a session - agents run as background daemons, survive terminal close
- TUI supervisor - full-screen view of all agents, keyboard-driven
- Instant switching - one keystroke to attach/detach from any agent
- State tracking - see at a glance which agents are working, waiting for input, or blocked
- Session picker - resume previous sessions or start fresh, with automatic session discovery
- Composable - works with any worktree/project workflow (designed to pair with yawn)
cargo install zinc-cli zinc-daemon# Spawn a Claude agent in your project
cd ~/projects/myapp
zinc spawn
# Or with a prompt
zinc spawn "fix the failing auth tests"
# Attach (resolves from current directory)
zinc attach
# Open the TUI to see all agents
zincRunning zinc with no arguments opens the interactive supervisor:
zinc -3 agents (1 needs input)
STATE AGENT ID DIRECTORY UPTIME
● work claude fix-auth ~/worktrees/myapp--fix-auth 12m
▸ ▲ input claude fix-nav ~/worktrees/myapp--fix-nav 8m
● work claude fix-api ~/worktrees/myapp--fix-api 3m
enter:attach n:new d:kill q:quit
j/kor arrows to navigateenterto attach to the selected agentnto spawn a new agentdto kill the selected agentqto quit (agents keep running)
When attached, a status bar shows the agent info. ctrl-] detaches back to the list.
zinc # open TUI
zinc spawn [prompt] [options] # launch a new agent
zinc attach [id] # attach to an agent (resolves from CWD if omitted)
zinc list [--json] # list all agents and their states
zinc kill <id> # stop an agent
zinc shutdown # stop all agents and the daemon
zinc status # check if the daemon is runningzinc spawn # shows session picker if sessions exist
zinc spawn "fix the bug" # start with a prompt
zinc spawn --new # skip session picker, always start fresh
zinc spawn --new "fix the bug" # new session with a prompt
zinc spawn --agent codex --dir ~/project # explicit agent and directoryArguments:
<prompt>- initial prompt text (positional, optional)
Flags:
--agent <name>- provider to use (default: from config, orclaude)--dir <path>- working directory (default: current directory)--id <name>- agent ID (default: derived from directory name)--new- skip session picker, always start a new session
zinc attach fix-auth # attach by ID
zinc attach # attach to the agent in current directoryWhen no ID is given, zinc finds the agent running in your current directory. Errors clearly if there are zero or multiple agents.
zinc uses layered detection to track what each agent is doing:
| State | Meaning |
|---|---|
working |
Actively producing output |
blocked |
Needs user action (e.g. permission prompt) |
input |
Waiting for new user prompt |
idle |
Running but inactive |
For Claude Code, state is detected via hooks (immediate, distinguishes input from blocked). For other agents, PTY activity heuristics are used as a fallback.
~/.config/zinc/config.toml - all fields optional:
[spawn]
default_agent = "claude" # default provider (alias: agent)
namer = "yawn prettify {dir}" # derive agent ID from directory
[daemon]
scrollback = 1048576 # scrollback buffer size in bytes (1MB)
[notify]
command = "notify-send 'zinc: {id}' '{state}'" # command to run on state change
on_states = ["input", "blocked"] # which states trigger (default)The notify.command field runs a command when an agent transitions to a matching state. Placeholders {id}, {state}, {old_state} are shell-quoted and substituted.
# Linux (libnotify)
command = "notify-send 'zinc: {id}' '{state}'"
# macOS
command = "osascript -e 'display notification \"{state}\" with title \"zinc: {id}\"'"The spawn.namer field runs a command to derive the agent ID from the directory. {dir} is replaced with the shell-quoted path:
namer = "basename {dir}" # use directory name as-is
namer = "yawn prettify {dir}" # use yawn for clean namesFallback chain: --id flag > namer > directory basename.
zinc and yawn are independent tools that compose through directories:
# Create worktree + spawn agent
yawn create fix-auth --init
cd "$(yawn resolve fix-auth)"
zinc spawn "fix the auth bug"
# Later: attach from the worktree
cd "$(yawn resolve fix-auth)"
zinc attachShell function for the common case:
yagent() {
yawn create "$1" --init
zinc spawn --new --dir "$(yawn resolve "$1")" "${@:2}"
}
yagent fix-auth "fix the auth bug"zinc uses a daemon-client architecture (like tmux):
zincd- long-running daemon that owns agent PTYs, tracks state, broadcasts eventszinc- short-lived client that connects to the daemon via Unix socket
The daemon starts automatically on first zinc command and keeps agents alive independently of any terminal.
cargo install zinc-cli zinc-daemonDownload binaries from the releases page.
inputs.zinc.url = "github:ComeBertrand/zinc";
# then in your packages:
inputs.zinc.packages.${system}.defaultShell completions are generated at build time:
# Bash
cp completions/zinc.bash ~/.local/share/bash-completion/completions/zinc
# Zsh (or place it anywhere in your $fpath)
cp completions/_zinc ~/.local/share/zsh/site-functions/_zinc
# Fish
cp completions/zinc.fish ~/.config/fish/completions/zinc.fishA man page is generated at build time:
man target/*/build/zinc-cli-*/out/man/zinc.1MIT