Skip to main content

tmux-pilot

A thin, opinionated CLI for managing tmux sessions, task worktrees, and PR metadata for AI coding agents (Claude Code, Codex, etc).

Why? When you run multiple AI coding agents in parallel — each in its own tmux session and often each in its own git worktree — you need a way to bootstrap task branches, list active sessions, peek at output, send follow-up instructions, refresh PR state, and clean up once the branch lands. tmux-pilot wraps the fiddly tmux and git conventions into a single tp command designed for both humans and AI orchestrators.

Install

# pip
pip install tmux-pilot

# pipx (isolated install)
pipx install tmux-pilot

# uv
uv tool install tmux-pilot

Requirements: Python 3.10+, tmux. Optional: fzf (for tp jump picker).

Quick Start

# Start Codex in an existing checkout.
# tmux-pilot runs: codex --profile yolo
tp new auth-flow --profile codex -c ~/repos/myapp

# Start Claude Code in an existing checkout.
# tmux-pilot runs: claude --permission-mode bypassPermissions
tp new review-pass --profile claude -c ~/repos/myapp

# Start Pi in an existing checkout.
# tmux-pilot runs: pi --offline --no-extensions --no-skills --no-prompt-templates --no-themes --session-dir ~/repos/pi-mono/.tmux-pilot/pi/sessions
tp new pi-local --profile pi -c ~/repos/pi-mono

# Bootstrap a task branch + worktree from a local repo, then launch Codex there.
# Default branch: feat/oauth-fix
# Default worktree: ~/worktrees/myapp-oauth-fix
tp new oauth-fix --profile codex --repo ~/repos/myapp -d "Fix OAuth callback handling"

# Bootstrap from GitHub if the repo is not cloned locally yet.
# The repo is cloned to ~/repos/pi-mono first, then a worktree is created.
tp new pi-smoke --profile pi --repo badlogic/pi-mono

# Check on all your sessions
tp ls

# Refresh PR/review metadata, then show a compact dashboard
tp refresh --repo myapp
tp ls --cols NAME,PR,STATUS,DIR

# Inspect the transcript trace bound to a session
tp trace auth-flow

# Turn PR state into the next follow-up prompt
tp prod --dry-run --repo myapp

# Peek at output without attaching
tp peek auth-flow -n 30

# Send a follow-up instruction
tp send auth-flow "now add tests for the auth module"

# Get detailed status
tp status auth-flow

# Done — tear it down
tp kill auth-flow

Docs

The published documentation site is intended to live at https://cmungall.github.io/tmux-pilot/.

Detailed documentation also lives under docs/ and is organized using Diataxis:

Commands

tp ls — List sessions

tp ls                          # table view
tp ls --json                   # JSON output (for AI orchestrators)
tp ls --status active          # filter by @status metadata
tp ls --repo myapp             # filter by repo (substring match)
tp ls --process claude-code    # filter by detected process
tp ls --all-metadata           # append known metadata columns
tp ls --cols NAME,PR,DIR       # compact PR dashboard
tp ls --json --status active   # combine filters with JSON

tp ls --all-metadata also exposes cached trace fields such as TRACE_AGENT and TRACE_PATH.

PR is a compact summary column. It starts with the PR number, then appends short review/merge codes when available:

  • M: merged
  • X: closed
  • A: approved
  • CR: changes requested
  • RR: review required
  • P: pending review state
  • D: dirty/conflicted
  • B: blocked
  • C: clean

Examples: 1548 RR D, 1553 CR, 1547 M.

tp new — Create a session

tp new NAME                    # bare session
tp new NAME -c ~/repos/myapp   # set working directory + @repo
tp new -c ~/repos/myapp        # infer session name from the directory
tp new NAME --here             # use cwd and infer repo/branch/worktree metadata
tp new --here                  # infer the session name from cwd/worktree
tp new NAME --here -j          # create, then auto-jump into the session
tp new NAME -d "description"   # set @desc metadata
tp new NAME -c DIR -d DESC     # both

