sync-worktrees 3.6.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +388 -263
  2. package/dist/components/App.d.ts +52 -0
  3. package/dist/components/App.d.ts.map +1 -0
  4. package/dist/components/BranchCreationWizard.d.ts +26 -0
  5. package/dist/components/BranchCreationWizard.d.ts.map +1 -0
  6. package/dist/components/HelpModal.d.ts +7 -0
  7. package/dist/components/HelpModal.d.ts.map +1 -0
  8. package/dist/components/LogPanel.d.ts +10 -0
  9. package/dist/components/LogPanel.d.ts.map +1 -0
  10. package/dist/components/LogViewer.d.ts +9 -0
  11. package/dist/components/LogViewer.d.ts.map +1 -0
  12. package/dist/components/OpenEditorWizard.d.ts +25 -0
  13. package/dist/components/OpenEditorWizard.d.ts.map +1 -0
  14. package/dist/components/StatusBar.d.ts +11 -0
  15. package/dist/components/StatusBar.d.ts.map +1 -0
  16. package/dist/components/WorktreeStatusView.d.ts +17 -0
  17. package/dist/components/WorktreeStatusView.d.ts.map +1 -0
  18. package/dist/constants.d.ts +112 -0
  19. package/dist/constants.d.ts.map +1 -0
  20. package/dist/errors/index.d.ts +59 -0
  21. package/dist/errors/index.d.ts.map +1 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +2327 -1082
  25. package/dist/index.js.map +4 -4
  26. package/dist/mcp/context.d.ts +143 -0
  27. package/dist/mcp/context.d.ts.map +1 -0
  28. package/dist/mcp/handlers.d.ts +46 -0
  29. package/dist/mcp/handlers.d.ts.map +1 -0
  30. package/dist/mcp/index.d.ts +2 -0
  31. package/dist/mcp/index.d.ts.map +1 -0
  32. package/dist/mcp/server.d.ts +9 -0
  33. package/dist/mcp/server.d.ts.map +1 -0
  34. package/dist/mcp/utils.d.ts +14 -0
  35. package/dist/mcp/utils.d.ts.map +1 -0
  36. package/dist/mcp/worktree-summary.d.ts +14 -0
  37. package/dist/mcp/worktree-summary.d.ts.map +1 -0
  38. package/dist/mcp-server.js +2513 -691
  39. package/dist/mcp-server.js.map +4 -4
  40. package/dist/services/InteractiveUIService.d.ts +85 -0
  41. package/dist/services/InteractiveUIService.d.ts.map +1 -0
  42. package/dist/services/branch-created-actions.service.d.ts +27 -0
  43. package/dist/services/branch-created-actions.service.d.ts.map +1 -0
  44. package/dist/services/clone-sync.service.d.ts +93 -0
  45. package/dist/services/clone-sync.service.d.ts.map +1 -0
  46. package/dist/services/config-loader.service.d.ts +28 -0
  47. package/dist/services/config-loader.service.d.ts.map +1 -0
  48. package/dist/services/file-copy.service.d.ts +19 -0
  49. package/dist/services/file-copy.service.d.ts.map +1 -0
  50. package/dist/services/git.service.d.ts +92 -0
  51. package/dist/services/git.service.d.ts.map +1 -0
  52. package/dist/services/hook-execution.service.d.ts +20 -0
  53. package/dist/services/hook-execution.service.d.ts.map +1 -0
  54. package/dist/services/logger.service.d.ts +24 -0
  55. package/dist/services/logger.service.d.ts.map +1 -0
  56. package/dist/services/path-resolution.service.d.ts +10 -0
  57. package/dist/services/path-resolution.service.d.ts.map +1 -0
  58. package/dist/services/progress-emitter.d.ts +14 -0
  59. package/dist/services/progress-emitter.d.ts.map +1 -0
  60. package/dist/services/repo-operation-lock.d.ts +16 -0
  61. package/dist/services/repo-operation-lock.d.ts.map +1 -0
  62. package/dist/services/sparse-checkout.service.d.ts +45 -0
  63. package/dist/services/sparse-checkout.service.d.ts.map +1 -0
  64. package/dist/services/sync-outcome.d.ts +47 -0
  65. package/dist/services/sync-outcome.d.ts.map +1 -0
  66. package/dist/services/sync-retry-policy.d.ts +18 -0
  67. package/dist/services/sync-retry-policy.d.ts.map +1 -0
  68. package/dist/services/worktree-metadata.service.d.ts +25 -0
  69. package/dist/services/worktree-metadata.service.d.ts.map +1 -0
  70. package/dist/services/worktree-mode-sync-runner.d.ts +36 -0
  71. package/dist/services/worktree-mode-sync-runner.d.ts.map +1 -0
  72. package/dist/services/worktree-status.service.d.ts +60 -0
  73. package/dist/services/worktree-status.service.d.ts.map +1 -0
  74. package/dist/services/worktree-sync-planner.d.ts +62 -0
  75. package/dist/services/worktree-sync-planner.d.ts.map +1 -0
  76. package/dist/services/worktree-sync.service.d.ts +49 -0
  77. package/dist/services/worktree-sync.service.d.ts.map +1 -0
  78. package/dist/types/index.d.ts +289 -0
  79. package/dist/types/index.d.ts.map +1 -0
  80. package/dist/types/sync-metadata.d.ts +16 -0
  81. package/dist/types/sync-metadata.d.ts.map +1 -0
  82. package/dist/utils/app-events.d.ts +21 -0
  83. package/dist/utils/app-events.d.ts.map +1 -0
  84. package/dist/utils/branch-filter.d.ts +3 -0
  85. package/dist/utils/branch-filter.d.ts.map +1 -0
  86. package/dist/utils/cli.d.ts +21 -0
  87. package/dist/utils/cli.d.ts.map +1 -0
  88. package/dist/utils/clone-skip-format.d.ts +3 -0
  89. package/dist/utils/clone-skip-format.d.ts.map +1 -0
  90. package/dist/utils/config-generator.d.ts +10 -0
  91. package/dist/utils/config-generator.d.ts.map +1 -0
  92. package/dist/utils/date-filter.d.ts +10 -0
  93. package/dist/utils/date-filter.d.ts.map +1 -0
  94. package/dist/utils/disk-space.d.ts +23 -0
  95. package/dist/utils/disk-space.d.ts.map +1 -0
  96. package/dist/utils/file-exists.d.ts +2 -0
  97. package/dist/utils/file-exists.d.ts.map +1 -0
  98. package/dist/utils/git-progress.d.ts +25 -0
  99. package/dist/utils/git-progress.d.ts.map +1 -0
  100. package/dist/utils/git-url.d.ts +23 -0
  101. package/dist/utils/git-url.d.ts.map +1 -0
  102. package/dist/utils/git-validation.d.ts +5 -0
  103. package/dist/utils/git-validation.d.ts.map +1 -0
  104. package/dist/utils/interactive.d.ts +3 -0
  105. package/dist/utils/interactive.d.ts.map +1 -0
  106. package/dist/utils/lfs-error.d.ts +35 -0
  107. package/dist/utils/lfs-error.d.ts.map +1 -0
  108. package/dist/utils/lock-path.d.ts +9 -0
  109. package/dist/utils/lock-path.d.ts.map +1 -0
  110. package/dist/utils/path-compare.d.ts +16 -0
  111. package/dist/utils/path-compare.d.ts.map +1 -0
  112. package/dist/utils/repo-mode.d.ts +8 -0
  113. package/dist/utils/repo-mode.d.ts.map +1 -0
  114. package/dist/utils/retry.d.ts +24 -0
  115. package/dist/utils/retry.d.ts.map +1 -0
  116. package/dist/utils/sanitize-name.d.ts +2 -0
  117. package/dist/utils/sanitize-name.d.ts.map +1 -0
  118. package/dist/utils/shell-escape.d.ts +5 -0
  119. package/dist/utils/shell-escape.d.ts.map +1 -0
  120. package/dist/utils/signal-handlers.d.ts +14 -0
  121. package/dist/utils/signal-handlers.d.ts.map +1 -0
  122. package/dist/utils/timing.d.ts +24 -0
  123. package/dist/utils/timing.d.ts.map +1 -0
  124. package/dist/utils/worktree-list-parser.d.ts +10 -0
  125. package/dist/utils/worktree-list-parser.d.ts.map +1 -0
  126. package/package.json +5 -2
