@cestoliv/wt 0.5.0 → 0.5.1

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.
package/README.md CHANGED
@@ -16,7 +16,10 @@ prompt.
16
16
  npm install -g @cestoliv/wt # also the update command
17
17
  ```
18
18
 
19
- Requires Node.js 20+ and Git. The command is `wt`.
19
+ Requires Node.js 20+ and Git. The command is `wt`. Optionally install the
20
+ [`gh`](https://cli.github.com/) and/or [`glab`](https://gitlab.com/gitlab-org/cli)
21
+ CLIs (authenticated) to let `wt prune` confirm merges via merged PRs/MRs — see
22
+ [Prune](#prune--wt-prune).
20
23
 
21
24
  ## Let your AI assistant set it up
22
25
 
@@ -37,7 +40,8 @@ infer from this project. Write the result to the config file (find its path with
37
40
  wt # Browse worktrees (interactive TUI)
38
41
  wt create my-feat # New worktree, opens your IDE
39
42
  wt agent my-feat "Plan the feature" # New worktree + AI agent in Zed (macOS)
40
- wt agent fix-bug "Fix bug" --mode auto # Use auto mode instead of plan
43
+ wt agent fix-bug "Fix bug" --mode auto # Use auto mode instead of the default
44
+ wt prune # Remove merged worktrees (per-branch confirm)
41
45
  wt config # Edit config in $EDITOR
42
46
  wt skill # Print the skill file (for AI agents)
43
47
  ```
@@ -51,14 +55,15 @@ wt agent refactor "Refactor API layer" --mode default
51
55
  ```
52
56
 
53
57
  Creates a worktree exactly like `wt create`, then auto-starts your agent
54
- (default `claude --permission-mode plan`) in Zed's integrated terminal —
55
- pre-filled with your prompt and left interactive for you to take over.
58
+ (default `claude`, run with `--permission-mode default`) in Zed's integrated
59
+ terminal — pre-filled with your prompt and left interactive for you to take over.
56
60
 
57
- **Available modes** (`--mode`, defaults to `plan`):
61
+ **Available modes** (`--mode`, defaults to `default`; change the default with
62
+ the `agent_mode` config key):
58
63
 
59
- - `default` — Standard interactive mode with approval for each action
64
+ - `default` — Standard interactive mode with approval for each action (default)
60
65
  - `acceptEdits` — Allow file changes but keep command execution controlled
61
- - `plan` — Architecture-first mode with no surprise mutations (default)
66
+ - `plan` — Architecture-first mode with no surprise mutations
62
67
  - `auto` — Claude's safety model makes decisions instead of prompting
63
68
  - `dontAsk` — Minimal interruptions in trusted environments
64
69
  - `bypassPermissions` — Skip all permission checks (dangerous, CI/sandbox only)
@@ -85,12 +90,12 @@ An interactive, fuzzy-searchable list of your worktrees:
85
90
 
86
91
  ```
87
92
  MY-PROJECT
88
- ▶ main ~/dev/my-project
93
+ ▶ main (main) ~/dev/my-project
89
94
  fix: resolve auth bug (2h ago)
90
95
  feat/dashboard ~/dev/my-project-feat-dashboard
91
96
  wip: add chart component (1d ago)
92
97
 
