Documentation
¶
Overview ¶
Package opencode implements the OpenCode adapter for agentsync.
Package opencode implements the OpenCode adapter for agentsync.
settings.go: JSONC-aware merge for opencode.json. Uses tailscale/hujson to PARSE JSONC (comments + trailing commas) so a hand-edited opencode.json is not rejected. NOTE: the merged result is re-emitted as plain JSON — comments and trailing commas are NOT preserved (see MergeJSONC). Foreign keys/values are preserved; only the formatting/comments are lost. Comment-preserving mutation is deferred to v1.x (matches the README "Known limits").
Index ¶
- func IngestMCPSpec(raw map[string]any) source.MCPServerSpec
- func MergeJSONC(existing []byte, ours map[string]any, ownedPointers []string) ([]byte, error)
- type Adapter
- func (a *Adapter) Apply(ops []adapter.FileOp, w adapter.DestWriter) error
- func (a *Adapter) Detect() (bool, error)
- func (a *Adapter) Ingest(scope adapter.Scope, project string) (source.Canonical, error)
- func (a *Adapter) KeyMergeStrategy() string
- func (a *Adapter) Name() string
- func (a *Adapter) Render(r secrets.Resolved, scope adapter.Scope, project string) ([]adapter.FileOp, []adapter.Skip, error)
- func (a *Adapter) SetStderr(w io.Writer)
- func (a *Adapter) VersionRoots(scope adapter.Scope, project string) []string
- type Options
- type Paths
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IngestMCPSpec ¶
func IngestMCPSpec(raw map[string]any) source.MCPServerSpec
IngestMCPSpec translates one OpenCode-native MCP server spec — the value under opencode.json `/mcp/<id>` — into the canonical MCPServerSpec. It is the exact inverse of opencodeMCPSpec (Render). Exported so reconcile's key-level write-back reconstructs an opencode MCP server through the SAME translation the adapter uses, rather than assuming the dest shape matches the canonical model (it doesn't: command is an array, env is `environment`, type is local/remote).
func MergeJSONC ¶
MergeJSONC merges ours into existing JSONC content, removing ownedPointers no longer present in ours. Foreign keys and values are preserved.
Thin wrapper over the shared jsonkeys.MergeJSONC engine (hujson parse → Standardize → MergeKeys → plain-JSON emit; comments are NOT preserved — see its doc and the README "Known limits").
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter implements adapter.Adapter for OpenCode.
func (*Adapter) Apply ¶
Apply routes every destination write through the supplied DestWriter rather than calling iox.AtomicWrite directly. This is the contract that keeps the foreign-collision backup guarantee honest — see the doc on adapter.DestWriter.
func (*Adapter) Ingest ¶
Ingest reads OpenCode's native config files and returns a partial source.Canonical. It is the inverse of Render.
Round-trip notes:
- Subagent `mode` (primary/all/subagent) is PRESERVED into the canonical frontmatter. The canonical `source.Subagent.Frontmatter` is a free-form map, so an OpenCode-specific key round-trips without a schema change; the render side re-emits an ingested `mode` verbatim, so a native primary/all agent is no longer demoted to subagent on the next apply.
- Subagents still lose the Claude-side `tools`/`color` fields (dropped on render because OpenCode has no equivalent). Ingest reconstructs only what is present on disk.
- Only agentsync-OWNED agents/commands are captured (see destOwned): the agents/ and commands/ directories are shared with the user's own hand-authored files, which are left untouched and never pulled into the canonical source.
func (*Adapter) KeyMergeStrategy ¶
KeyMergeStrategy is opencode's single key-merge strategy: JSONC (opencode.json), which MUST be merged via hujson, not strict JSON.
func (*Adapter) Render ¶
func (a *Adapter) Render(r secrets.Resolved, scope adapter.Scope, project string) ([]adapter.FileOp, []adapter.Skip, error)
Render converts the resolved canonical into FileOps for OpenCode.
func (*Adapter) SetStderr ¶
SetStderr replaces the warning sink the adapter writes Ingest warnings to, so a CLI command can route adapter warnings through the same styled writer it uses for its own output. See claude.Adapter.SetStderr for the contract.
func (*Adapter) VersionRoots ¶ added in v0.7.5
VersionRoots implements adapter.VersionedDirs: ~/.config/opencode (its config dir) plus ~/.claude/skills (the shared Claude skills dir OpenCode also writes to). The apply tail de-nests + de-dups across adapters, so when Claude is also enabled, ~/.claude/skills is captured by Claude's ~/.claude repo instead of getting its own. Returns nil at project scope.
type Options ¶
type Options struct {
TargetRoot string // honors AGENTSYNC_TARGET_ROOT (real "/Users/x" in production)
// LookPath overrides exec.LookPath for testing. nil means use exec.LookPath.
LookPath func(file string) (string, error)
// Stderr receives Ingest warnings (lenient-YAML notices, dropped components).
// nil means os.Stderr.
Stderr io.Writer
}
Options configure the adapter at construction.
type Paths ¶
type Paths struct {
ConfigDir string // ~/.config/opencode
Settings string // ~/.config/opencode/opencode.json (user) or <proj>/opencode.json (project)
AgentsDir string // ~/.config/opencode/agents (user) or .opencode/agents (project)
CommandsDir string // ~/.config/opencode/commands (user) or .opencode/commands (project)
ClaudeSkillsDir string // ~/.claude/skills user / <proj>/.claude/skills project — SHARED with Claude, read by OpenCode too (see doc above)
Memory string // AGENTS.md path
}
Paths resolves the destination paths for a given (scope, project, target-root).
Skills go to the SHARED Claude directory (ClaudeSkillsDir: ~/.claude/skills at user scope, <project>/.claude/skills at project scope), NOT to a dedicated .opencode/skills / ~/.config/opencode/skills. This is a deliberate, verified choice: per OpenCode upstream docs (https://opencode.ai/docs/skills/) OpenCode scans BOTH its own skills dirs AND the Claude ones — globally ~/.config/opencode/skills, ~/.claude/skills, and ~/.agents/skills; per-project .opencode/skills, .claude/skills, and .agents/skills. Because a skill is identical between Claude and OpenCode, agentsync renders it ONCE to the shared ~/.claude/skills that both harnesses read, rather than duplicating byte-for- byte copies into a second OpenCode-only tree (which would double the on-disk footprint and the drift surface). So `.opencode/skills` is intentionally unused, not overlooked. (docs/capability-matrix.md OpenCode notes.)
func ResolvePaths ¶
ResolvePaths returns the Paths for the given target root and optional project. projectScope=true + non-empty project uses project-local .opencode/ dirs.