package/README.md CHANGED
@@ -1,34 +1,75 @@
1
1
  # sync-worktrees
2
2
 
3
- Automatically synchronize Git worktrees with remote branches. Keep your local worktrees in sync with remote repositories - perfect for multi-branch development workflows and automated testing setups.
3
+ > Keep every branch and every repo you work on checked out as predictable directories no stashing, no re-cloning, no re-orienting your AI assistant.
4
4
 
5
5
  ![sync-worktrees demo](./assets/sync-worktrees-demo-optimized.gif)
6
6
 
7
+ **Contents:** [Why](#why-sync-worktrees) · [How it works](#how-it-works) · [Quick start](#quick-start) · [MCP server](#mcp-server) · [Interactive TUI](#interactive-tui) · [CLI options](#cli-options)
8
+
9
+ ## Why sync-worktrees
10
+
11
+ If you've ever:
12
+
13
+ - Stashed half-finished work just to check out another branch
14
+ - Lost minutes hunting for where you cloned a sibling repo
15
+ - Switched branches in five repos because one feature spans them all
16
+ - Re-explained to an AI assistant which directory holds which branch
17
+
18
+ …sync-worktrees fixes that. It keeps the **entire branch and repo layout you work in materialized on disk** — one directory per branch, automatically kept in sync with the remote. Switching branches becomes `cd`. Searching across repos becomes `grep -r`. **AI agents see the same shape you do, so "look in the other repo" actually works.**
19
+
20
+ It's also a clean answer to **dev-environment bootstrapping**. One config file describes every repo, branch, and folder layout your team works in. Hand it to a new hire (or a fresh laptop) and `sync-worktrees` lays down the whole workspace in a single command — no day-one cloning checklist, no "where do I put this repo?" Slack threads.
21
+
22
+ Runs as a one-shot, a background daemon, or an interactive TUI — and ships an MCP server so AI assistants can list, create, and inspect worktrees themselves.
23
+
7
24
  ## How it works
8
25
 
9
- sync-worktrees maintains a **separate working directory for each remote branch**, all sharing the same Git repository:
26
+ The default **worktree mode** gives every remote branch its own directory while sharing one Git database underneath:
27
+
28
+ 1. **First run** clones the repo once as a bare repository (just the Git data, no working files).
29
+ 2. **Each sync**:
30
+ - Creates a directory for every remote branch (`main`, `develop`, `feature/*`).
31
+ - Fetches latest changes (no merge — your local work stays untouched).
32
+ - Removes directories for branches deleted upstream (preserves dirty trees and unpushed commits).
33
+
34
+ Smallest config that produces this:
35
+
36
+ ```javascript
37
+ // sync-worktrees.config.js
38
+ // @ts-check
39
+
40
+ /** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
41
+ const config = {
42
+ repositories: [
43
+ {
44
+ name: "my-repo",
45
+ repoUrl: "https://github.com/user/my-repo.git",
46
+ worktreeDir: "./worktrees/my-repo",
47
+ },
48
+ ],
49
+ };
50
+
51
+ export default config;
52
+ ```
10
53
 
11
- 1. **First run**: Clones your repository as a bare repository (no working files, just Git data)
12
- 2. **Automatic sync**:
13
- - Creates a dedicated worktree for **every remote branch** (`main`, `develop`, `feature/*`, etc.)
14
- - Each branch gets its own isolated directory with a full working copy
15
- - Fetches latest changes (doesn't merge - preserves your local work)
16
- - Removes worktrees when remote branches are deleted (preserves local changes)
54
+ Run `sync-worktrees` from the directory holding the config and you get:
17
55
 
18
- **Why this matters**: Switch between branches instantly without stashing, run tests on multiple branches simultaneously, or keep your CI and production branches always ready.
56
+ ```
57
+ .
58
+ ├── sync-worktrees.config.js
59
+ ├── .bare/
60
+ │ └── my-repo/ # Bare repository (shared Git objects)
61
+ └── worktrees/my-repo/
62
+ ├── main/ # Worktree for main branch
63
+ ├── feature-1/ # Worktree for feature-1 branch
64
+ └── feature-2/ # Worktree for feature-2 branch
65
+ ```
66
+
67
+ **Clone mode** (`mode: "clone"`) is a first-class alternative: a plain `git clone` of one branch into `worktreeDir`, no bare repo, no per-branch subfolders. Reach for it when you want a repo to live at a fixed path — a dependency sibling, a single-branch dev clone, or any case where one checkout is enough. See [Clone mode](#clone-mode).
19
68
 
20
69
  ## Features
21
70
 
22
- - 🔄 Automatically creates worktrees for all remote branches
23
- - 🗑️ Removes worktrees for deleted remote branches (preserves local changes)
24
- - ⏰ Run as a scheduled cron job or one-time execution
25
- - 🛡️ Safe operations - won't delete worktrees with uncommitted changes or unpushed commits
26
- - 📝 Clear logging with timestamps and progress indicators
27
- - 📋 Config file support for managing multiple repositories
28
- - 🔁 Automatic retry with exponential backoff for network and filesystem errors
29
- - 🕐 Branch age filtering - only sync branches active within a specified time period
30
- - 🔀 Smart handling of rebased/force-pushed branches with automatic divergence detection
31
- - 🤖 MCP server for AI assistants - inspect and manage worktrees from Claude Desktop, Claude Code, Cursor, etc.
71
+ - **Filtering & lifecycle** branch name globs, age filtering, sparse checkout, automatic divergence detection with `.diverged/` preservation, retry with exponential backoff.
72
+ - **Interactive TUI** Ink-based UI with wizards for opening worktrees, creating branches, and inspecting status; diverged-directory management; live log streaming; multi-repo filtering.
32
73
 
33
74
  ## Installation
34
75
 
@@ -36,99 +77,191 @@ sync-worktrees maintains a **separate working directory for each remote branch**
36
77
  npm install -g sync-worktrees
37
78
  ```
38
79
 
39
- Or with pnpm:
80
+ ## Quick start
81
+
82
+ `sync-worktrees` always runs against a config file. Create one once, then run the tool.
40
83
 
41
84
  ```bash
42
- pnpm add -g sync-worktrees
85
+ cd ~/projects/my-sync-dir
86
+ sync-worktrees init # interactive wizard → writes sync-worktrees.config.js
87
+ sync-worktrees # auto-loads the config in the current directory and starts syncing
43
88
  ```
44
89
 
45
- ## Usage
90
+ By default, bare `sync-worktrees` launches the [interactive TUI](#interactive-tui) and keeps syncing on the cron schedule from your config. Press `q` to quit. For a one-shot run (CI, scripts, ad-hoc), add `--runOnce`.
46
91
 
47
- Run `sync-worktrees` in any directory:
92
+ To manage multiple repositories, edit the generated config file and add entries under `repositories`. See [Configuration](#configuration).
48
93
 
49
- - **First run** — no config found interactive wizard asks for repo URL, worktree directory, and schedule, then saves `sync-worktrees.config.js` in the current directory and starts syncing.
50
- - **Subsequent runs** — `sync-worktrees` auto-loads `sync-worktrees.config.js` (or `.mjs` / `.cjs`) from the current directory. No flags needed.
94
+ If the config lives outside the current directory, pass it explicitly:
51
95
 
52
96
  ```bash
53
- cd ~/projects/my-sync-dir
54
- sync-worktrees # wizard → saves config → runs
55
- sync-worktrees # re-uses the saved config
97
+ sync-worktrees --config /path/to/sync-worktrees.config.js
98
+ sync-worktrees --config /path/to/sync-worktrees.config.js --runOnce
99
+ sync-worktrees list --config ./config.js --filter "frontend-*"
56
100
  ```
57
101
 
58
- To manage multiple repositories, edit the generated config file and add entries under `repositories`. See [Configuration File](#configuration-file).
102
+ ## MCP server
59
103
 
60
- ### Explicit config path
104
+ sync-worktrees ships a [Model Context Protocol](https://modelcontextprotocol.io) server so AI assistants (Claude Desktop, Claude Code, Cursor, Windsurf, etc.) can inspect and operate your workspace directly. Installing the package exposes a second binary, `sync-worktrees-mcp`, that speaks MCP over stdio.
61
105
 
62
- Useful when the config lives outside the current directory:
106
+ In a single call, an AI assistant can discover every repo and worktree you have configured — so an agent working in `frontend/` can grep across `backend/` and `shared/` without you reorienting it. That call is `detect_context` with `includeAllWorktrees: true`; the response also includes a per-capability `{ available, reason }` block telling the agent which operations are reachable from its current vantage point, so there's no guessing whether `sync` will work. See [Available tools](#available-tools) for the full surface.
107
+
108
+ ### Getting started
109
+
110
+ Install the sync-worktrees MCP server with your client.
111
+
112
+ **Standard config** works in most tools:
113
+
114
+ ```json
115
+ {
116
+ "mcpServers": {
117
+ "sync-worktrees": {
118
+ "command": "npx",
119
+ "args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
120
+ "env": {
121
+ "SYNC_WORKTREES_CONFIG": "/absolute/path/to/sync-worktrees.config.js"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ If installed globally, replace `command` with `sync-worktrees-mcp` and drop `args`. `SYNC_WORKTREES_CONFIG` is optional — without it the server runs in **auto-detect mode**: when the client's CWD sits inside a worktree managed by sync-worktrees, the server locates the bare repo, enumerates sibling worktrees, and enables per-worktree operations. `sync` and `initialize` require a loaded config (or call `load_config` at runtime).
129
+
130
+ <details>
131
+ <summary>Claude Code</summary>
132
+
133
+ Use the Claude Code CLI:
63
134
 
64
135
  ```bash
65
- sync-worktrees --config /path/to/sync-worktrees.config.js
66
- sync-worktrees --config ./config.js --filter "frontend-*"
67
- sync-worktrees --config ./config.js --list
136
+ claude mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp
68
137
  ```
69
138
 
70
- ### Opening a worktree from the TUI
139
+ To pass a config path, append `-e SYNC_WORKTREES_CONFIG=/absolute/path/to/sync-worktrees.config.js` to the command.
71
140
 
72
- Press `o` in the interactive TUI to open the selected worktree. The wizard supports two modes, toggled with `Tab`:
141
+ </details>
73
142
 
74
- - **Terminal** (default) — launches a new terminal window with a `tmux` session attached to the worktree directory. Session name is `<repo>-<sanitized-branch>`; re-opening the same worktree attaches to the existing session instead of creating a duplicate.
75
- - **Editor** — launches `$EDITOR` / `$VISUAL` (falls back to `code`) in the worktree.
143
+ <details>
144
+ <summary>Claude Desktop</summary>
76
145
 
77
- Terminal mode requires [`tmux`](https://github.com/tmux/tmux) to be installed.
146
+ Edit `claude_desktop_config.json` and paste the **standard config** above into the `mcpServers` block. Default locations:
78
147
 
79
- #### Environment variables
148
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
149
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
80
150
 
81
- | Variable | Purpose | Default behavior |
82
- |----------|---------|------------------|
83
- | `SYNC_WORKTREES_TERMINAL` | Override the terminal launcher on any platform. Value is a command string; the tmux invocation is appended via `sh -c`. Example: `SYNC_WORKTREES_TERMINAL="alacritty -e"`. | See per-platform defaults below. |
84
- | `TERMINAL` | Linux-only fallback when `SYNC_WORKTREES_TERMINAL` is unset. Same format. | Probes `gnome-terminal`, `konsole`, `alacritty`, `kitty`, `xterm` in order. |
85
- | `EDITOR` / `VISUAL` | Editor mode launcher. | Falls back to `code`. |
151
+ Restart Claude Desktop after editing.
86
152
 
87
- Per-platform terminal defaults (when no env override is set):
153
+ </details>
88
154
 
89
- - **macOS** — Ghostty if `Ghostty.app` is installed, otherwise Terminal.app via AppleScript.
90
- - **Linux** — `$TERMINAL` if set; otherwise the first found among the candidates above.
155
+ <details>
156
+ <summary>Cursor</summary>
157
+
158
+ Edit `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project). Paste the **standard config** above.
159
+
160
+ Or open `Cursor Settings` → `MCP` → `Add new MCP Server`, pick `command` type, and enter `npx -y -p sync-worktrees sync-worktrees-mcp`.
161
+
162
+ </details>
163
+
164
+ <details>
165
+ <summary>Windsurf</summary>
166
+
167
+ Follow the Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/mcp) and use the **standard config** above.
168
+
169
+ </details>
170
+
171
+ <details>
172
+ <summary>VS Code</summary>
173
+
174
+ Use the VS Code CLI:
175
+
176
+ ```bash
177
+ code --add-mcp '{"name":"sync-worktrees","command":"npx","args":["-y","-p","sync-worktrees","sync-worktrees-mcp"]}'
178
+ ```
179
+
180
+ Or follow the VS Code MCP install [guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) and use the **standard config** above.
181
+
182
+ </details>
183
+
184
+ <details>
185
+ <summary>Codex</summary>
186
+
187
+ Use the Codex CLI:
188
+
189
+ ```bash
190
+ codex mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp
191
+ ```
192
+
193
+ Or edit `~/.codex/config.toml`:
194
+
195
+ ```toml
196
+ [mcp_servers.sync-worktrees]
197
+ command = "npx"
198
+ args = ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
199
+ ```
200
+
201
+ </details>
91
202
 
92
- ## MCP Server
203
+ <details>
204
+ <summary>Gemini CLI</summary>
93
205
 
94
- sync-worktrees ships a [Model Context Protocol](https://modelcontextprotocol.io) server so AI assistants (Claude Desktop, Claude Code, Cursor, Windsurf, etc.) can inspect and manage your worktrees directly. Installing the package exposes a second binary, `sync-worktrees-mcp`, that speaks MCP over stdio.
206
+ Follow the Gemini CLI MCP install [guide](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#configure-the-mcp-server-in-settingsjson) and use the **standard config** above.
95
207
 
96
- ### Setup
208
+ </details>
97
209
 
98
- Add the server to your MCP client config. Use `npx` if the package is not installed globally:
210
+ <details>
211
+ <summary>Cline</summary>
212
+
213
+ Edit `cline_mcp_settings.json` (see [Configuring MCP Servers](https://docs.cline.bot/mcp/configuring-mcp-servers)) and add:
99
214
 
100
215
  ```json
101
216
  {
102
217
  "mcpServers": {
103
218
  "sync-worktrees": {
219
+ "type": "stdio",
104
220
  "command": "npx",
105
221
  "args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
106
- "env": {
107
- "SYNC_WORKTREES_CONFIG": "/absolute/path/to/sync-worktrees.config.js"
108
- }
222
+ "disabled": false
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ </details>
229
+
230
+ <details>
231
+ <summary>opencode</summary>
232
+
233
+ Edit `~/.config/opencode/opencode.json`:
234
+
235
+ ```json
236
+ {
237
+ "$schema": "https://opencode.ai/config.json",
238
+ "mcp": {
239
+ "sync-worktrees": {
240
+ "type": "local",
241
+ "command": ["npx", "-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
242
+ "enabled": true
109
243
  }
110
244
  }
111
245
  }
112
246
  ```
113
247
 
114
- If installed globally, replace `command` with `sync-worktrees-mcp` and drop `args`. `SYNC_WORKTREES_CONFIG` is optional — without it the server runs in **auto-detect mode**: when the client's CWD sits inside a worktree managed by sync-worktrees, the server locates the bare repo, enumerates sibling worktrees, and enables per-worktree operations. Sync and initialize require a loaded config (or call `load_config` at runtime).
248
+ </details>
115
249
 
116
- Client-specific locations:
250
+ <details>
251
+ <summary>Warp</summary>
117
252
 
118
- | Client | Config file |
119
- |--------|-------------|
120
- | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) |
121
- | Claude Code | `claude mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp` |
122
- | Cursor | `~/.cursor/mcp.json` (or project-level `.cursor/mcp.json`) |
253
+ Open `Settings` `AI` `Manage MCP Servers` → `+ Add` (see [Warp MCP docs](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server)) and paste the **standard config** above. Alternatively, run `/add-mcp` in the prompt.
254
+
255
+ </details>
123
256
 
124
257
  ### Available tools
125
258
 
126
259
  | Tool | Purpose |
127
260
  |------|---------|
128
- | `detect_context` | Inspect a path, resolve the bare repo, enumerate sibling worktrees, report capabilities. |
129
- | `list_worktrees` | List worktrees with status label (`clean`/`dirty`/`stale`/`current`), divergence, `safeToRemove`, last sync. |
261
+ | `detect_context` | Inspect a path, resolve the bare repo, enumerate sibling worktrees, report config-driven sibling repositories and capabilities. Pass `includeAllWorktrees: true` to include every configured repo's worktrees keyed by repo name. |
262
+ | `list_worktrees` | List worktrees with status label (`clean`/`dirty`/`stale`/`current`), divergence, `safeToRemove`, last sync. Without `repoName` and with a loaded config, results are grouped across all configured repos. |
130
263
  | `get_worktree_status` | Detailed status for one worktree (dirty files, unpushed commits, stashes, operation in progress). |
131
- | `create_worktree` | Create a worktree for a branch; optionally create the branch from `baseBranch` and push. |
264
+ | `create_worktree` | Create a worktree for a branch; optionally create the branch from `baseBranch`. Newly created branches are pushed to origin unless `push=false`. |
132
265
  | `remove_worktree` | Remove a worktree after safety checks; `force=true` skips validation. |
133
266
  | `update_worktree` | Fast-forward one worktree to match upstream. |
134
267
  | `sync` | Full sync cycle (fetch, create, prune, update). Requires config. Streams progress notifications. |
@@ -142,288 +275,280 @@ All tools that target a single repo accept an optional `repoName`. When omitted,
142
275
 
143
276
  - `remove_worktree` refuses to delete worktrees with uncommitted changes, unpushed commits, stashes, or operations in progress (merge/rebase/cherry-pick/revert/bisect). Pass `force=true` to override.
144
277
  - `create_worktree` rejects sanitized-path collisions (e.g. `feature/foo` vs `feature-foo` both resolving to `feature-foo/`) before touching disk.
278
+ - Branches created by sync-worktrees use `--no-track` first, then publish with `git push -u origin <branch>`, so they do not inherit `origin/main` as their upstream.
145
279
  - Path-targeted tools verify the supplied path is a registered worktree of the selected repository.
146
280
 
147
- ## Options
281
+ ## Interactive TUI
148
282
 
149
- | Option | Alias | Description | Default |
150
- |--------|-------|-------------|---------|
151
- | `--config` | `-c` | Path to JavaScript config file (auto-detected in CWD when omitted) | - |
152
- | `--filter` | `-f` | Filter repositories by name (wildcards supported) | - |
153
- | `--list` | `-l` | List configured repositories and exit | `false` |
154
- | `--runOnce` | - | Override config to run once and exit | `false` |
155
- | `--no-update-existing` | - | Disable automatic updates of existing worktrees | `false` |
156
- | `--debug` | `-d` | Show detailed reasons for skipped cleanups | `false` |
157
- | `--help` | `-h` | Show help | - |
283
+ Running `sync-worktrees` without `runOnce` drops you into an interactive terminal UI with live log streaming, manual sync triggers, and wizards for the common operations.
158
284
 
159
- Most sync behavior (repo URL, worktree directory, cron schedule, branch filtering, LFS, retry) is configured in the config file. The CLI flags that only make sense for one-off runs (`--repoUrl`, `--worktreeDir`, `--cronSchedule`, `--branchMaxAge`, `--branchInclude`, `--branchExclude`, `--skip-lfs`, `--bareRepoDir`) are still supported — run `sync-worktrees --help` for the full list.
285
+ ### Keybindings
160
286
 
161
- ## Configuration File
287
+ | Key | Action |
288
+ |-----|--------|
289
+ | `s` | Manually trigger sync for all repositories |
290
+ | `c` | Create a new branch (wizard) |
291
+ | `o` | Open a worktree in terminal or editor (wizard) |
292
+ | `w` | View worktree status across repos |
293
+ | `r` | Reload configuration and re-sync |
294
+ | `?` / `h` | Toggle help screen |
295
+ | `q` / `Esc` | Gracefully quit |
296
+ | `j` / `↓` | Scroll log down one line |
297
+ | `k` / `↑` | Scroll log up one line |
298
+ | `gg` | Jump to top of log |
299
+ | `G` | Jump to bottom (re-enables auto-scroll) |
162
300
 
163
- For managing multiple repositories, create a JavaScript ES module config file:
301
+ ### Wizards
302
+
303
+ - **Open wizard (`o`)** — select a worktree across all configured repos with live filtering (just type to narrow the list). Press `Tab` to flip between **Terminal** mode (launches a new terminal window attached to a `tmux` session in the worktree) and **Editor** mode (launches `$EDITOR` / `$VISUAL`, falling back to `code`). Re-opening the same worktree attaches to the existing tmux session instead of creating a duplicate.
304
+ - **Branch creation wizard (`c`)** — pick a repo, pick a base branch from a live-filtered list, type the new branch name. Names are validated against Git's rules; if the desired name already exists, a numeric suffix (`-2`, `-3`, …) is suggested automatically.
305
+ - **Worktree status view (`w`)** — flat list of every worktree across every configured repo, each tagged with status flags:
306
+
307
+ | Flag | Meaning |
308
+ |------|---------|
309
+ | `✓` | Clean |
310
+ | `M` | Modified / uncommitted changes |
311
+ | `↑` | Unpushed commits |
312
+ | `S` | Stashed changes |
313
+ | `⚠` | Operation in progress (merge/rebase/cherry-pick/revert/bisect) |
314
+ | `⊞` | Modified submodules |
315
+ | `✗` | Upstream branch is gone |
316
+
317
+ Press `Enter` on an entry to expand file/commit/stash counts. The view also surfaces `.diverged/` directories preserved from past force-pushes; press `d` (with `y`/`n` confirmation) to delete one after reviewing.
318
+
319
+ ### Terminal mode environment variables
320
+
321
+ | Variable | Purpose | Default behavior |
322
+ |----------|---------|------------------|
323
+ | `SYNC_WORKTREES_TERMINAL` | Override the terminal launcher on any platform. Value is a command string; the tmux invocation is appended via `sh -c`. Example: `SYNC_WORKTREES_TERMINAL="alacritty -e"`. | See per-platform defaults below. |
324
+ | `TERMINAL` | Linux-only fallback when `SYNC_WORKTREES_TERMINAL` is unset. Same format. | Probes `gnome-terminal`, `konsole`, `alacritty`, `kitty`, `xterm` in order. |
325
+ | `EDITOR` / `VISUAL` | Editor mode launcher. | Falls back to `code`. |
326
+
327
+ Per-platform terminal defaults (when no env override is set):
328
+
329
+ - **macOS** — Ghostty if `Ghostty.app` is installed, otherwise Terminal.app via AppleScript.
330
+ - **Linux** — `$TERMINAL` if set; otherwise the first found among the candidates above.
331
+
332
+ Terminal mode requires [`tmux`](https://github.com/tmux/tmux) to be installed.
333
+
334
+ ## Configuration
335
+
336
+ Config files are JavaScript ES modules. Relative paths resolve from the config file's location, and you have full access to `process.env` and Node module loading.
337
+
338
+ ### Minimal config
164
339
 
165
340
  ```javascript
166
- export default {
167
- // Optional defaults for all repositories
341
+ // @ts-check
342
+
343
+ /** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
344
+ const config = {
345
+ repositories: [
346
+ {
347
+ name: "my-project",
348
+ repoUrl: "https://github.com/user/my-project.git",
349
+ worktreeDir: "./worktrees/my-project",
350
+ },
351
+ ],
352
+ };
353
+
354
+ export default config;
355
+ ```
356
+
357
+ ### Multi-repo config
358
+
359
+ ```javascript
360
+ // @ts-check
361
+
362
+ /** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
363
+ const config = {
168
364
  defaults: {
169
- cronSchedule: "0 * * * *", // Hourly
170
- runOnce: false,
171
- branchMaxAge: "30d", // Only sync branches active in last 30 days
172
- branchExclude: ["wip-*", "tmp-*"], // Exclude WIP and temporary branches
173
- updateExistingWorktrees: true // Auto-update worktrees that are behind (default: true)
365
+ cronSchedule: "0 * * * *", // hourly
366
+ branchMaxAge: "30d", // ignore stale branches
367
+ branchExclude: ["wip-*", "tmp-*"],
368
+ updateExistingWorktrees: true,
174
369
  },
175
370
 
176
- // Retry configuration (optional - these are the defaults)
177
371
  retry: {
178
- maxAttempts: 'unlimited', // or a number like 5
179
- initialDelayMs: 1000, // Start with 1 second
180
- maxDelayMs: 600000, // Max 10 minutes between retries
181
- backoffMultiplier: 2 // Double the delay each time
372
+ maxAttempts: "unlimited",
373
+ initialDelayMs: 1000,
374
+ maxDelayMs: 600000,
375
+ backoffMultiplier: 2,
182
376
  },
183
377
 
184
378
  repositories: [
185
379
  {
186
- name: "frontend", // Unique identifier
380
+ name: "frontend",
187
381
  repoUrl: "https://github.com/company/frontend.git",
188
- worktreeDir: "./worktrees/frontend", // Relative paths supported
189
- cronSchedule: "*/30 * * * *" // Override default
382
+ worktreeDir: "./worktrees/frontend",
383
+ cronSchedule: "*/30 * * * *", // override default
190
384
  },
191
385
  {
192
386
  name: "backend",
193
- repoUrl: process.env.BACKEND_REPO_URL, // Environment variables supported
387
+ repoUrl: process.env.BACKEND_REPO_URL || "https://github.com/company/backend.git",
194
388
  worktreeDir: "/absolute/path/backend-worktrees",
195
- branchMaxAge: "6m", // Override: only sync branches active in last 6 months
196
- branchInclude: ["feature/*", "release-*", "main"], // Only sync specific branches
197
- // Uses default schedule
198
- retry: { maxAttempts: 10 } // Override retry for this repo
199
- }
200
- ]
389
+ branchMaxAge: "6m",
390
+ branchInclude: ["feature/*", "release-*", "main"],
391
+ retry: { maxAttempts: 10 }, // per-repo override
392
+ },
393
+ ],
201
394
  };
395
+
396
+ export default config;
202
397
  ```
203
398
 
204
- **Notes:**
205
- - Relative paths are resolved from the config file location
206
- - `bareRepoDir` defaults to `.bare/<repo-name>` if not specified
207
- - Repository-specific settings override defaults
399
+ Notes:
208
400
 
209
- ### Retry Configuration
401
+ - `bareRepoDir` defaults to `.bare/<repo-name>` if not specified.
402
+ - Repository-specific settings override `defaults`.
210
403
 
211
- The tool automatically retries on network errors and filesystem race conditions:
404
+ ### Clone mode
212
405
 
213
- - **Default behavior**: Unlimited retries with exponential backoff (1s, 2s, 4s... up to 10 minutes)
214
- - **Network errors**: Connection timeouts, DNS failures, repository access issues
215
- - **Filesystem errors**: Busy files, permission issues, race conditions
406
+ Set `mode: "clone"` to clone one checked-out branch directly into `worktreeDir` instead of maintaining one worktree per remote branch:
216
407
 
217
- Simple retry examples:
218
408
  ```javascript
219
- // Global retry configuration
220
- retry: { maxAttempts: 5 } // Try 5 times then stop
221
- retry: { maxAttempts: 'unlimited' } // Keep trying forever (default)
222
- retry: { maxDelayMs: 60000 } // Cap retry delay at 1 minute
223
- retry: { initialDelayMs: 5000 } // Start with 5 second delay
224
-
225
- // Per-repository override
226
- repositories: [{
227
- name: "critical-repo",
228
- // ... other config ...
229
- retry: { maxAttempts: 'unlimited', initialDelayMs: 10000 }
230
- }]
231
- ```
232
-
233
- ### Git LFS Support
234
-
235
- For repositories with Git LFS issues or when large files aren't needed:
236
-
237
- ```bash
238
- # Skip LFS downloads
239
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --skip-lfs
240
-
241
- # Or in config file
242
- defaults: {
243
- skipLfs: true
409
+ {
410
+ name: "game-platform",
411
+ repoUrl: "ssh://git@example.com/game-platform.git",
412
+ worktreeDir: "./slots/game-platform",
413
+ mode: "clone",
414
+ branch: "main",
415
+ depth: 1, // optional shallow clone
244
416
  }
245
417
  ```
246
418
 
247
- The tool automatically handles LFS errors by retrying with LFS disabled (max 2 retries by default, configurable via `retry.maxLfsRetries`).
248
-
249
- ### Branch Name Filtering
419
+ Clone mode keeps the normal `+refs/heads/*:refs/remotes/origin/*` fetch refspec, so `git branch -r` and `git fetch --all --prune` can see all remote branches. `branch` controls the checked-out branch that sync-worktrees fast-forwards on each sync. Omit `branch` and the remote HEAD is resolved at clone time.
250
420
 
251
- You can control which branches get synced using include and exclude patterns. This is useful for repositories where you only care about specific branch types (e.g., feature branches) or want to skip certain patterns (e.g., WIP branches).
421
+ `depth` is valid only for clone-mode repositories and must be a positive safe integer. Shallow clones use `--no-single-branch` so all remote branch refs remain visible at the configured depth. If you later remove `depth` from the config, the next sync unshallows the existing clone with `git fetch --unshallow`.
252
422
 
253
- **Pattern syntax**: Patterns support `*` wildcards that match any characters (including `/` in branch names).
254
- - `feature/*` - matches `feature/login`, `feature/auth/oauth`, etc.
255
- - `release-*` - matches `release-1.0`, `release-2.0-beta`, etc.
256
- - `*-hotfix` - matches `urgent-hotfix`, `prod-hotfix`, etc.
423
+ Clone mode rejects `branchInclude`, `branchExclude`, `branchMaxAge`, `updateExistingWorktrees`, and `bareRepoDir` at validation time (whether set directly or inherited via `defaults`) — they have no meaning for a single-branch checkout.
257
424
 
258
- **Filtering semantics**:
259
- - `branchInclude` - only branches matching at least one pattern are synced
260
- - `branchExclude` - branches matching any pattern are skipped
261
- - When both are set, include runs first, then exclude removes from the result
262
- - The default branch (e.g., `main`) is always retained regardless of filters
425
+ ### Sparse checkout
263
426
 
264
- **Examples**:
265
- ```bash
266
- # Command line
267
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
268
- --branchInclude "feature/*,release-*"
427
+ For monorepos where you only need a subset of folders, set `sparseCheckout` per repository entry. The tool runs `git worktree add --no-checkout`, configures sparse-checkout, then materializes only the included paths. The same repository URL can be listed multiple times under different `name`s with different sparse patterns to build domain-grouped layouts.
269
428
 
270
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
271
- --branchExclude "wip-*,tmp-*"
429
+ ```javascript
430
+ // @ts-check
272
431
 
273
- # Config file - global default
274
- defaults: {
275
- branchExclude: ["wip-*", "tmp-*"]
276
- }
432
+ /** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
433
+ const config = {
434
+ repositories: [
435
+ {
436
+ name: "roulette-game-client",
437
+ repoUrl: "https://github.com/acme/casino-monorepo.git",
438
+ worktreeDir: "/Users/me/game-clients/roulette",
439
+ sparseCheckout: { include: ["game-client"] },
440
+ },
441
+ {
442
+ name: "roulette-autocue",
443
+ repoUrl: "https://github.com/acme/casino-monorepo.git",
444
+ worktreeDir: "/Users/me/autocues/roulette",
445
+ sparseCheckout: { include: ["autocue"] },
446
+ },
447
+ ],
448
+ };
277
449
 
278
- # Config file - per repository
279
- repositories: [{
280
- name: "frontend",
281
- branchInclude: ["feature/*", "release-*"],
282
- branchExclude: ["feature/wip-*"],
283
- }]
450
+ export default config;
284
451
  ```
285
452
 
286
- **Combining with age filtering**: Branch name filtering runs first, then age filtering (`branchMaxAge`) is applied to the remaining branches. This lets you narrow down to specific branch types and further filter by activity.
453
+ **Modes:**
287
454
 
288
- ```bash
289
- # Only feature branches active in the last 30 days
290
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
291
- --branchInclude "feature/*" --branchMaxAge 30d
292
- ```
455
+ - `cone` (default): pass folder names in `include`. Fast and recommended.
456
+ - `no-cone`: pass gitignore-style patterns including `!negation`. Required for `exclude` and any `!`-prefixed include.
293
457
 
294
- ### Branch Age Filtering
458
+ If you set `exclude` or `!`-prefixed patterns while `mode: "cone"` is explicit, the tool auto-promotes to `no-cone` and logs a warning.
295
459
 
296
- To reduce clutter and save disk space, you can configure sync-worktrees to only sync branches that have been active within a specified time period. This is particularly useful for repositories with many stale or abandoned branches.
460
+ **Duplicate `repoUrl` handling:** The first entry per `repoUrl` keeps the URL-derived bare path (`.bare/<repo-slug>`). Subsequent duplicate entries auto-derive `bareRepoDir` from `name` (`.bare/<name>`). Pin `bareRepoDir` explicitly on duplicate entries if you want config order to be irrelevant.
297
461
 
298
- **Duration format**: `<number><unit>`
299
- - `h` - hours (e.g., `24h`)
300
- - `d` - days (e.g., `30d`)
301
- - `w` - weeks (e.g., `4w`)
302
- - `m` - months (e.g., `6m`)
303
- - `y` - years (e.g., `1y`)
462
+ **Narrowing safety:** When a sync would narrow an existing worktree's sparse patterns (remove a previously included path), it first checks the worktree is clean. If there are uncommitted changes, unpushed commits, or in-progress operations, the sparse update is skipped with a warning.
304
463
 
305
- **Examples**:
306
- ```bash
307
- # Command line
308
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --branchMaxAge 30d
464
+ ### Branch filtering
309
465
 
310
- # Config file - global default
466
+ Two filters can be combined:
467
+
468
+ ```javascript
311
469
  defaults: {
312
- branchMaxAge: "90d" // Only sync branches active in last 90 days
470
+ branchInclude: ["feature/*", "release-*", "main"],
471
+ branchExclude: ["feature/wip-*"],
472
+ branchMaxAge: "30d",
313
473
  }
314
-
315
- # Config file - per repository
316
- repositories: [{
317
- name: "active-project",
318
- branchMaxAge: "14d", // Very active project - only last 2 weeks
319
- }, {
320
- name: "legacy-project",
321
- branchMaxAge: "1y", // Legacy project - keep branches from last year
322
- }]
323
474
  ```
324
475
 
325
- When branch filtering is active, the tool will:
326
- - Fetch commit timestamps for all remote branches
327
- - Filter out branches older than the specified age
328
- - Log how many branches were excluded
329
- - Only create/maintain worktrees for active branches
330
-
331
- ### Handling Rebased and Force-Pushed Branches
332
-
333
- sync-worktrees intelligently handles branches that have been rebased or force-pushed to prevent data loss:
334
-
335
- **Automatic behavior (no configuration needed):**
476
+ - **Name patterns** support `*` wildcards (including across `/`): `feature/*` matches `feature/login` and `feature/auth/oauth`.
477
+ - **`branchInclude`** keeps only matching branches; **`branchExclude`** removes matching branches. When both are set, include runs first, then exclude.
478
+ - **`branchMaxAge`** drops branches whose latest commit is older than the duration (`h`/`d`/`w`/`m`/`y` — e.g. `24h`, `30d`, `6m`, `1y`). Applied after name filtering.
479
+ - The default branch is always retained regardless of filters.
336
480
 
337
- 1. **Clean rebases** - When a branch is rebased but the file content remains identical:
338
- - Automatically resets the worktree to match the upstream
339
- - No data loss since the content is the same
481
+ ### Diverged branches
340
482
 
341
- 2. **Diverged branches with NO local changes** - When someone force-pushes but you haven't made local commits:
342
- - Automatically resets to the new upstream state
343
- - No move to `.diverged` since you have no work to preserve
344
- - Keeps `.diverged` clean by only preserving actual user work
483
+ When upstream is force-pushed and your worktree contains divergent local commits, sync-worktrees moves the worktree to a hidden `.diverged/` directory before creating a fresh one from the new upstream. No data loss; you can review the old state later.
345
484
 
346
- 3. **Diverged branches WITH local changes** - When a branch has different content AND you've made local commits:
347
- - Moves the worktree to `.diverged` directory within your worktrees folder
348
- - Preserves all your local changes and commits
349
- - Creates a fresh worktree from the upstream branch
350
- - Logs clear instructions for reviewing diverged changes
351
-
352
- **Example diverged structure:**
353
485
  ```
354
486
  my-repo-worktrees/
355
487
  ├── main/
356
488
  ├── feature-a/
357
- ├── feature-b/
358
- └── .diverged/ # Hidden diverged directory
359
- ├── 2024-01-15-feature-x/ # Timestamp + branch name
360
- │ ├── .diverged-info.json # Metadata about the divergence
361
- │ └── [all your local files]
362
- └── 2024-01-16-feature-y/
489
+ └── .diverged/
490
+ └── 2024-01-15-feature-x/
363
491
  ├── .diverged-info.json
364
492
  └── [all your local files]
365
493
  ```
366
494
 
367
- **Reviewing diverged worktrees:**
495
+ Reviewing a diverged worktree:
496
+
368
497
  ```bash
369
- # See what's different
370
498
  cd my-repo-worktrees/.diverged/2024-01-15-feature-x
371
499
  git diff origin/feature-x
372
500
 
373
- # If you want to keep your changes
374
- git push --force-with-lease
375
-
376
- # If you want to discard and use upstream
377
- cd ../..
378
- rm -rf .diverged/2024-01-15-feature-x
501
+ # keep local: git push --force-with-lease
502
+ # discard: cd ../.. && rm -rf .diverged/2024-01-15-feature-x
379
503
  ```
380
504
 
381
- This ensures you never lose work due to force pushes while keeping your worktrees in sync with upstream.
505
+ The TUI's worktree status view (`w`) lists diverged directories and offers a guided delete (`d` with `y`/`n` confirmation) once you've decided.
382
506
 
383
- ### Sparse Checkout
507
+ Clean rebases where file content matches the upstream are auto-applied with no detour through `.diverged/`. Diverged-but-no-local-commits is also handled without preservation, since there's no user work to keep.
384
508
 
385
- For monorepos where you only need a subset of folders, set `sparseCheckout` per repository entry. The tool runs `git worktree add --no-checkout`, configures sparse-checkout, then materializes only the included paths. The same repository can be listed multiple times under different `name`s with different sparse patterns to build domain-grouped layouts.
509
+ ### Retry and LFS
386
510
 
387
- ```js
388
- export default {
389
- repositories: [
390
- {
391
- name: "roulette-game-client",
392
- repoUrl: "https://github.com/acme/casino-monorepo.git",
393
- worktreeDir: "/Users/me/game-clients/roulette",
394
- sparseCheckout: { include: ["game-client"] },
395
- },
396
- {
397
- name: "roulette-autocue",
398
- repoUrl: "https://github.com/acme/casino-monorepo.git",
399
- worktreeDir: "/Users/me/autocues/roulette",
400
- sparseCheckout: { include: ["autocue"] },
401
- // bareRepoDir: ".bare/roulette-autocue", // pin to make config reorder-proof
402
- },
403
- ],
404
- };
511
+ The tool retries network errors (timeouts, DNS failures, access issues) and filesystem race conditions automatically:
512
+
513
+ ```javascript
514
+ retry: { maxAttempts: 5 } // try 5 times then stop
515
+ retry: { maxAttempts: "unlimited" } // keep trying forever (default)
516
+ retry: { maxDelayMs: 60000 } // cap retry delay at 1 minute
405
517
  ```
406
518
 
407
- **Modes:**
519
+ For repositories with Git LFS issues or large files you don't need, set `skipLfs: true` in `defaults` or per repository. The tool also retries LFS-specific failures with LFS disabled (configurable via `retry.maxLfsRetries`).
408
520
 
409
- - `cone` (default): pass folder names in `include`. Fast and recommended.
410
- - `no-cone`: pass gitignore-style patterns including `!negation`. Required for `exclude` and any `!`-prefixed include.
521
+ ### Hooks and file copying
522
+
523
+ Two lifecycle hooks the example config covers in depth:
524
+
525
+ - `hooks.onBranchCreated` — array of shell commands run after a new branch's worktree is created. Placeholders: `{BRANCH_NAME}`, `{WORKTREE_PATH}`, `{REPO_NAME}`, `{BASE_BRANCH}`, `{REPO_URL}`. Fire-and-forget.
526
+ - `filesToCopyOnBranchCreate` — paths copied into every newly created worktree (e.g. `.env.local`, `.npmrc`). Glob patterns are resolved relative to the config file's directory.
411
527
 
412
- If you set `exclude` or use `!`-prefixed patterns while `mode: "cone"` is explicit, the tool auto-promotes to `no-cone` and logs a warning.
528
+ In clone mode, `filesToCopyOnBranchCreate` fires once on the initial clone, and `hooks.onBranchCreated` fires only for TUI-initiated branch creation (clone mode tracks a single fixed branch with no later branch-creation events).
413
529
 
414
- **Duplicate `repoUrl` handling:**
530
+ For every knob (timeouts, parallelism, jitter, sparse-update behavior, retry tuning), see [`sync-worktrees.config.example.js`](./sync-worktrees.config.example.js).
415
531
 
416
- The first entry per `repoUrl` keeps the URL-derived bare path (`.bare/<repo-slug>`). Subsequent duplicate entries auto-derive `bareRepoDir` from `name` (`.bare/<name>`) so they do not collide. Pin `bareRepoDir` explicitly on duplicate entries if you want config order to be irrelevant.
532
+ ## CLI options
533
+
534
+ The CLI loads a config file and runs it. Most run-mode settings (branch filters, retry, parallelism, LFS, clone mode, depth, etc.) live in the config file. Use `--runOnce` for an ad-hoc one-shot run without editing config.
535
+
536
+ | Option | Alias | Description | Default |
537
+ |--------|-------|-------------|---------|
538
+ | `--config` | `-c` | Path to JavaScript config file (auto-detected in CWD when omitted) | - |
539
+ | `--runOnce` | - | Run a sync once and exit, overriding config `runOnce` settings for this invocation | `false` |
540
+ | `--help` | `-h` | Show help | - |
541
+ | `--version` | - | Print version | - |
417
542
 
418
- **Narrowing safety:**
543
+ Subcommands:
419
544
 
420
- When a sync would narrow an existing worktree's sparse patterns (remove a previously included path), it first checks the worktree is clean. If there are uncommitted changes, unpushed commits, or in-progress operations, the sparse update is skipped with a warning. Clean or stash local changes to apply the narrower patterns.
545
+ - `sync-worktrees init [--config <path>] [--force]` interactive wizard that writes a minimal config file (`./sync-worktrees.config.js` by default). Refuses to overwrite an existing target unless `--force` is passed.
546
+ - `sync-worktrees list [--config <path>] [--filter <pattern>]` — print the resolved repositories and exit.
421
547
 
422
548
  ## Requirements
423
549
 
424
550
  - Node.js >= 22.0.0
425
551
  - Git
426
- - [`tmux`](https://github.com/tmux/tmux) (optional, required only for Terminal mode in the TUI)
427
552
  - An MCP-capable client (optional, only for the `sync-worktrees-mcp` server)
428
553
 
429
554
  ## Contributing