anomalyco / anomalyco/opencode

[FEATURE]: Reviving #14798: plugin-registerable VCS backends, re-based on the current architecture

Open
#43,705 1 comment 3 reactions 1 assignee View on GitHub

@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:

  1. The codebase has moved since #14798 was written. That design targets the pre-Effect architecture (Info as a zod schema, Project.fromDirectory, Instance.state, Bus). Core is now Effect-based: Vcs.Service as a Layer, project resolution in packages/core/src/project.ts (resolve()), ProjectSchema.Vcs as a Schema.Union, InstanceState, EventV2Bridge, LayerNode deps. The proposal needs re-basing onto those seams.
  2. 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.tsPluginInput.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:

  • VcsBackendRegistry modeled on control-plane/adapters, with git registered as the builtin.
  • PluginInput.experimental_vcs.register(type, backend).

Then route the git-only seams through it:

  • Vcs.ServicegetBackend(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-driven type, 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.