sync-worktrees 3.6.3 → 4.1.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 +383 -261
  2. package/dist/components/App.d.ts +50 -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 +14 -0
  15. package/dist/components/StatusBar.d.ts.map +1 -0
  16. package/dist/components/WorktreeStatusView.d.ts +14 -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 +2523 -1106
  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 +2347 -640
  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 +94 -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 +303 -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 +31 -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,92 +77,182 @@ 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
109
223
  }
110
224
  }
111
225
  }
112
226
  ```
113
227
 
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).
228
+ </details>
115
229
 
116
- At session start, agents should call `detect_context` with `includeAllWorktrees: true`. With a loaded config, that returns config-driven `siblingRepositories` for every other configured repo, including nested `worktreeDir` paths, plus `allWorktreesByRepo` keyed by repository name. If a configured repo cannot be enumerated, `allWorktreeErrorsByRepo` carries the per-repo error.
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
243
+ }
244
+ }
245
+ }
246
+ ```
117
247
 
118
- Client-specific locations:
248
+ </details>
119
249
 
120
- | Client | Config file |
121
- |--------|-------------|
122
- | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) |
123
- | Claude Code | `claude mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp` |
124
- | Cursor | `~/.cursor/mcp.json` (or project-level `.cursor/mcp.json`) |
250
+ <details>
251
+ <summary>Warp</summary>
252
+
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>
125
256
 
126
257
  ### Available tools
127
258
 
@@ -147,286 +278,277 @@ All tools that target a single repo accept an optional `repoName`. When omitted,
147
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.
148
279
  - Path-targeted tools verify the supplied path is a registered worktree of the selected repository.
149
280
 
150
- ## Options
281
+ ## Interactive TUI
151
282
 
152
- | Option | Alias | Description | Default |
153
- |--------|-------|-------------|---------|
154
- | `--config` | `-c` | Path to JavaScript config file (auto-detected in CWD when omitted) | - |
155
- | `--filter` | `-f` | Filter repositories by name (wildcards supported) | - |
156
- | `--list` | `-l` | List configured repositories and exit | `false` |
157
- | `--runOnce` | - | Override config to run once and exit | `false` |
158
- | `--no-update-existing` | - | Disable automatic updates of existing worktrees | `false` |
159
- | `--debug` | `-d` | Show detailed reasons for skipped cleanups | `false` |
160
- | `--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.
284
+
285
+ ### Keybindings
286
+
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) |
300
+
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.
161
318
 
162
- 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.
319
+ ### Terminal mode environment variables
163
320
 
164
- ## Configuration File
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
165
335
 
166
- For managing multiple repositories, create a JavaScript ES module config file:
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
167
339
 
168
340
  ```javascript
169
- export default {
170
- // 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 = {
171
364
  defaults: {
172
- cronSchedule: "0 * * * *", // Hourly
173
- runOnce: false,
174
- branchMaxAge: "30d", // Only sync branches active in last 30 days
175
- branchExclude: ["wip-*", "tmp-*"], // Exclude WIP and temporary branches
176
- 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,
177
369
  },
178
370
 
179
- // Retry configuration (optional - these are the defaults)
180
371
  retry: {
181
- maxAttempts: 'unlimited', // or a number like 5
182
- initialDelayMs: 1000, // Start with 1 second
183
- maxDelayMs: 600000, // Max 10 minutes between retries
184
- backoffMultiplier: 2 // Double the delay each time
372
+ maxAttempts: "unlimited",
373
+ initialDelayMs: 1000,
374
+ maxDelayMs: 600000,
375
+ backoffMultiplier: 2,
185
376
  },
186
377
 
187
378
  repositories: [
188
379
  {
189
- name: "frontend", // Unique identifier
380
+ name: "frontend",
190
381
  repoUrl: "https://github.com/company/frontend.git",
191
- worktreeDir: "./worktrees/frontend", // Relative paths supported
192
- cronSchedule: "*/30 * * * *" // Override default
382
+ worktreeDir: "./worktrees/frontend",
383
+ cronSchedule: "*/30 * * * *", // override default
193
384
  },
194
385
  {
195
386
  name: "backend",
196
- repoUrl: process.env.BACKEND_REPO_URL, // Environment variables supported
387
+ repoUrl: process.env.BACKEND_REPO_URL || "https://github.com/company/backend.git",
197
388
  worktreeDir: "/absolute/path/backend-worktrees",
198
- branchMaxAge: "6m", // Override: only sync branches active in last 6 months
199
- branchInclude: ["feature/*", "release-*", "main"], // Only sync specific branches
200
- // Uses default schedule
201
- retry: { maxAttempts: 10 } // Override retry for this repo
202
- }
203
- ]
389
+ branchMaxAge: "6m",
390
+ branchInclude: ["feature/*", "release-*", "main"],
391
+ retry: { maxAttempts: 10 }, // per-repo override
392
+ },
393
+ ],
204
394
  };
