#tmux-session-manager #git #tmux #session-manager #productivity

bin+lib celeris

A powerful, git-aware tmux session manager written in Rust with a dynamic control layer in lua, which allows for fast switching between sessions with a fuzzy picker or anything else thanks to the modular cli design

4 releases

Uses new Rust 2024

0.1.3 Oct 29, 2025
0.1.2 Sep 3, 2025
0.1.1 Aug 20, 2025
0.1.0 Aug 20, 2025

#1247 in Command line utilities

Download history 210/week @ 2025-08-15 95/week @ 2025-08-22 113/week @ 2025-08-29 56/week @ 2025-09-05 7/week @ 2025-09-12 1/week @ 2025-09-19 3/week @ 2025-09-26 4/week @ 2025-10-03 71/week @ 2025-10-24

76 downloads per month

MIT/Apache

4MB
3.5K SLoC

celeris

A powerful, git-aware session manager written in Rust with a dynamic control layer in lua

tests crates.io license

FeaturesInstallationUsageConfiguration

video showcasing the main functionality

video showcasing the main functionality

Features

  • Quickly switch between sessions
  • Create pre-configured layouts with lua which grants a lot more freedom than a declarative config
  • Create layouts from git repos detected on the system
  • Use custom templates to apply a default configuration for all layouts
  • Integrate celeris into the tmux status bar
  • Quickly switch to the last loaded layout(is helpful for example to launch the last loaded layout whenever tmux opens)
  • Assemble your own workflows thanks to the modular design of the cli

Requirements

  • tmux

Installation

cargo install celeris

Usage

Creating a layout

Creating a layout is pretty simple, just pass the path:

celeris create <path>

Optionally a custom name can be supplied with the -n flag(will be deduced automatically otherwise). The layout file will be opened in your $EDITOR or if you set the editor in the main config that will take precedence. You can also disable opening the layout file in the editor and just rely on the template file which we'll cover in next sections with the -d flag.
Created layouts are located in <config-dir>/celeris/layouts/(which is most commonly ~/.config/celeris/layouts/)

Creating layouts from git repos

You can search for git repositories on your system by doing:

celeris search

[!NOTE]
For this to work you have to specify roots from which the search should be started in the main config file. Please look at the config section for exact info on how to do that.

Now this can be used in a number of ways. Firstly you can create layouts from all those repos with:

celeris search | celeris create-all

A default template will be used for all of them, of course it can be changed. Secondly you can combine it with fzf to get a nice picker of the repos you want to create:

celeris create "$(celeris search | fzf --tmux)"

Configuring celeris

There will be a generated config usually at ~/.config/celeris/config.toml.

depth = 10 # Set the default depth of search
search_subdirs = false # Search in subdirectories of repositories. Default is `false`. Note, enabling this can significantly lengthen the search.

# Search roots from which the search will begin
search_roots = [
    { path = "/home/sentience/sources/projects/", depth = 3 }, # optionally a depth on a per-root basis can be supplied
    { path = "/home/sentience/dotfiles", excludes = ["wallpapers"] } # optionally an exclude list on a per-root basis can be supplied
] 

excludes = ["_deps"] # Excludes supplied directory names from the search
disable_template = false # Don't generate a template for each layout created
editor = "nvim" # Overrides the $EDITOR environment variable

Configuring the layout

The configuration of a layout as mentioned uses lua. It serves excellently as a dynamic control layer for all tmux commands. The interface is designed to be way nicer to the user than the default tmux experience. Great effort was put into making the state explicit, instead of relying on the usual implicitness of tmux. As an example let us look at a slightly modified version of the default template:

[!NOTE] All options supplied to constructor functions(those .new functions) are optional, they're shown here to demonstrate the full functionality

local celeris = require("celeris")

local session_root = "/tmp/";

-- Create a session
local session = celeris.Session.new({
    root = session_root -- the root is the working directory in which the session will start in
})

