# Configuration
psmux reads its config on startup from the **first file found** (in order):
1. `~/.psmux.conf`
2. `~/.psmuxrc`
3. `~/.tmux.conf`
4. `~/.config/psmux/psmux.conf`
Config syntax is **tmux-compatible**. Most `.tmux.conf` lines work as-is.
You can also specify a custom config file path with the `-f` flag:
```powershell
# Use a specific config file instead of default search
psmux -f ~/.config/psmux/custom.conf
# Use an empty config (no settings loaded)
psmux -f NUL
```
This sets the `PSMUX_CONFIG_FILE` environment variable internally, which the server checks before searching the default locations.
## Basic Config Example
Create `~/.psmux.conf`:
```tmux
# Change prefix key to Ctrl+a
set -g prefix C-a
# Enable mouse
set -g mouse on
# Window numbering base (default is 1)
set -g base-index 1
# Customize status bar
set -g status-left "[#S] "
set -g status-right "%H:%M %d-%b-%y"
set -g status-style "bg=green,fg=black"
# Cursor style: block, underline, or bar
set -g cursor-style bar
set -g cursor-blink on
# Scrollback history
set -g history-limit 5000
# Prediction dimming (disable for apps like Neovim)
set -g prediction-dimming off
# Key bindings
bind-key -T prefix h split-window -h
bind-key -T prefix v split-window -v
```
## Choosing a Shell
psmux launches **PowerShell 7 (pwsh)** by default. You can change this:
```tmux
# Use cmd.exe
set -g default-shell cmd
# Use PowerShell 5 (Windows built-in)
set -g default-shell powershell
# Use PowerShell 7 (explicit path)
set -g default-shell "C:/Program Files/PowerShell/7/pwsh.exe"
# Use Git Bash
set -g default-shell "C:/Program Files/Git/bin/bash.exe"
# Use Nushell
set -g default-shell nu
# Use Windows Subsystem for Linux (via wsl.exe)
set -g default-shell wsl
```
You can also launch a window with a specific command without changing the default:
```powershell
psmux new-window -- cmd /K echo hello
psmux new-session -s py -- python
psmux split-window -- "C:/Program Files/Git/bin/bash.exe"
```
## All Set Options
| `prefix` | Key | `C-b` | Prefix key |
| `prefix2` | Key | `none` | Secondary prefix key (optional) |
| `base-index` | Int | `0` | First window number |
| `pane-base-index` | Int | `0` | First pane number |
| `escape-time` | Int | `500` | Escape delay (ms) |
| `repeat-time` | Int | `500` | Repeat key timeout (ms) |
| `history-limit` | Int | `2000` | Scrollback lines per pane |
| `display-time` | Int | `750` | Message display time (ms) |
| `display-panes-time` | Int | `1000` | Pane overlay time (ms) |
| `status-interval` | Int | `15` | Status refresh (seconds) |
| `mouse` | Bool | `on` | Mouse support |
| `mouse-selection` | Bool | `on` | psmux's client-side drag selection. Set `off` to let in-pane TUI apps (opencode, nvim, etc.) handle their own mouse selection without psmux drawing on top |
| `scroll-enter-copy-mode` | Bool | `on` | Enter copy mode on mouse scroll (set `off` to disable) |
| `pwsh-mouse-selection` | Bool | `off` | Windows 11 PowerShell-style word/line selection (double/triple-click) |
| `paste-detection` | Bool | `on` | Detect Ctrl+V paste from console host and send as bracketed paste (set `off` to let Ctrl+V reach child apps like neovim) |
| `choose-tree-preview` | Bool | `off` | Open `choose-session` / `choose-tree` pickers with the live preview pane already visible (saves pressing `p`). See [preview.md](preview.md) |
| `status` | Bool/Int | `on` | Show status bar (number = line count) |
| `status-position` | Str | `bottom` | `top` or `bottom` |
| `status-justify` | Str | `left` | `left`, `centre`, `right`, `absolute-centre` |
| `status-left-length` | Int | `10` | Max width of status-left |
| `status-right-length` | Int | `40` | Max width of status-right |
| `focus-events` | Bool | `off` | Pass focus events to apps |
| `mode-keys` | Str | `emacs` | `vi` or `emacs` |
| `renumber-windows` | Bool | `off` | Auto-renumber windows on close |
| `automatic-rename` | Bool | `on` | Rename windows from foreground process |
| `monitor-activity` | Bool | `off` | Flag windows with new output |
| `monitor-silence` | Int | `0` | Seconds before silence flag (0=off) |
| `visual-activity` | Bool | `off` | Visual indicator for activity |
| `synchronize-panes` | Bool | `off` | Send input to all panes |
| `remain-on-exit` | Bool | `off` | Keep panes after process exits |
| `aggressive-resize` | Bool | `off` | Resize to smallest client |
| `window-size` | Str | `latest` | `largest`, `smallest`, `manual`, `latest` |
| `destroy-unattached` | Bool | `off` | Exit server when no clients attached |
| `exit-empty` | Bool | `on` | Exit server when all windows closed |
| `set-titles` | Bool | `off` | Update terminal title |
| `set-titles-string` | Str | | Terminal title format |
| `default-shell` | Str | `pwsh` | Shell to launch |
| `default-command` | Str | | Alias for default-shell |
| `word-separators` | Str | `" -_@"` | Copy-mode word delimiters |
| `activity-action` | Str | `other` | Action on window activity: `any`, `none`, `current`, `other` |
| `silence-action` | Str | `other` | Action on window silence: `any`, `none`, `current`, `other` |
| `bell-action` | Str | `any` | Bell action: controls audible bell forwarding and status bar flag (`any`, `none`, `current`, `other`) |
| `visual-bell` | Bool | `off` | Visual bell indicator |
| `allow-passthrough` | Str | `off` | Allow terminal passthrough sequences (`on`/`off`/`all`) |
| `allow-rename` | Bool | `on` | Allow programs to set window title via escape sequences |
| `allow-set-title` | Bool | `off` | Allow programs to set pane title via OSC 0/2 escape sequences (see [pane-titles.md](pane-titles.md)) |
| `allow-predictions` | Bool | `off` | Preserve PSReadLine prediction settings (see below) |
| `default-terminal` | Str | | Terminal type string (sets `TERM` env var in panes) |
| `update-environment` | Str | *(tmux defaults)* | Space-separated list of env vars to refresh on client attach |
| `warm` | Bool | `on` | Pre-spawn shells for instant window/pane creation (see [warm-sessions.md](warm-sessions.md)) |
| `copy-command` | Str | | Shell command for clipboard pipe |
| `set-clipboard` | Str | `on` | Clipboard interaction (`on`/`off`/`external`) |
| `main-pane-width` | Int | `0` | Main pane width in main-vertical layout |
| `main-pane-height` | Int | `0` | Main pane height in main-horizontal layout |
### Style Options
| `status-left` | Str | `[#S] ` | Left status bar content |
| `status-right` | Str | | Right status bar content |
| `status-style` | Str | `bg=green,fg=black` | Status bar style |
| `status-left-style` | Str | | Left status style |
| `status-right-style` | Str | | Right status style |
| `message-style` | Str | `bg=yellow,fg=black` | Message style |
| `message-command-style` | Str | `bg=black,fg=yellow` | Command prompt style |
| `mode-style` | Str | `bg=yellow,fg=black` | Copy-mode highlight |
| `pane-border-style` | Str | | Inactive border style |
| `pane-active-border-style` | Str | `fg=green` | Active border style |
| `pane-border-format` | Str | | Pane border format string (e.g. `#{pane_index}: #{pane_title}`) |
| `pane-border-status` | Str | | Pane border status position (`top`/`bottom`/`off`) |
| `window-status-format` | Str | `#I:#W#F` | Inactive tab format |
| `window-status-current-format` | Str | `#I:#W#F` | Active tab format |
| `window-status-separator` | Str | `" "` | Tab separator |
| `window-status-style` | Str | | Inactive tab style |
| `window-status-current-style` | Str | | Active tab style |
| `window-status-activity-style` | Str | `reverse` | Activity tab style |
| `window-status-bell-style` | Str | `reverse` | Bell tab style |
| `window-status-last-style` | Str | | Last-active tab style |
### Multi-line Status Bar (`status-format[]`)
psmux supports a multi-line status bar using the `status-format[]` array. Set the `status` option to a number to control how many lines the status bar displays:
```tmux
# Enable a 2-line status bar
set -g status 2
# Configure each line (0-indexed)
set -g status-format[0] "#[align=left]#S #[align=right]%H:%M"
set -g status-format[1] "#[align=left]#{W:#I:#W }"
```
The first line (`status-format[0]`) replaces the default status bar content. Additional lines stack below (or above, depending on `status-position`).
### Pane Border Labels
Show pane information on the border between panes:
```tmux
# Enable pane border labels at the top of each pane
set -g pane-border-status top
# Customize what the label shows
set -g pane-border-format " #{pane_index}: #{pane_title} [#{pane_current_command}] "
# Disable pane border labels
set -g pane-border-status off
```
Use `select-pane -T "title"` to set a pane title that appears in the border label. Clear a title with `select-pane -T ""`. The default pane title is the hostname, matching tmux convention.
> **Note:** PowerShell 7 automatically sets the pane title to the current working directory on every prompt via OSC escape sequences. If you see a file path in your pane border labels instead of the hostname, see [pane-titles.md](pane-titles.md) for details and options to control this.
### Bell
When a program inside a pane emits BEL (`\x07`), psmux forwards the bell character to your host terminal so you hear the audible beep. The `bell-action` option controls when this happens and when the status bar tab gets a bell flag (`!`):
```tmux
# Forward bell from any window (default)
set -g bell-action any
# Forward bell only from the active window
set -g bell-action current
# Forward bell only from non-active windows
set -g bell-action other
# Mute bell completely (no sound, no status bar flag)
set -g bell-action none
```
The `window-status-bell-style` option controls how the tab looks when flagged:
```tmux
set -g window-status-bell-style "fg=red,bold"
```
PowerShell example to test:
```powershell
# These should all produce an audible beep inside psmux:
Write-Host "`a"
[Console]::Beep()
[char]7
```
### Mouse Configuration
Mouse support is enabled by default. You can customize how the mouse interacts with psmux:
```tmux
# Disable mouse entirely (no click, scroll, or drag)
set -g mouse off
# Disable entering copy mode on mouse scroll
set -g scroll-enter-copy-mode off
# Enable Windows 11 PowerShell-style word/line selection
# Double-click selects a word, triple-click selects a line
set -g pwsh-mouse-selection on
```
When `scroll-enter-copy-mode` is `off`, scrolling in a pane does not enter copy mode and instead passes scroll events directly to the running application.
#### Disabling psmux's drag selection (`mouse-selection`)
Some TUI applications render their own internal layouts (multiple columns, sidebars, panels) inside a single psmux pane. Examples include `opencode`, `lazygit`, `nvim` with split windows, and similar dashboards.
psmux's own client-side drag selection does not know about those internal layouts, so a left-click drag inside such an app draws a selection rectangle that crosses the app's internal columns instead of respecting them.
If you would rather have the application handle mouse selection itself, disable psmux's drag selection:
```tmux
# Let the app inside the pane handle its own mouse selection.
# psmux will no longer render its drag-selection rectangle.
set -g mouse-selection off
```
What still works when `mouse-selection` is `off`:
- Click on a pane to focus it
- Click on a window tab in the status bar to switch to it
- Mouse wheel scrolling and scroll-into-copy-mode
- Pane border drag-to-resize
- Mouse events being forwarded to applications that request mouse tracking (DECSET 1000/1002/1003), so `opencode`, `htop`, `nvim`, `claude`, etc. continue to receive their clicks and drags
What changes when `mouse-selection` is `off`:
- psmux no longer draws its own selection rectangle on left-click drag
- Right-click clipboard copy via psmux's selection is no longer triggered (selection never starts)
- The Windows 11 style word/line multi-click (`pwsh-mouse-selection`) is suppressed too while `mouse-selection off` is in effect
To restore the default behaviour:
```tmux
set -g mouse-selection on
```
You can also toggle this at runtime without restarting:
```
psmux set-option -g mouse-selection off
psmux set-option -g mouse-selection on
```
This option is independent of `mouse` (which controls whether mouse events are received at all) and `pwsh-mouse-selection` (which only affects the style of the drag selection when it is active).
### Paste Detection (Ctrl+V Passthrough)
On Windows, the console host intercepts Ctrl+V, reads the clipboard, and injects the content as character events. psmux detects this pattern and reassembles it into a single bracketed paste for child applications. This is the `paste-detection` option and it is enabled by default.
If you use TUI applications like **neovim** or **vim** where Ctrl+V has a different meaning (visual block mode), the paste detection will intercept the keypress before it reaches the application. To let Ctrl+V pass through to the child app:
```tmux
# Disable paste detection so Ctrl+V reaches child apps
set -g paste-detection off
```
With paste detection off, you can still paste using:
* **Ctrl+Shift+V** (Windows Terminal default paste shortcut)
* **Right click** (paste in most terminals)
* **Prefix + ]** (psmux paste from buffer)
* **`psmux send-keys C-v`** from another terminal
> **Note:** `unbind-key -n C-v` alone is not sufficient to stop Ctrl+V interception because the paste detection operates outside the key binding system. You must use `set -g paste-detection off`.
### Live Preview in Choosers
`choose-session` (prefix + s) and `choose-tree` (prefix + w) include a live preview pane that mirrors the selected session or window in real time. By default it is hidden and you press `p` to toggle it. To make it visible by default:
```tmux
# Open all choosers with the preview pane already visible
set -g choose-tree-preview on
```
You can still press `p` inside the chooser to hide it for the current session. The setting is read once when the chooser opens, so changes to the option take effect immediately on the next open. See [preview.md](preview.md) for the full feature documentation.
### Command Chaining
psmux supports tmux-style command chaining with the `;` operator. Multiple commands on a single line are executed sequentially:
```tmux
# Split and move focus in one binding
bind-key M-s split-window -h \; select-pane -L
# Create a development layout
bind-key D split-window -v -p 30 \; split-window -h \; select-pane -t 0
```
In config files, escape the semicolon with `\;` so it is not treated as a comment delimiter.
### Case-Sensitive Key Bindings
psmux distinguishes between lowercase and uppercase letters in key bindings, matching tmux behavior:
```tmux
# These are two different bindings:
bind-key t clock-mode # Prefix + t (lowercase)
bind-key T choose-tree # Prefix + Shift+T (uppercase)
# Uppercase bindings for plugin managers
bind-key I run-shell '~/.psmux/plugins/ppm/scripts/install_plugins.ps1'
bind-key U run-shell '~/.psmux/plugins/ppm/scripts/update_plugins.ps1'
```
### Ctrl+Space as Prefix
Multi-character key names like `Space`, `Enter`, `Tab`, and `Escape` are fully supported in prefix configuration:
```tmux
set -g prefix C-Space
unbind-key C-b
bind-key C-Space send-prefix
```
### psmux Extensions (Windows-specific)
| `prediction-dimming` | Bool | `off` | Dim predictive/speculative text |
| `paste-detection` | Bool | `on` | Detect Ctrl+V paste from console host (set `off` for neovim/vim Ctrl+V) |
| `cursor-style` | Str | | Cursor shape: `block`, `underline`, or `bar` |
| `cursor-blink` | Bool | `off` | Cursor blinking |
| `env-shim` | Bool | `on` | Inject Unix-compatible `env` function in PowerShell panes |
| `claude-code-fix-tty` | Bool | `on` | Patch Node.js process.stdout.isTTY for Claude Code |
| `claude-code-force-interactive` | Bool | `on` | Set CLAUDE_CODE_FORCE_INTERACTIVE=1 in panes |
Style format: `"fg=colour,bg=colour,bold,dim,underscore,italics,reverse,strikethrough"`
Colours: `default`, `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `colour0`–`colour255`, `#RRGGBB`
## Environment Variables
```powershell
# Default session name used when not explicitly provided
$env:PSMUX_DEFAULT_SESSION = "work"
# Enable prediction dimming (off by default; dims predictive/speculative text)
$env:PSMUX_DIM_PREDICTIONS = "1"
# Disable warm pane pre-spawning (same as set -g warm off)
$env:PSMUX_NO_WARM = "1"
# Override the config file path (same effect as -f flag)
$env:PSMUX_CONFIG_FILE = "C:\Users\me\.psmux-alt.conf"
# These are set INSIDE psmux panes (tmux-compatible):
# TMUX - socket path and server info
# TMUX_PANE - current pane ID (%0, %1, etc.)
```
## Managing Environment Variables
Use `set-environment` to set env vars that are inherited by newly created panes:
```powershell
# Set a global env var (inherited by all new panes)
psmux set-environment -g EDITOR vim
# Set a session-scoped env var
psmux set-environment MY_VAR value
# Unset an env var
psmux set-environment -gu MY_VAR
# Show all environment variables
psmux show-environment
psmux show-environment -g
```
Environment variables set this way are injected at the process level when new panes spawn, so they are completely invisible (no commands echoed in the shell).
## PSReadLine Predictions (Intellisense / Autocompletion)
By default, psmux disables PSReadLine inline predictions (the grayed-out autocompletion/intellisense suggestions that appear as you type) to avoid additional unexpected bugs caused by the interaction between predictions and ConPTY. This means `PredictionSource` defaults to `None` inside psmux, even if your profile sets it to `HistoryAndPlugin` ([#150](https://github.com/psmux/psmux/issues/150)).
If enough people test predictions and the community supports enabling them by default, this will be changed in a future release.
To preserve your prediction/autocompletion settings, enable `allow-predictions`:
```tmux
set -g allow-predictions on
```
With this enabled:
- If your profile sets `PredictionSource`, psmux respects your choice
- If your profile does not set it, psmux restores the system default (typically `HistoryAndPlugin`)
## Prediction Dimming
Prediction dimming is off by default. If you want psmux to dim predictive/speculative text (e.g. shell autosuggestions), you can enable it in `~/.psmux.conf`:
```tmux
set -g prediction-dimming on
```
You can also enable it for the current shell only:
```powershell
$env:PSMUX_DIM_PREDICTIONS = "1"
psmux
```
To make it persistent for new shells:
```powershell
setx PSMUX_DIM_PREDICTIONS 1
```
## Reloading Configuration at Runtime
You can reload your config file without restarting psmux. From the command prompt (`Prefix + :`), run:
```tmux
source-file ~/.psmux.conf
```
Or from outside psmux:
```powershell
psmux source-file ~/.psmux.conf
```
This re-executes every line in the config file, applying any changes to options, key bindings, hooks, and styles immediately.
## Window and Pane Numbering
By default, windows and panes are numbered starting from 0. You can change the starting index for both:
```tmux
# Start window numbering at 1
set -g base-index 1
# Start pane numbering at 1
set -g pane-base-index 1
```
The `pane-base-index` setting affects:
- **Display Panes overlay** (`Prefix + q`): The numbers shown on each pane start from your configured base index
- **Pane targets**: When referencing panes by number (e.g. `select-pane -t 1`), numbering follows your base index
- **Format variables**: `#{pane_index}` reflects the base index setting
- **Status bar and border labels**: Pane numbers in format strings use the configured base
A common setup for both windows and panes to start at 1:
```tmux
set -g base-index 1
set -g pane-base-index 1
```
## Display Panes Overlay
Press `Prefix + q` to show numbered overlays on each pane. While the overlay is visible, press any displayed number key to jump to that pane. The overlay auto-dismisses after `display-panes-time` milliseconds (default: 1000ms).
```tmux
# Show pane numbers for 3 seconds
set -g display-panes-time 3000
```
The numbers shown respect your `pane-base-index` setting. For example, with `pane-base-index 1`, three panes show as 1, 2, 3 instead of 0, 1, 2.
You can also trigger this overlay from the command line:
```powershell
psmux display-panes
```
## Split Window Options
When splitting panes, you can control the size and starting directory of the new pane:
```tmux
# Split vertically, new pane takes 30% of the space
split-window -v -p 30
# Split horizontally, new pane takes 70% of the space
split-window -h -p 70
# Split and start in a specific directory
split-window -v -c "C:\Projects\myapp"
# Split and start in the current pane's directory
split-window -h -c "#{pane_current_path}"
# Split and run a specific command
split-window -v -- python
```
These flags also work when creating new windows:
```tmux
# New window with a specific name
new-window -n "logs"
# New window in a specific directory
new-window -c "C:\Projects"
# New window running a specific command with a name
new-window -n "build" -- cargo build --watch
```
When you set a window name with `-n`, the `automatic-rename` flag is turned off for that window so psmux does not overwrite your chosen name with the foreground process name. To re-enable automatic renaming for that window:
```tmux
set-option -w automatic-rename on
```
## Detach and Exit Policies
Control what happens when clients disconnect or all windows close:
```tmux
# Exit the server when no clients are attached (default: off)
set -g destroy-unattached on
# Exit the server when the last window/session closes (default: on)
set -g exit-empty on
```
With `destroy-unattached on`, the server process terminates as soon as the last client detaches. This is useful for single-use sessions.
With `exit-empty off`, the server stays alive even after all sessions are closed, allowing new sessions to be created without restarting.
## Dead Panes and Respawn
When a process inside a pane exits, the pane normally closes. To keep the pane visible after its process exits:
```tmux
set -g remain-on-exit on
```
A pane with a dead process shows its last output and can be respawned:
```powershell
# Restart the default shell in the pane
psmux respawn-pane
# Kill any remaining process and restart
psmux respawn-pane -k
# Respawn in a different directory
psmux respawn-pane -c "C:\Projects"
# Respawn with a specific command
psmux respawn-pane -- python app.py
```
This is useful for monitoring: if a long-running process crashes, you can see its final output and restart it without losing the pane layout.
## Session Environment Variables
You can set environment variables at the session or global level that get inherited by all new panes:
```powershell
# Set a global env var (all new panes in all sessions inherit this)
psmux set-environment -g EDITOR vim
# Set a session-scoped env var
psmux set-environment MY_VAR value
# Unset a global env var
psmux set-environment -gu MY_VAR
# View all environment variables
psmux show-environment
psmux show-environment -g
```
You can also pass environment variables when creating a new session:
```powershell
# Create a session with custom environment
psmux new-session -s work -e "PROJECT=myapp" -e "ENV=production"
```
## Status Bar Time Updates
The status bar supports time format variables that update in real time:
```tmux
# Show current time in the status bar (updates every second)
set -g status-right "%H:%M:%S %d-%b-%y"
# Common time format variables:
# %H Hour (24-hour, 00-23)
# %I Hour (12-hour, 01-12)
# %M Minute (00-59)
# %S Second (00-59)
# %p AM/PM
# %r Full time in 12-hour format (e.g. 02:30:45 PM)
# %R Hour:Minute in 24-hour format (e.g. 14:30)
# %d Day of month (01-31)
# %b Abbreviated month name (Jan, Feb, ...)
# %Y Full year (2025)
# %a Abbreviated weekday (Mon, Tue, ...)
```
Time variables refresh based on the `status-interval` option (default: 15 seconds). For second-level precision, reduce the interval:
```tmux
# Update status bar every second (for live clock)
set -g status-interval 1
```
## PSReadLine ListView
psmux supports PSReadLine's ListView prediction style, which shows a dropdown list of suggestions:
```powershell
# In your PowerShell profile ($PROFILE)
Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Set-PSReadLineOption -PredictionViewStyle ListView
```
For this to work inside psmux, enable `allow-predictions` in your psmux config:
```tmux
set -g allow-predictions on
```
Without `allow-predictions on`, psmux resets PSReadLine's prediction settings during initialization, which disables ListView mode.