Component map
A package-by-package index of the codebase. Each entry lists the package’s responsibility, its key exported symbols, and which internal packages it depends on. For how the pieces fit together, read the architecture; this page is the directory.
cmd/agentsync/ # main(): inject version ldflags, call cli.Execute()internal/├── cli/ # cobra command tree (entry layer)├── source/ # the canonical model + loaders/writers ← the schema├── secrets/ # ${secret:}/${env:} resolve · re-reference · mask├── project/ # .agentsync.toml overlay discovery + merge├── adapter/ # the per-agent Adapter interface + registry│ ├── claude/ # full adapter (reference implementation)│ ├── opencode/ # adapter (hooks/LSP skipped)│ └── noop/ # placeholder for unimplemented agents├── render/ # the apply pipeline: plan · write · report├── capture/ # the single dest▶source write-back funnel├── drift/ # the 3-way classifier (pure, no IO)├── state/ # targets.json (last-applied hashes)├── marketplace/ # fetch marketplaces/plugins · project components├── iox/ # atomic write + file lock├── jsonkeys/ # per-key JSON-pointer merge (preserve foreign keys)├── paths/ # AGENTSYNC_HOME / TARGET_ROOT / HOME resolution├── log/ # slog setup└── testenv/ # hermetic-container test guardEntry layer
Section titled “Entry layer”cmd/agentsync
Section titled “cmd/agentsync”The binary’s main. Injects Version/Commit/Date via -ldflags and calls
cli.Execute(). Nothing else lives here.
internal/cli
Section titled “internal/cli”Wires every cobra subcommand into the root tree and dispatches to handlers; this is the only package that depends on nearly all the others.
- Key:
NewRoot() *cobra.Command,Execute() error,Version/Commit/Date. - Commands:
init,agent {add,remove,list,enable,disable},apply,status,diff,reconcile,import,doctor,verify,mcp {add,remove,list},plugin {install,upgrade,enable,disable,remove,list},marketplace {add,remove,list},update,secrets {edit,get,set},explain. - Depends on: adapter, source, state, secrets, paths, render, marketplace, project, drift, log.
- Files:
root.go+ one file per command group.
Core model
Section titled “Core model”internal/source — the schema
Section titled “internal/source — the schema”Loads and represents ~/.agentsync/. The TOML-tagged structs here are the
canonical model that adapters render from; also provides write-back helpers and
memory-fragment expansion.
- Key:
Canonical(the root model:Config,MCPServers,Skills,Subagents,Commands,Hooks,LSPServers,Plugins,Marketplaces,Memory,Project);Load(fs, home);ParseFrontmatter; theWrite*family (WriteMCP,WriteLSP,WritePlugin,WriteMarketplace,WriteSkill,WriteSubagent,WriteCommand,WriteHooks,WriteMemory);ReadMCP/ReadLSP(carry source-only fields);ExpandMemoryImports;RenderManagedMemory/StripManagedBanner(inject / strip the managed-file banner — seedocs/architecture.md). - Depends on: iox, jsonkeys.
- Files:
schema.go,loader.go,writer.go,memory.go.
internal/secrets
Section titled “internal/secrets”Resolves ${secret:dotted.key} and ${env:NAME} at apply time; re-references
cleartext back to ${secret:…} for write-back; masks resolved values for display.
The Resolved wrapper type is the load-bearing leak guard.
- Key:
Resolver(interface);Resolved(resolved-model wrapper);SubstituteCanonical(→Resolved);ReReferenceCanonical;CollectResolved;UnresolvedSecretRefs;MaskResolved;AgeBackend/EnvBackend/NopResolver;SelectBackend; and the single field listwalkSecretFields(inwalk.go). - Depends on: source, iox.
- Files:
secrets.go,age.go,resolved.go,substitute.go,rereference.go,mask.go,walk.go,secretpaths.go.
internal/project
Section titled “internal/project”Discovers a repo’s .agentsync.toml marker by walking up from the cwd and merges
its overlay (project MCP servers, plugin enable/disable, extra memory) onto the
base canonical model.
- Key:
MarkerFile(.agentsync.toml);Marker;Discover(cwd);Merge(base, m) source.Canonical. - Depends on: source.
- Files:
project.go.
Translation layer
Section titled “Translation layer”internal/adapter
Section titled “internal/adapter”Declares the per-agent Adapter contract and a registry; the DestWriter
interface funnels all destination writes through the foreign-collision backup.
- Key:
Adapter(interface);DestWriter(interface);Capability(bitmask:CapMCP,CapMemory,CapSkill,CapSubagent,CapCommand,CapHook,CapLSP);Scope(ScopeUser/ScopeProject);FileOp;Skip;Registry(NewRegistry,Register,Lookup,Names). - Files:
adapter.go,registry.go.
internal/adapter/claude
Section titled “internal/adapter/claude”The reference adapter — MCP, memory, skills, subagents, commands, and hooks, with per-key merge
into shared JSON files (~/.claude.json, settings.json, and a project’s
repo-root .mcp.json for project-scope MCP servers) that preserves foreign
keys. IngestPlugins reads enabledPlugins / extraKnownMarketplaces
to discover plugins on import; Render projects each plugin’s components to
Claude’s native paths (~/.claude/skills/<name>/, mcpServers in
.claude.json, …) and deliberately leaves the enablement keys themselves
untouched. The asymmetry is the cross-adapter rule, not a Claude quirk — see
architecture.md § PluginIngester (read-only).
- Key:
New(Options) *Adapter; theAdapter+PluginIngestermethods;ParseFrontmatter/EncodeFrontmatter;MergeKeys. - Depends on: adapter, secrets, source, paths, iox, jsonkeys.
- Files:
claude.go,render.go,ingest.go,ingest_plugins.go,apply.go,paths.go,frontmatter.go,skill.go,command.go,subagent.go,hook.go,lsp.go,memory.go,settings.go.
internal/adapter/opencode
Section titled “internal/adapter/opencode”The OpenCode adapter — MCP, memory, skills, subagents, commands via JSONC
round-trip (tailscale/hujson). Omits CapHook/CapLSP (skipped with a warning).
- Key:
New(Options) *Adapter; theAdaptermethods. - Depends on: adapter, secrets, source, paths, iox.
- Files:
opencode.go,render.go,ingest.go,apply.go,paths.go,skill.go,subagent.go,command.go,memory.go,settings.go.
internal/adapter/codex
Section titled “internal/adapter/codex”The Codex CLI adapter — MCP, memory, skills, subagents, slash commands, and
hooks. MCP servers ([mcp_servers.*]) and hooks (inline [hooks.*]) both merge
into the TOML ~/.codex/config.toml via the merge-toml-keys strategy
(MergeTOML in settings.go, which preserves the user’s foreign keys) — so
config.toml is the adapter’s single key-merge file; skills land in the shared
~/.agents/skills/; subagents project to Codex’s TOML agent format and commands
to global-only custom prompts.
Implements PluginIngester (parses [plugins."<name>@<source>"] enable-state
on import); Render does not re-emit those tables on apply, matching the
cross-adapter invariant — see
architecture.md § PluginIngester (read-only).
Omits CapLSP (Codex has no LSP concept).
- Key:
New(Options) *Adapter; theAdapter+PluginIngestermethods;MergeTOML;IngestMCPSpec. - Depends on: adapter, adapter/claude (frontmatter helpers), secrets, source, paths, iox, jsonkeys, go-toml/v2.
- Files:
codex.go,render.go,mcp.go,ingest.go,ingest_plugins.go,apply.go,paths.go,skill.go,command.go,subagent.go,hook.go,memory.go,settings.go.
internal/adapter/cursor
Section titled “internal/adapter/cursor”The Cursor adapter — MCP, memory, skills, subagents, slash commands, and hooks.
MCP lands in .cursor/mcp.json (the same mcpServers shape as Claude) and hooks
in .cursor/hooks.json ({ "version": 1, "hooks": { … } }) — both JSON, so the
adapter’s single key-merge strategy is merge-json-keys. The required hooks
version is injected post-merge in applyWrite (never rendered into op.Content,
so it is never an orphan-strippable owned key). Memory projects to the repo-root
AGENTS.md at project scope only (user-level rules live in Cursor’s app-local
storage); skills to .cursor/skills/; subagents to .cursor/agents/<name>.md
(tools/color dropped); commands to .cursor/commands/<name>.md (plain
markdown — frontmatter dropped). Omits CapLSP (Cursor has no LSP concept).
Implements no PluginIngester yet — Cursor’s native plugin enable-state location
is undocumented, so plugin discovery on import is deferred; apply still fans
out plugin components like every adapter.
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (frontmatter/skill/extra helpers), secrets, source, paths, iox, jsonkeys, afero.
- Files:
cursor.go,render.go,mcp.go,ingest.go,apply.go,paths.go,skill.go,command.go,subagent.go,hook.go,memory.go.
internal/adapter/gemini
Section titled “internal/adapter/gemini”The Gemini CLI adapter — MCP, memory, slash commands, subagents, and hooks. MCP
(mcpServers, with Gemini’s url/httpUrl transport split) and hooks (hooks,
the same nested shape as Claude) both merge into .gemini/settings.json via
merge-json-keys — settings.json is the adapter’s single key-merge file, so the
user’s other keys (theme, model, …) are preserved. Memory projects to
GEMINI.md (~/.gemini/GEMINI.md user / repo-root GEMINI.md project); commands
to .gemini/commands/<name>.toml (description + prompt); subagents to
.gemini/agents/<name>.md. Omits CapSkill (Gemini uses extensions, not Agent
Skills) and CapLSP (no LSP concept) — both ✗ skip. No PluginIngester (no
native plugin enable-state agentsync models).
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (frontmatter helpers), secrets, source, paths, iox, jsonkeys, go-toml/v2.
- Files:
gemini.go,render.go,mcp.go,ingest.go,apply.go,paths.go,command.go,subagent.go,hook.go,memory.go.
internal/adapter/continuedev
Section titled “internal/adapter/continuedev”The Continue adapter (package continuedev — continue is a Go keyword; the
agent name is still continue). MCP, memory, and slash commands, projected as
Continue “blocks” — one file per item, so there is no key-merge
(KeyMergeStrategy() returns ""): MCP → .continue/mcpServers/<id>.yaml
(stdio command/args/env; remote streamable-http/sse + url +
requestOptions.headers); memory → .continue/rules/agentsync.md (a
frontmatter-less always-apply rule); commands → .continue/prompts/<name>.md
prompt blocks. Skills/subagents/hooks/LSP have no faithful Continue target and
are skipped with a report. Omits CapSkill/CapSubagent/CapHook/CapLSP. No
PluginIngester.
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (frontmatter/Extra helpers), secrets, source, paths, iox, sigs.k8s.io/yaml.
- Files:
continue.go,render.go,mcp.go,ingest.go,apply.go,paths.go,command.go,memory.go.
internal/adapter/windsurf
Section titled “internal/adapter/windsurf”The Windsurf (Cascade) adapter — MCP, memory, and slash commands, scope-
asymmetric to match Windsurf’s layout: MCP renders at user scope only
(~/.codeium/windsurf/mcp_config.json, JSON mcpServers via merge-json-keys;
remote uses serverUrl), while memory (.windsurf/rules/agentsync.md, plain
markdown) and commands (.windsurf/workflows/<name>.md, plain markdown workflows)
render at project scope only; the non-applicable scope reports a skip. Skills/
subagents/hooks/LSP have no Windsurf concept and are skipped. Emits no Ingest
warnings (rules/workflows are plain markdown), so it does not implement
WarnEmitter. No PluginIngester.
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (Extra helpers), secrets, source, paths, iox, jsonkeys.
- Files:
windsurf.go,render.go,mcp.go,ingest.go,apply.go,paths.go,command.go,memory.go.
internal/adapter/roo
Section titled “internal/adapter/roo”The Roo Code adapter — MCP, memory, and slash commands via clean filesystem
.roo/ paths. MCP → .roo/mcp.json (project-level, mcpServers via
merge-json-keys; remote uses explicit type: streamable-http/sse); memory →
.roo/rules/agentsync.md (plain markdown rule) and commands →
.roo/commands/<name>.md (markdown + frontmatter — keeps description +
argument-hint), both at user and project scope. Roo’s global MCP is VS Code
globalStorage (not targeted — user-scope MCP is reported as a skip). Omits
CapSkill/CapSubagent/CapHook/CapLSP. No PluginIngester.
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (frontmatter/Extra helpers), secrets, source, paths, iox, jsonkeys.
- Files:
roo.go,render.go,mcp.go,ingest.go,apply.go,paths.go,command.go,memory.go.
internal/adapter/cline
Section titled “internal/adapter/cline”The Cline adapter — MCP, memory, and slash commands, scope-asymmetric: MCP
renders at user scope into the Cline CLI’s clean ~/.cline/mcp.json
(merge-json-keys; transport inferred, no type — remote uses url+headers),
while memory (.clinerules/agentsync.md, plain markdown) and commands
(.clinerules/workflows/<name>.md, plain markdown) render at project scope; the
non-applicable scope reports a skip. Cline has no project MCP file (its VS Code
extension uses OS/editor-specific globalStorage agentsync does not write) and its
global rules live in ~/Documents/Cline/ (also not targeted). Skills/subagents/
hooks/LSP have no Cline concept and are skipped. Emits no Ingest warnings
(rules/workflows are plain markdown), so it does not implement WarnEmitter. No
PluginIngester.
- Key:
New(Options) *Adapter; theAdaptermethods;IngestMCPSpec. - Depends on: adapter, adapter/claude (Extra helpers), secrets, source, paths, iox, jsonkeys.
- Files:
cline.go,render.go,mcp.go,ingest.go,apply.go,paths.go,command.go,memory.go.
internal/adapter/generic
Section titled “internal/adapter/generic”The breadth-tier adapter: one data-driven Adapter implementation that serves
many agents from a table of verified Specs (specs.go) rather than a package
each. Covers memory (a rules/instructions file, plain markdown), MCP where
the agent reads a JSON server-map agentsync can express, and Agent Skills
(SKILL.md directories) where the agent natively scans a skills directory — every
other component is reported as a skip. A Spec declares per-scope memory/MCP/skills
paths plus MCP “dialect” knobs that capture the tail’s variance (top-level key
mcpServers/servers/mcp/context_servers/the flat namespaced amp.mcpServers;
transport field type/transport/inferred; stdio value stdio/local; remote
URL key url/httpUrl/serverUrl). The MCP merge is JSONC-tolerant (hujson), so a
commented settings file (Zed/Copilot/Amp) is preserved, not clobbered (re-emitted
as plain JSON, like OpenCode). Skills need no dialect — the on-disk format is
uniform — so the tier reuses the deep adapters’ shared claude.SkillFileOps
projection; an agent’s Skills target is usually the cross-vendor .agents/skills/
(byte-identical to Codex, so the render pipeline dedupes the ops). Breadth agents
register through the normal registry and flow through apply/import (drift, secrets,
capture). Adding an agent is a verified table row, not a package.
- Key:
Spec,New(Spec, Options) *Adapter; theAdaptermethods;Specs(). - Depends on: adapter, adapter/claude (Extra + SkillFileOps helpers), secrets, source, paths, iox, jsonkeys.
- Files:
generic.go,render.go,ingest.go,apply.go,specs.go.
internal/adapter/noop
Section titled “internal/adapter/noop”Placeholder adapter that detects true and renders nothing. Used as a registry
stand-in in tests; no production agent is registered as a noop today (every valid
agent has a real adapter). agent add/import still reject any future
noop-registered agent unless AGENTSYNC_ALLOW_UNIMPLEMENTED=1.
- Depends on: adapter, secrets, source. Files:
noop.go.
Pipeline & state
Section titled “Pipeline & state”internal/render
Section titled “internal/render”Orchestrates apply: canonical + registry → per-agent FileOps/Skips, runs
collision detection and backups, records state, synthesizes cleanup ops for
orphaned owned keys, and builds the translation report.
- Key:
Plan;Apply;PreviewApply(dry-run: collision preview + synced/would-change verdict);Writer(NewWriter/NewPreviewWriter);TranslationReport(PrintText/PrintJSON);BuildReport;RecordOpsState;OrphanFiles;PruneStaleState;BackupFile/PruneBackups;CollisionReport. - Depends on: adapter, secrets, source, state, paths, iox, drift.
- Files:
pipeline.go,writer.go,state_apply.go,report.go.
internal/capture
Section titled “internal/capture”The single dest→source write-back path: re-references secrets, preserves
source-only fields, writes via source.Write*. Used by import and reconcile.
- Key:
Capture(home, ingested, opts) (Result, error);Opts;Result. - Depends on: source, secrets, paths, iox.
- Files:
capture.go,leak_fixture.go(compile-time leak guard).
internal/drift
Section titled “internal/drift”Pure 3-way classifier — no IO.
- Key:
Class(Clean,Pending,Drift,Converged,Conflict,New,ForeignCollision,Orphan,OrphanDrifted);Classify(hsrc, happlied, hdest);SafeForAutoApply(c). - Files:
classifier.go.
internal/state
Section titled “internal/state”Persists last-applied hashes and plugin/marketplace pins to
.state/targets.json; schema-versioned with migrators.
- Key:
SchemaVersion;Targets(Files,Keys,Marketplaces,Plugins);FileEntry;KeyEntry;Load/Save;migrate. - Depends on: iox. Files:
schema.go,store.go,migrate.go.
internal/marketplace
Section titled “internal/marketplace”Models the Claude marketplace/plugin format, fetches sources, and projects plugin manifests into canonical components.
- Key:
Marketplace,PluginEntry,Source,PluginManifest;ProjectionResult;Project/ProjectWithReader;ProjectInstalled(one installed plugin in isolation — letsexplain <id>attribute coverage to the named plugin rather than the flattened union);Fetcher(interface) withGitFetcher/NPMFetcher/RelativeFetcher;LoadProjected/LoadProjectedLenient/LoadProjectedExcluding. - Depends on: source, log.
- Files:
manifest.go,projection.go,loadprojected.go,fetcher.go,fetch_git.go,fetch_npm.go,fetch_relative.go,update.go.
Infrastructure (leaf packages, no internal deps)
Section titled “Infrastructure (leaf packages, no internal deps)”internal/iox
Section titled “internal/iox”Atomic file IO and locking.
- Key:
AtomicWrite(dest, data, mode);Lock/AcquireLock/AcquireLockTimeout;ErrSymlinkDest;AllowSymlinkDestEnv. - Files:
atomic.go,lock.go.
internal/jsonkeys
Section titled “internal/jsonkeys”Per-key JSON-pointer merge that preserves foreign keys and uses json.Number
(no float64 rounding).
- Key:
DecodeObject;DecodeYAML;MergeKeys(existing, ours, ownedPointers). - Files:
jsonkeys.go.
internal/paths
Section titled “internal/paths”Resolves AGENTSYNC_HOME, AGENTSYNC_TARGET_ROOT, and $HOME; converts between
absolute and ${HOME}-relative forms for portable state.
- Key:
Env(interface),OSEnv,MapEnv;HomeDir;AgentsyncHome;HomeRelative/FromHomeRelative. - Files:
paths.go.
internal/log
Section titled “internal/log”slog setup. Key: New(w, verbose) *slog.Logger. Files: log.go.
internal/untrusted
Section titled “internal/untrusted”The display trust boundary for fetched/native metadata. Owns Sanitize (strip
terminal-control + deceptive bidi/zero-width runes) and the Text defined string
type whose String() sanitizes — so a plugin/marketplace id, version, or name
typed untrusted.Text is safe to print through fmt by construction; the raw
value is reachable only via the explicit Unverified(). ui.Sanitize delegates
here. See architecture §7 and SECURITY.md.
- Key:
Text(.String()/.Unverified()/.Empty());Wrap;Sanitize. - Files:
untrusted.go.
internal/testenv
Section titled “internal/testenv”Guards FS-touching tests so they only run in the hermetic container.
- Key:
RequireContainer(t);MustRunInContainer();InContainer() bool;EnvVar(AGENTSYNC_TEST_IN_CONTAINER). - Files:
container.go.
Dependency direction at a glance
Section titled “Dependency direction at a glance”cli sits on top of everything. render, capture, and the adapters depend on
source + secrets. source/secrets/state depend only on the leaf infra
packages (iox, jsonkeys, paths, and — for the canonical plugin/marketplace
identity fields typed untrusted.Text — untrusted). drift, iox,
jsonkeys, paths, log, and untrusted depend on nothing internal — they’re
the foundation. See the rendered dependency graph in
architecture §10.