{"id":"11c51b9a-8186-4e09-a737-85b903ffa100","slug":"spxrogers-agentsync","kind":"mcp","name":"agentsync","summary":"Sync AI coding-agent configs (Claude Code, OpenCode, Codex, and more) from one canonical, committable source.","owner":"spxrogers","repoUrl":"https://github.com/spxrogers/agentsync","license":"MIT","licenseTier":"permissive","trust":"community","stars":35,"match":null,"fewStars":false,"format":"mcp","createdAt":"2026-05-03T13:38:51.000Z","addedAt":"2026-10-14T05:44:19.885Z","logoUrl":"https://avatars.githubusercontent.com/u/7064929?u=3488bb8ca4b41f77a1e60334a2347adcfddee404&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools"],"tags":["claude-code","cli","dev-tools","dotfiles","golang","ai","codex","cursor"],"runtime":"go","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-17T06:43:25.283Z","sample":false,"url":"https://conduits.dev/tools/spxrogers-agentsync","language":"Go","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"<div align=\"center\">\n\n# agentsync\n\n**One source of truth for every AI coding agent on your machine.**\n\nDefine your MCP servers, memory, skills, and marketplace plugins once in\n`~/.agentsync/`. Run `agentsync apply`. They land — correctly translated — in\n**32 agents**: ten deep adapters (Claude Code, OpenCode, Codex CLI, Cursor,\nGemini CLI, Continue, Windsurf, Roo Code, Cline, Grok Build) plus a breadth tier of 22 more\n(amp, goose, qwen, warp, zed, kiro, junie, factory, copilot, crush, …).\n\n[Quickstart](#quickstart) · [Install](#install) · **[Docs site → agentsync.cc](https://agentsync.cc)** · [User guide](docs/user-guide.md) · [Known limits](#known-limits)\n\n</div>\n\n> **Status: beta (v0.1.0).** Ships 32 agents — ten deep adapters (Claude, OpenCode, Codex, Cursor, Gemini, Continue, Windsurf, Roo, Cline, Grok Build) + a 22-agent breadth tier — end-to-end.\n> The tool is functional and tested under `just test-release`; the canonical\n> layout, CLI surface, and state schema are stabilizing toward `1.0.0` and may\n> still change. A few documented trade-offs remain (see [Known limits](#known-limits)).\n\n---\n\n## What it does\n\nIf you use more than one AI coding agent, you keep re-entering the same config in\neach one's native format, you forget to install a plugin everywhere, and your\nconfigs quietly drift apart. agentsync fixes the fan-out:\n\n- **Edit once, apply everywhere.** Add an MCP server or install a plugin a single\n  time; it's projected into each agent's native config — fully where the agent\n  has the concept, lossily-but-reported where it doesn't, skipped (never\n  silently) where it can't.\n- **Bidirectional, chezmoi-style.** When an agent edits its own config, agentsync\n  detects the drift and offers a merge: adopt the edit into your source, or\n  re-impose the source. Nothing is overwritten behind your back.\n- **Secrets stay secret.** Reference `${secret:github.token}`; agentsync resolves\n  it at apply time from an age-encrypted vault and **never** writes the cleartext\n  back into your (committable) source.\n- **A bad apply is revertible.** `apply` keeps each destination dir (`~/.claude`,\n  …) in a **local-only** git history (opt-out, never pushed), so `agentsync revert`\n  rolls back to a prior checkpoint — while leaving any untracked/gitignored files\n  you put in the dir untouched.\n\nNew here? The **[User guide](docs/user-guide.md)** takes you 0→100.\n\n## Quickstart\n\n    agentsync init\n    agentsync agent add claude\n    agentsync agent add opencode\n    agentsync mcp add github --command npx --args \"-y,@modelcontextprotocol/server-github\"\n    agentsync mcp add linear --type http --url https://mcp.linear.app/sse \\\n      --header \"Authorization: Bearer ${secret:LINEAR_TOKEN}\"   # remote auth via a secret ref\n    agentsync apply --dry-run    # preview what would change vs. is already synced\n    agentsync apply\n    # … in CI, fail the build on drift (exit 2 when out of sync, 0 when clean):\n    agentsync status --exit-code\n    agentsync diff --agents claude --exit-code\n    # … later, if an apply goes wrong:\n    agentsync revert claude      # roll ~/.claude back to the previous checkpoint\n\n## Documentation\n\nThe full docs are published with search and rendered diagrams at\n**[agentsync.cc](https://agentsync.cc)** (source in [`website/`](website/)). The\ncanonical markdown also lives in [`docs/`](docs/):\n\n| Doc | What it covers |\n| --- | --- |\n| **[User guide](docs/user-guide.md)** | Install → first sync → daily loop → secrets → plugins → project config. |\n| [Concepts & glossary](docs/concepts.md) | The three-state model, drift, reconcile — every term in one page. |\n| [Capability matrix](docs/capability-matrix.md) | Exactly what each agent supports, and what's lossy or deferred. |\n| [Architecture](docs/architecture.md) | The apply/capture pipelines, drift classifier, and secret invariants. |\n| [Component map](docs/components.md) | The codebase, package by package. |\n| [CONTRIBUTING](CONTRIBUTING.md) · [SECURITY](SECURITY.md) · [CHANGELOG](CHANGELOG.md) | Contributing, threat model, release history. |\n\n## Supported agents at a glance\n\nThe ten **deep adapters** (rich, agent-specific, often bidirectional):\n\n| Agent | Status | Component coverage |\n| --- | --- | --- |\n| **Claude Code** | ✓ adapter | MCP, memory, skills, subagents, commands, hooks (◐, command hooks; other handler types reported). LSP skipped unless provided by Claude plugins. |\n| **OpenCode** | ✓ adapter | MCP, memory, skills, subagents, commands. Hooks + LSP skipped. |\n| **Codex CLI** | ✓ adapter | MCP (TOML `config.toml`), memory, skills, subagents (◐), slash commands (◐, global-only), hooks (◐) + plugin import. No LSP concept. |\n| **Cursor** | ✓ adapter | MCP (`.cursor/mcp.json`), memory (◐, project-scope `AGENTS.md`), skills (`.cursor/skills/`), subagents (◐), slash commands (◐), hooks (◐, `.cursor/hooks.json`). No LSP concept. |\n| **Gemini CLI** | ✓ adapter | MCP + hooks (◐) in `.gemini/settings.json`, memory (`GEMINI.md`), subagents (◐), slash commands (◐, TOML). No skills (uses extensions) or LSP concept. |\n| **Continue** | ✓ adapter | MCP (`.continue/mcpServers/`), memory (`.continue/rules/`), slash commands (◐, `.continue/prompts/`) — projected as Continue blocks. No skills/subagents/hooks/LSP concept. |\n| **Windsurf** | ✓ adapter | MCP (`~/.codeium/windsurf/mcp_config.json`, user scope), memory (◐, `.windsurf/rules/`, project scope), slash commands (◐, `.windsurf/workflows/`, project scope). No skills/subagents/hooks/LSP concept. |\n| **Roo Code** | ✓ adapter | MCP (`.roo/mcp.json`, project scope), memory (`.roo/rules/`) + slash commands (◐, `.roo/commands/`) at both scopes. No skills/subagents/hooks/LSP concept. |\n| **Cline** | ✓ adapter | MCP (`~/.cline/mcp.json` CLI, user scope), memory (◐, `.clinerules/`) + slash commands (◐, `.clinerules/workflows/`) at project scope. No skills/subagents/hooks/LSP concept. |\n| **Grok Build** | ✓ adapter | MCP (◐, TOML), instructions, complete skills, commands (◐), and command hooks (◐) at user and project scopes. [Supported paths and limits](docs/grok.md). Subagent/LSP projection and plugin import deferred. |\n\nPlus a **breadth tier** of 22 more via one data-driven generic adapter — **memory**\nfor all, **MCP** where the agent reads a JSON server-map (15 of 22), **Agent\nSkills** where the agent natively scans a `SKILL.md` directory (18 of 22), and\ncommand hooks for Factory only: `amp`,\n`goose`, `qwen`, `warp`, `jules`, `junie`, `openhands`, `amazonq`, `zed`,\n`kilocode`, `kiro`, `trae`, `jetbrains`, `firebase`, `antigravity`, `augmentcode`,\n`copilot`, `copilot-cli`, `crush`, `factory`, `pi`, `mistral`. Each is a *verified*\nspec (paths cross-referenced against upstream docs + prior art), flowing through the\nsame drift/secrets/capture pipeline as the deep adapters. See the\n[capability matrix → Breadth tier](docs/capability-matrix.md#breadth-tier).\n\nFull ✓/◐/✗ breakdown per component: **[capability matrix](docs/capability-matrix.md)**.\n\n## Install\n\n### macOS — Homebrew\n\n    brew tap spxrogers/tap\n    brew install agentsync\n\n### Linux — deb / rpm\n\nPick the package for your architecture — both `amd64` and `arm64` are published\nevery release (use the matching URL below).\n\nDebian/Ubuntu:\n\n    # amd64\n    curl -fsSL https://github.com/spxrogers/agentsync/releases/latest/download/agentsync_linux_amd64.deb -o agentsync.deb\n    # arm64\n    curl -fsSL https://github.com/spxrogers/agentsync/releases/latest/download/agentsync_linux_arm64.deb -o agentsync.deb\n    sudo dpkg -i agentsync.deb\n\nRPM:\n\n    # amd64\n    sudo rpm -i https://github.com/spxrogers/agentsync/releases/latest/download/agentsync_linux_amd64.rpm\n    # arm64\n    sudo rpm -i https://github.com/spxrogers/agentsync/releases/latest/download/agentsync_linux_arm64.rpm\n\n### Windows — Scoop or Chocolatey\n\nScoop:\n\n    scoop bucket add spxrogers https://github.com/spxrogers/scoop-bucket\n    scoop install agentsync\n\nChocolatey:\n\n    choco install agentsync\n\nEach new Chocolatey version goes through the community repository's automated\nchecks before it's publicly installable, so a fresh release can lag the other\nchannels by a few hours.\n\n### Any platform — prebuilt binary\n\nDownload the archive for your OS/arch from the\n[latest release](https://github.com/spxrogers/agentsync/releases/latest)\n(`linux` / `darwin` / `windows` × `amd64` / `arm64`), extract it, and put the\n`agentsync` binary on your `PATH`.\n\nOn **macOS**, the prebuilt binaries are not signed or notarized, so Gatekeeper\nblocks the first run with *\"agentsync cannot be opened because the developer\ncannot be verified.\"* Clear the quarantine attribute before running it:\n\n    xattr -dr com.apple.quarantine ./agentsync\n\n(Homebrew users don't need this — the cask strips the quarantine attribute for\nyou on install.)\n\n### From source\n\n    go install github.com/spxrogers/agentsync/cmd/agentsync@latest\n\nor clone and `go build ./cmd/agentsync`.\n\n### Coming soon\n\nAUR (Arch) packaging is wired in `.goreleaser.yaml` but not published yet —\ntracked in [issue #13](https://github.com/spxrogers/agentsync/issues/13). Until\nthen, use the prebuilt binary or `go install` above.\n\n## Cross-machine sync\n\nagentsync is single-machine. To sync `~/.agentsync/` across machines, use chezmoi (or any dotfile manager):\n\n    chezmoi add ~/.agentsync\n\n## Secrets — age key backup\n\nIf you lose your age private key, you lose access to all encrypted secrets. Recommended: store the key in a 1Password Secure Note or your machine-setup repo. agentsync does not back up the key for you.\n\n`agentsync secret set` accepts the value three ways:\n\n    agentsync secret set github.token --stdin    # value from stdin (best for scripts / 1Password CLI)\n    agentsync secret set github.token            # prompt with echo off\n    agentsync secret set github.token=ghp_…      # back-compat; warns — argv is visible to ps(1) and shell history\n\n`agentsync diff` redacts every resolved `${secret:…}` value before printing, so a piped diff doesn't leak credentials to logs.\n\n## Known limits\n\n- **OpenCode hooks**: OpenCode hooks are JS/TS plugins, not declarative shell commands. agentsync does NOT auto-translate Claude hooks to OpenCode. Hand-author a small JS/TS plugin if you need a hook on OpenCode.\n- **Codex projections are lossy where Codex differs**: subagents project to Codex's TOML agent format (the `tools`/`color` frontmatter has no target and is dropped, reported in the apply report); slash commands map to Codex *custom prompts* which are global-only, so a **project-scope** command is skipped; hooks mirror Claude's declarative hook schema as inline `[hooks.*]` tables in `~/.codex/config.toml` but only for the events Codex recognizes (Claude's `SessionEnd`/`Notification` drop). All of these are surfaced in the translation report — nothing is dropped silently.\n- **Cursor projections are lossy where Cursor differs**: MCP and skills are full-fidelity (`.cursor/mcp.json` shares Claude's `mcpServers` shape; skills share the `SKILL.md` directory). Memory projects to the repo-root `AGENTS.md` at **project scope only** — Cursor keeps user-level rules in app-local storage, so user-scope memory is reported as a skip. Subagents (`.cursor/agents/`) drop Claude's `tools`/`color` (no Cursor field); slash commands (`.cursor/commands/`) are plain markdown so command frontmatter drops; hooks (`.cursor/hooks.json`) remap Claude's events to Cursor's camelCase names (events with no Cursor equivalent drop) and always carry the required top-level `version`. All losses are surfaced in the translation report.\n- **Gemini CLI projections are lossy where Gemini differs**: MCP and memory are full-fidelity (`.gemini/settings.json` `mcpServers` with Gemini's `url`/`httpUrl` transport split; `GEMINI.md`). Subagents (`.gemini/agents/`) drop Claude's `tools` (Gemini's tool vocabulary differs) and `color`; slash commands become TOML (`.gemini/commands/*.toml`, `description` + `prompt`) so `argument-hint`/`allowed-tools` drop and Claude's `$ARGUMENTS` placeholder isn't auto-translated to Gemini's `{{args}}`; hooks live in `settings.json` (same nested shape as Claude) with events remapped (`PreToolUse`→`BeforeTool`, …) and unmapped events dropped. Skills have no Gemini concept (it uses extensions) and are skipped. All losses are surfaced in the translation report.\n- **Continue projections are lossy where Continue differs**: Continue composes \"blocks\" (one file per item under `.continue/`). MCP is full-fidelity (`.continue/mcpServers/<id>.yaml`, with remote auth headers under `requestOptions.headers`); memory lands as a frontmatter-less always-apply rule (`.continue/rules/agentsync.md`, byte-clean). Slash commands become prompt blocks (`.continue/prompts/*.md`) so `argument-hint`/`allowed-tools` drop. Continue has no Agent Skills, no per-file subagents (its \"agents\" are top-level assistants), no declarative hooks, and no LSP config, so those are skipped. All losses are surfaced in the translation report.\n- **Windsurf MCP is scope-asymmetric**: Windsurf's MCP config is global-only (`~/.codeium/windsurf/mcp_config.json`), so MCP renders at **user scope** (skipped + reported at project scope). Memory and slash commands render at **both** scopes: project memory → `.windsurf/rules/agentsync.md` (with the documented `trigger: always_on` activation frontmatter; stripped on import), user memory → the global `~/.codeium/windsurf/memories/global_rules.md` (always-on, 6k-char limit enforced by Windsurf); commands → `.windsurf/workflows/` (project) / `~/.codeium/windsurf/global_workflows/` (user), plain markdown so command frontmatter drops. All losses are surfaced in the translation report.\n- **Roo Code is project-MCP-only**: Roo's clean MCP file is project-level (`.roo/mcp.json`); its *global* MCP lives in VS Code globalStorage (OS/editor-specific), which agentsync does not write (matching every other config-sync tool) — so user-scope Roo MCP is reported as a skip. Memory (`.roo/rules/`) and commands (`.roo/commands/`, which keep `description` + `argument-hint`) work at both scopes.\n- **Cline targets the CLI's MCP file**: Cline has no project MCP file and its VS Code-extension MCP lives in OS/editor-specific globalStorage (which no config-sync tool writes), so agentsync targets the Cline CLI's clean `~/.cline/mcp.json` at user scope (project-scope MCP reported as a skip). Memory (`.clinerules/`) + workflows (`.clinerules/workflows/`) render at project scope (Cline's global rules are a non-XDG `~/Documents/Cline/` path agentsync does not target).\n- **LSP projection beyond Claude**: OpenCode LSP support is deferred (Codex, Cursor, Gemini, Continue, Windsurf, Roo, and Cline have no LSP concept at all). Claude plugins that include LSP servers install correctly on Claude itself; on other agents you'll see `lsp server X skipped` in the apply translation report.\n- **TOML / JSONC comment preservation**: comments in `~/.agentsync/mcp/*.toml`, in agent-side `opencode.json`, in Gemini's `.gemini/settings.json`, in the breadth tier's JSONC settings files (Zed's `settings.json`, Amp's `settings.json`, Copilot's `.vscode/mcp.json`), and in Codex's `~/.codex/config.toml` are NOT preserved across reconcile `[w]`rite-back or import / apply. For TOML, hand-edited comments survive in unrelated sections; for the JSONC files the whole file is re-emitted as plain JSON on the first agentsync write (foreign keys and values are preserved; the original is backed up). For Zed this is your main editor settings file — expect comments to be stripped and keys re-sorted on the first apply. Deferred to a later release.\n- **Hand-edits to agentsync-owned keys** in shared agent files (e.g. an MCP server entry in `~/.claude.json` that agentsync owns): the next `apply` overwrites them with NO foreign-collision backup, because agentsync considers them its own. Use `agentsync reconcile` (the drift classifier catches the edit and offers `[w]`rite-back) BEFORE the next apply if you want to keep them.\n- **Destination git backup is local-only**: `apply` keeps each managed destination dir (`~/.claude`, `~/.codex`, …) in its own git history so `agentsync revert` can undo a bad apply — but that history is **never pushed** (the rendered files hold `${secret:…}` references resolved to **cleartext**, so its commits may too; the `.git` dir is hardened to `0700`, which is **POSIX-only** — a Windows no-op, where NTFS ACLs are the boundary). A destination dir already under **your own** source control is detected as `foreign source control` and left un-versioned (only `agentsync-versioned` dirs are reverted; an `untracked` dir is a candidate for init). `$HOME`-level strays (Claude's `~/.claude.json`) are never versioned — agentsync never inits a repo at `$HOME`. `revert`'s \"nothing is lost\" guarantee covers **tracked files only**: untracked / gitignored scratch files in the dir are left untouched, never snapshotted.\n- **Plain-http / git:// plugin sources** are rejected by default to prevent MITM swap. Set `AGENTSYNC_ALLOW_INSECURE_URLS=1` for internal mirrors.\n- **Symlinked destinations** (e.g. `~/.claude.json` is a chezmoi symlink into your dotfiles repo) are rejected by default — a rename onto the path would replace the symlink with a regular file and strand your linked source. Set `AGENTSYNC_ALLOW_SYMLINK_DEST=1` to write through the symlink instead (the underlying file is updated in place; the link survives). The same switch governs how agentsync READS a symlinked whole-file destination (a `CLAUDE.md`, a skill, a subagent), so `apply` and every drift command — `status`, `diff`, `reconcile`, `explain` — need it: without it `status`, `diff`, `reconcile` and `explain` report the file as drifted rather than comparing through the link (and `diff` says so, with a `symlink` hunk naming the switch); with it they resolve the link and compare the file it points at, so a chezmoi setup reports clean after a successful apply. A key-merged file such as `~/.claude.json` is read through the link either way.\n- **Aider** and **Firebender**: deliberately deferred — no faithful generic projection (Aider has no MCP and only an `.aider.conf.yml` `read:` pointer for memory; Firebender's config is unverified).\n\n## Environment overrides\n\n| Env var | Purpose |\n| --- | --- |\n| `AGENTSYNC_HOME` | Override `~/.agentsync/` location (absolute path). |\n| `AGENTSYNC_TARGET_ROOT` | Redirect `$HOME` for testing (used by the hermetic test container). A sandbox: every path agentsync resolves — the canonical source included — lands under it, and `AGENTSYNC_HOME` / `GROK_HOME` are ignored while it is set. |\n| `GROK_HOME` | Grok Build's own home override, honoured at user scope (absolute path; see [Grok support](docs/grok.md)). `/` and your home directory are refused. |\n| `AGENTSYNC_ALLOW_SYMLINK_DEST=1` | Write through symlinked destinations, and compare through them when reading (chezmoi-managed files). Needed by `apply` and by `status`/`diff`/`reconcile`/`explain`. |\n| `AGENTSYNC_ALLOW_INSECURE_URLS=1` | Accept http:// and git:// plugin / marketplace sources. |\n| `AGENTSYNC_ALLOW_UNIMPLEMENTED=1` | Register an agent that has no implemented adapter yet (none today — every valid agent is real). |\n| `AGENTSYNC_ALLOW_PLUGIN_DRIFT=1` | Bypass the plugin-cache manifest-SHA check (after hand-editing). |\n| `AGENTSYNC_ALLOW_OFFLINE_VERIFY=1` | Skip `${secret:…}` resolution in `agentsync check`; reference *shape* is still validated (CI without an age key). |\n| `AGENTSYNC_NO_UPGRADE_NOTICE=1` | Never show the one-time first-run-after-upgrade notice. |\n| `AGENTSYNC_AGE_SKIP_PERM_CHECK=1` | Skip the 0600 **mode** check on the age identity file (ACL'd NFS). The identity must still be a regular file. |\n| `AGENTSYNC_MAX_TARBALL_MB=<N>` | Override the per-tarball decompressed-bytes cap (default 512). 0 disables. |\n| `AGENTSYNC_LOCK_TIMEOUT_MS=<N>` | Override how long a mutating command waits for the global lock before erroring (default 30000ms). |\n| `NO_COLOR` | Disable ANSI color and bold under the default `--color=auto` (any value counts, even empty). `--color=always` still forces color on. |\n| `EDITOR` | The editor `secret edit` opens the decrypted vault in (default `vi`; flags are allowed, e.g. `code --wait`). |\n| `AGENTSYNC_TEST_IN_CONTAINER=1` | Bypass the host test guard (use only with `go test -run` for a single case). |\n\nThis table is complete for everything agentsync gives its own meaning to: every\n`AGENTSYNC_*` override, `GROK_HOME`, and the standard variables it honours\nspecially (`NO_COLOR`, `EDITOR`). Standard process-environment conventions that\nGo and its libraries honour behave as they do for any program and are not\nlisted — `HOME`, `PATH`, `TMPDIR` (`TMP`/`TEMP` on Windows; where scratch files\nland, including the vault `secret edit` decrypts while your editor is open),\n`HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` for marketplace fetches, `SSH_AUTH_SOCK`\nfor ssh plugin sources — nor are the `${env:NAME}` references you write\nyourself. A guard test (`TestEnvOverridesDocumented`, `internal/testenv`) keeps\nthe table complete: it fails when production code reads or names a variable\nthat this table or the [website reference](https://agentsync.cc/reference/environment/)\nlacks, when the two tables disagree, or when a row names something the code no\nlonger reads. The test harness's own `AGENTSYNC_TEST_*` / `AGENTSYNC_LIVE_*`\nsignals (beyond `AGENTSYNC_TEST_IN_CONTAINER`, kept here because debugging a\nsingle test needs it) are contributor-only;\n[CONTRIBUTING.md](CONTRIBUTING.md#test-harness-environment-signals) lists them.\n\n## Troubleshooting\n\nReach for `agentsync doctor` first — it checks the machine (PATH, home/state\nwritability, adapter detection, secrets backend), while `agentsync check`\nvalidates the config itself.\n\n- **Every command refuses, naming `agentsync migrate subagents`**: the canonical\n  subagent directory moved from `agents/` to `subagents/`. Run `agentsync\n  migrate subagents` once per tree (add `--project <root>` for a project tree);\n  it moves the files and rewrites that tree's recorded state in one step. The\n  refusal is deliberate — an unmigrated tree loads with *zero* subagents, and\n  one `apply` would then delete every subagent it had already rendered. See\n  [Upgrading](https://agentsync.cc/reference/upgrading/).\n- **First apply on a populated machine**: agentsync sees pre-existing native config files and triggers `foreign-collision`. The original is backed up to `~/.agentsync/.state/backups/<ts>/` before the new content lands. Recommend `agentsync apply --dry-run` first to preview the translation report.\n- **One-time backup churn after upgrading**: state keys are now stored `${HOME}`-relative (portable across machines) instead of with absolute paths. If you ran a pre-portability build, the first `apply` after upgrading will not recognize the old absolute-path entries, so every managed destination is treated as a `foreign-collision` once: the current content is backed up to `~/.agentsync/.state/backups/<ts>/` and then re-owned. This is expected, non-destructive (nothing is lost — it's backed up), and self-heals after that single apply. Run `agentsync apply --dry-run` first if you want to see which files will be backed up.\n- **`agentsync plugin outdated` fails to fetch a marketplace**: verify the marketplace URL with `git ls-remote`. agentsync uses `go-git` and falls back to system `git` for sparse clones if needed.\n- **`${secret:foo}` not resolving**: run `agentsync secret get foo` to verify the key exists in the decrypted file. age library errors will surface here.\n\n## Testing\n\nEvery `just test*` recipe runs **inside a hermetic container** (podman first,\ndocker fallback) — except the two explicit on-host opt-ins (`test-fast` and\n`test-live`) called out below. The repo is mounted read-only, the network\nis off, and every test's `HOME` is a fresh tmpdir — the suite cannot touch\nyour real `~/.claude.json`, `~/.config/opencode/`, or `~/.agentsync/`.\n\n| Layer                                       | Question it answers                          | Command             |\n| ------------------------------------------- | -------------------------------------------- | ------------------- |\n| Unit + integration (`internal/*/*_test.go`) | Did I break an internal contract?            | `just test`         |\n| Lifecycle e2e (`test/e2e`, build tag `e2e`) | Does the binary survive the happy path?      | `just test-e2e`     |\n| BDD Gherkin lock (`test/bdd`, tag `bdd`)    | Are the spec's north-star behaviours intact? | `just test-bdd`     |\n| **All layers in one container run**         | Can I safely cut a release right now?        | `just test-release` |\n\nIf `just test-release` is green, ship.\n\nFor fast in-place iteration without spinning up the container, `just test-fast`\nruns the unit/integration layer directly on the host. The existing tests\nalready redirect `HOME` via `AGENTSYNC_TARGET_ROOT`, so they are still safe;\nthe container is the release gate.\n\nIf you reach for the obvious `go test ./...`, the filesystem-touching\npackages refuse to run on the host and print a long banner pointing you\nat the recipes above. To bypass the guard manually (e.g. for `go test -run`\non a single test), set `AGENTSYNC_TEST_IN_CONTAINER=1`:\n\n    AGENTSYNC_TEST_IN_CONTAINER=1 go test ./internal/cli/ -run TestApply_FirstRunBacksUpForeignFile\n\n`just test-live` runs the **live cohort** (build tag `live`) — currently the\n`obra/superpowers` projection check, which clones the real upstream plugin\nto verify agentsync's projector keeps working as the upstream evolves. It\nruns on host because it needs network access; it is opt-in and **NOT** part\nof `test-release`, so the release gate stays hermetic and offline.\n\n## License\n\nMIT.\n"}