Custom Commands¶
Custom commands let you bind shell commands, tmux sessions, zellij sessions, Herdr workspaces or sessions, OCI container execution, or output views to keys. Commands can appear in help/footer and in the command palette. Prefix a command key with _ to make it command-palette only.
Defaults: t opens tmux, Z opens zellij, and H opens Herdr. Override any key with your own command definition.
Quick Start Patterns¶
Complete Configuration Example¶
custom_commands:
universal:
e:
command: nvim
description: Editor
show_help: true
s:
command: zsh
description: Shell
show_help: true
T: # Run tests and wait for keypress
command: make test
description: Run tests
show_help: false
wait: true
o: # Show output in the pager
command: git status -sb
description: Status
show_help: true
show_output: true
c: # Open Claude CLI in a new terminal tab (Kitty, WezTerm, or iTerm)
command: claude
description: Claude Code
new_tab: true
show_help: true
t: # Open a tmux session with multiple windows
description: Tmux
show_help: true
tmux: # If you specify zellij or herdr instead of tmux this would manage those sessions
attach: true
on_exists: switch
windows:
- name: claude
command: claude
- name: shell
command: zsh
- name: lazygit
command: lazygit
Palette lists the tmux sessions and Herdr workspaces tagged with a worktree under their full names, plus zellij sessions, Herdr sessions and older untagged sessions matching session_prefix (default: wt-). Herdr session entries are labelled without that prefix, and selecting one attaches to the full session name exactly as discovered. See Session Discovery.
Palette-only commands keep their _name identifier for configuration and CLI use, but they do not consume a direct TUI keybinding or appear in footer key hints.
Pane Scoping¶
Custom commands are nested under a pane scope. universal commands are available in every pane. Use a pane name to restrict a command to that pane. Pane-specific bindings override universal bindings for the same key.
Available pane names: universal, worktrees, info, status, log, notes, agent_sessions
custom_commands:
universal:
e:
command: nvim
description: Editor
show_help: true
status:
e:
command: nvim --diff
description: Editor (diff mode)
show_help: true
Field Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
command |
string | "" |
Command to execute (optional when using a container) |
description |
string | "" |
Shown in help and palette |
show_help |
bool | false |
Show in help screen (?) and footer |
wait |
bool | false |
Wait for keypress after completion |
show_output |
bool | false |
Show stdout/stderr in pager (ignores wait) |
new_tab |
bool | false |
Launch in new terminal tab. Can be used with tmux/zellij/herdr (Kitty with remote control enabled, WezTerm, or iTerm). Inside Herdr, workspace mode focuses in place instead. |
tmux |
object | null |
Configure tmux session |
zellij |
object | null |
Configure zellij session |
herdr |
object | null |
Configure Herdr workspace or session |
container |
object | null |
Run inside an OCI container; specifying just an image uses its defaults (combinable with tmux/zellij/herdr) |
tmux Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
session_name |
string | #<number> <title> for pull request worktrees, otherwise the worktree name |
Session name (env vars supported, special chars replaced) |
attach |
bool | true |
Attach immediately; if false, show modal with instructions |
on_exists |
string | switch |
Behaviour if session exists: switch, attach, kill, new |
windows |
list | [ { name: "shell" } ] |
Window definitions for the session |
If windows is empty, a single shell window is created.
tmux Window Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | window-N |
Window name (supports env vars) |
command |
string | "" |
Command to run in the window (empty uses your default shell) |
cwd |
string | $WORKTREE_PATH |
Working directory for the window (supports env vars) |
zellij Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
session_name |
string | wt:$WORKTREE_NAME |
Session name (env vars supported, special chars replaced) |
attach |
bool | true |
Attach immediately; if false, show modal with instructions |
on_exists |
string | switch |
Behaviour if session exists: switch, attach, kill, new |
windows |
list | [ { name: "shell" } ] |
Tab definitions for the session |
If windows is empty, a single shell tab is created. Session names with /, \, : are replaced with -.
zellij Window Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | window-N |
Tab name (supports env vars) |
command |
string | "" |
Command to run in the tab (empty uses your default shell) |
cwd |
string | $WORKTREE_PATH |
Working directory for the tab (supports env vars) |
Herdr Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
mode |
string | workspace |
workspace creates a workspace per worktree; session creates a named Herdr session |
session_name |
string | #<number> <title> for pull request worktrees, otherwise the worktree name |
Workspace label in workspace mode, session name in session mode (env vars supported; session names have special chars replaced and default to session_prefix plus the worktree name) |
herdr_session |
string | "" |
Herdr session hosting the workspace (workspace mode only; empty uses the current or default session) |
attach |
bool | true |
Attach or focus immediately; if false, show modal with instructions |
on_exists |
string | switch |
Behaviour if the workspace or session exists: switch, attach, kill, new (for Herdr, attach and switch both reuse the existing workspace or session) |
worktree_group |
bool | true |
Nest workspaces under their repository in the Herdr sidebar via herdr worktree open; false creates plain workspaces |
windows |
list | [ { name: "shell" } ] |
Tab definitions for the workspace |
If windows is empty, a single shell tab is created. Herdr session names are limited to letters, digits, ., _, and -, with a maximum of 64 bytes; other characters are replaced with -. Workspace labels are used exactly as configured. A name that sanitises to nothing is rejected rather than silently falling back to the default session.
Herdr Window Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | window-N |
Tab label (supports env vars) |
command |
string | "" |
Command to run in the tab (empty uses your default shell) |
cwd |
string | $WORKTREE_PATH |
Working directory for the tab (supports env vars) |
Herdr tab commands, including multi-line ones, are written to a temporary script and run with bash -l from the tab's shell, as tmux windows are. When the command finishes, the tab returns to your default shell.
Container Fields¶
For a step-by-step walkthrough with examples, see the Container Execution guide.
| Field | Type | Default | Description |
|---|---|---|---|
image |
string | required | Container image (e.g. golang:1.22, node:20) |
runtime |
string | auto-detect | Container runtime binary (docker or podman; podman preferred) |
mounts |
list | [] |
Additional bind mounts (worktree auto-mounted to working dir) |
env |
map | {} |
Extra environment variables for the container |
working_dir |
string | /workspace |
Working directory inside the container |
extra_args |
list | [] |
Additional docker/podman run arguments |
args |
list | [] |
Arguments passed after the image (as CMD) |
interactive |
bool | false |
Allocate TTY for interactive use (-it flags) |
Each mount entry has:
| Field | Type | Default | Description |
|---|---|---|---|
source |
string | required | Host path |
target |
string | required | Container path |
read_only |
bool | false |
Mount as read-only |
The worktree path is automatically mounted to the working directory. If a user-specified mount targets the same path as working_dir, the automatic mount is skipped. Managed worktree and LAZYWORKTREE_* environment variables are forwarded into the container automatically.
entrypoint and command: entrypoint replaces the image's default executable. command passes arguments to it through sh -c. When both are set, the custom entrypoint runs with the command as its argument. With only entrypoint, the container runs it directly, which suits interactive shells or tools that need no extra arguments. With only command, the image's default entrypoint runs it. If neither is set, the image uses its defaults.
When combined with tmux, zellij, or herdr, each window/tab command is individually wrapped in a container invocation.
Environment Variables¶
Available to commands, tmux/zellij/herdr templates, terminal tabs, show_output, and containers:
| Variable | Description |
|---|---|
WORKTREE_BRANCH |
Branch checked out in the selected worktree |
MAIN_WORKTREE_PATH |
Path to the main/root worktree |
WORKTREE_PATH |
Full path to the selected worktree |
WORKTREE_NAME |
Basename of the selected worktree directory |
REPO_NAME |
Repository key, usually owner/repo |
REPO_OWNER |
Repository owner when available |
REPO_REPONAME |
Repository name without the owner |
LAZYWORKTREE_TYPE |
Source context type, such as pr, issue, or diff when known |
LAZYWORKTREE_NUMBER |
PR/MR or issue number when known |
LAZYWORKTREE_TEMPLATE |
Branch-name template used during PR/MR or issue creation when known |
LAZYWORKTREE_SUGGESTED_NAME |
LazyWorktree's default branch/worktree name suggestion when known |
LAZYWORKTREE_TITLE |
PR/MR or issue title when known |
LAZYWORKTREE_URL |
PR/MR or issue URL when known |
LAZYWORKTREE_DESCRIPTION |
PR/MR or issue body when known |
LazyWorktree manages all of these keys for command execution. If source context is unavailable, contextual LAZYWORKTREE_* values are set to empty strings so stale parent-shell values do not leak into commands.
For custom_create_menus, the pre-create command runs before a branch and worktree path exist. It receives MAIN_WORKTREE_PATH, REPO_NAME, REPO_OWNER, REPO_REPONAME, and empty managed WORKTREE_*/LAZYWORKTREE_* values. Its post_command runs after creation and receives the full generated worktree environment.
Supported Key Formats¶
- Single keys:
e,s - Modifiers:
ctrl+e,alt+t - Special keys:
enter,esc,tab,space - Palette-only identifiers:
_review,_deploy
Example:
custom_commands:
universal:
"ctrl+e":
command: nvim
description: Open editor with Ctrl+E
"alt+t":
command: make test
description: Run tests with Alt+T
wait: true
Key Precedence¶
keybindings: entries take priority over custom commands, which in turn override built-in keys. Pane-specific bindings (in both keybindings: and custom_commands:) override universal bindings for the same key when that pane is focused.