395
+
396
+ export default config;
205
397
  ```
206
398
 
207
- **Notes:**
208
- - Relative paths are resolved from the config file location
209
- - `bareRepoDir` defaults to `.bare/<repo-name>` if not specified
210
- - Repository-specific settings override defaults
399
+ Notes:
211
400
 
212
- ### Retry Configuration
401
+ - `bareRepoDir` defaults to `.bare/<repo-name>` if not specified.
402
+ - Repository-specific settings override `defaults`.
213
403
 
214
- The tool automatically retries on network errors and filesystem race conditions:
404
+ ### Clone mode
215
405
 
216
- - **Default behavior**: Unlimited retries with exponential backoff (1s, 2s, 4s... up to 10 minutes)
217
- - **Network errors**: Connection timeouts, DNS failures, repository access issues
218
- - **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:
219
407
 
220
- Simple retry examples:
221
408
  ```javascript
222
- // Global retry configuration
223
- retry: { maxAttempts: 5 } // Try 5 times then stop
224
- retry: { maxAttempts: 'unlimited' } // Keep trying forever (default)
225
- retry: { maxDelayMs: 60000 } // Cap retry delay at 1 minute
226
- retry: { initialDelayMs: 5000 } // Start with 5 second delay
227
-
228
- // Per-repository override
229
- repositories: [{
230
- name: "critical-repo",
231
- // ... other config ...
232
- retry: { maxAttempts: 'unlimited', initialDelayMs: 10000 }
233
- }]
234
- ```
235
-
236
- ### Git LFS Support
237
-
238
- For repositories with Git LFS issues or when large files aren't needed:
239
-
240
- ```bash
241
- # Skip LFS downloads
242
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --skip-lfs
243
-
244
- # Or in config file
245
- defaults: {
246
- 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
247
416
  }
248
417
  ```
249
418
 
250
- The tool automatically handles LFS errors by retrying with LFS disabled (max 2 retries by default, configurable via `retry.maxLfsRetries`).
251
-
252
- ### 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.
253
420
 
254
- 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`.
255
422
 
256
- **Pattern syntax**: Patterns support `*` wildcards that match any characters (including `/` in branch names).
257
- - `feature/*` - matches `feature/login`, `feature/auth/oauth`, etc.
258
- - `release-*` - matches `release-1.0`, `release-2.0-beta`, etc.
259
- - `*-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.
260
424
 
261
- **Filtering semantics**:
262
- - `branchInclude` - only branches matching at least one pattern are synced
263
- - `branchExclude` - branches matching any pattern are skipped
264
- - When both are set, include runs first, then exclude removes from the result
265
- - The default branch (e.g., `main`) is always retained regardless of filters
425
+ ### Sparse checkout
266
426
 
267
- **Examples**:
268
- ```bash
269
- # Command line
270
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
271
- --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.
272
428
 
273
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
274
- --branchExclude "wip-*,tmp-*"
429
+ ```javascript
430
+ // @ts-check
275
431
 
276
- # Config file - global default
277
- defaults: {
278
- branchExclude: ["wip-*", "tmp-*"]
279
- }
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
+ };
280
449
 
281
- # Config file - per repository
282
- repositories: [{
283
- name: "frontend",
284
- branchInclude: ["feature/*", "release-*"],
285
- branchExclude: ["feature/wip-*"],
286
- }]
450
+ export default config;
287
451
  ```
288
452
 
289
- **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:**
290
454
 
291
- ```bash
292
- # Only feature branches active in the last 30 days
293
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
294
- --branchInclude "feature/*" --branchMaxAge 30d
295
- ```
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.
296
457
 
297
- ### 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.
298
459
 
299
- 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.
300
461
 
301
- **Duration format**: `<number><unit>`
302
- - `h` - hours (e.g., `24h`)
303
- - `d` - days (e.g., `30d`)
304
- - `w` - weeks (e.g., `4w`)
305
- - `m` - months (e.g., `6m`)
306
- - `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.
307
463
 
308
- **Examples**:
309
- ```bash
310
- # Command line
311
- sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --branchMaxAge 30d
464
+ ### Branch filtering
312
465
 
