Documentation
¶
Overview ¶
Package generic implements a data-driven "breadth-tier" adapter: one Go implementation that supports many agents from a table of Specs, rather than a hand-written package per agent. It is for the long tail of agents whose agentsync surface is a rules/memory file plus (optionally) an `mcpServers` JSON and/or an Agent-Skills (SKILL.md) directory — the same shapes the deep adapters (Windsurf/Cline/Continue/Codex) already take.
The DEEP adapters (claude, codex, cursor, gemini, opencode, continuedev, windsurf, roo, cline) are NOT generic — they have richer, agent-specific component support (subagents, commands, hooks, …) and bidirectional nuances that don't fit a table. The generic tier covers memory, MCP, and Agent Skills (the open SKILL.md spec, where the agent natively scans a skills directory), and reports every other component as a skip, so its coverage is never overstated. Factory Droid is the one exception: its spec declares a hooks.json, and Register wraps that adapter so only it implements HookIngestGuard. The hook dialect (root event map, Droid's event list, settings.json fallback) is Factory's. Setting Hooks on another spec would inherit that dialect, which is not a generic knob. Skills fit the table because their on-disk format is uniform across agents — only the scanned directory varies, with no dialect to model. Breadth-tier agents still flow through agentsync's normal apply/import pipeline, so they inherit drift detection, secret resolution, and capture — which a one-way "rules dump" (ruler/rulesync) does not provide.
Each agent is one verified Spec (see specs.go); adding an agent is a data entry, not a package. Every path in a Spec MUST be verified against the agent's upstream docs (and corroborated by prior-art tools) before being added — the count stays honest because each row reflects the agent's real coverage.
Index ¶
- func Register(spec Spec, opts Options) adapter.Adapter
- 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) IngestMCPSpec(raw map[string]any) source.MCPServerSpec
- 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 FileTarget
- type MCPTarget
- type Options
- type Spec
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter implements adapter.Adapter for one Spec.
func (*Adapter) Apply ¶
Apply routes every destination write through the supplied DestWriter rather than calling iox.AtomicWrite directly. The DestWriter owns the foreign-collision backup invariant — see the doc on adapter.DestWriter.
func (*Adapter) Ingest ¶
Ingest reads this breadth-tier agent's memory, MCP, and skills back into a partial canonical, plus hooks when the spec declares a hooks file (Factory). Inverse of Render. Components the tier never projects are simply not read.
func (*Adapter) IngestMCPSpec ¶ added in v0.15.0
func (a *Adapter) IngestMCPSpec(raw map[string]any) source.MCPServerSpec
IngestMCPSpec satisfies adapter.MCPSpecIngester for the breadth tier by applying THIS agent's Spec dialect — the same MCPTarget Ingest reads with, so a per-agent knob (RootKey, TransportKey, StdioValue, RemoteURLKey, SSEURLKey) can never be honored on read and missed on write-back.
func (*Adapter) KeyMergeStrategy ¶
KeyMergeStrategy is merge-jsonc-keys when the spec has an MCP file (the only key-merge surface); otherwise "" (memory is a whole-file write). The breadth tier always uses the JSONC-tolerant merge so a commented settings file (Zed, Copilot's .vscode/mcp.json, Amp) is parsed rather than clobbered — hujson reads plain JSON identically, so dedicated *.json MCP files are unaffected.
func (*Adapter) Render ¶
func (a *Adapter) Render(r secrets.Resolved, scope adapter.Scope, project string) ([]adapter.FileOp, []adapter.Skip, error)
Render projects the canonical model into this breadth-tier agent's memory (rules) file, its mcpServers JSON, its Agent-Skills directory, and, when the spec declares one, its hooks.json. Subagents, commands, and LSP are always reported as skips. Hooks are a skip unless the spec declares a hooks file (Factory Droid). A component the spec does not support at the requested scope (e.g. user-scope memory for a project-only agent) is likewise reported as a skip rather than written somewhere wrong.
func (*Adapter) SetStderr ¶ added in v0.17.0
SetStderr replaces the ingest warning sink. Nil restores os.Stderr.
func (*Adapter) VersionRoots ¶ added in v0.7.5
VersionRoots implements adapter.VersionedDirs: the set of top-level directories this breadth-tier agent writes into at user scope, so the apply tail can version them as a local-only rollback history (issue #118). Derived from the Spec's user-scope targets. The apply tail unions, de-nests, and de-dups these across all enabled adapters, so a shared cross-agent dir (e.g. ~/.agents/skills, which many breadth agents and Codex all target) is versioned exactly once. Returns nil at project scope.
type FileTarget ¶
type FileTarget struct {
User string // file path or directory root under the user home (targetRoot)
Project string // file path or directory root under the project root
}
FileTarget is a per-scope path (relative to the scope root) for a file component (Memory: a single markdown file) or a directory component (Skills: the Agent-Skills root directory under which each skill is a <name>/SKILL.md subtree). An empty path means the component is unsupported at that scope.
type MCPTarget ¶
type MCPTarget struct {
User string
Project string
RootKey string
TransportKey string
StdioValue string
RemoteValue string
RemoteURLKey string
SSEURLKey string
}
MCPTarget describes an agent's MCP servers JSON file and its on-disk dialect. The breadth tier only handles JSON `<rootKey>` server-map files; agents whose MCP is TOML, app-storage, or otherwise unmodeled simply leave User/Project empty (MCP unsupported) and are reported as a skip.
The dialect knobs capture the variance the prior-art research surfaced across the tail (all with Claude-compatible defaults):
- RootKey: the top-level object key. Default "mcpServers"; e.g. "servers" (VS Code Copilot), "mcp" (Crush).
- TransportKey: the per-server field naming the transport. "" = inferred from command-vs-url (no field). Else e.g. "type" or "transport".
- StdioValue / RemoteValue: the TransportKey values for stdio vs remote (defaults "stdio"/"http"; Copilot CLI uses "local" for stdio). Ignored when TransportKey == "".
- RemoteURLKey: the per-server field holding a remote server's URL. Default "url"; some agents use "httpUrl" (Qwen) or "serverUrl" (Antigravity).
- SSEURLKey: for Gemini-lineage agents that split remote transports across TWO url fields (Qwen: `httpUrl` = streamable HTTP, `url` = SSE), the field holding an SSE server's URL. "" = no split (SSE shares RemoteURLKey). When set, ingest reads both fields (RemoteURLKey wins when both are present, mirroring the upstream precedence) and render routes a canonical `sse` server to this key.
type Options ¶
type Options struct {
TargetRoot string
LookPath func(file string) (string, error)
// Stderr receives ingest warnings. Nil means os.Stderr.
Stderr io.Writer
}
Options configure a generic adapter instance.
type Spec ¶
type Spec struct {
Name string // agent name (Adapter.Name())
DetectBin string // binary on PATH (best-effort); "" to skip
DetectDir string // dir under targetRoot whose existence implies installed; "" to skip
Memory FileTarget // rules/instructions FILE path (plain markdown)
MCP MCPTarget // mcpServers JSON
Skills FileTarget // Agent-Skills DIRECTORY root (holds <name>/SKILL.md subtrees); empty = unsupported
// Hooks is a hooks.json FILE whose root is the event map (Factory Droid).
// Empty means the agent has no command-hook file agentsync manages.
// The legacy settings.json "hooks" key is an import fallback only.
Hooks FileTarget
}
Spec is the verified, data-driven description of one breadth-tier agent.
func Specs ¶
func Specs() []Spec
Specs returns the verified breadth-tier agent table. Each entry's paths were cross-referenced against the agent's upstream docs AND prior-art config-sync tools (ruler, rulesync) before inclusion — see docs/capability-matrix.md (§ "Breadth tier") for the per-agent basis and the deliberate exclusions.
Rules of the table:
- MEMORY is the universal axis: every entry projects the canonical memory to the agent's verified rules/instructions file (the AGENTS.md standard where the agent reads it, else its agent-specific file). Plain markdown body.
- MCP is enabled ONLY where the agent reads a JSON server-map file whose dialect the engine can express faithfully (root key + transport field + url key). Agents whose MCP is an array, YAML, TOML, app-storage, or otherwise unmodeled leave MCP empty — the engine reports it as a skip rather than write a shape the agent can't read.
- SKILLS is enabled where the agent natively scans Agent-Skills (SKILL.md) directories (the open agentskills.io spec). The on-disk skill format is uniform across agents, so there is no dialect to model — only the scanned directory varies. Most agents read the cross-vendor `.agents/skills` convention (shared with Codex, so the render pipeline dedupes the byte-identical ops); a few scan only their own `.<agent>/skills`. Agents that don't natively scan a SKILL.md directory — Jules/Firebase (publish skills FOR other agents), Amazon Q (skills only via an MCP server, a different component), OpenHands (programmatic load, no auto-scanned dir) — leave Skills empty and report it as a skip.
- A scope with no verified target is left empty (reported as a skip), never guessed. Low-confidence user-scope paths from the research are omitted.
Deep, agent-specific adapters (claude, codex, cursor, gemini, opencode, continuedev, windsurf, roo, cline) are NOT here — they live in their own packages with richer component support.