Skip to main content

Terminal UI to navigate, resume, and start Claude Code and Codex sessions

Project description

Railmux — session manager for Claude Code & Codex

Tests PyPI Python License

A terminal UI to navigate, resume, and start Claude Code and Codex sessions across all your projects. Railmux lives in the left pane of a tmux window; the right pane shows the active agent. Each session runs in its own detached tmux session, so switching never interrupts in-progress responses or tool calls.

  • Claude Code mode — reads ~/.claude/projects/*, lists sessions by project, resume with claude --resume
  • Codex mode — reads ~/.codex/sessions/*, same sidebar workflow for Codex sessions
  • Press m to cycle through the available modes

Demo

Demo GIF coming soon. This slot is reserved for the interactive Railmux walkthrough and can be replaced without restructuring the README.

Why Railmux?

Without Railmux, managing multiple agent sessions means manually tracking tmux windows, remembering which session lives where, and copy-pasting session IDs. Sessions pile up across projects, context gets lost, and switching between them is friction.

Railmux replaces all of that with a single keystroke:

  • One sidebar, all sessions — browse every Claude Code and Codex session across every project, filter by name, star favourites
  • Instant switching — press Enter and the right pane attaches to a different background tmux session; every agent keeps running, no responses lost
  • Zero manual bookkeeping — no more tmux ls | grep cc- or hunting through ~/.claude/projects/

Quick start

pip install railmux
# or: pip3 install railmux
railmux

Requires Python 3.9+, tmux, less, and at least one supported agent CLI on PATH. Claude Code and Codex are independent: a missing provider does not stop you from using the other one.

If tmux is missing, an interactive Railmux launch can offer to install it with Homebrew on macOS or apt-get on Debian/Ubuntu/WSL. Railmux shows the exact command and requires explicit confirmation (default: no); it never installs Homebrew itself or modifies the system during non-interactive runs. Other common Linux package managers receive a copyable installation command.

If you are not already inside tmux, Railmux launches its own tmux session. Run railmux --doctor for a privacy-safe dependency and environment report when setup does not behave as expected.

Keys

Navigation

Key Action
/ Move selection within the focused pane
Tab / Shift-Tab Cycle focus through Projects, Sessions, Running panes
Esc Move focus up: Running → Sessions → Projects
/ Filter the focused Projects, Sessions, or Running pane by name

Session actions

Key Action
Enter Resume or start the selected session
n Start a fresh session in the current project
i Popup with session details
r Rename the focused session
s Toggle star — starred sessions pinned to top with ⭐
k Kill the running agent process (keeps session file)
d Delete the focused session (prompts for confirmation)
t Open a terminal in the active project directory
m Cycle through available agent modes
F9 Fullscreen the agent pane (toggle) for clean text selection
? Full help popup with all keybindings
q or Ctrl-C Quit with confirmation

+ New project works in both Claude Code and Codex modes. Browse to an existing directory and choose . (use this path), or type a new relative, absolute, or ~-based path. When no existing entry matches, select the explicit + create … row (it is focused automatically) and press Enter; railmux creates the directory before starting the agent.

When a mode has no projects or sessions, its empty state names the active provider and points to + New project or n, so an unavailable provider never looks like data from the previous mode.

Running-pane filtering is in-memory and never reads transcript bodies. Plain text matches the visible session label, project, and provider; add project:<name> to restrict a search to one project. Claude Code and Codex keep independent Running filters. Blocked sessions move ahead of other Running rows on the existing throttled recency sort, while their red status dots still update immediately.

Mouse

Action Effect
Left-click (non-running) Preview session history in the right pane
Left-click (running) Attach to the running session (focus stays left)
Double-click Open/attach and move focus to the right pane
Right-click Context menu (Open, Info, Rename, Star, Kill, Term, Delete)

History preview

Left-click a non-running session to view its conversation history in the right pane without starting or resuming the agent. The preview reads the saved JSONL directly, so it cannot send a message or mutate the session. It follows the providers' conversation structure as closely as their saved data allows: user/assistant messages, tool calls and abbreviated tool output are colour-coded, while internal system context and encrypted reasoning are hidden. Plaintext Codex reasoning summaries are shown when present.

The viewer uses less, the standard pager available on both Linux and macOS, because neither provider currently exposes a native read-only history view. It opens at the latest activity and streams at most the latest 2,000 JSONL records for fast previews of large sessions. Press / to search, n/N to move between matches, and q to exit; the right pane restores whatever was there before. Shell/editor commands and pager history are disabled in preview mode. Double-click to skip the preview and open the session directly.

Clicking a running session attaches to it immediately (focus stays left so you can keep browsing). Double-clicking steals focus to the right pane for both running and non-running sessions.

Status indicators

Each running session shows a coloured ● reflecting its current state:

  • Green — idle (assistant last responded normally)
  • Yellow — busy (assistant is processing)
  • Red — blocked (waiting for tool approval)

An independent magenta ! marks the last provider outcome that still needs attention, such as a generic abort or provider error. It does not replace the activity dot: a live session can be idle and still show !, while a stopped historical session keeps its neutral liveness marker alongside the badge. Session Info and Running Info show the sanitized category and summary.

A grass-green title identifies a live tmux session independently of its status; stopped sessions use a neutral hollow ○. The same grass green is used for the focused pane chrome and tmux status bar. The current cursor uses a deeper green background, while the session displayed in the agent pane remains marked in neutral slate after keyboard focus moves away.

For Codex rollouts with lifecycle events, only task_complete, turn_aborted, or thread_rolled_back ends an active turn; intermediate assistant messages and tool results remain busy. Older rollouts without lifecycle records fall back to their last user/assistant message. Because Codex has no reliable approval-wait signal, a pending tool must remain unchanged for two minutes before it turns red, reducing false alerts from normal long-running commands.

Codex error details are read only from dedicated lifecycle/error records, never from user or assistant transcript text. Current observed rollouts do not persist a reliable capacity/rate-limit reason, so Railmux labels those records generically instead of guessing from message content.

How it works

railmux reads agent session files from ~/.claude/projects/* (Claude Code) or ~/.codex/sessions/* (Codex) and lists everything. Pressing Enter on a session does two things: (1) if a detached tmux session for that session doesn't already exist, railmux creates one with tmux new-session -d; (2) railmux's right pane displays it so you see and interact with the agent. By default Railmux transactionally swaps the real agent pane into the display window, while its detached home session stays alive behind a placeholder. Unsupported or unverified environments automatically use the compatibility nested-tmux display instead.

Soft quit keeps detached agents alive. On restart, process-bearing recovery state is isolated to the exact outer tmux pane, so separate Railmux windows or private tmux servers cannot restore one another's display. Mode, project, and sidebar filters are also saved as non-process view preferences for use on a later login; those portable preferences never authorize an attach or kill. If Railmux stops while a new provider is still creating its session UUID, the Running pane restores it as an explicit unresolved entry. It can be reopened or stopped by exact tmux identity, but Railmux will not guess at or delete an unknown provider history file.

Configuration

Optional config at ~/.config/railmux/config.toml:

[claude]
# Path to the claude binary (default: "claude")
binary = "claude"

[codex]
# Path to the codex binary (default: "codex")
binary = "codex"
home = "~/.codex"

[projects]
# Show projects with no resumable sessions (default: false)
show_empty_projects = false

[live]
# How often to refresh the session list (ms)
poll_interval_ms = 1000

# Prefer the real agent pane through validated transactional tmux swaps.
# Default: "swap". Set "nested" to force the compatibility display.
agent_transport = "swap" # or "nested"

The swap transport requires tmux 2.7 or newer and the auto-launched railmux tmux session. Railmux automatically falls back to nested for old tmux, unmanaged sessions, independent clients, unsupported agent topology, or any unverified transition. See docs/DENESTED_AGENT_PANE.md for tested lifecycle behavior, fallbacks, performance observations, and limitations.

Diagnostics

railmux --doctor

The doctor command works even when tmux is missing. It reports component versions, terminal capability hints, configuration health, and whether provider data directories are accessible. Its output is designed for issue reports: it does not include hostnames, usernames, session IDs, transcripts, credentials, environment values, configured commands, or raw custom paths.

FAQ

1. How do I copy text from the agent pane?

Under tmux the sidebar and agent share the screen, and over SSH your clipboard lives on the local machine.

OSC 52 (iTerm2, kitty, WezTerm, Alacritty, foot, Windows Terminal): drag-select in the agent pane copies to the local clipboard automatically, even over SSH, no Shift needed. (iTerm2: enable Settings → General → Selection → "Applications in terminal may access clipboard".)

Without OSC 52 (Terminal.app, etc.): press F9 to fullscreen the agent → Shift‑drag to select → Cmd+C / Ctrl+C to copy → F9 to return.

Ctrl-B z also toggles fullscreen (built into tmux) but zooms whichever pane has focus — it may fullscreen the sidebar instead of the agent.

2. Mouse clicks or F9 don't work — what's wrong?

These are usually terminal‑side settings, not tmux or railmux.

Mouse: your terminal must forward mouse events to tmux. Check your terminal's profile settings for "Report mouse events" or "Mouse reporting" and make sure it's enabled. tmux's set -g mouse on should already be in place (railmux sets it for its own session).

F9 (fullscreen): on macOS, F9 is often captured by Mission Control. Fix: System Settings → Keyboard → "Use F1, F2, etc. keys as standard function keys". On Windows laptops the Fn‑lock key (Fn+Esc) toggles function‑key behaviour. If your terminal has a "Pass function keys to terminal" option, enable it.

3. Using railmux over SSH

railmux works over SSH out of the box — the scroll‑agent integration is SSH‑transparent, so mouse scrolling in the agent pane works the same as locally. These tweaks improve responsiveness and scrollback:

Server (~/.tmux.conf on the remote machine):

set -sg escape-time 0         # eliminate delay after Escape key
set -g  history-limit 10000   # generous scrollback per pane

Client (~/.ssh/config on your local machine):

Host your-server
    Compression yes           # smoother tmux pane scrolling over SSH

If the connection is so slow that the sidebar can't refresh one frame per second, skip the mouse and use keyboard navigation — ↑↓ / Tab / Enter cover every operation and don't depend on a fast redraw.

4. Will automated review sessions pollute my session list?

Codex: sessions created by codex exec (headless automation, pre‑commit hooks, CI) are filtered automatically — railmux only shows interactive sessions (codex-tui, codex_cli_rs).

Claude Code: for one-shot automated reviews, disable session persistence so the consultation never appears in /resume:

# Print mode only; the review is not saved as a resumable session
claude -p --no-session-persistence "review this diff"

(--no-session-persistence is a Claude Code print-mode option.)

5. pip reports "externally-managed-environment"

Create a virtual environment, then install Railmux with that environment's pip. This works on macOS, Linux, and WSL without modifying the system Python:

python3 -m venv ~/.venvs/railmux
source ~/.venvs/railmux/bin/activate
pip install railmux

pipx install railmux is an optional convenience for a globally available CLI; it is not required on macOS or any other platform.

Acknowledgements

The tmux sidebar idea and initial architecture came from regmi-saugat/ccmgr. railmux extends it with Codex support, session history preview, starring, in-app renaming, mouse interaction, and a status bar integrated into the tmux status line.

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

railmux-0.1.2.tar.gz (287.8 kB view details)

Uploaded Source

Built Distribution

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

railmux-0.1.2-py3-none-any.whl (182.3 kB view details)

Uploaded Python 3

File details

Details for the file railmux-0.1.2.tar.gz.

File metadata

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

File hashes

Hashes for railmux-0.1.2.tar.gz
Algorithm Hash digest
SHA256 a8f8915a9d51fe31c64c9703fc541c4551d8a4985f371f5c22a26132dd8f5169
MD5 4d11c90dfbffd6cb93de061e8295f397
BLAKE2b-256 cfd70056fdbb9d811c99f5b7500c36183a27746a540959eb2bf275c3bdc28a85

See more details on using hashes here.

Provenance

The following attestation bundles were made for railmux-0.1.2.tar.gz:

Publisher: release.yml on Rightglow/Railmux

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

File details

Details for the file railmux-0.1.2-py3-none-any.whl.

File metadata

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

File hashes

Hashes for railmux-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 46e53e559cc275bae2f1ff35c583a5c0d0c38028bede9bb84dada105a066edc6
MD5 f0de1d0c13e72f6acfb301ff62fff667
BLAKE2b-256 3072d3586634455043818113c535775edc1e0cf1aed7deee1d6034abd80bf32d

See more details on using hashes here.

Provenance

The following attestation bundles were made for railmux-0.1.2-py3-none-any.whl:

Publisher: release.yml on Rightglow/Railmux

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