Lightweight tmux session and project manager written in Bash
It creates sessions when they don’t exist and reuses them when they do — idempotent by default.
It can start sessions from simple YAML project files.
And if you hate writing YAML, it can generate the file for you from an existing session.
Note: demo gif file was captured with
asciinemaso any visible render glitches were generated during the conversion process togif
Over the years I've used tools like tmuxinator, tmuxp and recently smug.
They are all great but in practice I keep going back to my own t function from my dotfiles to manage my tmux sessions.
Eventually I realized:
- I didn’t need more features
- I needed something simpler.
So I built t - mux manager: a small standalone script that does exactly what I need, with no dependencies beyond yq
t - mux manager is built around a few principles:
- Single command workflow
- Minimal dependencies
- Simple
YAMLstructure - Idempotent session and project handling
- Plain tmux commands. No unnecessary complexity. Just
tmux
And why just t as the command name?
It's short, and easy to type when you're already in a terminal all day
- tmux >= 3.0
- bash >= 3.2
- yq
Install yq:
# macOS
brew install yq
# Arch
sudo pacman -S yqbrew install rgcr/formulae/t-mux-manager
Clone the repository and symlink the script into a directory in your PATH
(for example, /usr/local/bin):
git clone git@github.com:rgcr/t-mux-manager.git ~/.t-mux-manager
chmod +x ~/.t-mux-manager/t
ln -s ~/.t-mux-manager/t /usr/local/bin/tAlternatively, if you prefer installing to ~/.local/bin:
git clone git@github.com:rgcr/t-mux-manager.git ~/.t-mux-manager
chmod +x ~/.t-mux-manager/t
mkdir -p ~/.local/bin
ln -s ~/.t-mux-manager/t ~/.local/bin/tDownload the latest version and place it in a directory that is in your PATH
(for example, ~/.local/bin):
curl -fsSL -o t https://raw.githubusercontent.com/rgcr/t-mux/main/t
chmod +x t
mkdir -p ~/.local/bin
mv t ~/.local/bin/Make sure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"
Tab completions for zsh, bash, and fish are available in the completions/ directory.
Source directly in your .zshrc:
source /path/to/t-mux-manager/completions/t.zshOr add to fpath (the #compdef header handles registration):
fpath=(/path/to/t-mux-manager/completions $fpath)
autoload -Uz compinit && compinitSource in your .bashrc:
source /path/to/t-mux-manager/completions/t.bashCopy to your fish completions directory:
cp /path/to/t-mux-manager/completions/t.fish ~/.config/fish/completions/t # List active sessions and available projects
t myproject # Apply config, attach to session, or create new session
t -f <config.yml> # Use an explicit project config file
t -e myproject # Open project config in $EDITOR
t -s # Save current tmux session to a config file
t --reapply myproject # Re-apply config and re-send commands
t -k myproject # Kill the tmux session
t -d myproject # Preview tmux commands (dry-run)
t -n myproject # Create session without attachingWhen you run t <name>, it resolves in order:
- If config file
~/.config/tmux-projects/<name>.ymlexists — apply it and attach - If tmux session named
<name>already exists — attach to it - Neither — create a new bare session named
<name>and attach
If the session already exists, t just attaches without reprocessing the config. Use --reapply to force re-apply.
Project configs live in ~/.config/tmux-projects/. The directory is created automatically on first run.
Run t -s or t --save-project from inside a tmux session to save it as a project config. The current session's windows, pane layout, and working directories are captured. Commands cannot be saved (tmux doesn't store them).
Layout strings are only saved for multi-pane windows. If the config file already exists, it will ask for confirmation before overwriting.
| Flag | Short | Description |
|---|---|---|
--file <path> |
-f |
Explicit project config file |
--edit |
-e |
Open project config in $EDITOR |
--save-project |
-s |
Save current tmux session to config file (must be inside a tmux session) |
--reapply |
Re-apply config and re-send commands to existing session | |
--dry-run |
-d |
Print tmux commands instead of executing |
--no-attach |
-n |
Create session but don't attach |
--kill |
-k |
Kill the named tmux session |
--rm |
Delete project config file | |
--sessions |
List only tmux sessions | |
--projects |
List only available projects | |
--verbose |
-v |
Verbose/debug output |
--no-color |
Disable colored output | |
--help |
-h |
Show help |
--version |
-V |
Show version |
Very minimal project config file, almost all my configs looks like this
session: minimal
root: ~/workspace
windows:
- name: w1
- name: w2
- name: w3Another basic example:
session: myproject # required — tmux session name
root: ~/projects/myapp # optional — default working directory
windows:
- name: editor # required — window name
root: ./src # optional — overrides session root
commands: # optional — sent to the first pane
- vim .
- name: services
layout: even-horizontal # optional — tmux layout (named or raw string)
panes: # optional — if absent, single pane
- commands:
- npm run dev:frontend
split: horizontal # horizontal (default) or vertical
- commands:
- npm run dev:backend
split: horizontal
size: 40 # percentage of the splitDon't you want to waste time writing YAML from scratch? Neither do I. Just run t -s from an existing tmux session to save it as a project config, then edit the file with t -e <project> to add commands and customize.
| Field | Default | Description |
|---|---|---|
commands |
— | List of commands to run in the pane |
split |
horizontal |
Split direction: horizontal (panes side by side) or vertical (panes stacked top/bottom). Follows tmux convention where the name refers to the divider line direction |
size |
— | Pane size as a percentage |
root |
— | Working directory, overrides window/session root |
By default t just attaches to an existing session. Project config is only applied when creating a new session or when --reapply is passed.
Windows created by t have automatic-rename off set, so tmux won't rename them when a command runs (e.g. vim won't rename the "editor" window to "vim").
Layout application is resilient — if a layout string doesn't match the pane count, t warns instead of crashing.
t is safe to run repeatedly:
- Session — reuses if it exists, creates if not
- Windows — reuses by name, creates missing ones
- Panes — creates only the missing panes (splits)
- Commands — sent only to newly created windows/panes. Use
--reapplyto re-send to existing ones
See the examples/ directory:
minimal.yml— very minimal exampleminimal-commands.yml— two simple windows with commandsfullstack.yml— multiple windows with panes, layouts, and per-pane commandscustom-layout.yml— named and raw tmux layout strings
Tests use bats-core:
# macos
brew install bats-core
# Arch
sudo pacman -S bats-core # or see bats-core docs for other platforms
# run all tests
bash tests/run.shWe ❤️ contributions!
- Fork the repo
- Create your feature branch:
git checkout -b my-new-feature - Commit your changes:
git commit -m 'Add some feature' - Push the branch:
git push origin my-new-feature - Open a Pull Request 🚀
