Skip to content

tmux

.config/tmux/tmux.conf is a thin loader; the real config is split across modular files under .config/tmux/conf/.

.config/tmux/tmux.conf
source ~/.config/tmux/conf/plugins.conf
source ~/.config/tmux/conf/keymaps.conf
source ~/.config/tmux/conf/options.conf

# install tpm if not already installed
if 'test ! -d #{TMUX_PLUGIN_MANAGER_PATH}/tpm' {
    run 'git clone https://github.com/tmux-plugins/tpm #{TMUX_PLUGIN_MANAGER_PATH}/tpm'
    run '#{TMUX_PLUGIN_MANAGER_PATH}/tpm/bin/install_plugins'
}

run -b '#{TMUX_PLUGIN_MANAGER_PATH}/tpm/tpm'
source ~/.config/tmux/conf/theme.conf

TPM (the Tmux Plugin Manager) is auto-installed on first launch. No manual bootstrap.

Keymaps

.config/tmux/conf/keymaps.conf
unbind C-a
set -g prefix C-a
bind C-a send-prefix
Binding Action
Ctrl+A Prefix (sent through with Ctrl+A Ctrl+A).
Ctrl+A | Split window horizontally, preserve cwd.
Ctrl+A - Split window vertically, preserve cwd.
Ctrl+A Ctrl+P Previous window.
Ctrl+A Ctrl+N Next window.
Ctrl+A Shift+C New window running Claude Code in the current pane path if available.
Ctrl+A Shift+O New window running OpenCode in the current pane path if available.
Ctrl+A H / J / K / L Resize pane left / down / up / right (repeatable).
Ctrl+A M Toggle pane zoom.
Ctrl+A Backspace Kill current session.
Ctrl+A F Kill current session (alias).

Copy mode uses vi keys (v to start selection, y to copy). Mouse drag-select does not exit copy mode, so you can refine a selection after dragging.

The agent bindings show a tmux message instead of opening a broken window when their CLI is missing from PATH.

Options

.config/tmux/conf/options.conf
set -g mouse on
set -g status-position top
set -g base-index 1
set -g renumber-windows 1
set -g default-shell $SHELL
set -g escape-time 0
set -g history-limit 50000
set -g extended-keys on
set -g extended-keys-format xterm

Highlights:

  • Status bar at the top to match the prompt direction.
  • base-index 1 + pane-base-index 1 + renumber-windows — windows and panes number from 1 and stay contiguous.
  • escape-time 0 — kill the default 500ms delay that breaks Vim mode.
  • extended-keys — full xterm key reporting so modified keys work.
  • Shift+Enter → Ctrl+J — root-level binding lets Claude Code (which uses Shift+Enter for newline-without-submit) work inside tmux despite tmux not supporting the kitty keyboard protocol.

Claude Code tmux environment

options.conf exports two CLAUDE_CODE_* variables via set-environment -g so they only apply to processes spawned inside tmux, not to plain terminal shells:

Variable Value Effect
CLAUDE_CODE_TMUX_TRUECOLOR true Tells Claude Code to emit 24-bit colour even when $TERM advertises 256.
CLAUDE_CODE_NO_FLICKER true Suppresses the redraw flicker Claude Code's TUI shows under tmux.

A third toggle, CLAUDE_CODE_DISABLE_MOUSE=true, is kept commented out — turning it on hands mouse events to tmux instead of Claude Code, but the trade-off (no in-app scroll / no selection inside the TUI) didn't carry its weight. Uncomment it in options.conf if you'd rather let tmux's mouse on win drag-select.

Plugins

.config/tmux/conf/plugins.conf
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'wfxr/tmux-fzf-url'
set -g @plugin 'christoomey/vim-tmux-navigator'
set -g @plugin 'joshmedeski/tmux-nerd-font-window-name'
set -g @plugin 'tmux-plugins/tpm'
Plugin What it does
tmux-sensible Sane defaults that don't conflict with custom options.
tmux-fzf-url ++prefix++ U to pick a URL from the visible buffer with fzf.
vim-tmux-navigator Ctrl+H / Ctrl+J / Ctrl+K / Ctrl+L move between Vim splits and tmux panes seamlessly.
tmux-nerd-font-window-name Automatic window names use Nerd Font icons for known processes.
tpm The plugin manager itself.

Theme

conf/theme.conf uses Catppuccin for tmux with a cyberdream flavour. The flavour file isn't shipped by upstream Catppuccin, so the config self-bootstraps by curl-ing it from the cyberdream.nvim extras directory on first run.

The status bar shows: current pane command + cwd (left) and session name (right, recoloured red while the prefix is held).

Agent-is-waiting indicator

theme.conf makes the window-status format react to a per-window @agent_status user option that holds a state token so a window can flag what a coding agent (Claude Code or opencode) needs. Both window-status-format and window-status-current-format gain a leading conditional segment that maps the token to a style:

.config/tmux/conf/theme.conf (style segment)
#{?@agent_status,#{?#{==:#{@agent_status},attention},#[bg=#{@thm_red}#,fg=#{@thm_bg}#,bold],#[bg=#{@thm_peach}#,fg=#{@thm_bg}]},}
Token Set when Look
(empty) cleared base window-status-style
waiting turn finished (your move) calm peach background,
attention blocked on permission / attention bold red background, 󰂚

A matching glyph segment appends or 󰂚 after the window name via the same #{==:…,attention} test. The literal commas inside #[…] are escaped as #, because , separates the conditional's branches; the commas inside nested #{…} are protected by the braces and left bare. The active and last-window styles use @thm_blue (not the indicator's @thm_peach/@thm_red), so a waiting or attention window pops by colour even when it is the focused one — no overlap between the "this is the current window" cue and the "this window needs you" cue.

The option is toggled by the agent-tmux-status script, wired into Claude Code's Stop/Notification/UserPromptSubmit/SessionEnd hooks and opencode's status-indicator plugin.

Companion scripts

See scripts/tmux for start-tmux-session (fzf-driven repo browser that creates a named session per repo) and attach-tmux-session.