An unofficial Neovim integration for Worktrunk.
Manage worktrees without leaving Neovim.
The plugin runs wt asynchronously and updates Neovim's working directory to
the path returned by Worktrunk after a switch.
- Neovim 0.10+
- Worktrunk available as
wt
With lazy.nvim:
{
"RayXpub/worktrunk.nvim",
opts = {},
keys = {
{ "<leader>ww", "<cmd>Worktrunk<cr>", desc = "Worktrees" },
{ "<leader>wc", "<cmd>WorktrunkCreate<cr>", desc = "Create worktree" },
},
}For local development, point your plugin manager at this directory or add it
to runtimepath:
vim.opt.runtimepath:prepend("/path/to/worktrunk.nvim")
require("worktrunk").setup():Worktrunk " Select an existing worktree
:Worktrunk switch feature-auth " Switch by branch or path
:Worktrunk switch --create fix " Pass switch flags directly to wt
:Worktrunk create fix main " Create branch `fix` from `main`
:Worktrunk previous " Switch to the previous worktree
:Worktrunk list " Show wt list in a scratch buffer
:Worktrunk remove feature-auth " Run another wt command in a terminalConvenience commands are also available:
:WorktrunkSwitch [branch]
:WorktrunkCreate [branch] [base]
:WorktrunkListSwitching changes Neovim's working directory; existing buffers remain open.
File explorers and other integrations can react to the User WorktrunkSwitch
event. The event's data contains Worktrunk's JSON switch result:
vim.api.nvim_create_autocmd("User", {
pattern = "WorktrunkSwitch",
callback = function(event)
print("Now in " .. event.data.path)
end,
})Defaults:
require("worktrunk").setup({
command = "wt",
cwd_scope = "global", -- "global", "tab", or "window"
confirm_create = true,
picker = {
include_branches = false,
include_remotes = false,
status = "icons", -- "icons", "symbols", or "none"
icons = {
["^"] = "🏠", -- main worktree
["|"] = "✅", -- synchronized with upstream
["!"] = "📝", -- modified files
["?"] = "✨", -- untracked files
["+"] = "📦", -- staged files
["↑"] = "⬆️", -- ahead of main
["↓"] = "⬇️", -- behind main
["⇡"] = "⬆️", -- ahead of upstream
["⇣"] = "⬇️", -- behind upstream
["✘"] = "❌", -- conflicts
["_"] = "🧹", -- empty and safe to remove
},
},
})The complete default icon mapping is in lua/worktrunk/config.lua. Override
only the entries you want to change; setup merges them with the defaults.
| Icon | Worktrunk symbol | Meaning |
|---|---|---|
| 🏠 | ^ |
Main worktree |
| ✅ | | |
Synchronized with upstream |
| 📝 | ! |
Modified files |
| ✨ | ? |
Untracked files |
| 📦 | + |
Staged files |
| ⬆️ | ↑ |
Ahead of the default branch |
| ⬆️ | ⇡ |
Ahead of upstream; commits need pushing |
| ⬇️ | ↓ |
Behind the default branch |
| ⬇️ | ⇣ |
Behind upstream |
| 🔄 | ↕ |
Diverged from the default branch |
| 🔄 | ⇅ |
Diverged from upstream |
| ❌ | ✘ |
Merge conflicts |
| ❌ | ✗ |
Would conflict with the default branch |
| 🧹 | _ |
Empty, clean, and safe to remove |
| ✅ | ⊂ |
Integrated and safe to remove |
| 🟰 | – |
At the default branch commit with uncommitted changes |
| 🚫 | ∅ |
No common ancestor with the default branch |
| 🔄 | ↻ |
Git operation in progress |
| 🗑️ | ⊟ |
Prunable worktree with a missing directory |
| 🔒 | ⊞ |
Locked worktree |
⚑ |
Worktree path mismatch or duplicate branch | |
| 🌿 | / |
Branch without a worktree |
Setting include_branches or include_remotes widens the picker. Selecting a
branch without a worktree asks Worktrunk to create its worktree before the
plugin switches to it.
Commands other than switch, create, and list open in a terminal so that
interactive prompts continue to work. Since a child terminal cannot change
Neovim's directory, use the native switch commands when changing worktrees.
mise install
mise run lint
mise run test
mise run coverageThe coverage report is written to luacov.report.out.
User-facing changes should include a Changeset:
npm run changesetOn main, the release workflow creates or updates a release pull request.
Merging that pull request creates a vX.Y.Z tag and GitHub Release. The
package.json is private and is used only for versioning; nothing is published
to npm.