-- Create a named window
local window = celeris.Window.new(session, {
    name = "editor" -- name of the window
    root = "/tmp" -- window's working directory
})

-- Runs a command on a pane
window:default_pane():run_command("nvim")

-- Splits a pane into two panes either vertically or horizontally. The direction argument can be either "horizontal" or "vertical"
local _another_pane = window:default_pane():split("horizontal", {
    size = "20%" -- the size of a pane can be a percentage or can be an absolute value(just omit the %)
    root = "/tmp" -- pane's working directory
})

-- Selects a window to be focused
window:select()

-- Finally attaches to a session
session:attach()

And that's pretty much all there is to it.

One more thing

We have all these nice functions, but what if there isn't a function that makes tmux to do what I want? Well say no more my friend I got something for you.

local output = celeris.rawCommand({ "display-message", "-p", "hello world!" })
print(output)

As you may have figured out already the rawCommand function will execute something similar to tmux display-message -p "hello world!" under the hood. Of course we will get a nice hello world! message back. This by itself, should satisfy some usecases but not all. What if I'd like to interact with a pane that is managed by the lua interface? Well here target methods come in:

local window = celeris.Window.new(session, { name = "foo" })

local window_target = window:target()
local window_name = celeris.rawCommand({ "display-message", "-p", "-t", window_target, "#{window_name}"})
assert(window_name == "foo")

Each tmux component(session, window, pane) has a target method that can be used for interoperability with custom tmux commands.

[!WARNING] Remember with great power comes great responsibility. If you interact with components managed by celeris without knowing exactly what you're doing all sorts of weird things may happen. For example if you delete the pane that celeris manages it will most likely error out on you when it will try to use it.

Switching between layouts

Relevant commands for this section:

celeris list 

Lists running and configured sessions(can be tweaked).

celeris switch

If a session is running switches to it, if it's not then loads it from the layout file if exists.

There is the -l/--last-session flag which spawns the last layout loaded previously. Combining this with a bit of shell script in .zshrc(or .bashrc, whatever you use):

# if tmux is not running launch celeris
if [[ ! `tmux server-info` ]]; then
    celeris switch -l
fi

And we get this: video showcasing the last tmux session being auto-opened


Now when we've created our layouts we can switch between the sessions quickly by configuring a binding in tmux:

bind 'j' run-shell "celeris switch `celeris list | fzf --tmux` || true"

[!NOTE] Note the || true at the end. It's there as a workaround because normally when a process exits with a non-zero exit code tmux shows it's output on the screen, which can be useful for debugging, but it's also incredibly annoying

Now you can use whichever picker you want here. The possibilities are endless.

Integrating with tmux

set -g status-right " #(celeris list --tmux-format --only-running)"

With this we get a nice status bar which shows us in which session we are and which other ones are running: image of a status bar showing active sessions

Other obvious commands

Here are some helper commands which can be useful

celeris edit <name>

Opens the layout with this name in the $EDITOR(or you can set it to something else it in the main config).

celeris remove <name/s>

Removes one or more layouts with supplied names:

Custom template

This template will be automatically written in by default to every layout created. To use a custom one create a file at <config_dir>/template.lua(which is usually ~/.config/celeris/template.lua) Here's an example file:

local celeris = require("celeris")

local session_root = "{{session_root}}" -- Gets replaced with the real path on creation
local session = celeris.Session.new({ root = session_root })

local nvim = celeris.Window.new(session, { name = "nvim" })
nvim:default_pane():run_command("nvim")

local build = celeris.Window.new(session, {})
build:default_pane():split("horizontal", {})

nvim:select()
session:attach()

The only notable thing here is {{session_root}} it will get replaced with the real path at creation. The template file uses the handlebars templating syntax. Here is a list of patterns that will be replaced at runtime:

  • {{session_root}}
  • {{session_name}}

Contributions

Contributions are always welcome! If you have a bug or a feature feel free to make an issue or a PR.

Acknowledgments

Dependencies

~31MB
~603K SLoC