A tiny tmux project session manager. Every project gets its own named,
persistent tmux session. Run roost <name> to
drop into a project (creating its session if needed), or roost -s to boot all
your common projects at once. Sessions survive closing the terminal; they only
end on reboot or an explicit kill.
If you tend to drown in a dozen look-alike terminal windows, roost replaces them with one window and a handful of named sessions you switch between.
- One session per project. No more hunting through identical terminals.
- Persistent. Close the terminal, come back later, everything is still there.
- Startup groups. Boot your daily projects with a single
roost -s. - Just tmux. No daemon, no runtime. A single Bash script and a text config.
- tmux (
brew install tmuxon macOS) - Bash 3.2+ (the macOS system Bash is fine) and zsh for tab-completion
git clone https://github.com/<you>/roost.git ~/code/roost
cd ~/code/roost
./install.shinstall.sh symlinks bin/roost into ~/.local/bin and seeds
~/.config/roost/projects.conf from the example. It then prints the two lines
to add to your ~/.zshrc (put ~/.local/bin on PATH and enable completion).
Reload with source ~/.zshrc or open a new terminal.
Verify:
roost --listYour projects live in ~/.config/roost/projects.conf, outside this repository.
It is personal and never published: the repo ships only a generic
projects.example.conf, and projects.conf is gitignored so your real project
names and paths stay on your machine.
The file is one project per line, pipe-delimited:
name | path | flags | command
| Field | Meaning |
|---|---|
name |
Session name and the argument to roost <name> |
path |
Project directory (a leading ~ is expanded) |
flags |
Comma-separated; startup includes it in roost -s |
command |
Optional; run in the new session (an editor, agent, dev server) |
display |
Optional label in roost --list; defaults to the folder name |
Trailing fields are optional, so name | path alone works. Lines starting with
# are comments; blank lines are ignored. Example:
web | ~/code/web-app | startup | npm run dev | Web App
api | ~/code/api-service | startup | | API
notes | ~/notes | | |
A project does not have to be listed. roost <name> also works for any
directory at your root (default ~/code/<name>), so ad-hoc projects need no
config entry.
Optional @-directives at the top of the config set defaults, so you do not
need environment variables for everyday settings. A matching environment
variable still takes precedence over the directive.
@root ~/code # base dir for unlisted projects (roost <name> -> <root>/<name>)
@default_cmd nvim # command run in every new session
@main main # name of the base session that plain `roost` attaches to
Setting precedence is: environment variable, then @-directive, then built-in
default.
roost Attach to your base session (creates it if needed)
roost <name> Create or re-attach a session for a project
roost -s, --startup Start every 'startup' project, attach to the first
roost -l, --list List configured projects and their session status
roost -h, --help Show help
roost is smart about where you run it:
- From a plain terminal, it attaches that terminal to the session.
- From inside tmux, it switches your current client to the session (no nested-session mess).
Either way you never spawn a duplicate: an already-running session is reused.
roost -s # morning: boot web, api, and friends; land in the first
# ... work, Ctrl-a s to hop between them ...
roost notes # jump to a project not in the startup groupSpotlight only launches .app bundles, not shell commands. To make roost
Spotlight-searchable, build a small wrapper app:
extras/make-launcher.sh # app runs: roost
extras/make-launcher.sh -s # app runs: roost -s (boot startup projects)It creates ~/Applications/Roost.app. Press Cmd+Space, type Roost, and hit
Return to open Terminal straight into your sessions. Remove it with
rm -rf ~/Applications/Roost.app.
| Variable | Default | Purpose |
|---|---|---|
ROOST_CONFIG |
~/.config/roost/projects.conf |
Config file path |
ROOST_ROOT |
~/code |
Base dir for unlisted projects |
ROOST_DEFAULT_CMD |
(none) | Command run in every new session |
ROOST_MAIN |
main |
Name of the base session |
ROOST_NO_ATTACH |
0 |
1 creates sessions but does not attach |
ROOST_DEFAULT_CMD is handy if you always open the same tool in a project. For
example, export ROOST_DEFAULT_CMD=nvim starts each session in your editor.
roost is a thin wrapper over standard tmux commands:
roost <name>runstmux new-session -d(only if the session is not already running), optionallysend-keysfor the project command, thenattach-sessionorswitch-clientdepending on whether you are inside tmux.roost -srepeats that for every project flaggedstartup.- The session name equals the project name, matched exactly, so names never collide by prefix.
Nothing runs in the background except tmux itself. Delete the script and your config and roost is gone; your tmux sessions are unaffected.
roost gets you into a session; tmux keys move you around inside it. The default
prefix is Ctrl-b (many people remap it to Ctrl-a). Press the prefix, release,
then the next key.
| Action | Keys |
|---|---|
| Detach (leave it running) | prefix then d |
| List / switch sessions | prefix then s |
| New window (tab) | prefix then c |
| Rename window | prefix then , |
| Next / previous window | prefix then n / p |
| Split panes | prefix then % / " |
Reattach later from any terminal: tmux attach -t <name> (or just
roost <name>).
rm -f ~/.local/bin/roost # remove the command
rm -rf ~/.config/roost # remove your config (optional)
rm -rf ~/Applications/Roost.app # remove the launcher, if created
rm -rf ~/code/roost # remove the cloneRemove the roost block you added to ~/.zshrc as well.
Issues and pull requests are welcome. roost aims to stay small: a single
dependency-free Bash script that does one thing well. Please run
shellcheck on bin/roost before submitting.
MIT (c) 2026 Corvalon.