# Launch a built-in agent profile in-place
tp new NAME --profile codex -c ~/repos/myapp

# Bootstrap a task branch + worktree from a repo, then launch the profile
tp new NAME --profile claude --repo ~/repos/myapp

# `--repo` accepts a local path, GitHub owner/repo, or GitHub URL
tp new NAME --profile pi --repo badlogic/pi-mono
tp new NAME --profile pi --repo https://github.com/badlogic/pi-mono.git

# Override branch/base selection when needed
tp new NAME --profile codex --repo ~/repos/myapp --branch chore/name-cleanup
tp new NAME --profile codex --repo ~/repos/myapp --base-ref origin/release/1.2

Concrete profile examples:

# Launches `codex --profile yolo` in ~/repos/myapp
tp new auth-pass --profile codex -c ~/repos/myapp

# Launches `claude --permission-mode bypassPermissions` in ~/repos/myapp
tp new review-pass --profile claude -c ~/repos/myapp

# Launches `pi --offline --no-extensions --no-skills --no-prompt-templates --no-themes --session-dir ~/repos/pi-mono/.tmux-pilot/pi/sessions`
tp new pi-local --profile pi -c ~/repos/pi-mono

When --repo is used, tp new now handles the full task bootstrap flow:

  • resolves or clones the repo
  • derives a task branch from the session name (or --issue)
  • creates a git worktree under the configured worktree base
  • starts the requested agent inside that worktree

Bootstrap worktrees are named <repo>-<session> by default. If NAME already starts with <repo>-, tp reuses NAME as the worktree leaf directory instead of doubling the repo prefix.

Concrete bootstrap examples:

# Creates branch `feat/oauth-fix`, worktree `~/worktrees/myapp-oauth-fix`,
# then launches `codex --profile yolo` inside that worktree.
tp new oauth-fix --profile codex --repo ~/repos/myapp

# Creates branch `fix/771-issue-771`, fetches the issue title for @desc,
# then launches `claude --permission-mode bypassPermissions`.
tp new issue-771 --profile claude --repo ~/repos/myapp --issue 771

# If ~/repos/pi-mono does not exist yet, clone it first.
# Then create branch `feat/pi-smoke`, worktree `~/worktrees/pi-mono-pi-smoke`,
# and launch Pi with a worktree-local session dir.
tp new pi-smoke --profile pi --repo badlogic/pi-mono

# Pin the branch name or starting point when needed.
tp new cleanup --profile codex --repo ~/repos/myapp --branch chore/cleanup
tp new backport --profile codex --repo ~/repos/myapp --base-ref origin/release/1.2

Built-in launch profiles:

  • codex: codex --profile yolo
  • claude: claude --permission-mode bypassPermissions
  • pi: pi --offline --no-extensions --no-skills --no-prompt-templates --no-themes --session-dir {worktree}/.tmux-pilot/pi/sessions

Recommended profile config lives at ~/.config/tmux-pilot/profiles.toml:

[default]
extends = "codex"
worktree_base = "~/worktrees"
clone_base = "~/repos"

[profiles.pi]
extends = "pi"
branch_prefix = "task"

[profiles.myapp]
extends = "codex"
repo = "~/repos/myapp"
branch_prefix = "feat"
base_ref = "origin/main"

[prod]
[[prod.rules]]
name = "changes-requested"
match = { pr_review = "CHANGES_REQUESTED", pr_state = "OPEN" }
prompt = "Address all requested review comments on {pr_display}. Re-check each thread, update tests, and push the fixes."

[[prod.rules]]
name = "merge-blocked"
match = { pr_state = "OPEN", pr_merge_state = ["BLOCKED", "DIRTY"] }
prompt = "Your PR {pr_display} is not mergeable. Resolve the conflicts or other merge blockers, then push an update."

extends can target another configured profile or one of the built-in profiles above. Config values override the inherited profile, so you can keep reusable agent defaults separate from repo-specific task defaults.

