seela
A tmux session manager. Lets you fuzzy-find your projects and handles the window/pane layout based on your config.
Installation
Make sure $CARGO_HOME/bin (usually ~/.cargo/bin) is in your PATH.
Build from source
Usage
Bind it to a key in tmux: (In tmux.conf)
bind g display-popup -w 80% -h 80% -E "seela"
Config file
seela looks for config in this order:
--configflag$SEELA_CONFIG_HOME/config.toml$XDG_CONFIG_HOME/seela/config.toml~/.config/seela/config.toml
Folders
[]
= ["~/projects", "~/work"]
= ["~/projects/archive"]
= ["~/dotfiles"]
search_dirs— walked recursively, any directory with.gitis a projectexclude_paths— skipped entirely, including subdirectoriesforce_include— always included, ignores exclusion rules
If a search_dir is inside an excluded path, it still gets searched.
# ~/code/old is excluded, but ~/code/old/needed is still searched
= ["~/code", "~/code/old/needed"]
= ["~/code/old"]
fzf
[]
= true
= "tree -C -L 2 {}"
= "--height 40% --layout=reverse"
Sessions
When you pick a project, seela matches it to a session config in this order:
- Exact path match
- Project type match
- Closest path prefix
default_session
[]
= ["editor", "terminal"]
= "editor"
[[]]
= ["~/projects/myapp"] # exact or prefix match
= ["rust"] # or by project type
= ["editor", "bacon"]
= "editor"
paths and types are OR'd, either one matching is enough.
Project types
A type matches if any of its files exist in the project directory.
[[]]
= "rust"
= ["Cargo.toml"]
[[]]
= "web"
= ["package.json", "tsconfig.json"]
Windows
Windows are defined globally and referenced by name from sessions.
[[]]
= "editor"
[[]]
= ["nvim"]
Pane splits
split on a pane describes how its children are arranged:
"vertical"— side by side"horizontal"— top and bottom
ratio controls proportional size. Omit it for equal splits.
[[]]
= "dev"
[[]]
= "vertical"
[[]]
= ["nvim"]
= 0.7
[[]]
= "horizontal"
= 0.3
[[]]
= ["cargo watch"]
= 0.6
[[]]
= ["lazygit"]
= 0.4
Exec operators
Used inside a pane's exec list to control how commands run.
@confirm
Prompts before running. Good for destructive or slow commands.
= ["@confirm cargo run --release"]
# Run "cargo run --release"? [Y/n]
@run
Runs a script or command with these environment variables passed to it:
| Variable | Value |
|---|---|
SEELA_SESSION_PATH |
Absolute path to the project |
SEELA_SESSION_NAME |
tmux session name |
SEELA_WINDOW_NAME |
Current window name |
SEELA_PANE_ID |
tmux pane ID |
Supports ~ and paths relative to the config file.
= ["@run ~/scripts/setup.sh"]
= ["@run scripts/build.sh"] # relative to config file
= ["@run notify-send done"] # plain commands work too
@send-key / @sk
Sends a raw key sequence to the pane. Useful for interacting with a running app.
= ["nvim", "@sk g"] # open neovim, then sends 'g'
Keys follow tmux key names: Enter, Escape, C-c, C-l, Space, etc.
@wait / @wait-milli
Pauses before the next command.
= ["start-server", "@wait 2", "connect-client"]
= ["start-server", "@wait-milli 500", "connect-client"]
Example combining operators
[[]]
= "app"
[[]]
= "vertical"
[[]]
= [
"@confirm RUST_LOG=debug cargo run",
]
[[]]
= [
"@confirm tail -f /tmp/app.log",
]
Window hooks
Scripts that run when a window opens, not tied to any pane. Good for background tasks, notifications, or setup that doesn't need a terminal.
[[]]
= "editor"
= ["notify-send 'opened' '$SEELA_SESSION_NAME'"]
[[]]
= ["nvim"]
These environment variables are passed to hooks:
| Variable | Value |
|---|---|
SEELA_SESSION_PATH |
Absolute path to the project |
SEELA_SESSION_NAME |
tmux session name |
SEELA_WINDOW_NAME |
Current window name |
By default hooks run sequentially. Set hooks_parallel = true to run them in parallel.
[[]]
= "editor"
= true
= [
"notify-send 'ready' 'editor'",
"~/scripts/sync.sh",
]
Supports ~, paths relative to the config file, and plain commands.
If a hook fails, seela prints the exit code and stderr to your terminal. Successful hooks are silent.
Example: desktop notification on open
#!/bin/env bash
# ~/.config/seela/scripts/notify.sh
[[]]
= "editor"
= ["scripts/notify.sh"]
[[]]
= ["nvim"]
Timing
seela uses shell readiness polling to know when a pane is ready for the next command. You can tune them if commands are firing too early.
[]
= 600 # wait before sending any commands
= 60 # delay between keystrokes
= 200 # delay after Enter