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
V2: worktree created by a plugin strategy resolves to a new project when the project has no .git/.hg #51672
A plugin can register its own worktree strategy. If the project directory has no .git or .hg, the new worktree does not belong to the source project. opencode resolves its directory to a new project with a new ID.
Possible cause: Project.resolve finds the project by .git or .hg only. For any other directory it uses Hash("directory:" + path). A new worktree has a new path, so it gets a new project ID.
Worktree.create already saves the new directory in the worktree table with the source project ID and the strategy ID. But Project.resolve does not read this table.
Plugins
No response
OpenCode version
v2.0.18
Steps to reproduce
Open a project whose directory has no .git or .hg.
Register a worktree strategy through ctx.worktree.transform.
Create a worktree.
Open the new worktree directory.
Compare the project ID of the new location with the source project ID.
This issue might be a duplicate of existing issues. Please check:
#48870: Project.resolve does not consult project_directory for non-git directories, so a registered directory may not resolve to its owning project. This closely overlaps with the worktree-table association described here.
#47885: project identity handling for non-git directories can assign a separate identity based on the directory path.
changed the title [-]Worktree created by a plugin strategy resolves to a new project when the project has no .git/.hg[/-][+]V2: worktree created by a plugin strategy resolves to a new project when the project has no .git/.hg[/+]on Sep 28, 2026
Thanks for the report. I reproduced this on 2.0.18, and it still happens on the latest dev (86ce19d).
What I did, using the embedded SDK (the script below):
Created a source project, registered a plugin worktree strategy that copies the source into the new directory without.git, and created a worktree with it.
worktree.list for the source project includes the new directory with strategy: "copy", so the worktree row is stored under the source project ID.
A session opened in the new worktree directory gets a different project ID: the directory:-hash fallback in Project.resolve, which never reads the worktree table. This matches your analysis.
One difference: I could only get this far when the source directory was a git repo. With a source that has no VCS at all, worktree.create fails earlier with Worktree source not found: <source dir> on both 2.0.18 and dev. For projects without VCS, the source directory's own worktree row is never saved. If your setup does get past that step, a short note on how would help.
Reproduction script (repro.ts)
// Repro for anomalyco/opencode#51672: a worktree created by a plugin strategy in a// project with no .git/.hg resolves to a brand-new project ID.// Run: bun add @opencode/[email protected] @opencode/[email protected] && bun repro.ts (NO_GIT=1 for a non-VCS source)import{mkdtemp,mkdir,rm,cp,writeFile}from"node:fs/promises"import{tmpdir}from"node:os"import{join}from"node:path"import{Plugin}from"@opencode/plugin"import{OpenCode}from"@opencode/sdk"constroot=awaitmkdtemp(join(tmpdir(),"oc-51672-"))constsource=join(root,"project")// plain directory: no .git, no .hgawaitmkdir(source)awaitwriteFile(join(source,"README.md"),"hello\n")// Source is a git repo; the plugin strategy produces a copy WITHOUT .git/.hg.// (With a plain non-VCS source, worktree.create already fails: "Worktree source not found".)if(process.env.NO_GIT!=="1")Bun.spawnSync(["sh","-c","git init -q && git add . && git -c user.email=a@b -c user.name=a commit -qm init"],{cwd: source})process.env.HOME=join(root,"home")process.env.XDG_DATA_HOME=join(root,"data")process.env.XDG_CONFIG_HOME=join(root,"config")process.env.XDG_STATE_HOME=join(root,"state")process.env.XDG_CACHE_HOME=join(root,"cache")constplugin=Plugin.define({id: "copy-worktrees",asyncsetup(ctx){awaitctx.worktree.transform((editor)=>editor.add({id: "copy",asynccreate(input){awaitcp(input.sourceDirectory,input.directory,{recursive: true,filter: (src)=>!src.includes("/.git")})return{directory: input.directory}},asyncremove(input){awaitrm(input.directory,{recursive: true,force: true})},asynclist(){return[]},}),)},})await using opencode=awaitOpenCode.create({plugins: [plugin],config: {directory: source,project: false},models: {fetch: false},fs: {filewatcher: false},}asany)consts1=awaitopencode.sessions.create({location: {directory: source}})console.log("source project ID: ",s1.projectID)for(leti=0;i<50&&(awaitopencode.worktree.list({projectID: s1.projectID})).length===0;i++)awaitBun.sleep(100)constwt=awaitopencode.worktree.create({projectID: s1.projectID,directory: join(root,"copies"),name: "task"})console.log("worktree: ",JSON.stringify(wt))console.log("worktree list: ",JSON.stringify(awaitopencode.worktree.list({projectID: s1.projectID})))consts2=awaitopencode.sessions.create({location: {directory: wt.directory}})console.log("worktree project ID:",s2.projectID)console.log(s1.projectID===s2.projectID ? "OK: same project" : "BUG: worktree resolved to a different project")
Activity
github-actions commentedon Sep 27, 2026
This issue might be a duplicate of existing issues. Please check:
Project.resolvedoes not consultproject_directoryfor non-git directories, so a registered directory may not resolve to its owning project. This closely overlaps with the worktree-table association described here.[-]Worktree created by a plugin strategy resolves to a new project when the project has no .git/.hg[/-][+]V2: worktree created by a plugin strategy resolves to a new project when the project has no .git/.hg[/+]opencodejr commentedon Oct 28, 2026
Thanks for the report. I reproduced this on 2.0.18, and it still happens on the latest
dev(86ce19d).What I did, using the embedded SDK (the script below):
.git, and created a worktree with it.worktree.listfor the source project includes the new directory withstrategy: "copy", so the worktree row is stored under the source project ID.directory:-hash fallback inProject.resolve, which never reads the worktree table. This matches your analysis.One difference: I could only get this far when the source directory was a git repo. With a source that has no VCS at all,
worktree.createfails earlier withWorktree source not found: <source dir>on both 2.0.18 anddev. For projects without VCS, the source directory's own worktree row is never saved. If your setup does get past that step, a short note on how would help.Reproduction script (repro.ts)
Run it:
bun repro.ts