Skip to content

syncode 2.0: compose, link and adopt - #7

Merged
donnes merged 3 commits into
mainfrom
v2
Oct 22, 2026
Merged

donnes merged 3 commits into
mainfrom
v2

Conversation

@donnes

@donnes donnes commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Problem

1.x symlinked whole agent folders (~/.codex, ~/.config/opencode, …) into the config repo. Everything the agents write there got committed: auth.json, sessions, sqlite logs, plugin binaries. The code was ~20 near-identical adapter classes plus unrelated machine setup (install.sh, dotfiles), with no dry run, no non-interactive mode, and conflicts handled by renaming to .backup. The toolchain targeted Node 20, which is past end of life.

Solution

A smaller tool modeled on how I now manage my own machines:

  • Repo layout: instructions/AGENTS.md (plus optional macos.md/linux.md and an untracked local.md), skills/<name>/, and agents/<id>/ for config files you explicitly adopt. Nothing else is synced, and credential files are refused.
  • Per-file links: instructions become CLAUDE.md, AGENTS.md or GEMINI.md; skill folders go into ~/.agents/skills (read by Codex, OpenCode, Gemini CLI, Cursor and Copilot CLI) and ~/.claude/skills.
  • One planner behind apply, status (an exact dry run, --json available) and unlink. A state file prunes links that are no longer wanted.
  • Conflicts: you choose keep, replace (backed up to ~/.syncode/backups/), update the repo, or view the diff. Without a terminal, the local file is kept and reported.
  • Commands: init, apply, status, adopt, pull, push, unlink. init migrates 1.x setups by turning whole-folder links back into real copies.
  • Agents are now a data table: Claude Code, Codex, OpenCode, Gemini CLI, Cursor, Copilot CLI. The rest of the 1.x list, machine and dotfiles are dropped.
  • Toolchain: Node >= 22.12, tsdown, Vitest, TypeScript 7 with stricter flags, clack 1.x, CI on Linux and macOS, and npm trusted publishing in place of NPM_TOKEN. Release notes now come from GitHub's generated notes instead of an LLM script.

Stacked on #6. Its commit drops out of this diff once #6 merges.

Before releasing

  • Configure npm trusted publishing for @donnes/syncode → repo donnes/syncode, workflow publish.yml. Then the NPM_TOKEN secret can be deleted.
  • Run "Release & Publish" with major to publish 2.0.0.

Verification

  • bun run check passes: lint, typecheck, 8 end-to-end tests, build. The built dist/index.mjs runs under Node.
  • The end-to-end tests run the real CLI against a sandbox home and cover init from local files, 1.x migration, non-TTY conflicts, composed instructions, skill linking and pruning, adopt (including refusing auth.json), and unlink. I checked that removing the prune step, the non-TTY keep, the credential guard, composition, or the migration's skills-link filter each makes a test fail.
  • Rehearsed migration on a real 1.x repo (my own, from before I stopped using 1.x) with its Linux links recreated in a sandbox. That found two gaps, now fixed: editor settings linked one folder deeper (~/.config/Cursor/User, ~/.config/Code/User) weren't restored, and instructions weren't seeded when the repo had none. After the fix, every 1.x link is restored, nothing points into configs/, and status reports all 28 entries linked.
  • An independent review found 7 bugs, all fixed. The four High/Medium ones have regression tests that fail on the old code.
  • Ran interactive init in a pseudo-terminal against a sandbox home: agent picker, skill import, conflict prompt, and the backup on replace.

🤖 Generated with Claude Code

donnes and others added 3 commits October 22, 2026 04:35
1.x symlinked whole agent folders into the repo, which committed runtime
data and credentials (auth.json, sessions, logs), and carried ~20
near-identical adapter classes plus unrelated machine setup.

2.0 keeps a small repo of instructions/, skills/ and explicitly adopted
agents/<id>/ files, and links them per file:

- One planner drives apply, status (an exact dry run) and unlink; a
  state file prunes links that are no longer wanted.
- Instructions are AGENTS.md plus optional OS and untracked local parts,
  linked as CLAUDE.md, AGENTS.md and GEMINI.md.
- Skill folders are linked into ~/.agents/skills and ~/.claude/skills.
- Differing local files prompt keep/replace/update/diff, back up replaced
  copies, and are kept when there is no terminal.
- adopt refuses credential files; init migrates 1.x setups by restoring
  whole-folder links as real copies.
- Agents are a data table: Claude Code, Codex, OpenCode, Gemini CLI,
  Cursor and Copilot CLI.

Toolchain: Node >= 22.12, tsdown, Vitest end-to-end tests against a
sandbox home, TypeScript 7 with stricter flags, clack 1.x, CI on Linux
and macOS, and npm trusted publishing instead of a token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Conflict "Replace" backs up the destination itself, so a symlink to a
  file elsewhere (a dotfiles repo, or the repo via its real path) moves
  as a link instead of taking that file with it.
- adopt refuses an agent's whole config folder and any folder containing
  credential files, not just credential paths.
- init refuses a non-empty folder that isn't a git repo instead of
  overwriting its README.md, .gitignore and instructions.
- Destinations that reach the same entry through a linked parent folder
  (1.x left ~/.claude/skills -> ~/.agents/skills) are planned once and
  never pruned while still wanted, so status matches apply.
- A broken parent link is reported instead of crashing apply.
- Migration only restores links into the 1.x configs/ and .agents/
  folders, not any home link into the repo.
- Configured repo paths are resolved, and backups for agent folders
  outside HOME stay inside the backups folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Running init against a real 1.x setup (on Linux) left ~/.config/Cursor/User
and ~/.config/Code/User linked into configs/, so deleting configs/ as the
migration suggests would have wiped those settings. Scan every folder 1.x
linked into, including editor User folders and Ghostty's config on Linux
and macOS.

1.x copied some agents' folders instead of linking them, so the repo may
have no instructions while the machine does. Fall back to the enabled
agents' instructions files, as a new init does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@donnes
donnes merged commit d261367 into main Oct 22, 2026
2 checks passed
@donnes
donnes deleted the v2 branch October 22, 2026 07:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant