# Zinc Is Not Cowork
> Agent multiplexer for the terminal
[](https://github.com/ComeBertrand/zinc/actions/workflows/ci.yml)
[](https://crates.io/crates/zinc-cli)
[](LICENSE)
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.
## Features
- **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](https://github.com/ComeBertrand/yawn))
## Quick start
```bash
cargo install zinc-cli zinc-daemon
```
```bash
# 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
zinc
```
## TUI
Running `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`/`k` or arrows to navigate
- `enter` to attach to the selected agent
- `n` to spawn a new agent
- `d` to kill the selected agent
- `q` to quit (agents keep running)
When attached, a status bar shows the agent info. `ctrl-]` detaches back to the list.
## CLI commands
```bash
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 running
```
### `zinc spawn`
```bash
zinc 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 directory
```
**Arguments:**
- `<prompt>` - initial prompt text (positional, optional)
**Flags:**
- `--agent <name>` - provider to use (default: from config, or `claude`)
- `--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`
```bash
zinc attach fix-auth # attach by ID
zinc attach # attach to the agent in current directory
```
When no ID is given, zinc finds the agent running in your current directory. Errors clearly if there are zero or multiple agents.
## State detection
zinc uses layered detection to track what each agent is doing:
| `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.
## Configuration
`~/.config/zinc/config.toml` - all fields optional:
```toml
[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)
```
### Notifications
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.
```toml
# Linux (libnotify)
command = "notify-send 'zinc: {id}' '{state}'"
# macOS
command = "osascript -e 'display notification \"{state}\" with title \"zinc: {id}\"'"
```
### Namer
The `spawn.namer` field runs a command to derive the agent ID from the directory. `{dir}` is replaced with the shell-quoted path:
```toml
namer = "basename {dir}" # use directory name as-is
namer = "yawn prettify {dir}" # use yawn for clean names
```
Fallback chain: `--id` flag > namer > directory basename.
## Composing with yawn
zinc and [yawn](https://github.com/ComeBertrand/yawn) are independent tools that compose through directories:
```bash
# 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 attach
```
Shell function for the common case:
```bash
yagent() {
yawn create "$1" --init
zinc spawn --new --dir "$(yawn resolve "$1")" "${@:2}"
}
yagent fix-auth "fix the auth bug"
```
## Architecture
zinc uses a daemon-client architecture (like tmux):
- **`zincd`** - long-running daemon that owns agent PTYs, tracks state, broadcasts events
- **`zinc`** - 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.
## Install
### From source
```bash
cargo install zinc-cli zinc-daemon
```
### GitHub Releases
Download binaries from the [releases page](https://github.com/ComeBertrand/zinc/releases).
### Nix flake
```nix
inputs.zinc.url = "github:ComeBertrand/zinc";
# then in your packages:
inputs.zinc.packages.${system}.default
```
## Shell completion & man page
Shell completions are generated at build time:
```bash
# 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.fish
```
A man page is generated at build time:
```bash
man target/*/build/zinc-cli-*/out/man/zinc.1
```
## License
MIT