Automatic Git worktree sync · TUI · MCP server
Keep every branch checked out. For you and your AI agents.
sync-worktrees turns every Git branch, in every repo, into a folder on disk — auto-synced with the remote. cd instead of stashing, grep -r across repos, and your AI agents see the whole workspace over MCP.
Git history is stored once and shared — each extra branch costs only its working files, not another clone.
repositories: [
{
name: "frontend",
repoUrl: "github.com/acme/frontend",
},
{
name: "backend",
repoUrl: "github.com/acme/backend",
},
]; Declare your repos and branches once. Commit the file — every teammate and every new machine rebuilds the exact same workspace.
Stash. Switch. Re-explain. Repeat.
Every live branch across repos becomes another context switch — for you and your agent.
- Stash half-finished work to glance at another branch
- Hunt for where you cloned that sibling repo last month
- Switch branches in five repos for one cross-cutting feature
- Re-explain to Claude or Cursor which directory holds which branch
- Onboard new hires through a brittle copy-paste setup doc
- Clean branches fast-forward themselves on a schedule — no manual
git fetch - Switching branches becomes
cd - Every repo at a predictable path, declared once in config
- Multi-repo search is one
grep -racross the workspace - AI agents call
detect_contextand see every repo at once - New machine: one config, one command, the whole workspace appears
Declare the workspace once. Get every selected branch at a stable path.
One config. It clones what's missing, makes a worktree per selected branch, and keeps each at a predictable path.
Git objects are stored once in .bare/; force-push survivors move to .diverged/ instead of being overwritten.
Create sync-worktrees.config.js
The config decides which repo branches get a folder.
// sync-worktrees.config.js
/** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
const config = {
defaults: {
branchInclude: ["main", "feature/*", "release-*"],
branchExclude: ["wip-*", "tmp-*"],
},
repositories: [
{
name: "frontend",
repoUrl: "[email protected]:company/frontend.git",
worktreeDir: "./worktrees/frontend",
},
{
name: "backend",
repoUrl: "[email protected]:company/backend.git",
worktreeDir: "./worktrees/backend",
branchMaxAge: "6m",
},
],
};
export default config; Selected branches only
Keep main, feature, and release branches while excluding wip branches or stale branches by age.
Stable paths for humans
Jump straight to ./worktrees/frontend/feature-login instead of checking out and stashing.
Same map for agents
AI tools can discover the configured workspace instead of asking where sibling repos live.
Worktree mode
Every remote branch gets its own directory, all sharing one .git database. Disk usage scales with working-tree size, not branch count.
Clone mode
When one checkout is enough, get a single branch checked out at a fixed path — no .bare/, no per-branch subfolders. First-class alternative for dependency siblings.
MCP server
Ships sync-worktrees-mcp as a second binary. AI agents call tools like detect_context, list_worktrees, create_worktree, sync, and update_worktree over stdio.
Interactive TUI
Ink-based terminal UI with wizards for opening worktrees in editor or terminal, creating branches, and inspecting multi-repo status.
Smart filtering
Glob include/exclude, age-based pruning, sparse checkout for monorepos, per-repo overrides. Keep huge repos manageable.
Safety by default
By default it won't touch your work: sync and removal refuse worktrees with uncommitted changes, unpushed commits, stashes, or in-progress operations, and force-pushed history is moved aside into .diverged/ rather than overwritten.
Three commands to a synced workspace.
- 1
Install
Global npm install. Node 22+, macOS or Linux.
terminalnpm install -g sync-worktrees - 2
Run the init wizard
Writes sync-worktrees.config.js where you run it. Add more repos later.
terminalcd ~/projects/my-sync-dir sync-worktrees init - 3
Launch the TUI — or sync once
Bare command = interactive UI + cron. --runOnce for CI.
terminalsync-worktrees # interactive TUI + cron sync-worktrees --runOnce # one-shot
Your workspace, exposed over MCP.
A second binary, sync-worktrees-mcp, speaks MCP over stdio. Wire it into any client; your agent lists, creates, inspects, and syncs worktrees directly.
Prerequisite: a loaded sync-worktrees config, or running from a managed worktree.
Use the Claude Code CLI. Append -e SYNC_WORKTREES_CONFIG=... to point at a config.
claude mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp Paste into ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project).
{
"mcpServers": {
"sync-worktrees": {
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
}
}
} Add to your Windsurf MCP config. SYNC_WORKTREES_CONFIG is optional — omit for auto-detect.
{
"mcpServers": {
"sync-worktrees": {
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
}
}
} Edit claude_desktop_config.json (~/Library/Application Support/Claude on macOS, %APPDATA%/Claude on Windows), then restart.
{
"mcpServers": {
"sync-worktrees": {
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
}
}
} Uses the GitHub Copilot Chat MCP integration. Or paste the standard JSON config manually.
code --add-mcp '{"name":"sync-worktrees","command":"npx","args":["-y","-p","sync-worktrees","sync-worktrees-mcp"]}' Or edit ~/.codex/config.toml directly.
codex mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp Edit cline_mcp_settings.json — see Cline's MCP configuration docs.
{
"mcpServers": {
"sync-worktrees": {
"type": "stdio",
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
"disabled": false
}
}
} Edit ~/.config/opencode/opencode.json.
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"sync-worktrees": {
"type": "local",
"command": ["npx", "-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
"enabled": true
}
}
} Settings → AI → Manage MCP Servers → + Add. Or run /add-mcp in the prompt.
{
"mcpServers": {
"sync-worktrees": {
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
}
}
} Follow the Gemini CLI MCP install guide and use the standard config.
{
"mcpServers": {
"sync-worktrees": {
"command": "npx",
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
}
}
}
Fix backend/release-2.4 from frontend/main.
You're in worktrees/frontend/main. You tell the agent: "inspect backend/release-2.4, branch a fix, and sync it." It chains the tools itself:
- 1
detect_context— finds the configuredbackendrepo, no path hunting - 2
get_worktree_status— readsrelease-2.4 - 3
create_worktree— branches the fix - 4
sync— fetches, creates, prunes
No "where's the other repo?" — detect_context returns every configured repo and worktree, each with a { available, reason } capability flag.
-
detect_contextResolve the workspace from any path. Can include every configured repo and worktree, plus per-capability { available, reason } flags so the agent knows what it can actually call. -
list_worktreesStatus-labelled list (clean / dirty / stale / current), divergence, safeToRemove, last sync. Cross-repo when no repoName is supplied. -
get_worktree_statusPer-worktree detail: dirty files, unpushed commits, stashes, operation in progress. -
create_worktreeCreate a worktree for a branch. When creating a new branch, optionally branch from baseBranch and publish with --no-track + push -u. -
syncFull sync cycle: fetch, create, prune, update. Streams progress notifications. -
update_worktreeFast-forward one worktree to match upstream.
Skip it for simple Git setups.
sync-worktrees earns its keep across many branches and repos. Skip it if:
- – Solo on your own repos, no collaboration
Branches don't change underneath you, so auto-sync buys little — and a small workspace rarely needs a bootstrapping config.
- – One repo, one branch you ever touch
Plain git worktree, or a single clone, is enough.
- – Tiny repos where re-cloning is instant
The disk and bookkeeping savings don't matter.
- – You don't want many persistent branch directories
sync-worktrees keeps a folder per branch on disk by design.
Common questions
What's the difference between git worktree and sync-worktrees?
git worktree is the underlying Git primitive: a single command that adds one extra working directory backed by a shared .git database (it works with any repo, bare or not). sync-worktrees is a workspace orchestrator built on top of it — in worktree mode it sets up a bare repo, creates a directory for every selected remote branch automatically, prunes them when branches are deleted upstream, and keeps the whole thing fresh on a cron schedule. It also ships an interactive TUI and an MCP server so AI agents can navigate and operate the workspace.
Worktrees share the object database for free, so the bare repo is not a redundant reference store — it is simply the layout that lets every branch (the default included) be a peer directory instead of one privileged checkout. What sync-worktrees adds is the bookkeeping around that primitive — mirroring the remote, pruning deleted branches, fast-forwarding clean trees on a schedule, across many repos from one config — not the worktrees themselves.
One caveat worth naming: this shared store covers the repository’s own objects only. Submodules keep Git’s normal per-worktree behavior, so each worktree maintains its own submodule checkout rather than sharing one across worktrees.
Why use this instead of cloning each branch separately?
Cloning duplicates the entire Git object database for every branch — wasteful on disk and slow to refresh. Worktrees share one .git directory among many checkouts, so disk usage scales with working-tree size, not with branch count. sync-worktrees automates the bookkeeping you’d otherwise have to do manually (creating worktrees for new remote branches, removing stale ones, handling force-pushes safely).
How does the MCP server help AI coding agents?
The bundled sync-worktrees-mcp binary exposes its tools over MCP (stdio). detect_context, list_worktrees, create_worktree, and sync let an agent discover every configured sibling repo and worktree in one detect_context call, so “go look in the other repo” works without manual re-orientation.
Does it work with monorepos?
Yes. Per-repo branch include/exclude globs and sparse-checkout support let you scope each worktree to the slice of the monorepo you actually care about. Combined with branchMaxAge to ignore stale branches, this keeps multi-million-line monorepos manageable on disk.
What happens when a branch is force-pushed or deleted upstream?
Force-pushes are detected and the divergent working copy is moved aside into a .diverged/ directory rather than overwritten — uncommitted work is never silently lost. Deleted branches have their worktrees removed only if they are clean, with no unpushed commits, no stashes, and no operation in progress; anything else is preserved and surfaced in the TUI’s status view.
Can I run it continuously or on a cron?
Yes. The default invocation launches the interactive TUI and syncs continuously based on the cronSchedule field in your config (per-repo overrides are supported). For one-shot or scripted use, pass --runOnce. For CI pipelines, point --config at your workspace config and the same one-shot path applies.
Is it safe with uncommitted work?
Yes. Sync operations never merge or rebase your working copies. They fetch, create missing worktrees, fast-forward eligible existing worktrees, and prune only when the safety checks pass. Worktree removal refuses on dirty trees, unpushed commits, stashes, or in-progress operations (merge/rebase/cherry-pick/revert/bisect). Newly created branches are pushed with explicit --no-track so they don’t inherit origin/main as upstream by accident.
Every branch, checked out and current. From one config.
Point it at your repos. Stop paying the branch-switching tax.