313
- # Config file - global default
466
+ Two filters can be combined:
467
+
468
+ ```javascript
314
469
  defaults: {
315
- branchMaxAge: "90d" // Only sync branches active in last 90 days
470
+ branchInclude: ["feature/*", "release-*", "main"],
471
+ branchExclude: ["feature/wip-*"],
472
+ branchMaxAge: "30d",
316
473
  }
317
-
318
- # Config file - per repository
319
- repositories: [{
320
- name: "active-project",
321
- branchMaxAge: "14d", // Very active project - only last 2 weeks
322
- }, {
323
- name: "legacy-project",
324
- branchMaxAge: "1y", // Legacy project - keep branches from last year
325
- }]
326
474
  ```
327
475
 
328
- When branch filtering is active, the tool will:
329
- - Fetch commit timestamps for all remote branches
330
- - Filter out branches older than the specified age
331
- - Log how many branches were excluded
332
- - Only create/maintain worktrees for active branches
333
-
334
- ### Handling Rebased and Force-Pushed Branches
335
-
336
- sync-worktrees intelligently handles branches that have been rebased or force-pushed to prevent data loss:
337
-
338
- **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.
339
480
 
340
- 1. **Clean rebases** - When a branch is rebased but the file content remains identical:
341
- - Automatically resets the worktree to match the upstream
342
- - No data loss since the content is the same
481
+ ### Diverged branches
343
482
 
344
- 2. **Diverged branches with NO local changes** - When someone force-pushes but you haven't made local commits:
345
- - Automatically resets to the new upstream state
346
- - No move to `.diverged` since you have no work to preserve
347
- - 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.
348
484
 
349
- 3. **Diverged branches WITH local changes** - When a branch has different content AND you've made local commits:
350
- - Moves the worktree to `.diverged` directory within your worktrees folder
351
- - Preserves all your local changes and commits
352
- - Creates a fresh worktree from the upstream branch
353
- - Logs clear instructions for reviewing diverged changes
354
-
355
- **Example diverged structure:**
356
485
  ```
357
486
  my-repo-worktrees/
358
487
  ├── main/
359
488
  ├── feature-a/
360
- ├── feature-b/
361
- └── .diverged/ # Hidden diverged directory
362
- ├── 2024-01-15-feature-x/ # Timestamp + branch name
363
- │ ├── .diverged-info.json # Metadata about the divergence
364
- │ └── [all your local files]
365
- └── 2024-01-16-feature-y/
489
+ └── .diverged/
490
+ └── 2024-01-15-feature-x/
366
491
  ├── .diverged-info.json
367
492
  └── [all your local files]
368
493
  ```
369
494
 
370
- **Reviewing diverged worktrees:**
495
+ Reviewing a diverged worktree:
496
+
371
497
  ```bash
372
- # See what's different
373
498
  cd my-repo-worktrees/.diverged/2024-01-15-feature-x
374
499
  git diff origin/feature-x
375
500
 
376
- # If you want to keep your changes
377
- git push --force-with-lease
378
-
379
- # If you want to discard and use upstream
380
- cd ../..
381
- 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
382
503
  ```
383
504
 
384
- 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.
385
506
 
386
- ### 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.
387
508
 
388
- 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
389
510
 
390
- ```js
391
- export default {
392
- repositories: [
393
- {
394
- name: "roulette-game-client",
395
- repoUrl: "https://github.com/acme/casino-monorepo.git",
396
- worktreeDir: "/Users/me/game-clients/roulette",
397
- sparseCheckout: { include: ["game-client"] },
398
- },
399
- {
400
- name: "roulette-autocue",
401
- repoUrl: "https://github.com/acme/casino-monorepo.git",
402
- worktreeDir: "/Users/me/autocues/roulette",
403
- sparseCheckout: { include: ["autocue"] },
404
- // bareRepoDir: ".bare/roulette-autocue", // pin to make config reorder-proof
405
- },
406
- ],
407
- };
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
408
517
  ```
409
518
 
410
- **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`).
411
520
 
412
- - `cone` (default): pass folder names in `include`. Fast and recommended.
413
- - `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.
414
527
 
415
- 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).
416
529
 
417
- **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).
418
531
 
419
- 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 | - |
420
542
 
421
- **Narrowing safety:**
543
+ Subcommands:
422
544
 
423
- 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.
424
547
 
425
548
  ## Requirements
426
549
 
427
550
  - Node.js >= 22.0.0
428
551
  - Git
429
- - [`tmux`](https://github.com/tmux/tmux) (optional, required only for Terminal mode in the TUI)
430
552
  - An MCP-capable client (optional, only for the `sync-worktrees-mcp` server)
431
553
 
432
554
  ## Contributing