Pronounced "Pix".
Fair warning: This entire project was vibe coded. The code works, but if you look under the hood expecting clean architecture and best practices, you're going to have a bad time. You've been warned.
A CLI tool that orchestrates multiple Claude Code agents using tmux, SQLite, and a task tracker. A coordinator agent dispatches work to worker agents, each running in its own tmux session. A release agent handles merging branches to main, and a daemon monitors the system.
Website: getpx.dev
px requires the following to be installed:
- tmux — terminal multiplexer used to run each agent in its own session
- Claude Code — Anthropic's CLI for Claude (
npm install -g @anthropic-ai/claude-code)
Download the latest binary from GitHub Releases.
Linux (x64):
curl -fsSL -o px https://github.com/Phoenixmatrix/px/releases/latest/download/px-linux-x64
chmod +x px
mkdir -p ~/.local/bin && mv px ~/.local/bin/Linux (arm64):
curl -fsSL -o px https://github.com/Phoenixmatrix/px/releases/latest/download/px-linux-arm64
chmod +x px
mkdir -p ~/.local/bin && mv px ~/.local/bin/macOS (Apple Silicon):
curl -fsSL -o px https://github.com/Phoenixmatrix/px/releases/latest/download/px-darwin-arm64
chmod +x px
mkdir -p ~/.local/bin && mv px ~/.local/bin/Make sure ~/.local/bin is on your PATH (export PATH="$HOME/.local/bin:$PATH" in your shell rc, or fish_add_path ~/.local/bin for fish).
Requires Bun.
bun installIn development, the bin/px wrapper runs everything through bun directly. Add the bin/ directory to your PATH or use bun run px.
bun run typecheck # Type-check all packages with tsgo
bun run lint # Lint all packages with oxlint
bun run check # Both typecheck and lintpx <command> [arguments]
| Command | Description |
|---|---|
start |
Start the coordinator in a tmux session |
tracker |
Task and worker tracker |
workers |
Worker manager |
daemon |
Status dashboard |
spawn-worker |
Spawn a new worker agent |
spawn-release |
Spawn the release agent |
get-agent-name |
Generate a unique agent name |
hooks |
Run a lifecycle hook (used by settings.json) |
personas |
Get the path to a persona directory |
Run px <command> --help for detailed usage of any command.
Manages the task board. Tasks have an auto-generated ID (e.g. T-1, R-3), a status (ready, assigned, in-progress, done), and an optional blocked_by dependency.
px tracker tasks # List active tasks
px tracker tasks json # Output active tasks as JSON
px tracker tasks json me # Output my assigned tasks as JSON
px tracker tasks create T "description" # Create task, prints ID (e.g. T-1)
px tracker tasks create T "step 2" --blocked-by T-1 # Create with dependency
px tracker tasks create T "in portfolio" --portfolio feature-branch # Create with portfolio
px tracker tasks assign T-1 worker-name # Assign to a worker
px tracker tasks start T-1 # Mark in-progress
px tracker tasks done T-1 # Mark complete
px tracker status # Show all tasks and workers
px tracker portfolio open feature-branch # Set session portfolio
px tracker portfolio # Show current portfolio
px tracker portfolio close # Clear session portfolioManages the worker registry. Workers are tracked in SQLite with their tmux session target.
px workers # List all workers (table view)
px workers json # List as JSON
px workers list # List worker-type agents (for coordinator use)A dashboard that polls the database and displays worker status. Automatically nudges idle workers after 20 seconds.
px daemon # Start in a tmux session (auto-managed)px startIf you're already in a tmux pane, the coordinator runs right there. If not, px creates a new tmux session for it. Either way, the release agent and daemon are spawned as separate tmux sessions, and Claude Code starts with the coordinator persona.
The coordinator is your interface. Tell it what you want done — but be explicit about how to break work into tasks, which tasks to assign to workers, and what can be parallelized. Think of it like delegating to a team lead who needs clear direction on how to split the work.
"We need to add authentication. Create three tasks: one for the login page UI, one for the auth API endpoints, and one for the session middleware. Spawn two workers — have one handle the login page and the other handle the API endpoints and session middleware in sequence."
The coordinator will:
- Create tasks with
px tracker tasks create T "description" - Generate unique worker names with
px get-agent-name - Spawn workers with
px spawn-worker <name> "initial prompt" - Assign tasks to workers with
px tracker tasks assign T-1 <name>
If you just say "add auth" without specifying how to split the work, the coordinator may handle it all in a single task or make its own choices about parallelization. Being explicit about task breakdown and worker assignment gives you better results.
Each worker runs in its own tmux session. Workers follow a loop:
- Check for assigned tasks (
px tracker tasks me) - Start the task (
px tracker tasks start T-1) - Do the work — edit code, run tests, commit
- Run quality gates (typecheck, lint) and fix any issues
- Push the branch to remote
- Create a release task, passing through the portfolio if the task has one (
px tracker tasks create R "Merge branch feature-x" --portfolio <name>) - Assign it to
release - Mark their task done (
px tracker tasks done T-1) - Loop back to check for more tasks
Workers never merge branches themselves. They hand off to the release agent.
The release agent handles merging worker branches. Its behavior depends on whether the task has a portfolio.
With a portfolio: The release agent creates or updates a branch named after the portfolio, merges the worker's feature branch into it, runs quality checks, and pushes the portfolio branch. This lets you collect multiple related changes on one branch before merging to main.
Without a portfolio: The release agent merges the feature branch directly into main, runs quality checks, and pushes.
A portfolio groups related tasks onto a named branch. The portfolio name must be a valid git branch name.
Opening a portfolio sets it for the current agent session. All tasks created in that session automatically get the portfolio:
px tracker portfolio open feature/auth
px tracker tasks create T "login page" # gets portfolio "feature/auth"
px tracker tasks create T "auth endpoints" # gets portfolio "feature/auth"Closing a portfolio clears the session, so subsequent tasks go to main:
px tracker portfolio close
px tracker tasks create T "quick fix" # no portfolio, merges to mainYou can also set a portfolio per-task with the --portfolio flag, which overrides the session portfolio:
px tracker tasks create T "isolated change" --portfolio hotfix/urgentPortfolios are scoped per agent — the coordinator opening a portfolio doesn't affect workers. Workers read the portfolio from their assigned task's JSON and pass it through when creating release tasks.
The daemon polls the database every 2 seconds. It displays all workers and their status. If a worker has been idle for more than 20 seconds, the daemon sends a nudge to wake it up and check for tasks.
You can run px start in multiple repos simultaneously. Each repo gets a short prefix derived from its name (e.g., px for a repo named "px", mca for "my-cool-app", fbb for "foo-bar-baz"). All tmux sessions for that repo use the prefix — the daemon becomes px-daemon, workers become px-alice, and so on.
Each repo has its own px.db database, so tasks, workers, and portfolios are fully isolated. If two repos happen to produce the same prefix, the second one gets a numeric suffix (e.g., fbb0).
Each agent runs in a separate tmux session. Use tmux list-sessions to see all of them — sessions are grouped by their repo prefix.
tmux list-sessions # See all sessions across repos
tmux attach -t px-daemon # Attach to the daemon for the "px" repo
tmux attach -t mca-release # Attach to the release agent for the "my-cool-app" repoWhen attached to a tmux session:
| Key | Action |
|---|---|
Ctrl-b d |
Detach from session |
Ctrl-b s |
List and switch between sessions |
Ctrl-b w |
List and switch between windows |
px stores its SQLite database (px.db) at the root of your git repository. If you use git worktrees, all worktrees share the same database. The file is automatically added to .gitignore on first run if not already ignored.