93
- ↕ navigate · Enter open · D delete · C create · A agent · Q quit
98
+ ↕ navigate · Enter open · D delete · P prune · C create · A agent · Q quit
94
99
  ```
95
100
 
96
101
  Type to fuzzy-filter branches instantly. Inside a repo it shows that repo's
@@ -104,8 +109,12 @@ and a permission mode — **worktree (repo → branch) → plan prompt → permi
104
109
  mode**. Pressing `Esc` steps back to the previous question (answers preserved),
105
110
  or back to the list from the first step. After creating, the list **refreshes
106
111
  and stays open** (preserving your search and cursor) instead of exiting — only
107
- `Enter` and `Q`/`Esc` leave the TUI. Note that `a`/`c`/`d` are command keys, so
108
- they can't be typed into the search box.
112
+ `Enter` and `Q`/`Esc` leave the TUI. `P` prunes every worktree whose branch has
113
+ already been merged (see [`wt prune`](#prune--wt-prune) below). Note that
114
+ `a`/`c`/`d`/`p` are command keys, so they can't be typed into the search box.
115
+
116
+ The main worktree is tagged `(main)` and is protected — `D` only removes linked
117
+ worktrees, never the main repository.
109
118
 
110
119
  ## Create — `wt create [branch]`
111
120
 
@@ -121,6 +130,34 @@ registered repos.
121
130
  If the path already exists, `wt create` offers to open it in your IDE instead of
122
131
  erroring (in a non-interactive shell it exits non-zero).
123
132
 
133
+ ## Prune — `wt prune`
134
+
135
+ ```bash
136
+ wt prune # remove every merged worktree, one confirmation per branch
137
+ ```
138
+
139
+ Cleans up the worktrees you're done with: it finds every worktree whose branch
140
+ has already been merged into the base branch (`base_branch`, default
141
+ `origin/main`) and removes it — **always confirming each branch individually**,
142
+ and force-confirming when git refuses (submodules or uncommitted changes), just
143
+ like a manual `D` delete. The branch itself stays; only the worktree is removed.
144
+ Your `teardown_commands` run before each removal.
145
+
146
+ Merge detection is tiered. Patch-id matches (via `git cherry`) catch a
147
+ single-commit branch **squash-merged** through a PR, offline. For the ambiguous
148
+ case — the branch tip is an ancestor of base but 0 commits ahead, which both a
149
+ **fast-forward / merge-commit** merge and a worktree holding only *uncommitted*
150
+ work produce — it consults the **forge**: a merged PR/MR (via `gh` for GitHub or
151
+ `glab` for GitLab, including self-hosted, auto-detected from the remote) is the
152
+ only reliable signal, so a branch with unmerged work-in-progress is never
153
+ mistaken for merged. If no forge CLI is available (or you're offline) such
154
+ branches are simply left alone. A worktree still sitting exactly on the base
155
+ commit is never offered. `wt prune` best-effort fetches
156
+ the remote first; if the base ref can't be resolved (offline, missing), it
157
+ removes nothing. The TUI exposes the same action under the `P` key. Works in
158
+ repo mode and across all registered repos in global mode (each against its own
159
+ `base_branch`).
160
+
124
161
  ## Configuration
125
162
 
126
163
  Edit with `wt config` (`wt config --path` prints the file location —
@@ -134,8 +171,10 @@ Edit with `wt config` (`wt config --path` prints the file location —
134
171
  | `worktree_path` | `"../"` | Where worktrees are placed (relative to repo) |
135
172
  | `setup_commands` | `[]` | Commands to run in new worktrees |
136
173
  | `teardown_commands` | `[]` | Commands to run in a worktree just before it is deleted (e.g. `["docker compose down -v"]`) |
137
- | `agent_command` | `"claude --permission-mode plan"` | Base command; `--permission-mode` replaced by `--mode` option, then prompt appended |
174
+ | `agent_command` | `"claude"` | Base command; `--permission-mode <mode>` injected, then prompt appended |
175
+ | `agent_mode` | `"default"` | Default permission mode for `wt agent` (overridden by `--mode`) |
138
176
  | `agent_trigger_chord` | `"ctrl-shift-cmd-c"` | Zed keymap chord `wt agent` installs and presses |
177
+ | `auto_refresh_minutes`| `5` | How often the interactive list re-fetches worktrees (shows a "last refreshed" header); `0` disables it |
139
178
  | `repo_overrides` | `{}` | Per-repo overrides for any key above |
140
179
 
141
180
  Override any key per repo:
package/SKILL.md CHANGED
@@ -17,7 +17,8 @@ Launch the interactive TUI. Shows worktrees for the current repo (repo mode) or
17
17
 
18
18
  - Arrow keys — navigate
19
19
  - `Enter` — open worktree in IDE (exits the TUI)
20
- - `D` — delete worktree
20
+ - `D` — delete worktree (the main worktree is tagged `(main)` and cannot be deleted — only linked worktrees can)
21
+ - `P` — prune all merged worktrees (per-branch confirmation)
21
22
  - `C` — create a new worktree (works in both repo and global mode)
22
23
  - `A` — create a worktree and start an AI agent in it (works in both modes)
23
24
  - type to search · `Backspace` — edit search
@@ -38,8 +39,8 @@ After a create or agent action the TUI **refreshes and stays open** on the list
38
39
  (your search and cursor are preserved) rather than exiting — only `Enter` (open)
39
40
  and `Q`/`Esc` exit.
40
41
 
41
- Because `a`/`A`, `c`/`C`, and `d`/`D` are reserved as command keys, those
42
- letters can't be typed into the search box.
42
+ Because `a`/`A`, `c`/`C`, `d`/`D`, and `p`/`P` are reserved as command keys,
43
+ those letters can't be typed into the search box.
43
44
 
44
45
  ### `wt create [branch]`
45
46
 
@@ -65,11 +66,12 @@ wt agent feature/fix 'Fix the bug in payment processing' --mode auto
65
66
  wt agent refactor/api 'Refactor the API layer' --mode default
66
67
  ```
