LAZYWORKTREE(1)
β¨ NAME β¨
Okay so real quick β say hi to lazyworktree (that's the LAZYWORKTREE(1) you're holding rn π
). It's a TUI tool to manage git worktrees. That's the whole one-liner, and honestly? It ate.
πΈ SYNOPSIS πΈ
Here's the shape of every way you can call her:
lazyworktree [OPTIONS]
lazyworktree wt-create [--from-branch BRANCH] [--name NAME] | [--from-pr
NUMBER] | [--from-pr-interactive|-P] | [--from-issue NUMBER] |
[--from-issue-interactive|-I] [--query|-q STRING] [--with-change]
[--silent]
lazyworktree wt-rename [--silent] [WORKTREE] [NEW-NAME]
lazyworktree wt-delete [--no-branch] [--silent]
lazyworktree cleanup [--all]
No cap, that's the whole menu of entry points π«Ά
π¦ DESCRIPTION π¦
lazyworktree is a BubbleTea-based Terminal User Interface (TUI) built for efficient Git worktree management. Literally what it says on the tin, bestie. It lets you:
- visualise the repository's status π
- oversee branches πΏ
- navigate between worktrees with ease β¨
It's giving "finally, my worktrees have a home screen."
π FEATURES π
Sit down, because the feature list is a whole mood board:
- Worktree Management β Create, rename, delete, absorb, and prune merged worktrees. The full lifecycle, sorted π
- Cherry-pick Commits β Copy commits from one worktree to another via an interactive worktree picker. Lowkey my fave π
- Commit Log Details β The commit pane shows author initials alongside commit subjects. Cute and useful π«
- Base Selection β Select a base branch or commit from a list, or enter a reference when creating a worktree. Pick your starting point, queen π
- Forge Integration β Fetch and display associated Pull Request (GitHub) or Merge Request (GitLab) status and CI checks with icons from the selected icon set when enabled, including optional author and reviewer avatar badges on Kitty-compatible terminals πΌοΈ
- Reviewers β Show how many people have submitted a review on the selected pull or merge request, who they are, and what each of them decided; see
pr_reviewersunder CONFIGURATION. Receipts, basically π΅οΈ - Create from PR/MR β Establish worktrees directly from open pull or merge requests via the create worktree menu (
c) β¨οΈ - Create from current branch β Start a worktree from the branch you currently occupy; the branch name prompt offers a friendly random suggestion that you may override, and the checkbox shown during naming optionally carries over uncommitted work. Yes, it saves your messy uncommitted stuff ππ«Ά
- Checkout existing branch β When selecting an existing local branch, choose to associate a worktree with it directly (no new branch created) or create a new branch based on it π±
- Create from Issue β Establish worktrees from GitHub/GitLab issues with automatic branch name generation π€
- Status at a Glance β View dirty state, ahead/behind counts, and divergence from main. Vibe check, but for your repo π
- Tmux Integration β Create and manage tmux sessions per worktree with multi-window support πͺ
- Zellij Integration β Create and manage zellij sessions per worktree with multi-tab support ποΈ
- Herdr Integration β Create and manage Herdr workspaces or named sessions per worktree with multi-tab support π
- Container Execution β Run custom commands inside OCI containers (
docker/podman) with automatic worktree mounting π³ - Diff Viewer β View diff with optional delta support π
- Repo Automation β
.wtinit/terminate commands with TOFU security π - LazyGit Integration β Launch
lazygitdirectly for the currently selected worktree π - Mouse Support β Click to focus panes, select items, and scroll with the mouse wheel π±οΈ
- Metadata Menu β Press
ein the worktree pane to edit the selected worktree's description, colour, notes, icon, and tags βοΈ
β OPTIONS β
Every flag, bestie. No skipping, no summarising, we're doing this properly π
--worktree-dir DIRβ Override the default worktree root directory. Default:~/.local/share/worktrees$LWT_REPO_PATHis automatically set to the git repository root, enabling repo-local placement:$LWT_REPO_PATH/.worktrees. When the directory is inside the repository, the<repoName>segment is omitted so worktrees land directly under the configured directory. Add the directory to.gitignoreso it does not appear ingit status. π--theme NAMEβ Select a UI theme. Available themes:dracula,dracula-light,narna,clean-light,catppuccin-latte,rose-pine-dawn,one-light,everforest-light,everforest-dark,solarized-dark,solarized-light,gruvbox-dark,gruvbox-light,nord,monokai,catppuccin-mocha,modern,tokyo-night,one-dark,rose-pine,ayu-mirage,kanagawa. If unspecified,lazyworktreeattempts to auto-detect the theme based on the terminal's background colour (defaulting torose-pinefor dark ordracula-lightfor light). Aesthetic autopilot β¨--show-syntax-themesβ Display the default delta syntax-theme values for each UI theme. So you can see what your diffs will look like before committing to a look π--search-auto-selectβ Start with filter focused and select first match on Enter. Efficiency icon π--output-selection FILEβ Write the selected worktree path toFILEon exit (for shell integration). Script girlies rise π--debug-log PATHβ Path to debug log file for troubleshooting. For when things are not slaying π§--versionβ Print version information and exit. π·οΈ--completion SHELLβ Generate shell completion script (bash,zsh,fish). Tab-tab-tab supremacy β¨οΈ--config-file FILEβ Path to configuration file, overriding the default XDG config directory location. Your config, your rules ποΈ--config KEY=VALUEβ Override config values from the command line (repeatable). Format:--config=lw.key=valueSupported keys:theme,worktree_dir,sort_mode,auto_refresh,disable_pr,search_auto_select,fuzzy_finder_input,icon_set,avatar_badges,pr_reviewers,palette_mru,palette_mru_limit,git_pager,git_pager_args,git_pager_interactive,git_pager_command_mode,pager,editor,commit.auto_generate_command,max_untracked_diffs,max_diff_chars,refresh_interval_seconds,trust_mode,merge_method,branch_name_script,worktree_note_script,worktree_notes_path,worktree_note_type,issue_branch_name_template,pr_branch_name_template,session_prefix,use_herdr_for_workspace_create,herdr_workspace_create_layout,init_commands,terminate_commands. Examples:--config=lw.theme=nord,--config=lw.sort_mode=activeThis has the highest precedence and overrides all other configuration sources. π Like, all of them. Periodt.
π§ SUBCOMMANDS π§
list
List all worktrees in tabular form without launching the TUI. Just the facts, no glitz π
Options:
--jsonβ Output worktrees as a JSON array. Each entry includespath,name,branch,head,is_main,is_current,has_changes,linked_pr,pr_status,description,tags,note_present,note_updated_at,agent_sessions,agent_open,agent_activity, andagent_countfields. Mutually exclusive with--pristine. π§Ύ--pristineβ Show only worktrees with no uncommitted changes. Mutually exclusive with--json. Clean girl aesthetic π§Ό--mainβ Include the main worktree in the output (excluded by default). She's shy π«£--no-agentβ Skip agent session loading. Omitsagent_sessions,agent_open,agent_activity, andagent_countfrom JSON output. Use for faster scripting when agent state is not needed. Speedrun mode β‘
create
Create a new worktree without launching the TUI. Headless, but make it fashion π
Options:
NAMEβ Optional positional argument for the worktree and branch name. If not provided, the source branch is checked out as is in a worktree named after it (sanitised: lowercase, alphanumeric characters and hyphens only); when that branch is already checked out elsewhere, a new branch with a generated name is created from it instead. Can be used with or without--from-branch. π·οΈ--from-branch BRANCHβ Create worktree from the specified branch. If not provided, uses the current branch from the working directory. If a name is provided as a positional argument, a branch of that name is created from the source branch. Otherwise the branch itself is checked out, unless it is already checked out in another worktree, in which case a new branch with a generated name is created. A remote branch such asorigin/foois checked out as a localfoobranch, created to track the remote when it does not yet exist. πΏ--from-pr NUMBERβ Create worktree from PR number. Fetches the specified PR and generates the worktree name frompr_branch_name_template(with optionalbranch_name_script). The local branch name always matches the PR head branch name. π--from-issue NUMBERβ Create worktree from issue number. Fetches the specified issue and creates a worktree with a sanitised branch name derived from theissue_branch_name_template(default:issue-{number}-{title}, e.g.,issue-42-fix-memory-leak). Uses the current branch as the base; combine with--from-branchto specify a different base. If the branch already exists locally it is reused instead; when it is already checked out in a worktree, that worktree's path is returned. ποΈ--from-issue-interactive,-Iβ Interactively select an open issue to create a worktree from. Whenfzfis installed, presents a searchable list with issue body preview; otherwise displays a numbered list for selection. Issues that appear to have a worktree already are marked[worktree]and shown in green; detection matches theissue_branch_name_templatetext before{number}, plus the issue number, against branch and worktree directory names, so renamed branches are not detected. Shares the same constraints as--from-issue(mutually exclusive with--from-pr, cannot combine with--with-changeor a positional name). Combine with--from-branchto specify a base branch. π’--from-pr-interactive,-Pβ Interactively select an open pull request to create a worktree from. Whenfzfis installed, presents a searchable list with PR metadata preview (author, branches, draft status, CI); otherwise displays a numbered list for selection. Pull requests whose branch is already checked out in a worktree are marked[worktree]and shown in green. Mutually exclusive with--from-pr,--from-issue,--from-issue-interactive, and--from-branch. Cannot combine with--with-changeor a positional name. π--query STRING,-q STRINGβ Pre-filter the interactive selection list. Requires--from-pr-interactiveor--from-issue-interactive. Whenfzfis available, the search box is pre-filled with the given string; otherwise the numbered list is filtered to items whose display line contains the string (case-insensitive substring match). Type-ahead slay π--generateβ Generate the worktree name automatically from the current branch. Cannot be used with a positional name argument or--from-pr. Auto-magic πͺ
β¦and that's genuinely not the end of the page, bestie β more is coming π
--with-changeβ carries your uncommitted changes over into the shiny new worktree π«Ά works with the current branch or with--from-branch. It stashes what's in your current worktree, creates the new worktree, then applies the stash. Nope, can't be used with--from-pror--from-issueβ those are a different vibe entirely.--no-workspaceβ skips worktree creation entirely, bestie. Instead it makes a local branch and switches to it right there in your current working directory. It must be used with--from-pr,--from-pr-interactive,--from-issue, or--from-issue-interactive. Cannot be combined with--with-change,--generate, or a positional name argument. And instead of a worktree path, it outputs... the branch name π--update-on-existing,-Uβ if the target worktree path already exists, this fast-forwards it to the latest source when that's actually safe to do β¨. Things that get left alone (with a message printed to stderr, though the path is still returned, no stress): worktrees with uncommitted or untracked changes, local commits ahead of the source, or diverged histories. For diverged worktrees the message suggestsgit rebaseandgit reset --hardcommands so you can resync manually. Local commits are never discarded β periodt. For PR/branch worktrees it fetches the remote head and fast-forwards; for issue worktrees it just returns the existing path. Can't be combined with--with-changeor--no-workspace.--silentβ mutes all the progress messages to stderr π€«. Only the worktree path goes to stdout, unless you're using--output-selection. Ideal for scripting and automation, iykyk.--exec COMMANDβ runs a post-create command through your current shell environment. It runs in the created worktree directory, or in the current directory when you're using--no-workspace. Shell mode follows$SHELL: zsh uses-ilc, bash uses-ic, and other shells fall back to-lc.--output-selection FILEβ writes the created worktree path to FILE π. When it's set, stdout output gets suppressed.--note TEXTβ sets an initial note body on the worktree you just made. The note lives in the configured notes file.--note-file FILEβ reads the initial note body from FILE. Use-to read from stdin. Mutually exclusive with--noteβ pick a lane πΈ.--description TEXTβ sets a short description for the worktree. It replaces the directory name in the worktree list for display purposes, and filtering matches against it too π--tags LISTβ comma-separated list of tags to attach to the worktree (likebug,frontend). Tags get stored in the note frontmatter and show up as coloured badges in the TUI π.--jsonβ outputs the created worktree details as JSON to stdout, while progress messages go to stderr. The JSON object containspath,name,branch,description, andtagsfields.--exec-mode MODEβ controls how the--execcommand gets invoked. Valid values:login-shellβ the default, uses interactive login flagsshellβ non-interactive$SHELL -cdirectβ splits the command string and execs without a shell wrapper
rename π¦
Rename a worktree without ever launching the TUI. Iconic, honestly.
It renames the worktree directory. The branch only gets renamed when the current worktree directory name matches the branch name π. Shell completion for the first rename argument suggests available worktree basenames.
Options:
NEW-NAMEβ the new worktree name. It's sanitised to lowercase letters, numbers, and hyphens. When it's given as the only argument, the current worktree (detected from the working directory) is what gets renamed.WORKTREE NEW-NAMEβ when you pass two arguments, WORKTREE is the worktree path, branch name, or directory name to rename, and NEW-NAME is the new name. If you pass no arguments at all,lazyworktreelists the available worktrees and prints usage π.--silentβ suppresses all progress messages to stderr. Great for scripting and automation.--jsonβ outputs the rename result as JSON to stdout. The JSON object containsold_name,old_path,new_name, andnew_pathfields. Progress messages go to stderr.
delete π
Delete a worktree without launching the TUI.
It auto-detects the current worktree from the working directory. If you're not in a worktree, it shows a selection list. It automatically deletes the associated branch if the worktree directory name matches the branch name. Shell completion for the first delete argument suggests available worktree basenames.
Options:
--no-branchβ skips branch deletion entirely, even if the worktree name matches the branch name. No branch deleting for you π--silentβ suppresses all progress messages to stderr. Useful for scripting and automation.--jsonβ outputs the delete result as JSON to stdout. The JSON object containsname,path, andbranch_deletedfields. Progress messages go to stderr.
cleanup β¨
Removes merged worktrees, stale branches, and orphaned worktree directories β all without launching the TUI.
By default it shows a numbered menu π. You can select entries with individual numbers, comma-separated numbers, ranges, or all. Press Enter with no selection to cancel. Merged local branches that have no worktrees are included when prune_stale_branches is enabled. Dirty merged worktrees stay visible with a warning and get skipped if you select them.
Terminate commands run only before eligible clean merged worktrees are removed. Orphaned directories get revalidated against Git immediately before deletion β safety first, always π«Ά
Options:
--all,--non-interactiveβ attempts every candidate without prompting. Registered worktrees with uncommitted changes are reported and skipped; clean worktrees, stale branches, and orphaned directories remain eligible for cleanup. Worktree status is rechecked immediately before removal. Actual candidate failures cause a non-zero exit, while safety skips do not.--jsonβ emits a JSON object to standard output describing the cleanup result, including aggregate counts and a per-item list recording each worktree, its branch, the detection source, whether it was skipped, and whether removal succeeded. Skipped worktrees include a reason. Requires--all. Progress messages β including terminate command notices β are suppressed.
exec π₯
Run a command, or trigger a custom command key action, in a worktree straight from the CLI.
The exec subcommand is the bridge between CLI scripting and the TUI's custom command system, so you can execute shell commands or trigger tmux/zellij/herdr sessions without ever entering the TUI. Obsessed.
Options:
COMMANDβ optional positional argument for the shell command to execute. Mutually exclusive with--key. The command runs through your shell environment ($SHELL) with the appropriate flags (zsh -ilc,bash -ic, or fallback-lc).--workspace NAME,-w NAMEβ targets a worktree by name, path, or branch name. If you don't provide one,lazyworktreeauto-detects the worktree from the current directory. Shell completion suggests available worktree names β¨--key KEY,-k KEYβ the custom command key to trigger (e.g.,'t'for tmux,'Z'for zellij,'H'for Herdr, or'_review'for a palette-only action). Mutually exclusive with the positional command argument. The key must exist in your configuration'scustom_commands. Supportsshell,tmux,zellij,herdr, andshow-outputcommand types. Heads up:new_tabis ignored for tmux and zellij commands, because they have no terminal tab to open β their configured attach setting decides whether the session opens in the current terminal or gets created in the background with instructions for attaching later.new_tabis not supported for Herdr or plain commands in CLI mode.--jsonβ captures the command output and emits a JSON object containingname,path,command, andexit_codefields. In JSON mode, child stdout is redirected to stderr. Mutually exclusive with--key.
Environment:
The exec command sets the same managed environment variables as custom commands in the TUI: WORKTREE_BRANCH, MAIN_WORKTREE_PATH, WORKTREE_PATH, WORKTREE_NAME, REPO_NAME, REPO_OWNER, REPO_REPONAME, LAZYWORKTREE_TYPE, LAZYWORKTREE_NUMBER, LAZYWORKTREE_TEMPLATE, LAZYWORKTREE_SUGGESTED_NAME, LAZYWORKTREE_TITLE, LAZYWORKTREE_URL, and LAZYWORKTREE_DESCRIPTION πΈ
Contextual LAZYWORKTREE_* values are empty when no PR/MR, issue, or diff source context is known.
note π
Show or edit worktree notes from the CLI without launching the TUI β perfect for scripting, piping, and quick edits.
Subcommands:
show [worktree-name]β prints the raw note text for a worktree to stdout. Defaults to the worktree detected from the current directory. Suitable for piping.edit [worktree-name]β opens the note in$EDITOR(falls back to$VISUAL, thenvi). The file uses YAML frontmatter for metadata (icon,color,updated_at) followed by markdown body text.
Options (show):
--jsonβ outputs the note as a JSON object containingworktree_name,path,note,description,icon,tags, andupdated_atfields. Progress messages are written to stderr.
Options (edit):
--input FILE,-i FILEβ reads the note from a file instead of opening an editor. Use-to read from stdin. The input format is the same frontmatter+markdown the editor uses.
doctor π©Ί
Reports CLI, repository, and helper tool health for automation.
The doctor command is built for coding agents and scripts. It reports configuration loading, repository detection, worktree visibility, and helper tool availability without needing the whole TUI.
Options:
--jsonβ outputs the diagnostic result as JSON. Setup gaps are reported in the payload rather than causing a hard failure. No dramatic meltdowns π
worktrees π
Discover, resolve, and inspect worktrees with stable machine-readable output.
Subcommands:
listβ lists worktrees for the current repository. Supports--json,--main,--limit, and--no-agent.resolveβ resolves a worktree name, branch, path, or cwd into a canonical worktree path. Supports--name,--path,--cwd, and--json.get WORKTREEβ reads one exact worktree by canonical path, branch, or basename. Supports--json.context WORKTREEβ reads note and agent-session context for one worktree. Supports--includeand--json.
notes π
Reads worktree notes with machine-readable output.
Subcommands:
get WORKTREE
Returns note metadata for one exact worktree β like, exactly the one you name, no fuzzy matching bestie π
. Pass --json and you get a stable payload with worktree_name, path, note, description, icon, tags, and updated_at. Script-friendly, chef's kiss π
β¨ describe β¨
Emits the CLI command structure as machine-readable JSON. It's literally built for coding agents and scripts that need to discover flags and subcommands without parsing --help output like some kind of animal π¦.
Synopsis:
lazyworktree describe [command] [subcommand]
lazyworktree describe --all
Options:
--allβ Describes all commands and their flags. Same energy as passing no arguments at all, fr fr.
πΈ setup-hooks πΈ
Installs agent session hooks for Claude Code, the Codex CLI, and the Copilot CLI. Obsessed with this one π.
Synopsis:
lazyworktree setup-hooks [--dry-run]
The setup-hooks command adds lifecycle hooks to ~/.claude/settings.json, $CODEX_HOME/hooks.json (or ~/.codex/hooks.json when CODEX_HOME is unset), and $COPILOT_HOME/hooks/lazyworktree.json (or ~/.copilot/hooks/lazyworktree.json when COPILOT_HOME is unset) so that agent sessions report their state directly to lazyworktree. Hook events give you precise, low-cost session tracking and let Codex CLI and Copilot CLI sessions show up in the agents pane π
Claude Code and Copilot CLI hooks also report question dialogs, which means lazyworktree can show when an agent is waiting for user input instead of just thinking. Codex CLI does not currently expose an equivalent question-dialog hook π.
Existing settings are preserved, and a timestamped backup is written before any modification happens. The command is idempotent β run it as many times as you want, it's not gonna snowball on you π«Ά.
Hook events are the default liveness source. The former process-table scan (ps/lsof) is deprecated and disabled by default; it may be re-enabled with theagent_sessions.process_scanconfiguration key.
Heads up bestie π£: the Codex CLI requires newly installed hooks to be approved with the /hooks command inside a Codex session. And the Copilot CLI loads hook files at startup, so restart any running copilot session after installing. No cap.
Options:
--dry-runβ Shows you the configuration changes without writing any files. Vibes only, zero commitment.
π₯ EXAMPLES π₯
π¦ Worktree Management
List worktrees (table format):
lazyworktree list
List worktrees (paths only, suitable for scripting):
lazyworktree list --pristine
List worktrees as JSON:
lazyworktree list --json
List only the main branch worktree:
lazyworktree list --main
List only the main branch worktree as JSON:
lazyworktree list --main --json
Inspect CLI and repository health for automation:
lazyworktree doctor --json
Resolve a worktree name to its canonical path:
lazyworktree worktrees resolve --name my-feature --json
Read one exact worktree as JSON:
lazyworktree worktrees get my-feature --json
Read worktree context as JSON:
lazyworktree worktrees context my-feature --json
Read note metadata as JSON:
lazyworktree notes get my-feature --json
Create from current branch (auto-generated name):
lazyworktree create
Create from current branch with explicit name:
lazyworktree create my-feature
Check out an existing branch in its own worktree:
lazyworktree create --from-branch feature/new-feature
Create from specific branch with explicit name:
lazyworktree create --from-branch main my-feature
Create from PR number:
lazyworktree create --from-pr 123
Create from issue number (base: current branch):
lazyworktree create --from-issue 42
Create from issue number with explicit base branch:
lazyworktree create --from-issue 42 --from-branch main
Interactively select a PR (fzf or numbered list):
lazyworktree create -P
Interactively select a PR, pre-filtered:
lazyworktree create -P -q dark
Interactively select an issue, pre-filtered:
lazyworktree create -I --query login
Checkout PR branch without creating a worktree:
lazyworktree create --from-pr 123 --no-workspace
Checkout issue branch without creating a worktree:
lazyworktree create --from-issue 42 --no-workspace
Interactively select issue and checkout without creating a worktree:
lazyworktree create -I --no-workspace
Interactively select PR and checkout without creating a worktree:
lazyworktree create -P --no-workspace
Create with uncommitted changes:
lazyworktree create my-feature --with-change
Create and run a setup command in the new worktree:
lazyworktree create my-feature --exec npm install
Delete worktree (and branch if name matches):
lazyworktree delete
Delete worktree without deleting branch:
lazyworktree delete --no-branch
Choose merged worktrees and other stale entries from a numbered menu:
lazyworktree cleanup
Clean up every candidate without prompting:
lazyworktree cleanup --all
Rename the current worktree (detected from cwd):
lazyworktree rename new-feature
Rename a worktree by name:
lazyworktree rename feature new-feature
Rename by path:
lazyworktree rename /path/to/worktree new-worktree
Run a command in a specific worktree:
lazyworktree exec --workspace=my-feature make test
Run a command in the current worktree (auto-detected):
lazyworktree exec npm run build
Trigger a tmux custom command:
lazyworktree exec --key=t --workspace=my-feature
Trigger a zellij custom command in the current worktree:
lazyworktree exec -k Z
Trigger a Herdr custom command:
lazyworktree exec -k H -w my-feature
π Notes
Show the note for the current worktree:
lazyworktree note show
Show the note for a specific worktree:
lazyworktree note show my-feature
Edit a worktree note in your editor:
lazyworktree note edit my-feature
Set a note from stdin:
echo release prep | lazyworktree note edit -i -
Set a note from a file:
lazyworktree note edit my-feature -i note.md
β TUI Launch
Launch the TUI (default):
lazyworktree
Launch with custom worktree directory:
lazyworktree --worktree-dir ~/worktrees
Override theme via CLI:
lazyworktree --config lw.theme=nord
π Shell Completion
Generate zsh completion:
lazyworktree --completion zsh > ~/.zsh/completions/_lazyworktree
Generate bash completion:
lazyworktree --completion bash > /etc/bash_completion.d/lazyworktree
Generate fish completion:
lazyworktree --completion fish > ~/.config/fish/completions/lazyworktree.fish
π KEY BINDINGS π
π§ General Navigation
j,k,Up,Downβ Navigate between items in the focused pane or selection menus. Baby's first muscle memory πHomeβ Go to first item in focused pane.Endβ Go to last item in focused pane.h,lβ Shrink/grow worktree pane (h=shrink,l=grow).Tab,]β Cycle to next pane.[β Cycle to previous pane.1β Switch to Worktree pane (toggle zoom if already focused).2β Switch to Status pane (toggle zoom if already focused).3β Switch to Git Status pane (toggle zoom if already focused).4β Switch to Commit pane (toggle zoom if already focused).5β Switch to Notes pane (toggle zoom if already focused). Only visible when the selected worktree has a note π6β Switch to Agent Sessions pane (toggle zoom if already focused). Only visible when open Claude, Codex, Copilot, or pi sessions are attached to the selected worktree by default. Pressing6also reveals historical matching sessions when nothing is currently open; pressingAinside the pane toggles between open-only and all matching sessions.Tabcycling includes this pane last when visible. Iconic behaviour tbh π=β Toggle zoom for focused pane (full screen, press again to unzoom).Lβ Toggle layout between default (worktrees left with agent sessions and notes below when present, status/git status/commit stacked right) and top (worktrees full-width top, optional agent sessions and notes rows below, status/git status/commit side-by-side bottom).yβ Copy to clipboard (context-aware: worktree path in worktrees pane, file path in git status pane, commit SHA in commit pane). Uses OSC52, works over SSH. Slay π¦Yβ Copy the selected worktree's branch name to the clipboard.?β Show help screen.qβ Quit application.
π οΈ Worktree Operations
Enterβ Jump to worktree (exit and cd), or open commit file tree in commit pane.dβ Show diff in pager for the selected worktree (or selected commit in the commit pane).cβ Create new worktree (from branch, commit, PR/MR, or issue).iβ Open selected worktree notes. If a note exists, a viewer opens first; if no note exists, the editor opens directly.
In the note viewer, j/k (or arrow keys) scroll, Ctrl+D/Ctrl+U move half a page, g/G jump to top/bottom, e opens edit mode, and q/Esc closes. In the note editor, Ctrl+S saves, Ctrl+X opens the note in an external editor, Enter inserts a new line, and Esc cancels. Worktrees with non-empty notes show a note marker beside the name.
Set custom worktree icon is available in the TUI and command palette for quick task labelling, with curated icons for categories such as feature work, bugs, TODOs, reviews, refactors, blocked work, and releases. Set worktree colour and Set worktree description are available from the command palette only. The description is a short label that replaces the directory name in the worktree list for display purposes; the filter and search also match against descriptions. Set worktree colour stores a colour in notes/frontmatter and applies it to the worktree name; use the picker for named colours and 256 indices, or choose Custom for a hex value or another supported named colour.
Set worktree tags lets you type labels separated by commas when you want multiple tags (for example, "bug,frontend,urgent") and toggle existing labels in the same editor; tags display as coloured badges (guillemet brackets) after the worktree name and in the Info pane when present, each receiving a deterministic colour from the theme palette. Browse by worktree tags in the command palette to list existing labels with counts and apply an exact tag:<name> worktree filter. Tags are stored in notes/frontmatter and are included in filter and search matching πΈ
In the Info pane, notes render Markdown for headings, bold text, inline code, lists, quotes, links, and fenced code blocks. Uppercase note tags such as TODO, FIXME, or WARNING: are highlighted with icons outside fenced code blocks. Lowercase tags are left unchanged.
Tβ Opens the Taskboard, a Kanban-lite board grouped by worktree ποΈ that reads markdown checkbox items straight out of your notes (like- [ ] draft release notes). Usej/kto move around, Enter or Space to toggle a task done/not done,ato add a new task,fto filter tasks, andq/Escto close it. Honestly addictive, be warned πmβ Rename the selected worktree. Live your rebrand era β¨Dβ Delete the selected worktree. Gone, but like, respectfully π«ΆAβ Absorb a worktree into main β merges or rebases depending on your configuration. Main-character absorption fr πXβ Prune merged worktrees and stale branches. It auto-refreshes your PR/MR data from GitHub or GitLab (if you're connected to one), then sniffs out worktrees whose associated PR has been merged, or whose branch got merged into the main branch. For repos with no GitHub/GitLab remotes it just uses git-based merge detection, no cap. Whenprune_stale_branchesis enabled it also throws in local branches that are merged but have no associated worktree. Then it shows you a checklist so you pick exactly which items get removed π§Ήπ!β Run an arbitrary shell command in the selected worktree. Chaos mode: unlocked π₯rβ Refresh the worktree list (and also refreshes PR/MR/CI for the current worktree on GitHub/GitLab). The little refresh slay πRβ Fetch all remotes. All of them. No favourites π«‘Sβ Synchronise with upstream:git pull, thengit push, current branch only, requires a clean worktree, and honoursmerge_method. Very demure, very synchronised πPβ Push to the upstream branch. Current branch only, requires a clean worktree, and it prompts you to set upstream when it's missing. Push it, push it π¦sβ Cycle the sort mode (Path/Last Active/Last Switched). Reorganise your whole vibe π
π©Ί Status Pane β¨
The Status pane is where your PR info, CI checks, notes, and divergence status live. And bestie β in terminals that support OSC-8 hyperlinks, the PR/MR number in the info panel is actually clickable π±οΈβ¨
j,kβ Navigate CI checks (when they're visible).Enterβ Open the selected CI check URL in your browser πCtrl+vβ View the selected CI check logs in a pager. Reading era πCtrl+rβ Restart the CI job (GitHub Actions only). Rerun, bestie π
π³ Git Status Pane π
The Git Status pane shows your changed files in a collapsible tree view, grouped by directory. Directories get expand/collapse indicators and you toggle them with Enter. Files are sorted alphabetically within each directory level, and they come with icons from the selected icon set when that's enabled π
Enterβ Toggle a directory's expand/collapse, or show the diff for the selected file.sβ Stage/unstage the selected file or directory. Stage it or un-stage it, your call πDβ Delete the selected file or directory (with a confirmation, because we're not reckless).eβ Open the selected file in your editor βοΈcβ Commit staged changes. Press Ctrl+G from anywhere, orcin the status pane, to open the commit screen. The commit screen keeps the subject on its own line with the body beneath it, Enter moves you from subject to body, Ctrl+S saves, Ctrl+O auto-generates, and Ctrl+X opens the draft in your configured external editor when one's available. Chef's kiss commit flow ππCβ Stage all changes and commit. All of it, at once, iconic π₯dβ Show the full diff (all files) in a pager.ctrl+Left,ctrl+Rightβ Jump to the previous/next folder. Zoom around the tree π²/β Search file or directory names (incremental, so it updates as you type).Ctrl+d,Spaceβ Half page down.Ctrl+uβ Half page up.PageUp,PageDownβ Half page up/down. Two ways to do the exact same thing, and honestly? Love that for us π«Ά
π Commit Pane π§Ύ
j,k,Up,Downβ Move between commits.Enterβ Open the commit file tree view.dβ Show the full commit diff in a pager.Cβ Cherry-pick the commit to another worktree (with an interactive picker). Cherry on top, literally πctrl+jβ Move to the next commit and open the commit file tree. Two steps, one keystroke β¨/β Search commit titles (incremental).
π Commit File Tree πΈ
So when you press Enter on a commit in the commit pane, a file tree view opens showing all the files changed in that commit. The tree is collapsible by directory and includes icons from the selected icon set when enabled. It's giving organised π
j,k,Up,Downβ Navigate files and directories.Enterβ Toggle directory expand/collapse, or show the diff for the selected file.dβ Show the full commit diff in a pager.fβ Filter files by name./β Search files (incremental).n,Nβ Next/previous search match.Ctrl+d,Spaceβ Half page down.Ctrl+uβ Half page up.g,Gβ Jump to top/bottom.q,Escβ Return to the commit log. Bye bestie π
π Filter and Search π΅οΈββοΈ
fβ Filter the focused pane by fuzzy matching. When a filter is active, the pane title shows a filter indicator with an[Esc] Clearhint. Filtering narrows the visible items down to whatever matches your input. In selection menus, pressfto show the filter input, andEscreturns to the list while keeping the current filter. Fuzzy match my heart πAlt+n,Alt+pβ Move the selection and fill the filter input (worktree filter only). Fancy.Up,Down,Ctrl+j,Ctrl+kβ Move the selection without changing the filter input./β Search the focused pane incrementally. Unlike filter, search highlights matches while keeping all items visible. Usen/Nto bounce between matches.nβ Jump to the next search match.Nβ Jump to the previous search match.Escβ Clear the filter for the focused pane (when a filter is active).
π Forge Integration π
vβ View CI checks (Enter opens one in the browser, Ctrl+v views logs in a pager).Enterβ Open the selected CI job in your browser (within CI check selection).Ctrl+vβ View the selected CI check logs in a pager (within CI check selection).Ctrl+rβ Restart the selected CI job (GitHub Actions only, within CI check selection).oβ Open the PR/MR in your browser (or the root repository in your editor if you're on the main branch with a merged/closed/no PR). Either way you're going somewhere π
ποΈ Command Palette π
f1,ctrl+p,:β Open the command palette showing all available commands (like select theme, or open the commit screen) πͺ
The palette exposes a "Create from current" entry which copies the branch you currently occupy. When uncommitted files exist, the prompt shows an "Include current file changes" checkbox β Tab/Shift+Tab focuses it and Space toggles it. When it's selected, the diff gets passed to any configured branch_name_script for naming suggestions. Naming inspo, delivered β¨
Custom commands whose config key starts with _ appear in the command palette only and do not bind a direct TUI shortcut. Palette-exclusive, lowkey mysterious π
The command palette automatically lists active tmux and zellij sessions and Herdr workspaces and sessions under separate "Active ..." sections that appear after the Multiplexer section β so you can quickly switch to existing sessions without manually typing session names. Herdr workspaces are listed from the current or default Herdr session only. tmux sessions and Herdr workspaces are tagged with the worktree they belong to: they're listed whatever their name, shown under their full name with the worktree as description, and found again by that tag when the session name template changes. zellij sessions, Herdr sessions and untagged sessions from earlier releases are listed only when they start with the configured session prefix (default: wt-), which you can customise via the session_prefix configuration option. A session name already used by another worktree's session is reported as a conflict rather than reused or killed β no stealing, no murdering π. Also note that tmux does not permit colons (:) in session names, so any colons in the prefix get automatically converted to hyphens (-). The more you know π
π§ LazyGit β¨
gβ Open LazyGit for the currently selected worktree. Git, but make it lazy and cute π
π‘ Tips π
The loading modal shows contextual tips based on the current operation (for example refresh, fetch, synchronise, push, or CI rerun). When multiple tips are available for the same context, lazyworktree avoids showing the same tip consecutively β no repeats, we respect your attention span π
Curated tips are also listed in the in-application Help screen under "Tips & Shortcuts". Go read them, they're good π«Ά
π±οΈ MOUSE SUPPORT π
lazyworktree provides comprehensive mouse support for improved navigation and interaction. Yes bestie, you can click things. Groundbreaking, I know π
- Click on pane β Focus the clicked pane (
Worktree,Status,Git Status,Commit,Notes, orAgent Sessions). - Click on item β Select the clicked item in a table or list.
- Mouse wheel β Scroll up or down in the focused pane.
Mouse support works in all panes and is particularly useful for quickly switching context or selecting specific items without keyboard navigation. Your wrist says thank you π«Άβ¨
π SHELL INTEGRATION π
To enable the "jump" functionality, source the helper functions from the installation:
For source builds:
source /path/to/lazyworktree/shell/functions.zsh
jt() { worktree_jump ~/path/to/your/main/repo "$@"; }
For Homebrew:
source $(brew --prefix)/opt/lazyworktree/share/lazyworktree/functions.zsh
jt() { worktree_jump ~/path/to/your/main/repo "$@"; }
With completion (zsh):
_jt() { _worktree_jump ~/path/to/your/main/repo; }
compdef _jt jt
πΈ BRANCH NAMING CONVENTIONS πΈ
ok so when you're making worktrees with manual branch names, lazyworktree does a little glow-up on your special characters β it auto-converts them to hyphens π
. Why? Because Git and terminal multiplexers (tmux / zellij / herdr) need names they can actually handle. This keeps your worktree directories AND your session names valid across, like, every system. no cap.
β¨ Supported Characters
branch names can contain:
- Letters (
a-z,A-Z) - Numbers (
0-9) - Hyphens (
-)
everything else gets automatically turned into a hyphen. that's it, that's the whole guest list π
β¨ Character Conversions
feature.newβfeature-newβ dots are not allowed in Git branch namesbug fix hereβbug-fix-hereβ spaces are not allowed in branch namespath/to/branchβpath-to-branchβ slashes are reserved for path separatorsfeature:testβfeature-testβ colons are reserved intmux/zellij/herdrsession namestest_underscoreβtest-underscoreβ normalised to hyphens for consistency
β¨ Rules
- leading and trailing hyphens are removed
- consecutive hyphens are collapsed into a single hyphen
- maximum length: 50 characters (manual input), 100 characters (auto-generated names)
π¦ CONFIGURATION π¦
ok bestie, lazyworktree pulls config from a whole stack of places, and here's the pecking order β highest priority first, no exceptions:
- CLI overrides (using the
--configflag) - Git local configuration (repository-specific via
git config --local) - Git global configuration (user-wide via
git config --global) - YAML configuration file (
~/.config/lazyworktree/config.yamlorconfig.yml) - Built-in defaults
π Git Configuration
you can stash settings right inside Git's own config system using the lw. prefix. iconic behaviour tbh:
# Set globally
git config --global lw.theme nord
git config --global lw.worktree-dir ~/.local/share/worktrees
# Set per-repository (overrides global)
git config --local lw.theme dracula
git config --local lw.worktree-dir '$LWT_REPO_PATH/.worktrees' # Repo-local worktrees
git config --local lw.init-commands "link_topsymlinks"
git config --local lw.init-commands "npm install" # Multi-values supported
# View all lazyworktree settings
git config --global --get-regexp "^lw."
git config --local --get-regexp "^lw."
this is so flexible β you can set a default theme globally and then override it per-repository, or temporarily flip settings from the command line with --config. chef's kiss π«Ά
lazyworktree also reads ~/.config/lazyworktree/config.yaml (or config.yml) for your default settings. an example config is down below (and you can also find it in config.example.yaml).
π General Settings
worktree_dirβ the default worktree root directory. worktrees live under~/.local/share/worktrees/<repo_name>by default. the app tries to resolve repo locations viagh repo vieworglab repo view. if it can't detect the repository name, lazyworktree falls back to a locallocal-<hash>key for cache and last-selected storage. Default:~/.local/share/worktreesCan be overridden with--worktree-dir.worktree_notes_pathβ optional path to store all worktree notes in one single shared JSON file. when it's set, notes are keyed by repository/worktree-relative identifiers instead of absolute paths, which helps loads with synchronising across systems. whenworktree_note_typeissplitted, this field is treated as a path template. available variables:$REPO_NAME(owner/repo combined),$REPO_OWNER,$REPO_REPONAME, and$WORKTREE_NAME. Default: empty (per-repository.worktree-notes.jsonfiles underworktree_dir)worktree_note_typeβ the note storage type. options:onejson(single JSON file, default),splitted(individual markdown files with YAML frontmatter). insplittedmode, each worktree note gets stored as a separate markdown file at the path specified byworktree_notes_path. Default:onejsonsort_modeβ default sort order for worktrees. options:path(alphabetical),active(last commit date),switched(last accessed). Default:switchedNote: the oldsort_by_activeoption is still supported for backwards compatibility.layoutβ pane arrangement. options:default(worktrees on the left with optional agent sessions and notes below, status/log stacked on the right),top(worktrees full-width along the top, optional agent sessions and notes rows below, status/log side-by-side at the bottom). Default:defaultCan be toggled at runtime with the L key.layout_sizesβ configurable pane size weights. each sub-key accepts an integer between 1 and 100. values are relative weights that get normalised at computation time, soinfo: 30, git_status: 30, commit: 30means each one receives one-third of the secondary area. omitted fields keep their built-in defaults. focus-based dynamic resizing still applies on top of the configured baseline. sub-keys:worktrees(main pane width or height),info,git_status,commit,agent_sessions,notes. Default: unset (built-in ratios used).search_auto_selectβ start with filter focused and select the first match on Enter. Default:falseCan also be enabled with--search-auto-select.auto_refreshβ refresh git metadata and working tree status in the background. Default:trueSet it tofalseto rely on manual refresh (r).refresh_intervalβ background refresh interval in seconds. Default:10Set to0to disable timed refreshes.ci_auto_refreshβ periodically refresh CI status for GitHub repositories. when enabled, CI checks are refreshed automatically for open PRs or branches with pending CI jobs. disabled by default because it eats the GitHub API rate limit (5,000 requests/hour for authenticated users). π Default:falseci_remoteβ the git remote to target for CI and PR status queries on GitHub. when unset or set toauto, an upstream remote is preferred when present, otherwiseorigin. set it to a remote name (for exampleorigin) to target a specific remote without changing repository identity. GitLab MR/CI queries keep usingglab's own repository resolution. super useful for fork workflows where pull requests live on the upstream repository π§ Default:autodisable_prβ disable all PR/MR fetching and display. when enabled, no GitHub/GitLab API calls are made for PR/MR data, the PR column is hidden from the worktree table, and CI status fetching is disabled. handy for repositories that aren't hosted on GitHub or GitLab, or when you just don't want PR/MR integration. Default:falsedebug_logβ path to a debug log file for troubleshooting. when set, detailed debug info gets written to this file. Can also be set with--debug-log.
π User Interface
themeβ the UI colour theme. if it's left empty or unspecified, the theme is auto-detected from your terminal background. available built-in themes:dracula(default for dark),dracula-light(default for light),narna,clean-light,catppuccin-latte,rose-pine-dawn,one-light,everforest-light,everforest-dark,solarized-dark,solarized-light,gruvbox-dark,gruvbox-light,nord,monokai,catppuccin-mocha,modern,tokyo-night,one-dark,rose-pine,ayu-mirage,kanagawa. custom themes can be defined in the configuration file (seecustom_themesbelow) and will show up right alongside the built-in themes in the theme selection screen. Can also be set with--theme.icon_setβ choose your icon set. options:"nerd-font-v3","text". applies to file icons, UI indicators, and help headings. thetextset uses plain labels. if your icons render as placeholder glyphs, seticon_settotextor install a font patched with Nerd Font π. Default:"nerd-font-v3"pr_reviewersβ this one controls the reviewer line in the Info pane bestie β¨ Options:"auto"and"never".
With "auto", the Info pane spills the tea on how many people have submitted a review on the selected pull or merge request, and lists them all out β each one tagged as approved, changes requested, or reviewed without a verdict. Reviewers who got asked but haven't answered yet? Not counted, we don't do ghosting math π
Bots carry a bot icon; everybody else carries an avatar where avatar badges are available. With "never", the line is just gone β along with the forge request that would've populated it, so it's giving efficiency.
Reviewers are fetched for the selected worktree only, from GitHub or GitLab, and the result gets reused for a minute.
Default: "auto"
fuzzy_finder_inputβ turns on fuzzy finder suggestions in input dialogues π¦ When it's enabled, typing in text input fields shows you fuzzy-filtered suggestions pulled from the available options. Arrow keys to navigate the suggestions, Enter to select. It's giving "autocomplete but make it cute".
Default: false
palette_mruβ enables MRU (Most Recently Used) sorting for the command palette π₯ When enabled, a "Recently Used" section pops up at the top of the palette showing your most frequently used commands β main character commands only. Usage history is stored per-repository in.command-palette-history.json.
Default: true
palette_mru_limitβ how many recent commands show up in that command palette MRU section. Only applies whenpalette_mruis enabled, obviously.
Default: 8
max_untracked_diffsβ max number of untracked file diffs to show. Set it to0to hide untracked diffs entirely.
Default: 10
max_diff_charsβ max characters to display in diffs before truncation kicks in. Set it to0to disable truncation completely (chaotic, but valid).
Default: 200000
max_name_lengthβ max length for worktree names displayed in the table. Names longer than this get truncated with"..."appended. Set it to0to disable truncation entirely.
Default: 95
π Diff and Pager π
git_pagerβ the diff formatter/pager command. Default isdelta. Set it to an empty string to disable diff formatting and use plaingit diffoutput. You can also use alternatives such asdiff-so-fancy, no gatekeeping here.
VS Code girlies can set git_pager: code to open diffs via git difftool with proper side-by-side comparison (one window per file) π
Default: delta
git_pager_argsβ list of extra arguments passed togit_pagerfor diff rendering. If you omit it andgit_pagerisdelta, lazyworktree picks a syntax theme matching the final UI theme, including CLI theme overrides. The default mappings, in all their glory:
dracula -> Dracula, dracula-light -> Monokai Extended Light, narna -> OneHalfDark, clean-light -> GitHub, catppuccin-latte -> Catppuccin Latte, rose-pine-dawn -> GitHub, one-light -> OneHalfLight, everforest-light -> Gruvbox Light, solarized-dark -> Solarized (dark), solarized-light -> Solarized (light), gruvbox-dark -> Gruvbox Dark, gruvbox-light -> Gruvbox Light, nord -> Nord, monokai -> Monokai Extended, catppuccin-mocha -> Catppuccin Mocha.
Use --show-syntax-themes to display default values.
git_pager_interactiveβ enables interactive diff viewers that need terminal control (default:false). Set it totruefor tools likediffnav,ftdv, ortigthat give you keyboard navigation π§
When enabled, only unstaged changes (git diff) are shown. Non-interactive formatters like delta and diff-so-fancy should keep this false.
git_pager_command_modeβ enables command-based diff viewers that run their own git commands (default:false). Set it totruefor tools likelumenthat accept subcommands such aslumen diff,lumen diff <commit>.
When enabled, lazyworktree calls <git_pager> diff [args...] directly instead of piping git diff output. Iconic behaviour fr.
pagerβ the pager command forshow_outputcustom commands and the diff viewer. Default:$PAGER, fallback:less --use-color --wordwrap -qcR -P 'Press q to exit..', thenmore, thencat. A whole little ladder of options π«
When the pager is less, lazyworktree configures LESS= and LESSHISTFILE=- to ignore your user defaults. No cap.
ci_script_pagerβ pager command for CI check logs. When it's set, it runs interactively with direct terminal control (noset -o pipefail, no environment adjustments). Falls back topagerif it's not configured.
Example: ci_script_pager: "less -R" or ci_script_pager: "bat --style=plain"
CI-specific environment variables available to ci_script_pager:
LW_CI_JOB_NAMEβ name of the CI check/jobLW_CI_JOB_NAME_CLEANβ sanitised job name suitable for filenames (lowercase, hyphens only)LW_CI_RUN_IDβ GitHub Actions run ID (extracted from check URL)LW_CI_STARTED_ATβ job start time in ISO 8601 format (if available)
editorβ editor command for Status pane file edits. Default: config value, then$EDITOR, thennvim, thenvi. The classic chain, we love to see it π«Ά
π Forge Integration π
issue_branch_name_templateβ the template for branch names created from GitHub/GitLab issues β¨ Available placeholders:{number}(issue number),{title}(sanitised issue title), and{generated}(generated title frombranch_name_script, falls back to{title}if unavailable).
Default: issue-{number}-{title}
Example: with template "fix-{number}-{title}", issue #42 titled "Memory Leak" becomes branch "fix-42-memory-leak". With template "issue-{number}-{generated}" and a script configured, the generated title is used instead. Slay.
pr_branch_name_templateβ the template for worktree names created from pull/merge requests π Available placeholders:{number}(PR/MR number),{title}(sanitised PR/MR title),{pr_author}(sanitised PR author username), and{generated}(generated title frombranch_name_script, falls back to{title}if unavailable).
Default: pr-{number}-{title}
Example: with template "review-{number}", PR #123 becomes worktree name "review-123". With template "pr-{number}-{pr_author}-{generated}", PR #123 by alice can become "pr-123-alice-feat-fix-ci".
π Security and Behaviour π
trust_modeβ security setting for executing commands from.wtfiles. Options:tofu(default β prompts you on first use/change)never(never run commands)always(always run without prompting, risky β like, genuinely, we said what we said π)
merge_methodβ merge method for the "Absorb worktree" and "Synchronise with upstream" actions. Options:rebase(default β rebases onto main then fast-forwards; synchronise usesgit pull --rebase=true)merge(creates a merge commit and uses a standardgit pull)
β‘ Automation β‘
branch_name_scriptβ script to generate branch name suggestions when creating worktrees from changes, issues, or PRs π For issues/PRs, the script should output a title (e.g.,"feat-ai-session-manager") which then gets sanitised and made available via the{generated}placeholder in your configured template. For diffs, the script should output a complete branch name. The script receives content on stdin (git difffor changes, issue/PR title and body for issues/PRs). First line of output is used.
Available environment variables: LAZYWORKTREE_TYPE (pr/issue/diff), LAZYWORKTREE_NUMBER (PR/issue number), LAZYWORKTREE_TEMPLATE (configured template), LAZYWORKTREE_SUGGESTED_NAME (template-generated name using original PR/issue title).
Script operates under a 30-second timeout β so don't be doing the absolute most in there β³
worktree_note_scriptβ script to generate an initial worktree note when creating from a PR/MR or issue π The script receives title and body content on stdin and may return multiline output. If the script fails or returns empty output, worktree creation continues and no note is saved β no stress, it's not a blocker.
Available environment variables: LAZYWORKTREE_TYPE (pr/issue), LAZYWORKTREE_NUMBER, LAZYWORKTREE_TITLE, LAZYWORKTREE_URL, LAZYWORKTREE_DESCRIPTION.
Script operates under a 30-second timeout.
init_commandsβ list of commands to execute when creating a worktree. These execute before any repository-specific.wtcommands (if present).
Available environment variables: WORKTREE_BRANCH, MAIN_WORKTREE_PATH, WORKTREE_PATH, WORKTREE_NAME, REPO_NAME, REPO_OWNER, REPO_REPONAME, LAZYWORKTREE_TYPE, LAZYWORKTREE_NUMBER, LAZYWORKTREE_TEMPLATE, LAZYWORKTREE_SUGGESTED_NAME, LAZYWORKTREE_TITLE, LAZYWORKTREE_URL, LAZYWORKTREE_DESCRIPTION. Contextual LAZYWORKTREE_* values are empty when no PR/MR, issue, or diff source context is known.
Special built-in command: link_topsymlinks β and no, it's not a shell command, it's a whole vibe. It symlinks untracked/ignored files from the main worktree root, non-empty editor configs (.vscode, .idea, .cursor, .claude/settings.local.json) if it exists, ensures the tmp/ directory exists, and runs direnv allow if .envrc is present. Genuinely obsessed with this one π
use_herdr_for_workspace_createβ let Herdr create new worktrees withherdr worktree create, so each one opens as a workspace grouped under its repository β¨ Applies to the CLI, the TUI and the shell helpers. See Creating worktrees with Herdr below.
Default: false
herdr_workspace_create_layoutβ custom command key whose herdr block lays out workspaces created withuse_herdr_for_workspace_create, resolved as when pressing the key in the worktrees pane. Empty uses Herdr's plain workspace.
Default: none
terminate_commandsβ list of commands to execute when removing a worktree. These execute before any repository-specific.wtcommands (if present). Same environment variables asinit_commands.custom_commandsβ custom keybindings to run commands in the selected worktree πΈ Commands execute interactively (the TUI suspends, like lazygit) and appear in the command palette. Prefix a command key with_to make it command-palette only. Key-bound custom commands take precedence over built-in keys. The default bindings aretfortmux,ZforZellij, andHforHerdr.
Each custom command supports the following fields:
command: The command to execute (required, unless usingtmux/zellij/herdr)
descriptionβ the little blurb that shows up in the help screen and command palette (optional)show_helpβ show it in the help screen and footer hints (default:false)waitβ wait for a key press after the command finishes (default:false, and honestly it's kind of a lifesaver for quick commands)show_outputβ run non-interactively and dump stdout/stderr into the pager (default:false, and heads up: it ignoreswaitentirely)new_tabβ launch the command in a brand-new terminal tab (default:false). Can be combined withtmux,zellij, andherdrto open multiplexer sessions in new tabs instead of suspending the TUI. Currently supports Kitty (remote control has to be enabled for this, bestie), WezTerm, and iTerm. Inside Herdr, workspace mode focuses the workspace in place instead of opening a new tab. πtmuxβ configure a tmux session instead of executing a single command (object, optional)zellijβ configure a zellij session instead of executing a single command (object, optional)herdrβ configure a Herdr workspace or session instead of executing a single command (object, optional)containerβ run inside an OCI container (object, optional). Giving just an image uses its defaults. Can be combined withtmux,zellij, orherdrto run each window/tab inside a container.
Supported key formats, bestie: single keys (e, s, t), modifier combos (ctrl+e, alt+t), special keys (enter, esc, tab, space), and palette-only identifiers prefixed with _ (for example _review). π
Environment variables available in custom commands: WORKTREE_BRANCH, MAIN_WORKTREE_PATH, WORKTREE_PATH, WORKTREE_NAME, REPO_NAME, REPO_OWNER, REPO_REPONAME, LAZYWORKTREE_TYPE, LAZYWORKTREE_NUMBER, LAZYWORKTREE_TEMPLATE, LAZYWORKTREE_SUGGESTED_NAME, LAZYWORKTREE_TITLE, LAZYWORKTREE_URL, LAZYWORKTREE_DESCRIPTION. Contextual LAZYWORKTREE_* values are empty when no PR/MR, issue, or diff source context is known. (No context, no tea. π΅)
For tmux sessions, the following fields are available:
session_nameβ tmux session name, supports env vars (default:#<number> <title>for pull request worktrees, shortened to 40 characters, otherwise the worktree name). Colons, slashes, and backslashes are replaced with-. β¨attachβ iftrue, attach/switch immediately; iffalse, show info modal (default:true)on_existsβ behaviour if the session exists:switch,attach,kill,new(default:switch)windowsβ list of window definitions. Each window has:name(supports env vars),command(empty uses default shell),cwd(default:$WORKTREE_PATH). If omitted, creates a single shell window.
For zellij sessions, the following fields are available:
session_nameβ zellij session name, supports env vars (default:wt:$WORKTREE_NAME). Colons, slashes, and backslashes are replaced with-.attachβ iftrue, attach immediately; iffalse, show info modal (default:true)on_existsβ behaviour if the session exists:switch,attach,kill,new(default:switch)windowsβ list of tab definitions. Each tab has:name(supports env vars),command(empty uses default shell),cwd(default:$WORKTREE_PATH). If omitted, creates a single shell tab.
For Herdr workspaces or sessions, the following fields are available:
modeβworkspace(default) creates one workspace per worktree inside an existing Herdr session;sessioncreates a named Herdr session per worktree.session_nameβ workspace label in workspace mode, session name in session mode, supports env vars (default label:#<number> <title>for pull request worktrees, shortened to 40 characters, otherwise the worktree name; default session name: the session prefix followed by the worktree name). Herdr session names are limited to letters, digits,.,_, and-, with a maximum of 64 bytes; other characters are replaced with-. A name that sanitises to nothing is rejected instead of falling back to the default session. Workspace labels are used exactly as configured. π«Άherdr_sessionβ Herdr session hosting the workspace (workspace mode only; empty inherits the current session, default targets the default session explicitly).attachβ iftrue, attach or focus immediately; iffalse, show info modal (default:true). A reused workspace is focused before the external client attaches. Herdr cannot switch sessions from inside a running client, so session mode shows attach instructions when nested, including when the session already exists. In workspace mode, a session other than the current one shows attach instructions instead of focusing a workspace the current client cannot display.on_existsβ behaviour if the workspace or session exists:switch,attach,kill,new(default:switch). For Herdr,attachandswitchboth reuse the existing workspace or session.- Partial layouts β Herdr offers no reliable completion marker. A retry reuses a workspace left behind by a failed attempt and does not re-run the configured commands, so a partially configured workspace can be reused as-is. Use
on_exists: killto discard it and build a fresh layout;on_exists: newinstead builds a fresh workspace under an incremented label and leaves the old one in place. In session mode, these options apply when the session is running; reusing a running session ensures the worktree workspace exists, creating it when the previous attempt never got that far. - Normalised names β in session mode,
on_exists: killandon_exists: newact on the normalised session name, because Herdr sessions carry no worktree tag. When sanitisation makes two worktrees share a session name, killing or incrementing one can target the other worktree's session. Workspaces are found by their worktree tag instead. π (this one's a genuine gotcha, ngl) worktree_groupβ iftrue, open workspaces withherdr worktree openso they nest under their repository in the Herdr sidebar (default:true). Herdr opens the repository's main workspace when needed, and the repository is trusted. Bare repositories, or a refusal from Herdr, fall back to a plain workspace. Grouped workspaces are also matched by checkout path, so workspaces opened from Herdr's own picker are reused, listed and killed like tagged ones. Set tofalsefor plain workspaces.- Deleting worktrees β deleting a worktree from the TUI,
deleteorcleanupcloses its Herdr workspaces in every running Herdr session, stopping their programmes. The workspace hosting lazyworktree is left open. π₯
Creating worktrees with Herdr: set use_herdr_for_workspace_create to let herdr worktree create make every new worktree (CLI, TUI and shell helpers), grouped under its repository. Off by default. herdr_workspace_create_layout names a custom command key whose herdr block lays out the workspace, resolved as in the worktrees pane; its worktree_group is ignored. Without it, Herdr's plain workspace is used. herdr_session sees only worktree, branch and repository variables at that point. lazyworktree still prepares branches; a Herdr failure falls back to Git. The layout is applied after the init commands; a rolled-back worktree closes its workspace, and a workspace that was already open is never laid out or closed. On the trust prompt, Block skips the commands but applies the layout, whilst Cancel Setup keeps the worktree without commands or layout. A TUI started with --output-selection never focuses new workspaces. TUI warnings go to the debug log. π
windowsβ list of tab definitions. Each tab has:name(supports env vars),command(empty uses default shell),cwd(default:$WORKTREE_PATH). If omitted, creates a single shell tab. The first tab uses the name and cwd of the first window.
For container execution, the following fields are available:
imageβ container image to use (required, e.g."golang:1.22","node:20")runtimeβ container runtime binary (optional, auto-detected:podmanpreferred, thendocker)mountsβ additional bind mounts (optional). Each mount has:source(host path),target(container path),read_only(default:false). The worktree path is automatically mounted to the working directory.envβ extra environment variables passed to the container (optional). ManagedWORKTREE_*,REPO_*, andLAZYWORKTREE_*variables are forwarded automatically. π§working_dirβ working directory inside the container (default:/workspace)extra_argsβ additionaldocker/podman runarguments (optional, e.g.--network=host,--privileged)argsβ arguments passed after the image asCMD(optional). Whencommandis set, it becomes the container entrypoint via--entrypoint, andargsare passed as arguments. Whencommandis empty, the image's default entrypoint is used withargsappended. When neithercommandnorargsis set, the container runs with its image defaults.interactiveβ allocate TTY for interactive use (-itflags, default:false).
π οΈ custom_create_menus
Custom items for the worktree creation menu (triggered by the c key). The workflow, step by step: you first select a base branch, then the command runs to generate a branch name, and optionally a post-command runs after worktree creation. It's giving little assembly line, but cute. π
Each custom create menu entry supports the following fields:
labelβ display label shown in the menu (required)descriptionβ help text shown next to the label (optional)commandβ shell command that outputs a branch name on stdout (required)interactiveβ run interactively (default:false). Set totruefor TUI-based commands likefzforjayrah. Whentrue, lazyworktree suspends, the command runs in the terminal, and stdout is captured via a temp file. πΈ
post_commandβ a command that runs inside the shiny new worktree directory after it gets created (optional, bestie). It runs after the global/repoinit_commands. It gets the full managed command environment:WORKTREE_*,REPO_*, andLAZYWORKTREE_*variables. β¨post_interactiveβ run that post-command interactively (default:false). When it'strue, the TUI suspends itself so you can actually see the command's I/O. π«Ά
So like, by default commands run non-interactively with a 30-second timeout. When interactive: true, there's no timeout at all. The command output β first line only, whitespace trimmed, case preserved β becomes the suggested branch name. The pre-create command receives MAIN_WORKTREE_PATH, REPO_*, and empty managed WORKTREE_*/LAZYWORKTREE_* values, because duh, the new branch and worktree path don't exist yet π. Post-commands are the moment for automated setup stuff β creating initial commits, installing dependencies, opening editors, the whole vibe. If a post-command fails, the error gets shown but the worktree is kept. This is literally the feature for gluing in external tools like JIRA, Linear, or your own custom scripts. π₯
Example configuration:
custom_create_menus:
- label: "From JIRA ticket"
description: "Create from JIRA issue"
command: "jayrah browse 'SRVKP' --choose"
interactive: true
post_command: "git commit --allow-empty -m 'Initial commit for ${WORKTREE_BRANCH}'"
post_interactive: false
π¨ custom_themes π¨
Define custom themes that can inherit from built-in themes or from other custom themes. This is how you personalise the colour scheme without touching a single line of code, no cap. π
Two ways to define a custom theme:
- Inherit from a built-in theme and override specific colours:
custom_themes:
my-dark:
base: dracula
accent: "#FF6B9D"
text_fg: "#E8E8E8"
- Define a complete theme with no base (all 11 colour fields required, bestie β all of them):
custom_themes:
completely-custom:
accent: "#00FF00"
accent_fg: "#000000"
accent_dim: "#2A2A2A"
border: "#3A3A3A"
border_dim: "#2A2A2A"
muted_fg: "#888888"
text_fg: "#FFFFFF"
success_fg: "#00FF00"
warn_fg: "#FFFF00"
error_fg: "#FF0000"
cyan: "#00FFFF"
Custom themes can also inherit from other custom themes, which is giving family tree energy πΈ. Available colour fields (snake_case in YAML): accent, accent_fg, accent_dim, border, border_dim, muted_fg, text_fg, success_fg, warn_fg, error_fg, cyan.
Colour values must be hex format (#RRGGBB or #RGB). When you're using a base theme, only specify the colours you wanna override. When there's no base, all 11 colour fields are required. From the theme selection screen, custom themes show up right alongside the built-in ones β iconic behaviour honestly. π
π Example Configuration π
A practical config showing off the common settings, so you can steal it wholesale:
# UI and behaviour
theme: nord
icon_set: nerd-font-v3
sort_mode: switched
fuzzy_finder_input: true
auto_refresh: true
refresh_interval: 10
palette_mru: true
palette_mru_limit: 5
# Forge integration
issue_branch_name_template: "fix-{number}-{title}"
pr_branch_name_template: "pr-{number}-{generated}"
# Diff settings
git_pager: delta
max_diff_chars: 500000
# Security
trust_mode: tofu
merge_method: rebase
# Automation
init_commands:
- link_topsymlinks
- echo "Worktree ${WORKTREE_NAME} initialised"
# Custom commands
custom_commands:
t:
description: "Open tmux session"
show_help: true
tmux:
session_name: "${REPO_NAME}_${WORKTREE_NAME}"
attach: true
on_exists: switch
windows:
- name: "editor"
command: "nvim"
- name: "shell"
command: ""
_review:
command: "make review"
description: "Review current worktree"
β PREREQUISITES β
- Git 2.31+ (recommended, bestie, don't fight it)
- Forge CLI: GitHub CLI (
gh) or GitLab CLI (glab) for repository resolution and PR/MR status
π± OPTIONAL DEPENDENCIES π±
deltaβ for syntax-highlighted diffs β¨lazygitβ for full TUI git controltmuxβ for tmux integration supportzellijβ for zellij integration supportherdrβ for Herdr integration support
π EXAMPLES π
Open lazyworktree:
lazyworktree
Specify a custom worktree directory:
lazyworktree --worktree-dir ~/my-worktrees
Select a theme:
lazyworktree --theme nord
Use with the shell function for that "jump" functionality:
jt # Opens TUI, select a worktree and press Enter to jump
Custom issue branch template in configuration:
issue_branch_name_template: "bug-{number}-{title}"
This creates branches like "bug-42-memory-leak" from issue #42. Obsessed. π
π ENVIRONMENT π
These environment variables affect lazyworktree's behaviour when you use it with custom commands:
WORKTREE_BRANCHβ name of the git branchMAIN_WORKTREE_PATHβ path to the main repositoryWORKTREE_PATHβ path to the selected worktreeWORKTREE_NAMEβ name of the worktree (the directory name)REPO_NAMEβ name of the repository (from GitHub/GitLab)REPO_OWNERβ repository owner when availableREPO_REPONAMEβ repository name without the ownerLAZYWORKTREE_TYPEβ source context type, such aspr,issue, ordiffwhen knownLAZYWORKTREE_NUMBERβ PR/MR or issue number when knownLAZYWORKTREE_TEMPLATEβ branch-name template used during PR/MR or issue creation when knownLAZYWORKTREE_SUGGESTED_NAMEβ LazyWorktree's default branch/worktree name suggestion when knownLAZYWORKTREE_TITLEβ PR/MR or issue title when knownLAZYWORKTREE_URLβ PR/MR or issue URL when knownLAZYWORKTREE_DESCRIPTIONβ PR/MR or issue body when known
Heads up π«Ά β those contextual LAZYWORKTREE_* values are just empty when no PR/MR, issue, or diff source context is known. Not broken, just vibing empty.
π CONFIGURATION FILES π
~/.config/lazyworktree/config.yamlβ the main configuration file~/.local/share/lazyworktree/trusted.jsonβ Trust on First Use (TOFU) trusted file hashes~/.local/share/worktrees/<repo-name>/β the default worktree storage location
π SEE ALSO π
git-worktree(1), lazygit(1), tmux(1), zellij(1), herdr(1), delta(1)
π AUTHOR π
Chmouel Boudjnah <chmouel@chmouel.com>
π COPYRIGHT π
Apache Licence 2.0
π REPORTING BUGS π
Report bugs at https://github.com/chmouel/lazyworktree/issues β like, please do, we wanna know π
And that's the whole page, bestie π β this is lazyworktree, January 2026, page LAZYWORKTREE(1). Go forth and worktree slay π
β¨π¦
π¦ where this page comes from
- nixpkgs unstable: lazyworktree 1.52.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms