Skip to main content

A thin, opinionated CLI for managing tmux sessions that run AI coding agents

Project description

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 freshnesstp status shows when cached fields were last updated
  • Peek without attaching — critical for orchestrators monitoring sessions
  • JSON outputtp ls --json for machine-readable session data
  • Filteringtp ls --status/--repo/--process and tp refresh --repo to narrow results
  • fzf integration — optional fuzzy picker for tp jump

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tmux_pilot-0.1.1.tar.gz (142.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tmux_pilot-0.1.1-py3-none-any.whl (47.8 kB view details)

Uploaded Python 3

File details

Details for the file tmux_pilot-0.1.1.tar.gz.

File metadata

  • Download URL: tmux_pilot-0.1.1.tar.gz
  • Upload date:
  • Size: 142.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tmux_pilot-0.1.1.tar.gz
Algorithm Hash digest
SHA256 2413008cfa7719f2b4622ce3401fac97e564ae5e725a8a669b9c912fb664a893
MD5 662995e04095b628437151bc24598bf5
BLAKE2b-256 920b4abf66efb6247c3444a464ca0d51861990167f82865735ccfbfaa381c737

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmux_pilot-0.1.1.tar.gz:

Publisher: publish.yml on cmungall/tmux-pilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tmux_pilot-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: tmux_pilot-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 47.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tmux_pilot-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 99277ccf87369da36ded89eb7ae03d5318061e81b8966f87db1f22d54eb1a89f
MD5 f920c9c1884387c38ad0c381a99d5185
BLAKE2b-256 5192dc9c2f5ee854a83db634a1b24d367af226f5d32c670749a3f15ccf9367f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmux_pilot-0.1.1-py3-none-any.whl:

Publisher: publish.yml on cmungall/tmux-pilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page