67
68
 
68
- The `--mode` flag sets Claude Code's permission mode (defaults to `plan`):
69
+ The `--mode` flag sets Claude Code's permission mode (defaults to `default`;
70
+ change the default with the `agent_mode` config key):
69
71
 
70
- - `default` — Standard interactive mode with approval for each action
72
+ - `default` — Standard interactive mode with approval for each action (default)
71
73
  - `acceptEdits` — Allow file changes but keep command execution controlled
72
- - `plan` — Architecture-first mode with no surprise mutations (default)
74
+ - `plan` — Architecture-first mode with no surprise mutations
73
75
  - `auto` — Claude's safety model makes decisions instead of prompting
74
76
  - `dontAsk` — Minimal interruptions in trusted environments
75
77
  - `bypassPermissions` — Skip all permission checks (dangerous, CI/sandbox only)
@@ -97,6 +99,33 @@ If the worktree path already exists, `wt agent` prompts you to **open it in the
97
99
  IDE**, **open it and start the agent**, or **quit** — instead of erroring. (In a
98
100
  non-interactive shell it errors with a non-zero exit instead of prompting.)
99
101
 
102
+ ### `wt prune`
103
+
104
+ Remove every worktree whose branch has already been merged into the base
105
+ branch (`base_branch`, default `origin/main`). Each candidate is confirmed
106
+ individually — and force-confirmed when git refuses (submodules / uncommitted
107
+ changes), exactly like a manual `d` delete. The branch itself is left intact;
108
+ only the worktree is removed.
109
+
110
+ ```bash
111
+ wt prune # review and remove merged worktrees, one prompt per branch
112
+ ```
113
+
114
+ Merge detection works in tiers. A branch whose diff already exists in base by
115
+ patch id (via `git cherry`, so a single-commit branch **squash-merged** through a
116
+ PR is recognized offline) is merged. For the ambiguous case — the branch tip is
117
+ an ancestor of base but 0 commits ahead, which a **fast-forward / merge-commit**
118
+ merge and a worktree holding only *uncommitted* work both produce — it consults
119
+ the **forge**: a merged PR/MR (via `gh` for GitHub, `glab` for GitLab incl.
120
+ self-hosted, auto-detected from the remote) is the only reliable signal. If the
121
+ forge can't answer (CLI missing, offline, branch unpushed, no merged PR/MR) the
122
+ branch is left alone. A worktree still sitting exactly on the base commit is
123
+ never offered. `wt prune` also best-effort fetches the remote first so detection
124
+ sees up-to-date refs; if the
125
+ base ref can't be resolved (e.g. offline), nothing is removed. Works in repo
126
+ mode (current repo) and global mode (all registered repos, each against its own
127
+ `base_branch`). The TUI exposes the same action under the `p` key.
128
+
100
129
  ### `wt config`
101
130
 
102
131
  Open the global config file in `$EDITOR` (defaults to `nano`).
@@ -124,14 +153,16 @@ Config is stored as JSON. Get the path with `wt config --path`.
124
153
  | `teardown_commands` | `string[]` | `[]` | Commands to run in a worktree just before it is deleted (e.g. `["docker compose down -v"]`); on failure you are prompted whether to delete anyway |
125
154
  | `ide` | `string` | `"zed"` | IDE command to open worktrees with |
126
155
  | `ide_open_args` | `string[]` | `["-n"]` | Arguments passed to the IDE command |
127
- | `agent_command` | `string` | `"claude --permission-mode plan"` | Base command `wt agent` runs in Zed; any `--permission-mode` flag is replaced by the `--mode` option (defaults to `plan`), then `<plan_prompt>` is appended single-quoted |
156
+ | `agent_command` | `string` | `"claude"` | Base command `wt agent` runs in Zed; `--permission-mode <mode>` is injected (any existing one replaced), then `<plan_prompt>` is appended single-quoted |
157
+ | `agent_mode` | `string` | `"default"` | Default Claude Code permission mode for `wt agent`; the `--mode` flag overrides it. One of `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, `bypassPermissions` |
128
158
  | `agent_trigger_chord` | `string` | `"ctrl-shift-cmd-c"` | Zed keymap chord `wt agent` installs/presses to spawn the agent task |
159
+ | `auto_refresh_minutes`| `number` | `5` | How often the interactive list (`wt`) re-fetches worktrees and updates the "last refreshed" header; `0` disables auto-refresh |
129
160
  | `repos` | `string[]` | `[]` | Registered repo paths (auto-populated on first use) |
130
161
  | `repo_overrides` | `object` | `{}` | Per-repo config overrides (see below) |
131
162
 
132
163
  ### Per-repo overrides
133
164
 
134
- Override any field (`worktree_path`, `base_branch`, `setup_commands`, `teardown_commands`, `ide`, `ide_open_args`, `agent_command`, `agent_trigger_chord`) for a specific repo:
165
+ Override any field (`worktree_path`, `base_branch`, `setup_commands`, `teardown_commands`, `ide`, `ide_open_args`, `agent_command`, `agent_mode`, `agent_trigger_chord`, `auto_refresh_minutes`) for a specific repo:
135
166
 
136
167
  ```json
137
168
  {
@@ -3,9 +3,9 @@ import {
3
3
  openConfiguredIde,
4
4
  prepareWorktree,
5
5
  promptExistingWorktree
6
- } from "./chunk-QGSJG72F.js";
7
- import "./chunk-GHYUCETL.js";
8
- import "./chunk-FNAMNRUH.js";
6
+ } from "./chunk-OA55NRNT.js";
7
+ import "./chunk-XOP26UY4.js";
8
+ import "./chunk-OUWQ6NIV.js";
9
9
 
10
10
  // src/commands/agent.ts
11
11
  import * as clack from "@clack/prompts";
@@ -424,19 +424,30 @@ var VALID_MODES = [
424
424
  "dontAsk",
425
425
  "bypassPermissions"
426
426
  ];
427
+ var isValidMode = (mode) => VALID_MODES.includes(mode);
427
428
  var CLEANUP_DELAY_MS = 2e4;
428
429
  var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
