↑ subaud · local-first · MIT  

proj · the agent-aware session picker

The way in to a work session.

proj is a Bubble Tea picker for the sessions you actually work in. It doesn't just list tmux sessions — it reads what's happening in each: which agent is live, whether it's running or idle, and whether muster is holding mail for it. The highlighted row previews on the right, so you choose with context instead of guessing from a name.

what it shows

Read the room before you attach.

Open the picker with a bare proj. Every live tmux session across all per-project servers is listed as <project>/<work>, and each row carries the context you'd otherwise have to attach to discover.

the live agent

Which agent is attached — pi, claude, cursor, or blank — and its state (claude·run, codex·idle), resolved live from the pane, not a stale cache.

muster attention

A session waiting on muster mail glows with a flag, so the picker doubles as a glance at which work sessions need you.

preview pane

The highlighted row previews to the right — working directory, windows, the agent's current activity — so you attach to the right session the first time.

per-project servers

proj runs one tmux server per project (the proj-<name> socket convention muster uses), so a crash in one project never takes down another. Scope the picker to a project or span them all.

configurable sidebar

Beside the agent pane, a sidebar of your choosing — scratch, a file explorer, a shell — built on new work and arranged the way your day runs.

agent-safe commands

Non-interactive verbs an agent can run without hijacking your terminal: proj list --json, proj current --json, and proj new <project>/<work> --agent <name> to spawn a detached session.

agent + model

Choose the agent and model when you start work.

Selecting + new work… in a project opens an inline prompt to name the session. While you type, Tab cycles the agent that will launch into it, and Shift-Tab opens a filterable list of that agent's models — the footer shows both. Change the agent and the model re-seeds to that agent's default.

Per-agent model menus

Model menus are scoped to the agent, because agents don't share models. Claude Code only accepts Anthropic models, so its menu is a built-in list. pi can reach many providers, so its menu is discovered from pi --list-models, filtered to the providers you name in config, and offered as provider/id — launched as pi --model <provider/id>. Agents that take no model (cursor, none) simply don't show the list.

config

Two files, both optional.

proj reads a roots file (which directories are projects) and an optional config.toml (defaults, the sidebar, model menus, per-project overrides). Unset keys fall back to built-in defaults.

~/.config/proj/roots

One project root per line; blank lines and # comments ignored.

# work
~/GitHub/myapp
~/GitHub/infra

# personal
~/GitHub/dotfiles

~/.config/proj/config.toml

default_agent = "pi"          # what new work launches; "none" for a plain shell
sidebar       = true          # auto-build the sidebar column on new work

[sidebar_layout]
panes = ["scratch", "yazi", "shell"]   # top → bottom
sizes = { scratch = 12, shell = 10 }   # rows; the middle pane fills

[model_providers]             # pi's model menu = these providers' models
pi = ["claude-bridge", "openai-codex"]

[project."bettor-help"]       # optional per-project overrides
default_agent = "claude"
default_model = "claude-sonnet-4-6"    # pin this project's model
sidebar       = false

pi's list is discovered from pi --list-models and cached ~24h; it refreshes on its own, or when you run pi update.

keys

Getting around.

KeyIn the list
↑ ↓move the cursor
attach a session · drill into a project · start new work
type / filter the list
Tabtoggle folders ↔ live sessions
^xreap the highlighted session (confirm with a second ^x)
esc / qback out · quit
KeyNaming new work
Tabcycle the agent
Shift-Tabopen the model list (type to filter, pick, esc keep)
^stoggle the sidebar for this session
/ esccreate · cancel

shell

A thin shim, and auto-join.

proj needs a small zsh shim so the shell can eval the one thing the binary can't do inside its own process: attach a bare shell to a session on another server. The shim ships in the repo at shell/proj.zsh — source it from your rc.

$ source /path/to/tackle/shell/proj.zsh

The shim also installs auto-join: fired once when the shell starts, if you're interactive, not already inside tmux, and your $PWD is inside a known project root, it opens that project's picker automatically — so a new terminal in a project lands straight on proj. Opt out with NO_AUTO_TMUX=1 (per shell) or ~/.no-auto-tmux (global); set AUTO_CLAUDE=1 to auto-join with claude preselected.

install

Install proj, run it.

One command downloads proj, verifies its checksum, and puts it on your PATH (~/.local/bin). macOS and Linux, Apple silicon and x86-64.

$ curl -fsSL https://tackle.tools/install.sh | sh -s proj

# then
$ proj

Run it again to upgrade; check your version with proj version. Building from source instead? go install github.com/schuettc/tackle/cmd/proj@latest.