For interactive Codex sessions, codex --profile yolo --no-alt-screen plus tp send --wait is the current best-supported flow. Brand-new repos and worktrees can still stop at a Codex trust prompt before normal readiness begins. tp now verifies the tmux pane cwd before and immediately after agent launch and fails loudly if the shell or agent drifts out of the requested directory.

--here is plain-mode only. It uses your current working directory as the session directory, records inferred git metadata such as repo root, current branch, and whether the checkout is a linked worktree, and can infer the session name from that directory when you omit NAME. If that inferred name already exists, tp new auto-suffixes it as -1, -2, and so on. -j/--jump attaches or switches to the new session immediately after creation.

Concrete config-driven examples:

# Explicit in-place launch using the built-in Codex profile.
tp new rename-types --profile codex -c ~/repos/myapp

# Uses the repo/base branch from `[profiles.myapp]`,
# so `--repo ~/repos/myapp` is not needed here.
tp new api-cleanup --profile myapp

# Uses the customized Pi profile, so the derived branch is `task/pi-smoke`
# instead of the default `feat/pi-smoke`.
tp new pi-smoke --profile pi --repo badlogic/pi-mono

tp peek — View scrollback without attaching

tp peek NAME                   # last 50 lines (default)
tp peek NAME -n 100            # last 100 lines

tp send — Inject text into a session

tp send NAME "any command"     # sends text + Enter
tp send --wait NAME "follow-up instruction"
tp send NAME "claude-code --print 'fix the auth bug'"

tp prod — Send configured follow-up prompts

tp prod                        # all sessions, auto-refresh first
tp prod dragon-assign          # one named session
tp prod --repo dismech         # repo-scoped subset
tp prod --dry-run --repo myapp # preview prompts without sending
tp prod --json                 # machine-readable plan
tp prod --wait dragon-assign   # opt into wait-until-ready before sending

tp prod reads [prod] rules from ~/.config/tmux-pilot/profiles.toml, refreshes PR metadata by default, picks the first matching rule for each targeted session, renders its prompt template, and sends it via plain tp send semantics. Use --no-refresh to rely on cached metadata instead. Pass --wait only when you explicitly want to wait for readiness before sending.

tp jump — Attach or switch to a session

tp jump NAME                   # attach (or switch if inside tmux)
tp jump                        # fzf picker (requires fzf)

tp status — Detailed session info

Shows process, PID, working directory, all metadata, relative freshness for cached metadata, and the last 5 lines of scrollback.

tp status NAME

PR-related metadata is shown with refresh ages when available, for example:

@pr = 1548 (updated 2m ago)
@pr_review = REVIEW_REQUIRED (updated 2m ago)
@pr_merge_state = DIRTY (updated 2m ago)
@last_refresh = 2026-04-19T22:39:42.658Z

When a transcript has been resolved, tp status also shows the cached @trace_agent and @trace_path metadata for that session.

tp trace — Inspect the bound transcript trace

Use this when a tmux pane cwd is not the whole story and you want the actual transcript binding that tp will use for agent state.

tp trace auth-flow
tp trace auth-flow --refresh
tp trace auth-flow --json
tp trace auth-flow --show raw --lines 10
tp trace auth-flow --show json
tp trace auth-flow --show yaml
tp trace auth-flow --show tsv
tp trace auth-flow --show formatted
tp trace auth-flow --show yaml --color always

tp trace prefers cached session metadata (@trace_agent, @trace_path) and falls back to a cwd-based scan when needed. This makes it practical for one tp session to stay associated with one chat/session trace even after later checks no longer rely purely on pane_current_path.

Use --json for machine-readable trace metadata. Use --show raw|json|yaml|tsv|formatted when you want the transcript content itself, whether the underlying file is a Codex, Claude Code, or Pi JSONL trace. tsv emits normalized rows for scripts, while formatted renders a readable timeline. --color auto|always|never controls ANSI coloring for the human-readable render modes.

tp refresh — Refresh PR metadata without reaping