429
430
  async function createAgentWorktree(branch, planPrompt, options = {}) {
430
- const mode = options.mode ?? "plan";
431
- if (!VALID_MODES.includes(mode)) {
431
+ if (options.mode !== void 0 && !isValidMode(options.mode)) {
432
432
  console.error(
433
- pc.red(`Invalid mode "${mode}". Valid modes: ${VALID_MODES.join(", ")}`)
433
+ pc.red(
434
+ `Invalid mode "${options.mode}". Valid modes: ${VALID_MODES.join(", ")}`
435
+ )
434
436
  );
435
437
  process.exit(1);
436
438
  }
437
439
  const prepared = await prepareWorktree(branch, options);
438
440
  if (!prepared) return;
439
441
  const { status, config, worktreePath } = prepared;
442
+ let mode = options.mode ?? config.agent_mode ?? "default";
443
+ if (!isValidMode(mode)) {
444
+ console.warn(
445
+ pc.yellow(
446
+ `\u26A0 Invalid agent_mode "${mode}" in config; using "default". Valid modes: ${VALID_MODES.join(", ")}`
447
+ )
448
+ );
449
+ mode = "default";
450
+ }
440
451
  if (status === "exists") {
441
452
  const prompt = options.existingWorktreePrompt ?? promptExistingWorktree;
442
453
  const action = await prompt(worktreePath, { allowAgent: true });
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ fetchRemote,
4
+ getRegisteredRepos,
5
+ getRepoRoot,
6
+ isBranchMerged,
7
+ listWorktreeDirtyFiles,
8
+ listWorktrees,
9
+ openIde,
10
+ registerRepo,
11
+ removeWorktree,
12
+ runBranchInput,
13
+ runCommands,
14
+ runInteractiveList,
15
+ runRepoPicker,
16
+ runWizard
17
+ } from "./chunk-XOP26UY4.js";
18
+ import {
19
+ createStore,
20
+ getEffectiveConfig,
21
+ getGlobalConfig
22
+ } from "./chunk-OUWQ6NIV.js";
23
+
24
+ // src/commands/list.ts
25
+ import * as clack from "@clack/prompts";
26
+ import pc from "picocolors";
27
+ function buildWorktreeSteps(repoRoot, store, state) {
28
+ const steps = [];
29
+ if (!repoRoot) {
30
+ const repos = getRegisteredRepos(store);
31
+ steps.push(async () => {
32
+ const picked = await runRepoPicker(repos, state.pickedRepo);
33
+ if (!picked) return false;
34
+ state.pickedRepo = picked;
35
+ return true;
36
+ });
37
+ }
38
+ steps.push(async () => {
39
+ const entered = await runBranchInput(
40
+ state.pickedRepo,
41
+ state.branch ?? ""
42
+ );
43
+ if (!entered) return false;
44
+ state.branch = entered;
45
+ return true;
46
+ });
47
+ return steps;
48
+ }
49
+ async function prepareListItems(options = {}) {
50
+ const { cwd = process.cwd(), store = createStore() } = options;
51
+ let repoRoot = null;
52
+ try {
53
+ repoRoot = getRepoRoot(cwd);
54
+ } catch {
55
+ }
56
+ if (repoRoot) {
57
+ registerRepo(repoRoot, store);
58
+ const items2 = listWorktrees(repoRoot, cwd);
59
+ return { items: items2, mode: "repo", repoRoot };
60
+ }
61
+ const repos = getRegisteredRepos(store);
62
+ const items = repos.flatMap((repo) => {
63
+ try {
64
+ return listWorktrees(repo, cwd);
65
+ } catch {
66
+ return [];
67
+ }
68
+ });
69
+ return { items, mode: "global", repoRoot: null };
70
+ }
71
+ async function runList(options = {}) {
72
+ const { store = createStore(), cwd = process.cwd() } = options;
73
+ const { items, mode, repoRoot } = await prepareListItems({ cwd, store });
74
+ if (items.length === 0 && mode === "global") {
75
+ console.log(
76
+ pc.dim(
77
+ "No repos registered. Run `wt create` inside a repo to get started."
78
+ )
79
+ );
80
+ return;
81
+ }
82
+ const autoRefreshMinutes = mode === "repo" && repoRoot ? getEffectiveConfig(repoRoot, store).auto_refresh_minutes : getGlobalConfig(store).auto_refresh_minutes;
83
+ await runInteractiveList(
84
+ items,
85
+ mode,
86
+ {
87
+ onOpen: (item) => {
88
+ const config = getEffectiveConfig(item.repoRoot, store);
89
+ openIde(config.ide, config.ide_open_args, item.path);
90
+ },
91
+ onDelete: (item) => deleteWorktree(item, store),
92
+ onWipe: (items2) => wipeWorktrees(items2, store, { fetch: true }),
93
+ onCreate: async () => {
94
+ const state = { pickedRepo: repoRoot ?? void 0 };
95
+ const steps = buildWorktreeSteps(repoRoot, store, state);
96
+ if (!await runWizard(steps)) return;
97
+ if (state.pickedRepo === void 0 || state.branch === void 0)
98
+ return;
99
+ const { createWorktree } = await import("./create-K4OQIX7A.js");
100
+ await createWorktree(state.branch, { cwd: state.pickedRepo, store });
101
+ },
102
+ onAgent: async () => {
103
+ const { createAgentWorktree, VALID_MODES } = await import("./agent-QYE5UNA3.js");
104
+ const state = {
105
+ pickedRepo: repoRoot ?? void 0
106
+ };
107
+ const steps = buildWorktreeSteps(repoRoot, store, state);
108
+ steps.push(async () => {
109
+ const entered = await clack.text({
110
+ message: "Plan prompt for the agent:",
111
+ initialValue: state.plan,
112
+ validate: (v) => !v || v.length === 0 ? "Required" : void 0
113
+ });
114
+ if (clack.isCancel(entered)) return false;
115
+ state.plan = entered;
116
+ return true;
117
+ });
118
+ steps.push(async () => {
119
+ const configuredMode = state.pickedRepo ? getEffectiveConfig(state.pickedRepo, store).agent_mode : void 0;
120
+ const chosen = await clack.select({
121
+ message: "Permission mode:",
122
+ initialValue: state.mode ?? configuredMode,
123
+ options: VALID_MODES.map((m) => ({ value: String(m), label: m }))
124
+ });
125
+ if (clack.isCancel(chosen)) return false;
126
+ state.mode = chosen;
127
+ return true;
128
+ });
129
+ if (!await runWizard(steps)) return;
130
+ if (state.pickedRepo === void 0 || state.branch === void 0 || state.plan === void 0)
131
+ return;
132
+ await createAgentWorktree(state.branch, state.plan, {
133
+ cwd: state.pickedRepo,
134
+ store,
135
+ mode: state.mode
136
+ });
137
+ },
138
+ refreshItems: async () => {
139
+ const refreshed = await prepareListItems({ cwd, store });
140
+ return refreshed.items;
141
+ }
142
+ },
143
+ { autoRefreshMinutes }
144
+ );
145
+ }
146
+ async function deleteWorktree(item, store) {
147
+ const confirmed = await clack.confirm({
148
+ message: `Remove worktree ${pc.bold(item.branch)}? This cannot be undone.`
149
+ });
150
+ if (clack.isCancel(confirmed) || !confirmed) return false;
151
+ const config = getEffectiveConfig(item.repoRoot, store);
152
+ if (config.teardown_commands.length > 0) {
153
+ console.log(pc.dim("Running teardown commands..."));
154
+ const result = await runCommands(config.teardown_commands, item.path);
155
+ if (!result.success) {
156
+ clack.log.warn(
157
+ `Teardown command failed: ${result.failedCommand} (exit code ${result.exitCode})`
158
+ );
159
+ const proceed = await clack.confirm({
160
+ message: `Delete ${pc.bold(item.branch)} anyway?`
161
+ });
162
+ if (clack.isCancel(proceed) || !proceed) return false;
163
+ }
164
+ }
165
+ try {
166
+ removeWorktree(item.repoRoot, item.path);
167
+ console.log(pc.green(`\u2713 Removed ${item.branch}`));
168
+ return true;
169
+ } catch (err) {
170
+ const msg = String(err);
171
+ if (msg.includes("cannot be moved or removed")) {
172
+ clack.log.warn(
173
+ "Worktree contains git submodules, which prevent standard removal."
174
+ );
175
+ const force = await clack.confirm({
176
+ message: `Force delete ${pc.bold(item.branch)}? The worktree directory will be removed directly.`
177
+ });
178
+ if (clack.isCancel(force) || !force) return false;
179
+ try {
180
+ removeWorktree(item.repoRoot, item.path, true);
181
+ console.log(pc.green(`\u2713 Force-removed ${item.branch}`));
182
+ return true;
183
+ } catch (err2) {
184
+ console.error(pc.red(`\u2717 Failed to force-remove: ${String(err2)}`));
185
+ return false;
186
+ }
187
+ }
188
+ if (msg.includes("modified or untracked files")) {
189
+ const dirty = listWorktreeDirtyFiles(item.path);
190
+ if (dirty.length > 0) {
191
+ clack.log.warn(
192
+ `Worktree has uncommitted changes:
193
+ ${dirty.map((f) => ` ${f}`).join("\n")}`
194
+ );
195
+ }
196
+ const force = await clack.confirm({
197
+ message: `Force delete ${pc.bold(item.branch)}? All changes will be lost.`
198
+ });
199
+ if (clack.isCancel(force) || !force) return false;
200
+ try {
201
+ removeWorktree(item.repoRoot, item.path, true);
202
+ console.log(pc.green(`\u2713 Force-removed ${item.branch}`));
203
+ return true;
204
+ } catch (err2) {
205
+ console.error(pc.red(`\u2717 Failed to force-remove: ${String(err2)}`));
206
+ return false;
207
+ }
208
+ }
209
+ console.error(pc.red(`\u2717 Failed to remove: ${msg}`));
210
+ return false;
211
+ }
212
+ }
213
+ function selectWipeCandidates(items, isMerged) {
214
+ return items.filter(
215
+ (wt) => !wt.isCurrent && !wt.isMain && wt.branch !== "(detached)" && isMerged(wt)
216
+ );
217
+ }
218
+ function buildMergedPredicate(store) {
219
+ return (wt) => {
220
+ const config = getEffectiveConfig(wt.repoRoot, store);
221
+ const base = config.base_branch;
222
+ const baseLocal = base.split("/", 2).slice(1).join("/") || base;
223
+ if (wt.branch === base || wt.branch === baseLocal) return false;
224
+ return isBranchMerged(wt.repoRoot, wt.branch, base);
225
+ };
226
+ }
227
+ async function wipeWorktrees(items, store, options = {}) {
228
+ if (options.fetch) {
229
+ const seen = /* @__PURE__ */ new Set();
230
+ for (const wt of items) {
231
+ if (seen.has(wt.repoRoot)) continue;
232
+ seen.add(wt.repoRoot);
233
+ const parts = getEffectiveConfig(wt.repoRoot, store).base_branch.split(
234
+ "/",
235
+ 2
236
+ );
237
+ if (parts.length !== 2) continue;
238
+ const remote = parts[0] || "origin";
239
+ try {
240
+ fetchRemote(wt.repoRoot, remote);
241
+ } catch (err) {
242
+ console.warn(
243
+ pc.yellow(
244
+ `\u26A0 Could not fetch from ${remote} \u2014 using local state${err instanceof Error ? ` (${err.message})` : ""}`
245
+ )
246
+ );
247
+ }
248
+ }
249
+ }
250
+ const candidates = selectWipeCandidates(items, buildMergedPredicate(store));
251
+ if (candidates.length === 0) {
252
+ console.log(pc.dim("No merged worktrees to wipe."));
253
+ return [];
254
+ }
255
+ const removed = [];
256
+ for (const candidate of candidates) {
257
+ if (await deleteWorktree(candidate, store)) {
258
+ removed.push(candidate);
259
+ }
260
+ }
261
+ return removed;
262
+ }
263
+
264
+ export {
265
+ prepareListItems,
266
+ runList,
267
+ deleteWorktree,
268
+ selectWipeCandidates,
269
+ buildMergedPredicate,
270
+ wipeWorktrees
271
+ };
@@ -13,11 +13,11 @@ import {
13
13
  runCommands,
14
14
  runRepoPicker,
15
15
  setUpstreamTracking
16
- } from "./chunk-GHYUCETL.js";
16
+ } from "./chunk-XOP26UY4.js";
17
17
  import {
18
18
  createStore,
19
19
  getEffectiveConfig
20
- } from "./chunk-FNAMNRUH.js";
20
+ } from "./chunk-OUWQ6NIV.js";
21
21
 
22
22
  // src/commands/create.ts
23
23
  import { existsSync } from "fs";
@@ -68,8 +68,10 @@ var DEFAULT_CONFIG = {
68
68
  teardown_commands: [],
69
69
  ide: "zed",
70
70
  ide_open_args: ["-n"],
71
- agent_command: "claude --permission-mode plan",
71
+ agent_command: "claude",
72
72
  agent_trigger_chord: "ctrl-shift-cmd-c",
73
+ auto_refresh_minutes: 5,
74
+ agent_mode: "default",
73
75
  repos: [],
74
76
  repo_overrides: {}
75
77
  };