You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I run a git-worktree workflow on top of opencode.nvim session tabs: each tab is bound to a different worktree directory of the same repo, so several agents can work in parallel with file isolation (related: #511, which asks for the underlying APIs).
The workflow works, but getting the tab/directory semantics right required wrapping two internals, which suggests the current tab design has gaps worth addressing natively.
Pain points
session_locked is unconditional. When locked, :cd never switches the session — even to a completely different project. The natural semantics for directory-bound tabs is scoped: stay pinned while cwd moves within the same repo (main checkout and its linked worktrees), follow when cwd leaves for another repo.
Locking keeps the session but not the directory. On directory change / panel reopen (handle_directory_change / check_cwd), the tab's current_cwd is reset to nvim's cwd even when the session is locked. Consequence: open_input_new_session (<C-n>) and @ completion resolve against nvim's cwd, not the session's directory — a new session created from a "locked" tab silently lands in the wrong directory.
My config-level workaround (working well in daily use):
Wrap session_runtime.is_session_locked: for tabs created as worktree tabs, report locked only while vim.fn.getcwd() is under the tab's repo root.
Wrap state.context.set_current_cwd: ignore in-repo cwd changes for such tabs (keeps the tab pointing at its directory); when cwd leaves the repo, unpin the tab entirely so normal directory-follow takes over.
If this direction sounds reasonable I'm happy to help test or turn it into a PR.
@wzj-zz I am interested to learn more about your workflow as I am also dabbling with the idea of using worktrees inside Neovim. when you're referring to "opencode.nvim session tabs" what do you mean exactly? I am not aware of any "tab" concept within opencode.nvim, only with Neovim's native tabpages (:h tabpage)
lock_session_to_directory as a function (repo-wide same-repo / cross-repo rule)
All bound-tab semantics work: pin inside the repo, unpin outside, completion and new sessions follow the bound directory.
Two Windows issues in the PR itself:
Session reuse misses on Windows.open_session without new compares the plugin-side directory string against server-side session paths. On Windows the port mapping rewrites those paths, so the lookup never hits and every open spawns a fresh session. I work around it by matching already-bound tabs first; the underlying server-side path mapping needs a fix.
Two lock-policy unit tests fail on Windows because they hardcode /tmp/... paths; Windows normalizes them to C:/tmp/... and the string assertions miss.
Also, with the public API in place, the worktree lock/unlock logic in my config is now entirely via lock_session_to_directory and open_session — no more monkey patches. LGTM pending the Windows path fix.
@disrupted the session tabs I mentioned are the plugin's logical panel tabs (created with , cycled with <M-,>/<M-.>), not Neovim tabpages. Workflow: lists all git worktrees plus bare branches; each worktree gets or reuses a tab bound to that directory; multiple agents work in parallel without file conflicts. When a task is done the agent merges the branch back and I delete the worktree with . Previously this required wrapping two internals (is_session_locked / set_current_cwd); with #517 it's all public API.
@disrupted one clarification on my earlier reply: <M-c> creates a new session in a new logical tab, not an empty tab — the plugin has no standalone "tab" object; each tab is a container for one session. So the workflow is: pick a worktree → open (or reopen) that directory's session in its own tab → work → delete when done.
lock_session_to_directory as a function (repo-wide same-repo / cross-repo rule)
All bound-tab semantics work: pin inside the repo, unpin outside, completion and new sessions follow the bound directory.
Two Windows issues in the PR itself:
Session reuse misses on Windows.open_session without new compares the plugin-side directory string against server-side session paths. On Windows the port mapping rewrites those paths, so the lookup never hits and every open spawns a fresh session. I work around it by matching already-bound tabs first; the underlying server-side path mapping needs a fix.
Two lock-policy unit tests fail on Windows because they hardcode /tmp/... paths; Windows normalizes them to C:/tmp/... and the string assertions miss.
Also, with the public API in place, the worktree lock/unlock logic in my config is now entirely via lock_session_to_directory and open_session — no more monkey patches. LGTM pending the Windows path fix.
@sudo-tee Fix verified on Windows 11 / Neovim 0.11.
tests/unit/directory_session_spec.lua — 9/9 pass (including the new mapped-directory cases)
tests/unit/lock_policy_spec.lua — 9/9 pass (the /tmp path issue is gone)
The matches_directory helper comparing both the raw directory and the mapped server directory is the right shape. open_session reuse now hits correctly without my local tab-boundary workaround.
Activity
sudo-tee commentedon Oct 9, 2026
@wzj-zz
I created a PR for the requested api.
I tried to make them as agfnostic as possible to not enforce a perticular workflow,
Let me know if they work for you.
#517
disrupted commentedon Oct 9, 2026
@wzj-zz I am interested to learn more about your workflow as I am also dabbling with the idea of using worktrees inside Neovim. when you're referring to "opencode.nvim session tabs" what do you mean exactly? I am not aware of any "tab" concept within opencode.nvim, only with Neovim's native tabpages (
:h tabpage)wzj-zz commentedon Oct 10, 2026
Tested #517 on Windows 11 / Neovim 0.11 — the new APIs cover our workflow. Migrated my worktree integration from the two internal wrappers to:
require('opencode.api').open_session({ directory = ... })lock_session_to_directoryas a function (repo-wide same-repo / cross-repo rule)All bound-tab semantics work: pin inside the repo, unpin outside, completion and new sessions follow the bound directory.
Two Windows issues in the PR itself:
open_sessionwithoutnewcompares the plugin-side directory string against server-side session paths. On Windows the port mapping rewrites those paths, so the lookup never hits and every open spawns a fresh session. I work around it by matching already-bound tabs first; the underlying server-side path mapping needs a fix./tmp/...paths; Windows normalizes them toC:/tmp/...and the string assertions miss.Also, with the public API in place, the worktree lock/unlock logic in my config is now entirely via
lock_session_to_directoryandopen_session— no more monkey patches. LGTM pending the Windows path fix.wzj-zz commentedon Oct 10, 2026
@disrupted the session tabs I mentioned are the plugin's logical panel tabs (created with , cycled with <M-,>/<M-.>), not Neovim tabpages. Workflow: lists all git worktrees plus bare branches; each worktree gets or reuses a tab bound to that directory; multiple agents work in parallel without file conflicts. When a task is done the agent merges the branch back and I delete the worktree with . Previously this required wrapping two internals (is_session_locked / set_current_cwd); with #517 it's all public API.
wzj-zz commentedon Oct 10, 2026
@disrupted one clarification on my earlier reply:
<M-c>creates a new session in a new logical tab, not an empty tab — the plugin has no standalone "tab" object; each tab is a container for one session. So the workflow is: pick a worktree → open (or reopen) that directory's session in its own tab → work → delete when done.disrupted commentedon Oct 10, 2026
@wzj-zz thanks for the explanation. I had no idea this feature existed haha
sudo-tee commentedon Oct 10, 2026
@wzj-zz
I pushed a fis for the 2 windows path issues you had.
let me know if they work for you
wzj-zz commentedon Oct 10, 2026
@sudo-tee Fix verified on Windows 11 / Neovim 0.11.
tests/unit/directory_session_spec.lua— 9/9 pass (including the new mapped-directory cases)tests/unit/lock_policy_spec.lua— 9/9 pass (the/tmppath issue is gone)The
matches_directoryhelper comparing both the raw directory and the mapped server directory is the right shape.open_sessionreuse now hits correctly without my local tab-boundary workaround.LGTM to merge.
sudo-tee commentedon Oct 10, 2026
That was fast :)
wzj-zz commentedon Oct 10, 2026
@sudo-tee Thanks for the quick turnaround — the worktree workflow is working beautifully now. Really appreciate all the effort on this.
jensenojs commentedon Oct 12, 2026
Would you be willing to write a recipe to contribute to our documentation? @wzj-zz
sudo-tee commentedon Oct 12, 2026
@jensenojs
I already have one in the MR for documentation rework IAM doing.
#518