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
{{ message }}
Repository navigation
API requests: create session in a given directory, repo-scoped session lock, custom slash command registry #511
I built a git-worktree workflow on top of opencode.nvim: each session tab is bound to a worktree directory (<repo>/.worktrees/<branch>), so multiple agents can work in parallel on the same repo with full file isolation. It works well, but it currently depends on internal modules and monkey-patching, so any upstream refactor can silently break it. I'm requesting a few public APIs that would make this (and similar) workflows first-class.
Requests
1. Public "create/open a session in a given directory" API
Something like require('opencode.api').open_session({ directory = ..., new = true }).
Today this requires calling internals:
connection.operations.create_session(connection, { directory = ... }, ...) via opencode.server_job
plus manually fixing up opencode.state.context.set_current_cwd(dir) so @ completion and new sessions (open_input_new_session) resolve in that directory
Sessions bind their directory at creation time and the server already supports per-directory sessions (list_sessions_project), so this is mostly about exposing what's already there.
2. Repo-scoped session lock
lock_session_to_directory is currently boolean: when locked, :cdnever switches the session. For worktree workflows the natural semantics are:
:cd anywhere inside the same repo (main checkout or any linked worktree) → stay on the current session
:cd to a different repo → follow as usual (load that project's last session)
Suggest e.g. lock_session_to_directory = "repo" or a user predicate/hook deciding whether a directory change should swap the session. Today I wrap session_runtime.is_session_locked and state.context.set_current_cwd to get this behavior.
3. Registry for custom slash commands
Something like require('opencode').register_slash_command({ name = 'worktree', args = true, desc = ..., fn = function(args) ... end }).
Today a custom slash command must be written into two internal tables: opencode.commands.slash builtin definitions (execution) and opencode.slash_commands definitions (completion menu).
Happy to test any of these or contribute a PR if you can point me at the shape you'd prefer.
I'm not sure if you use Enter in the opencode output-window to jump to files and symbols. When I was developing that feature, I didn't take worktrees into account at all, so I'm not sure if the related behavior is reasonable ?
@jensenojs I don't use the <CR> jump feature and am not familiar with it. I tried to test it but couldn't get it to work — the jump doesn't trigger at all on Windows, even in a regular project without worktrees.
I'm not sure if I'm using it wrong or if it's a bug. Happy to help debug if you can clarify the expected behavior.
@jensenojs I dug into why <CR> never worked for me, and found two independent breakages under v2. Your #437 symbol jump is affected by both.
1. V2 normalization drops id from assistant text parts
v2/normalize.lua maps text parts to just { kind = 'text', text = part.text } — no id. But both reference_facts.collect_part_refs and formatter.current_part_text_references require part.id. Verified live in a v2 session: assistant text/reasoning parts have no id, and reference_facts.current_refs() stays empty. Tool parts keep their id (mapped_tool), so files the assistant edited still appear in the gr picker — which may have masked the bug. V1 keeps id on text parts, so this is v2-only. User-side ~ file attachments lose their refs too (mapped_file doesn't set id either).
2. Symbol targets never resolve (at least on Windows / Neovim 0.12.5)
Even with a file in current_files (via an edit tool call), symbol_snapshot.targets_for_token returns {}. I traced it: tree-sitter parses the file fine, query.get('lua', 'locals') succeeds, but query:iter_captures(root, content) yields a single local.scope capture for the whole file and nothing else. One thing that looks suspicious: symbol_snapshot's loop uses for id, node, metadata in query:iter_captures(...) and reads query.captures[id] as the capture name — but the real iter_captures returns (capture_id, node, metadata, match). Your tests mock iter_captures with a 3rd return value equal to the capture name string (tests/unit/symbol_snapshot_spec.lua), which doesn't match the real API signature. So the name-based filtering (@definition.function etc.) may never match against real query results.
Net effect under v2: no file/symbol targets from assistant text at all, and symbol jump is dead even for tool-referenced files.
I use this feature on a daily basis, and I'll take some time to look into what's going on with this issue...
--
ps. If you are interested in trying out this feature, you can take a deeper look. The owner previously did a lot of work on the v2 refactoring. Based on the symptoms, it seems related to the platform (I don't have a Windows machine either), but the opencode API doesn't feel like something that would be platform-dependent
Activity
wzj-zz commentedon Oct 10, 2026
Resolved by #517
jensenojs commentedon Oct 13, 2026
I'm not sure if you use Enter in the opencode output-window to jump to files and symbols. When I was developing that feature, I didn't take worktrees into account at all, so I'm not sure if the related behavior is reasonable ?
wzj-zz commentedon Oct 13, 2026
@jensenojs I don't use the
<CR>jump feature and am not familiar with it. I tried to test it but couldn't get it to work — the jump doesn't trigger at all on Windows, even in a regular project without worktrees.I'm not sure if I'm using it wrong or if it's a bug. Happy to help debug if you can clarify the expected behavior.
jensenojs commentedon Oct 13, 2026
sth like #437
wzj-zz commentedon Oct 13, 2026
@jensenojs I dug into why
<CR>never worked for me, and found two independent breakages under v2. Your #437 symbol jump is affected by both.1. V2 normalization drops
idfrom assistant text partsv2/normalize.luamaps text parts to just{ kind = 'text', text = part.text }— noid. But bothreference_facts.collect_part_refsandformatter.current_part_text_referencesrequirepart.id. Verified live in a v2 session: assistant text/reasoning parts have noid, andreference_facts.current_refs()stays empty. Tool parts keep theirid(mapped_tool), so files the assistant edited still appear in thegrpicker — which may have masked the bug. V1 keepsidon text parts, so this is v2-only. User-side~file attachments lose their refs too (mapped_filedoesn't setideither).2. Symbol targets never resolve (at least on Windows / Neovim 0.12.5)
Even with a file in
current_files(via an edit tool call),symbol_snapshot.targets_for_tokenreturns{}. I traced it: tree-sitter parses the file fine,query.get('lua', 'locals')succeeds, butquery:iter_captures(root, content)yields a singlelocal.scopecapture for the whole file and nothing else. One thing that looks suspicious:symbol_snapshot's loop usesfor id, node, metadata in query:iter_captures(...)and readsquery.captures[id]as the capture name — but the realiter_capturesreturns(capture_id, node, metadata, match). Your tests mockiter_captureswith a 3rd return value equal to the capture name string (tests/unit/symbol_snapshot_spec.lua), which doesn't match the real API signature. So the name-based filtering (@definition.functionetc.) may never match against real query results.Net effect under v2: no file/symbol targets from assistant text at all, and symbol jump is dead even for tool-referenced files.
jensenojs commentedon Oct 13, 2026
I use this feature on a daily basis, and I'll take some time to look into what's going on with this issue...
--
ps. If you are interested in trying out this feature, you can take a deeper look. The owner previously did a lot of work on the v2 refactoring. Based on the symptoms, it seems related to the platform (I don't have a Windows machine either), but the opencode API doesn't feel like something that would be platform-dependent