Use this when you want a review dashboard or fresh PR metadata without any destructive cleanup.

tp refresh                      # all sessions
tp refresh docs-pass            # one named session
tp refresh --repo myapp         # repo-scoped subset
tp refresh --json               # machine-readable output

tp refresh updates @pr, @pr_state, @pr_review, @pr_merge_state, and @last_refresh in tmux metadata. It does not kill sessions, remove worktrees, or delete branches.

tp prod builds directly on those cached fields, so the common orchestration loop is:

tp refresh --repo myapp
tp ls --cols NAME,PR,STATUS,DIR --repo myapp
tp prod --dry-run --repo myapp
tp prod --repo myapp

tp set / tp get — Session metadata

Metadata is stored as tmux user options (@-prefixed). Common built-in keys include repo, task, desc, status, origin, branch, needs, last_send, pr, pr_state, pr_review, pr_merge_state, last_refresh, trace_agent, and trace_path.

tp set NAME status "waiting-for-review"
tp set NAME branch "feat/auth"
tp get NAME status

For AI Orchestrators

tmux-pilot is designed to be called by AI coding agents and orchestration scripts, not just humans. The --json flag on tp ls outputs machine-readable JSON:

$ tp ls --json
[
  {
    "name": "auth-flow",
    "process": "claude-code",
    "working_dir": "/home/user/repos/myapp",
    "metadata": {
      "desc": "Implement OAuth2 login",
      "status": "active",
      "repo": "/home/user/repos/myapp"
    }
  }
]

A typical orchestrator loop:

# Refresh review state for a repo
tp refresh --repo myapp

# See which branches need attention
tp ls --cols NAME,PR,STATUS,DIR --repo myapp

# Drill into the sessions that need work
tp ls --json --repo myapp | jq -r '.[] | select(.metadata.pr_review == "CHANGES_REQUESTED") | .name' | while read name; do
  tp peek "$name" -n 20
  tp send --wait "$name" "address the requested review changes"
done

Key Features

  • Zero dependencies — stdlib only (subprocess calls to tmux)
  • Process detection — distinguishes claude-code vs codex vs bare shell
  • Task bootstrap — create task branches and worktrees directly from tp new --repo
  • PR refresh — cache PR number, state, review state, and merge state with tp refresh
  • Trace binding — cache the transcript trace for a session with tp trace
  • Metadata — tmux user options (@status, @desc, @repo, @branch, @pr, etc.)
  • Metadata freshness — tp status shows when cached fields were last updated
  • Peek without attaching — critical for orchestrators monitoring sessions
  • JSON output — tp ls --json for machine-readable session data
  • Filtering — tp ls --status/--repo/--process and tp refresh --repo to narrow results
  • fzf integration — optional fuzzy picker for tp jump

License

MIT

Release files for tmux-pilot 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tmux-pilot 0.1.3
File Size Uploaded
tmux_pilot-0.1.3.tar.gz 153.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tmux-pilot 0.1.3
File Interpreter ABI Platform
tmux_pilot-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 210.2 kB

Release files / tmux_pilot-0.1.3.tar.gz

Download URL tmux_pilot-0.1.3.tar.gz
Size 153.9 kB
Tags Source
SHA-256 checksum
How to use checksums
0c29210142b4130dbd8abf104da4a1e9ef5b9d3548322c97e4b8331bf54cc80f
BLAKE2b-256 checksum
How to use checksums
e4aaf44277b0f0b0295a37280f06c3fa95ddded0256727b104af5af7a0a91a03
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 16, 2026.

Transparency log

Release files / tmux_pilot-0.1.3-py3-none-any.whl

Download URL tmux_pilot-0.1.3-py3-none-any.whl
Size 56.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d9a5b3e02cf9e54ee1b4b552fb7ea45184e7d6dbccf00d495964a83047ee1bb0
BLAKE2b-256 checksum
How to use checksums
c106d6e8e63958c4b48424a4fc90c542f0c7cd69c439fcb04f66a925394b3b66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page