anomalyco / anomalyco/opencode
[FEATURE]: Reviving #14798: plugin-registerable VCS backends, re-based on the current architecture
@kitlangton is already working on this.
Since Aug 20, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Context
This picks up #14798 (Plugin Architecture for VCS Integration with Non-Git Repositories), which was auto-closed by the stalebot with no maintainer verdict. The bot itself invited reopening if still relevant. It is. Full credit to @diegohb: the design and especially the Hardcoded Git Assumptions gotchas inventory in that issue are the foundation here.
Two things prompt a fresh issue rather than a comment:
- The codebase has moved since #14798 was written. That design targets the pre-Effect architecture (
Infoas a zod schema,Project.fromDirectory,Instance.state,Bus). Core is now Effect-based:Vcs.Serviceas aLayer, project resolution inpackages/core/src/project.ts(resolve()),ProjectSchema.Vcsas aSchema.Union,InstanceState,EventV2Bridge,LayerNodedeps. The proposal needs re-basing onto those seams. - A directly reusable precedent now exists that didn't when #14798 was filed: plugin-registerable workspace adapters.
The key idea: mirror the workspace-adapter registry
opencode already lets plugins register backends for a sibling concern:
packages/opencode/src/control-plane/adapters/index.ts— builtin map (worktree) + per-project registry (getAdapter/registerAdapter).packages/plugin/src/index.ts—PluginInput.experimental_workspace.register(type, adapter).
#14798 implemented registration as a Hooks trigger ("vcs.register": (input, output) => void), which its own example shows is awkward (probing with an empty type, passing the handler back via output). The trigger model is for interception, not registration. Instead, add an imperative seam that matches the workspace pattern exactly:
VcsBackendRegistrymodeled oncontrol-plane/adapters, with git registered as the builtin.PluginInput.experimental_vcs.register(type, backend).
Then route the git-only seams through it:
Vcs.Service→getBackend(project.vcs.type)instead of!== "git"(packages/opencode/src/project/vcs.ts),Project.resolve()→ ask registered backends to detect, fall back to git (packages/core/src/project.ts),ProjectSchema.Vcs→ registry-driventype, git default (packages/core/src/project/schema.ts).
Backend interface (sketch)
Plain-object + Promise-based to match WorkspaceAdapter; core adapts it into the internal Vcs.Service and reuses the existing Vcs.FileStatus / Vcs.FileDiff schemas. Adopts @diegohb's capability flags so a backend declares what it supports instead of all-or-nothing:
type VcsContext = { directory: string; worktree: string; store: string }
type VcsCapabilities = {
worktrees?: boolean
snapshots?: boolean
apply?: boolean
}
type VcsBackend = {
type: string
name: string
capabilities: VcsCapabilities
detect(dir: string): Promise<{ store: string; worktree: string; id?: string } | undefined>
branch(ctx: VcsContext): Promise<string | undefined>
defaultBranch(ctx: VcsContext): Promise<string | undefined>
status(ctx: VcsContext): Promise<Vcs.FileStatus[]>
diff(ctx: VcsContext, mode: Vcs.Mode, options?: { context?: number }): Promise<Vcs.FileDiff[]>
diffRaw(ctx: VcsContext): Promise<string>
apply(ctx: VcsContext, patch: string): Promise<{ applied: boolean }>
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.