@solaqua/gji 0.7.1 → 0.7.2

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.
@@ -13074,15 +13074,21 @@ async function loadHistory(home = homedir2()) {
13074
13074
  async function appendHistory(path9, branch, home = homedir2()) {
13075
13075
  const historyPath = HISTORY_FILE_PATH(home);
13076
13076
  const existing = await loadHistory(home);
13077
- if (existing.length > 0 && existing[0].path === path9) {
13078
- return;
13079
- }
13080
13077
  const entry = { branch, path: path9, timestamp: Date.now() };
13081
- const next = [entry, ...existing].slice(0, MAX_HISTORY_ENTRIES);
13078
+ const next = [
13079
+ entry,
13080
+ ...existing.filter((existingEntry) => existingEntry.path !== path9)
13081
+ ].slice(0, MAX_HISTORY_ENTRIES);
13082
13082
  await mkdir2(dirname2(historyPath), { recursive: true });
13083
13083
  await writeFile2(historyPath, `${JSON.stringify(next, null, 2)}
13084
13084
  `, "utf8");
13085
13085
  }
13086
+ async function recordWorktreeUsage(path9, branch, home = homedir2()) {
13087
+ try {
13088
+ await appendHistory(path9, branch, home);
13089
+ } catch {
13090
+ }
13091
+ }
13086
13092
  function isHistoryEntry(value) {
13087
13093
  return typeof value === "object" && value !== null && "path" in value && typeof value.path === "string" && "timestamp" in value && typeof value.timestamp === "number";
13088
13094
  }
@@ -13381,13 +13387,6 @@ async function listWorktrees(cwd) {
13381
13387
  };
13382
13388
  });
13383
13389
  }
13384
- function sortByCurrentFirst(worktrees) {
13385
- return [...worktrees].sort((a, b3) => {
13386
- if (a.isCurrent && !b3.isCurrent) return -1;
13387
- if (!a.isCurrent && b3.isCurrent) return 1;
13388
- return 0;
13389
- });
13390
- }
13391
13390
  function findPorcelainValue(block, key) {
13392
13391
  const value = findOptionalPorcelainValue(block, key);
13393
13392
  if (!value) {
@@ -13821,43 +13820,63 @@ var dD = class extends x {
13821
13820
  });
13822
13821
  }
13823
13822
  };
13823
+ var mD = Object.defineProperty;
13824
+ var bD = (e2, u2, t) => u2 in e2 ? mD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
13825
+ var Z = (e2, u2, t) => (bD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
13826
+ var q = (e2, u2, t) => {
13827
+ if (!u2.has(e2)) throw TypeError("Cannot " + t);
13828
+ };
13829
+ var T = (e2, u2, t) => (q(e2, u2, "read from private field"), t ? t.call(e2) : u2.get(e2));
13830
+ var wD = (e2, u2, t) => {
13831
+ if (u2.has(e2)) throw TypeError("Cannot add the same private member more than once");
13832
+ u2 instanceof WeakSet ? u2.add(e2) : u2.set(e2, t);
13833
+ };
13834
+ var yD = (e2, u2, t, F2) => (q(e2, u2, "write to private field"), F2 ? F2.call(e2, t) : u2.set(e2, t), t);
13824
13835
  var A;
13825
- A = /* @__PURE__ */ new WeakMap();
13826
- var kD = Object.defineProperty;
13827
- var $D = (e2, u2, t) => u2 in e2 ? kD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
13828
- var H = (e2, u2, t) => ($D(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
13829
- var SD = class extends x {
13836
+ var _D = class extends x {
13830
13837
  constructor(u2) {
13831
- super(u2, false), H(this, "options"), H(this, "cursor", 0), this.options = u2.options, this.value = [...u2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: t }) => t === u2.cursorAt), 0), this.on("key", (t) => {
13832
- t === "a" && this.toggleAll();
13833
- }), this.on("cursor", (t) => {
13834
- switch (t) {
13838
+ super(u2, false), Z(this, "options"), Z(this, "cursor", 0), wD(this, A, void 0);
13839
+ const { options: t } = u2;
13840
+ yD(this, A, u2.selectableGroups !== false), this.options = Object.entries(t).flatMap(([F2, s]) => [{ value: F2, group: true, label: F2 }, ...s.map((i) => ({ ...i, group: F2 }))]), this.value = [...u2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: F2 }) => F2 === u2.cursorAt), T(this, A) ? 0 : 1), this.on("cursor", (F2) => {
13841
+ switch (F2) {
13835
13842
  case "left":
13836
- case "up":
13843
+ case "up": {
13837
13844
  this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
13845
+ const s = this.options[this.cursor]?.group === true;
13846
+ !T(this, A) && s && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
13838
13847
  break;
13848
+ }
13839
13849
  case "down":
13840
- case "right":
13850
+ case "right": {
13841
13851
  this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
13852
+ const s = this.options[this.cursor]?.group === true;
13853
+ !T(this, A) && s && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
13842
13854
  break;
13855
+ }
13843
13856
  case "space":
13844
13857
  this.toggleValue();
13845
13858
  break;
13846
13859
  }
13847
13860
  });
13848
13861
  }
13849
- get _value() {
13850
- return this.options[this.cursor].value;
13862
+ getGroupItems(u2) {
13863
+ return this.options.filter((t) => t.group === u2);
13851
13864
  }
13852
- toggleAll() {
13853
- const u2 = this.value.length === this.options.length;
13854
- this.value = u2 ? [] : this.options.map((t) => t.value);
13865
+ isGroupSelected(u2) {
13866
+ return this.getGroupItems(u2).every((t) => this.value.includes(t.value));
13855
13867
  }
13856
13868
  toggleValue() {
13857
- const u2 = this.value.includes(this._value);
13858
- this.value = u2 ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
13869
+ const u2 = this.options[this.cursor];
13870
+ if (u2.group === true) {
13871
+ const t = u2.value, F2 = this.getGroupItems(t);
13872
+ this.isGroupSelected(t) ? this.value = this.value.filter((s) => F2.findIndex((i) => i.value === s) === -1) : this.value = [...this.value, ...F2.map((s) => s.value)], this.value = Array.from(new Set(this.value));
13873
+ } else {
13874
+ const t = this.value.includes(u2.value);
13875
+ this.value = t ? this.value.filter((F2) => F2 !== u2.value) : [...this.value, u2.value];
13876
+ }
13859
13877
  }
13860
13878
  };
13879
+ A = /* @__PURE__ */ new WeakMap();
13861
13880
  var OD = Object.defineProperty;
13862
13881
  var PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
13863
13882
  var J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
@@ -13920,14 +13939,14 @@ var d2 = u("\u2514", "\u2014");
13920
13939
  var k2 = u("\u25CF", ">");
13921
13940
  var P2 = u("\u25CB", " ");
13922
13941
  var A2 = u("\u25FB", "[\u2022]");
13923
- var T = u("\u25FC", "[+]");
13942
+ var T2 = u("\u25FC", "[+]");
13924
13943
  var F = u("\u25FB", "[ ]");
13925
13944
  var $e = u("\u25AA", "\u2022");
13926
13945
  var _2 = u("\u2500", "-");
13927
13946
  var me = u("\u256E", "+");
13928
13947
  var de = u("\u251C", "+");
13929
13948
  var pe = u("\u256F", "+");
13930
- var q = u("\u25CF", "\u2022");
13949
+ var q2 = u("\u25CF", "\u2022");
13931
13950
  var D = u("\u25C6", "*");
13932
13951
  var U = u("\u25B2", "!");
13933
13952
  var K2 = u("\u25A0", "x");
@@ -14026,40 +14045,54 @@ ${import_picocolors2.default.cyan(d2)}
14026
14045
  }
14027
14046
  } }).prompt();
14028
14047
  };
14029
- var fe = (t) => {
14030
- const n = (r2, i) => {
14031
- const s = r2.label ?? String(r2.value);
14032
- return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
14048
+ var be = (t) => {
14049
+ const { selectableGroups: n = true } = t, r2 = (i, s, c = []) => {
14050
+ const a = i.label ?? String(i.value), l2 = typeof i.group == "string", $2 = l2 && (c[c.indexOf(i) + 1] ?? { group: true }), g = l2 && $2.group === true, p2 = l2 ? n ? `${g ? d2 : o} ` : " " : "";
14051
+ if (s === "active") return `${import_picocolors2.default.dim(p2)}${import_picocolors2.default.cyan(A2)} ${a} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
14052
+ if (s === "group-active") return `${p2}${import_picocolors2.default.cyan(A2)} ${import_picocolors2.default.dim(a)}`;
14053
+ if (s === "group-active-selected") return `${p2}${import_picocolors2.default.green(T2)} ${import_picocolors2.default.dim(a)}`;
14054
+ if (s === "selected") {
14055
+ const f = l2 || n ? import_picocolors2.default.green(T2) : "";
14056
+ return `${import_picocolors2.default.dim(p2)}${f} ${import_picocolors2.default.dim(a)} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
14057
+ }
14058
+ if (s === "cancelled") return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(a))}`;
14059
+ if (s === "active-selected") return `${import_picocolors2.default.dim(p2)}${import_picocolors2.default.green(T2)} ${a} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
14060
+ if (s === "submitted") return `${import_picocolors2.default.dim(a)}`;
14061
+ const v2 = l2 || n ? import_picocolors2.default.dim(F) : "";
14062
+ return `${import_picocolors2.default.dim(p2)}${v2} ${import_picocolors2.default.dim(a)}`;
14033
14063
  };
14034
- return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, validate(r2) {
14035
- if (this.required && r2.length === 0) return `Please select at least one option.
14064
+ return new _D({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, selectableGroups: n, validate(i) {
14065
+ if (this.required && i.length === 0) return `Please select at least one option.
14036
14066
  ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
14037
14067
  }, render() {
14038
- const r2 = `${import_picocolors2.default.gray(o)}
14068
+ const i = `${import_picocolors2.default.gray(o)}
14039
14069
  ${b2(this.state)} ${t.message}
14040
- `, i = (s, c) => {
14041
- const a = this.value.includes(s.value);
14042
- return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
14043
- };
14070
+ `;
14044
14071
  switch (this.state) {
14045
14072
  case "submit":
14046
- return `${r2}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
14073
+ return `${i}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => r2(s, "submitted")).join(import_picocolors2.default.dim(", "))}`;
14047
14074
  case "cancel": {
14048
- const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
14049
- return `${r2}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
14075
+ const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => r2(c, "cancelled")).join(import_picocolors2.default.dim(", "));
14076
+ return `${i}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
14050
14077
  ${import_picocolors2.default.gray(o)}` : ""}`;
14051
14078
  }
14052
14079
  case "error": {
14053
14080
  const s = this.error.split(`
14054
14081
  `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
14055
14082
  `);
14056
- return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
14083
+ return `${i}${import_picocolors2.default.yellow(o)} ${this.options.map((c, a, l2) => {
14084
+ const $2 = this.value.includes(c.value) || c.group === true && this.isGroupSelected(`${c.value}`), g = a === this.cursor;
14085
+ return !g && typeof c.group == "string" && this.options[this.cursor].value === c.group ? r2(c, $2 ? "group-active-selected" : "group-active", l2) : g && $2 ? r2(c, "active-selected", l2) : $2 ? r2(c, "selected", l2) : r2(c, g ? "active" : "inactive", l2);
14086
+ }).join(`
14057
14087
  ${import_picocolors2.default.yellow(o)} `)}
14058
14088
  ${s}
14059
14089
  `;
14060
14090
  }
14061
14091
  default:
14062
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
14092
+ return `${i}${import_picocolors2.default.cyan(o)} ${this.options.map((s, c, a) => {
14093
+ const l2 = this.value.includes(s.value) || s.group === true && this.isGroupSelected(`${s.value}`), $2 = c === this.cursor;
14094
+ return !$2 && typeof s.group == "string" && this.options[this.cursor].value === s.group ? r2(s, l2 ? "group-active-selected" : "group-active", a) : $2 && l2 ? r2(s, "active-selected", a) : l2 ? r2(s, "selected", a) : r2(s, $2 ? "active" : "inactive", a);
14095
+ }).join(`
14063
14096
  ${import_picocolors2.default.cyan(o)} `)}
14064
14097
  ${import_picocolors2.default.cyan(d2)}
14065
14098
  `;
@@ -14084,8 +14117,29 @@ function isHeadless() {
14084
14117
  }
14085
14118
 
14086
14119
  // src/worktree-info.ts
14120
+ var MAX_WORKTREE_INFO_READ_CONCURRENCY = 8;
14087
14121
  async function readWorktreeInfos(worktrees) {
14088
- return Promise.all(worktrees.map((worktree) => readWorktreeInfo(worktree)));
14122
+ return mapWithConcurrency(
14123
+ worktrees,
14124
+ MAX_WORKTREE_INFO_READ_CONCURRENCY,
14125
+ readWorktreeInfo
14126
+ );
14127
+ }
14128
+ async function mapWithConcurrency(items, limit, mapper) {
14129
+ const results = new Array(items.length);
14130
+ let nextIndex = 0;
14131
+ async function readNext() {
14132
+ for (; ; ) {
14133
+ const index = nextIndex;
14134
+ nextIndex += 1;
14135
+ if (index >= items.length) return;
14136
+ results[index] = await mapper(items[index]);
14137
+ }
14138
+ }
14139
+ await Promise.all(
14140
+ Array.from({ length: Math.min(limit, items.length) }, () => readNext())
14141
+ );
14142
+ return results;
14089
14143
  }
14090
14144
  async function readWorktreeInfo(worktree) {
14091
14145
  const [healthResult, lastCommitResult] = await Promise.allSettled([
@@ -14129,16 +14183,6 @@ function serializeWorktreeInfo(info) {
14129
14183
  upstream: info.upstream
14130
14184
  };
14131
14185
  }
14132
- function formatWorktreeHint(info) {
14133
- const details = [
14134
- `status: ${info.status}`,
14135
- `upstream: ${formatUpstreamState(info.upstream)}`
14136
- ];
14137
- if (info.lastCommitTimestamp !== null) {
14138
- details.push(`last: ${formatRelativeAge(info.lastCommitTimestamp)}`);
14139
- }
14140
- return `${info.path} (${details.join(", ")})`;
14141
- }
14142
14186
  function formatUpstreamState(upstream) {
14143
14187
  if (upstream.kind === "detached") {
14144
14188
  return "n/a";
@@ -14239,6 +14283,225 @@ function hasStderr(error) {
14239
14283
  return error instanceof Error && "stderr" in error && typeof error.stderr === "string";
14240
14284
  }
14241
14285
 
14286
+ // src/worktree-picker.ts
14287
+ async function buildWorktreePromptEntries(sources) {
14288
+ const [history, infos] = await Promise.all([
14289
+ loadHistory(),
14290
+ readWorktreeInfos(sources.map((source) => source.worktree))
14291
+ ]);
14292
+ const historyByPath = new Map(history.map((entry) => [entry.path, entry]));
14293
+ const entries = sources.map(
14294
+ (source, index) => buildWorktreePromptEntry(
14295
+ source,
14296
+ infos[index],
14297
+ historyByPath.get(source.worktree.path)?.timestamp ?? null,
14298
+ Date.now()
14299
+ )
14300
+ );
14301
+ return entries.sort(comparePromptEntries).map(
14302
+ ({ lastActivityTimestamp: _lastActivityTimestamp, ...entry }) => entry
14303
+ );
14304
+ }
14305
+ function resolveWorktreeQuery(sources, query) {
14306
+ const normalizedQuery = normalizeQuery(query);
14307
+ if (normalizedQuery === null) return null;
14308
+ const matches = findWorktreePromptSourceMatches(sources, normalizedQuery);
14309
+ if (isAmbiguousRepoOnlyQuery(matches, normalizedQuery)) return null;
14310
+ return matches[0]?.source ?? null;
14311
+ }
14312
+ function findWorktreePromptSourceMatches(sources, normalizedQuery) {
14313
+ return sources.flatMap((source) => {
14314
+ const matchScore = scoreWorktreeMatch(
14315
+ {
14316
+ ...source.worktree,
14317
+ repoName: source.repoName
14318
+ },
14319
+ normalizedQuery
14320
+ );
14321
+ return matchScore === null ? [] : [{ matchScore, source }];
14322
+ }).sort(compareQueryMatches);
14323
+ }
14324
+ function isAmbiguousRepoOnlyQuery(matches, query) {
14325
+ if (matches[0]?.matchScore === 1e3) return false;
14326
+ return matches.filter((match) => match.source.repoName.toLowerCase() === query).length > 1;
14327
+ }
14328
+ async function promptForSingleWorktree(message, worktrees) {
14329
+ const choice = await ve({
14330
+ message,
14331
+ options: worktrees.map((worktree) => ({
14332
+ label: worktree.label,
14333
+ value: worktree.path
14334
+ })),
14335
+ maxItems: 12
14336
+ });
14337
+ return pD(choice) ? null : choice;
14338
+ }
14339
+ async function promptForMultipleWorktrees(message, worktrees) {
14340
+ const choice = await be({
14341
+ message,
14342
+ options: groupPromptEntries(worktrees),
14343
+ required: true,
14344
+ selectableGroups: false
14345
+ });
14346
+ return pD(choice) ? null : choice;
14347
+ }
14348
+ function compareQueryMatches(a, b3) {
14349
+ if (a.matchScore !== b3.matchScore) {
14350
+ return b3.matchScore - a.matchScore;
14351
+ }
14352
+ if (a.source.worktree.isCurrent && !b3.source.worktree.isCurrent) return -1;
14353
+ if (!a.source.worktree.isCurrent && b3.source.worktree.isCurrent) return 1;
14354
+ return a.source.repoName.localeCompare(b3.source.repoName) || (a.source.worktree.branch ?? "").localeCompare(
14355
+ b3.source.worktree.branch ?? ""
14356
+ ) || a.source.worktree.path.localeCompare(b3.source.worktree.path);
14357
+ }
14358
+ function groupPromptEntries(worktrees) {
14359
+ const groups = {};
14360
+ for (const worktree of worktrees) {
14361
+ const group = worktree.group === "recent" ? "Recent worktrees" : "Other worktrees";
14362
+ groups[group] ??= [];
14363
+ groups[group].push({
14364
+ label: worktree.label,
14365
+ value: worktree.path
14366
+ });
14367
+ }
14368
+ return groups;
14369
+ }
14370
+ function buildWorktreePromptEntry(source, info, lastUsedTimestamp, now) {
14371
+ const lastWorkedTimestamp = info.lastCommitTimestamp === null ? null : info.lastCommitTimestamp * 1e3;
14372
+ const lastActivityTimestamp = lastUsedTimestamp ?? lastWorkedTimestamp;
14373
+ const lastActivityType = lastUsedTimestamp !== null ? "used" : lastWorkedTimestamp !== null ? "worked" : null;
14374
+ const branch = source.worktree.branch ?? "(detached)";
14375
+ const badges = buildStatusBadges(info);
14376
+ const recency = formatPromptRecency(
14377
+ lastActivityTimestamp,
14378
+ lastActivityType,
14379
+ now
14380
+ );
14381
+ const status = badges.length > 0 ? badges.map((badge) => `[${badge}]`).join(" ") : null;
14382
+ const path9 = middleEllipsize(source.worktree.path, 76);
14383
+ const label = [
14384
+ middleEllipsize(source.repoName, 22),
14385
+ middleEllipsize(branch, 34),
14386
+ status,
14387
+ recency,
14388
+ path9
14389
+ ].filter((part) => part !== null && part.length > 0).join(" \xB7 ");
14390
+ return {
14391
+ ...source.worktree,
14392
+ group: lastUsedTimestamp !== null ? "recent" : "other",
14393
+ label,
14394
+ lastActivityTimestamp,
14395
+ repoName: source.repoName
14396
+ };
14397
+ }
14398
+ function buildStatusBadges(info) {
14399
+ const badges = [];
14400
+ if (info.isCurrent) {
14401
+ badges.push("current");
14402
+ }
14403
+ if (info.branch === null) {
14404
+ badges.push("detached");
14405
+ }
14406
+ if (info.status === "dirty") {
14407
+ badges.push("dirty");
14408
+ }
14409
+ if (info.upstream.kind === "stale") {
14410
+ badges.push("stale", "gone");
14411
+ }
14412
+ if (isUpToDate(info.upstream)) {
14413
+ badges.push("up to date");
14414
+ }
14415
+ return badges;
14416
+ }
14417
+ function isUpToDate(upstream) {
14418
+ return upstream.kind === "tracked" && upstream.ahead === 0 && upstream.behind === 0;
14419
+ }
14420
+ function formatPromptRecency(timestamp, type, now) {
14421
+ if (timestamp === null || type === null) {
14422
+ return "last used: never";
14423
+ }
14424
+ const label = type === "used" ? "last used" : "last worked";
14425
+ return `${label}: ${formatPickerAge(timestamp, now)}`;
14426
+ }
14427
+ function formatPickerAge(timestamp, now) {
14428
+ const ageSeconds = Math.max(0, Math.floor((now - timestamp) / 1e3));
14429
+ if (ageSeconds < 60) {
14430
+ return "now";
14431
+ }
14432
+ if (ageSeconds < 60 * 60) {
14433
+ return `${Math.floor(ageSeconds / 60)}m ago`;
14434
+ }
14435
+ if (ageSeconds < 24 * 60 * 60) {
14436
+ return `${Math.floor(ageSeconds / (60 * 60))}h ago`;
14437
+ }
14438
+ if (isYesterday(timestamp, now)) {
14439
+ return "yesterday";
14440
+ }
14441
+ return new Intl.DateTimeFormat("en-US", {
14442
+ day: "numeric",
14443
+ month: "short"
14444
+ }).format(new Date(timestamp));
14445
+ }
14446
+ function isYesterday(timestamp, now) {
14447
+ const date = new Date(timestamp);
14448
+ const yesterday = new Date(now);
14449
+ yesterday.setDate(yesterday.getDate() - 1);
14450
+ return date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === yesterday.getDate();
14451
+ }
14452
+ function buildSearchText(repoName, worktree) {
14453
+ return [
14454
+ repoName,
14455
+ worktree.branch ?? "detached",
14456
+ worktree.path,
14457
+ `${repoName}/${worktree.branch ?? "detached"}`
14458
+ ].join(" ").toLowerCase();
14459
+ }
14460
+ function normalizeQuery(query) {
14461
+ const normalized = query?.trim().toLowerCase();
14462
+ return normalized && normalized.length > 0 ? normalized : null;
14463
+ }
14464
+ function comparePromptEntries(a, b3) {
14465
+ if (a.isCurrent && !b3.isCurrent) return -1;
14466
+ if (!a.isCurrent && b3.isCurrent) return 1;
14467
+ if (a.group !== b3.group) {
14468
+ return groupRank(a.group) - groupRank(b3.group);
14469
+ }
14470
+ const aRecent = a.lastActivityTimestamp ?? 0;
14471
+ const bRecent = b3.lastActivityTimestamp ?? 0;
14472
+ if (aRecent !== bRecent) {
14473
+ return bRecent - aRecent;
14474
+ }
14475
+ return a.repoName.localeCompare(b3.repoName) || (a.branch ?? "").localeCompare(b3.branch ?? "") || a.path.localeCompare(b3.path);
14476
+ }
14477
+ function groupRank(group) {
14478
+ return group === "recent" ? 0 : 1;
14479
+ }
14480
+ function scoreWorktreeMatch(entry, query) {
14481
+ const branch = entry.branch ?? "detached";
14482
+ const exactCandidates = [
14483
+ branch,
14484
+ entry.path,
14485
+ `${entry.repoName}/${branch}`
14486
+ ].map((candidate) => candidate.toLowerCase());
14487
+ if (exactCandidates.includes(query)) {
14488
+ return 1e3;
14489
+ }
14490
+ return buildSearchText(entry.repoName, entry).includes(query) ? 1 : null;
14491
+ }
14492
+ function middleEllipsize(value, maxLength) {
14493
+ if (value.length <= maxLength) {
14494
+ return value;
14495
+ }
14496
+ if (maxLength <= 1) {
14497
+ return "\u2026";
14498
+ }
14499
+ const keep = maxLength - 1;
14500
+ const start = Math.ceil(keep / 2);
14501
+ const end = Math.floor(keep / 2);
14502
+ return `${value.slice(0, start)}\u2026${value.slice(value.length - end)}`;
14503
+ }
14504
+
14242
14505
  // src/worktree-prompts.ts
14243
14506
  async function defaultConfirmForceRemoveWorktree(worktreePath) {
14244
14507
  const choice = await ye({
@@ -14299,7 +14562,14 @@ function createCleanCommand(dependencies = {}) {
14299
14562
  return 1;
14300
14563
  }
14301
14564
  const shouldSelectAll = options.force || options.dryRun && (options.stale || options.json || isHeadless());
14302
- const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(cleanupCandidates);
14565
+ const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(
14566
+ await buildWorktreePromptEntries(
14567
+ cleanupCandidates.map((worktree) => ({
14568
+ repoName: repository.repoName,
14569
+ worktree
14570
+ }))
14571
+ )
14572
+ );
14303
14573
  if (!selections || selections.length === 0) {
14304
14574
  options.stderr("Aborted\n");
14305
14575
  return 1;
@@ -14534,19 +14804,7 @@ function toMessage(error) {
14534
14804
  return error instanceof Error ? error.message : String(error);
14535
14805
  }
14536
14806
  async function defaultPromptForWorktrees(worktrees) {
14537
- const infos = await readWorktreeInfos(worktrees);
14538
- const choice = await fe({
14539
- message: "Choose worktrees to clean",
14540
- options: worktrees.map((worktree, i) => {
14541
- return {
14542
- hint: formatWorktreeHint(infos[i]),
14543
- label: worktree.branch ?? "(detached)",
14544
- value: worktree.path
14545
- };
14546
- }),
14547
- required: true
14548
- });
14549
- return pD(choice) ? null : choice;
14807
+ return promptForMultipleWorktrees("Choose worktrees to clean", worktrees);
14550
14808
  }
14551
14809
  async function defaultConfirmRemoval(worktrees) {
14552
14810
  const branchCount = worktrees.filter(
@@ -15567,7 +15825,7 @@ function createNewCommand(dependencies = {}) {
15567
15825
  } else {
15568
15826
  const choice = await prompt(worktreePath);
15569
15827
  if (choice === "reuse") {
15570
- appendHistory(worktreePath, worktreeName).catch(() => void 0);
15828
+ await recordWorktreeUsage(worktreePath, worktreeName);
15571
15829
  await writeOutput(worktreePath, options.stdout);
15572
15830
  return 0;
15573
15831
  }
@@ -15635,7 +15893,7 @@ function createNewCommand(dependencies = {}) {
15635
15893
  `
15636
15894
  );
15637
15895
  } else {
15638
- await appendHistory(worktreePath, worktreeName);
15896
+ await recordWorktreeUsage(worktreePath, worktreeName);
15639
15897
  await writeOutput(worktreePath, options.stdout);
15640
15898
  }
15641
15899
  if (options.open) {
@@ -15672,7 +15930,32 @@ function generateBranchPlaceholder(random = Math.random) {
15672
15930
  "newton",
15673
15931
  "lovelace",
15674
15932
  "nietzsche",
15675
- "kafka"
15933
+ "kafka",
15934
+ "sappho",
15935
+ "aristotle",
15936
+ "pythagoras",
15937
+ "artemis",
15938
+ "apollo",
15939
+ "minerva",
15940
+ "persephone",
15941
+ "icarus",
15942
+ "odysseus",
15943
+ "murasaki",
15944
+ "shakespeare",
15945
+ "frida",
15946
+ "davinci",
15947
+ "kepler",
15948
+ "copernicus",
15949
+ "faraday",
15950
+ "noether",
15951
+ "hopper",
15952
+ "boole",
15953
+ "shannon",
15954
+ "gauss",
15955
+ "ramanujan",
15956
+ "austen",
15957
+ "borges",
15958
+ "zeno"
15676
15959
  ];
15677
15960
  const antics = [
15678
15961
  "borrowed-a-bike",
@@ -15689,9 +15972,50 @@ function generateBranchPlaceholder(random = Math.random) {
15689
15972
  "watered-the-plants",
15690
15973
  "washed-the-dishes",
15691
15974
  "folded-the-laundry",
15692
- "took-a-nap"
15975
+ "took-a-nap",
15976
+ "lost-a-sock",
15977
+ "patched-the-boat",
15978
+ "alphabetized-the-spoons",
15979
+ "argued-with-the-calendar",
15980
+ "misplaced-the-moon",
15981
+ "painted-the-fence",
15982
+ "overcooked-the-rice",
15983
+ "packed-the-snacks",
15984
+ "dropped-the-spoon",
15985
+ "hid-the-remote",
15986
+ "untangled-the-cables",
15987
+ "rebooted-the-kettle",
15988
+ "indexed-the-attic",
15989
+ "forgot-the-password",
15990
+ "sorted-the-buttons",
15991
+ "mopped-the-ceiling",
15992
+ "polished-the-doorknob",
15993
+ "misread-the-map",
15994
+ "reheated-the-tea",
15995
+ "fixed-the-squeak",
15996
+ "labeled-the-drawer",
15997
+ "stacked-the-chairs",
15998
+ "overslept-the-standup",
15999
+ "claimed-the-last-bagel",
16000
+ "debugged-the-toaster"
15693
16001
  ];
15694
- return `${pickRandom(roots, random)}-${pickRandom(antics, random)}`;
16002
+ const root = pickRandom(roots, random);
16003
+ const antic = pickRandom(antics, random);
16004
+ const suffix = generateBranchPlaceholderSuffix(random);
16005
+ return `${root}-${antic}-${suffix}`;
16006
+ }
16007
+ function pickRandom(values, random) {
16008
+ const index = Math.floor(random() * values.length);
16009
+ return values[Math.min(index, values.length - 1)];
16010
+ }
16011
+ function generateBranchPlaceholderSuffix(random) {
16012
+ const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
16013
+ let suffix = "";
16014
+ for (let index = 0; index < 3; index += 1) {
16015
+ const characterIndex = Math.floor(random() * characters.length);
16016
+ suffix += characters[Math.min(characterIndex, characters.length - 1)];
16017
+ }
16018
+ return suffix;
15695
16019
  }
15696
16020
  function applyConfiguredBranchPrefix(branch, branchPrefix) {
15697
16021
  if (typeof branchPrefix !== "string" || branchPrefix.length === 0) {
@@ -15732,10 +16056,6 @@ async function defaultPromptForBranch(placeholder) {
15732
16056
  }
15733
16057
  return choice.trim();
15734
16058
  }
15735
- function pickRandom(values, random) {
15736
- const index = Math.floor(random() * values.length);
15737
- return values[Math.min(index, values.length - 1)];
15738
- }
15739
16059
  async function localBranchExists(repoRoot, branchName) {
15740
16060
  try {
15741
16061
  await execFileAsync3(
@@ -15892,7 +16212,7 @@ async function runWarpNavigate(options) {
15892
16212
  );
15893
16213
  return 0;
15894
16214
  }
15895
- appendHistory(target.path, target.branch).catch(() => void 0);
16215
+ await recordWorktreeUsage(target.path, target.branch);
15896
16216
  await writeShellOutput(WARP_OUTPUT_FILE_ENV, target.path, options.stdout);
15897
16217
  return 0;
15898
16218
  }
@@ -15975,18 +16295,6 @@ async function canonicalizeRepoPath2(repoPath) {
15975
16295
  return resolve5(repoPath);
15976
16296
  }
15977
16297
  }
15978
- function findByQuery(items, query) {
15979
- const slashIdx = query.indexOf("/");
15980
- if (slashIdx !== -1) {
15981
- const repoQuery = query.slice(0, slashIdx);
15982
- const branchQuery = query.slice(slashIdx + 1);
15983
- const match = items.find(
15984
- (item) => item.repoName === repoQuery && item.worktree.branch === branchQuery
15985
- );
15986
- if (match) return match;
15987
- }
15988
- return items.find((item) => item.worktree.branch === query) ?? null;
15989
- }
15990
16298
  async function resolveWarpTarget(options) {
15991
16299
  const cmd = options.commandName ?? "gji";
15992
16300
  const emitError4 = (message, hint) => {
@@ -16000,6 +16308,7 @@ async function resolveWarpTarget(options) {
16000
16308
  }
16001
16309
  };
16002
16310
  const registry = await loadRegistry();
16311
+ const currentRoot = await detectRepository(options.cwd).then((repository) => repository.currentRoot).catch(() => null);
16003
16312
  if (registry.length === 0) {
16004
16313
  emitError4(
16005
16314
  "not in a git repository and no repos registered yet.",
@@ -16018,57 +16327,42 @@ async function resolveWarpTarget(options) {
16018
16327
  if (result.status === "rejected") continue;
16019
16328
  const { repoName, worktrees } = result.value;
16020
16329
  for (const worktree of worktrees) {
16021
- allItems.push({ repoName, worktree });
16330
+ allItems.push({
16331
+ repoName,
16332
+ worktree: {
16333
+ ...worktree,
16334
+ isCurrent: currentRoot !== null && worktree.path === currentRoot
16335
+ }
16336
+ });
16022
16337
  }
16023
16338
  }
16024
16339
  if (allItems.length === 0) {
16025
16340
  emitError4("no accessible worktrees found in any registered repo.");
16026
16341
  return null;
16027
16342
  }
16343
+ const promptSources = allItems.map((item) => ({
16344
+ repoName: item.repoName,
16345
+ worktree: item.worktree
16346
+ }));
16028
16347
  if (options.branch) {
16029
- const match = findByQuery(allItems, options.branch);
16348
+ const match = resolveWorktreeQuery(promptSources, options.branch);
16030
16349
  if (!match) {
16031
16350
  emitError4(`no worktree found matching: ${options.branch}`);
16032
16351
  return null;
16033
16352
  }
16034
16353
  return { branch: match.worktree.branch, path: match.worktree.path };
16035
16354
  }
16036
- const path9 = await promptForWarpTarget(allItems);
16355
+ const promptEntries = await buildWorktreePromptEntries(promptSources);
16356
+ const path9 = await promptForWarpTarget(promptEntries);
16037
16357
  if (!path9) {
16038
16358
  options.stderr("Aborted\n");
16039
16359
  return null;
16040
16360
  }
16041
- const chosen = allItems.find((item) => item.worktree.path === path9);
16042
- return { branch: chosen?.worktree.branch ?? null, path: path9 };
16361
+ const chosen = promptEntries.find((item) => item.path === path9);
16362
+ return { branch: chosen?.branch ?? null, path: path9 };
16043
16363
  }
16044
16364
  async function promptForWarpTarget(items) {
16045
- const healthResults = await Promise.allSettled(
16046
- items.map((item) => readWorktreeHealth(item.worktree.path))
16047
- );
16048
- const choice = await ve({
16049
- message: "Warp to a worktree",
16050
- options: items.map((item, i) => {
16051
- const health = healthResults[i].status === "fulfilled" ? healthResults[i].value : null;
16052
- const upstream = health ? formatHint(item.worktree.branch, health) : null;
16053
- const label = `${item.repoName} \u203A ${item.worktree.branch ?? "(detached)"}`;
16054
- const pathHint = item.worktree.isCurrent ? `${item.worktree.path} (current)` : item.worktree.path;
16055
- const hint = upstream ? `${upstream} \xB7 ${pathHint}` : pathHint;
16056
- return { hint, label, value: item.worktree.path };
16057
- })
16058
- });
16059
- if (pD(choice)) {
16060
- return null;
16061
- }
16062
- return choice;
16063
- }
16064
- function formatHint(branch, health) {
16065
- if (branch === null) return null;
16066
- if (!health.hasUpstream) return "no upstream";
16067
- if (health.upstreamGone) return "upstream gone";
16068
- if (health.ahead === 0 && health.behind === 0) return "up to date";
16069
- if (health.ahead === 0) return `behind ${health.behind}`;
16070
- if (health.behind === 0) return `ahead ${health.ahead}`;
16071
- return `ahead ${health.ahead}, behind ${health.behind}`;
16365
+ return promptForSingleWorktree("Warp to a worktree", items);
16072
16366
  }
16073
16367
 
16074
16368
  // src/go.ts
@@ -16095,7 +16389,7 @@ function createGoCommand(dependencies = {}) {
16095
16389
  commandName: "gji go"
16096
16390
  });
16097
16391
  if (!target) return 1;
16098
- appendHistory(target.path, target.branch).catch(() => void 0);
16392
+ await recordWorktreeUsage(target.path, target.branch);
16099
16393
  await writeShellOutput(GO_OUTPUT_FILE_ENV, target.path, options.stdout);
16100
16394
  return 0;
16101
16395
  }
@@ -16105,8 +16399,14 @@ function createGoCommand(dependencies = {}) {
16105
16399
  );
16106
16400
  return 1;
16107
16401
  }
16108
- const prompted = options.branch ? null : await prompt(sortByCurrentFirst(worktrees));
16109
- const resolvedPath = options.branch ? worktrees.find((entry) => entry.branch === options.branch)?.path : prompted ?? void 0;
16402
+ const promptSources = worktrees.map((worktree) => ({
16403
+ repoName: repository.repoName,
16404
+ worktree
16405
+ }));
16406
+ const promptEntries = options.branch ? [] : await buildWorktreePromptEntries(promptSources);
16407
+ const queried = options.branch ? resolveWorktreeQuery(promptSources, options.branch) : null;
16408
+ const prompted = options.branch ? null : await prompt(promptEntries);
16409
+ const resolvedPath = options.branch ? queried?.worktree.path : prompted ?? void 0;
16110
16410
  if (!resolvedPath) {
16111
16411
  if (options.branch) {
16112
16412
  options.stderr(`No worktree found for branch: ${options.branch}
@@ -16135,44 +16435,14 @@ function createGoCommand(dependencies = {}) {
16135
16435
  },
16136
16436
  options.stderr
16137
16437
  );
16138
- appendHistory(resolvedPath, chosenWorktree?.branch ?? null).catch(
16139
- () => void 0
16140
- );
16438
+ await recordWorktreeUsage(resolvedPath, chosenWorktree?.branch ?? null);
16141
16439
  await writeShellOutput(GO_OUTPUT_FILE_ENV, resolvedPath, options.stdout);
16142
16440
  return 0;
16143
16441
  };
16144
16442
  }
16145
16443
  var runGoCommand = createGoCommand();
16146
16444
  async function promptForWorktree(worktrees) {
16147
- const healthResults = await Promise.allSettled(
16148
- worktrees.map((w2) => readWorktreeHealth(w2.path))
16149
- );
16150
- const choice = await ve({
16151
- message: "Choose a worktree",
16152
- options: worktrees.map((worktree, i) => {
16153
- const health = healthResults[i].status === "fulfilled" ? healthResults[i].value : null;
16154
- const pathHint = worktree.isCurrent ? `${worktree.path} (current)` : worktree.path;
16155
- const upstream = health ? formatUpstreamHint(worktree.branch, health) : null;
16156
- return {
16157
- value: worktree.path,
16158
- label: worktree.branch ?? "(detached)",
16159
- hint: upstream ? `${upstream} \xB7 ${pathHint}` : pathHint
16160
- };
16161
- })
16162
- });
16163
- if (pD(choice)) {
16164
- return null;
16165
- }
16166
- return choice;
16167
- }
16168
- function formatUpstreamHint(branch, health) {
16169
- if (branch === null) return null;
16170
- if (!health.hasUpstream) return "no upstream";
16171
- if (health.upstreamGone) return "upstream gone";
16172
- if (health.ahead === 0 && health.behind === 0) return "up to date";
16173
- if (health.ahead === 0) return `behind ${health.behind}`;
16174
- if (health.behind === 0) return `ahead ${health.ahead}`;
16175
- return `ahead ${health.ahead}, behind ${health.behind}`;
16445
+ return promptForSingleWorktree("Choose a worktree", worktrees);
16176
16446
  }
16177
16447
 
16178
16448
  // src/history-command.ts
@@ -16623,27 +16893,43 @@ function createOpenCommand(dependencies = {}) {
16623
16893
  detectRepository(options.cwd)
16624
16894
  ]);
16625
16895
  let targetPath;
16896
+ let targetWorktree;
16626
16897
  if (options.branch) {
16627
- const entry = worktrees.find((w2) => w2.branch === options.branch);
16628
- if (!entry) {
16898
+ const match = resolveWorktreeQuery(
16899
+ worktrees.map((worktree) => ({
16900
+ repoName: repository.repoName,
16901
+ worktree
16902
+ })),
16903
+ options.branch
16904
+ );
16905
+ if (!match) {
16629
16906
  options.stderr(
16630
- `gji open: no worktree found for branch: ${options.branch}
16907
+ `gji open: no worktree found matching: ${options.branch}
16631
16908
  `
16632
16909
  );
16633
16910
  options.stderr(`Hint: Use 'gji ls' to see available worktrees
16634
16911
  `);
16635
16912
  return 1;
16636
16913
  }
16637
- targetPath = entry.path;
16914
+ targetPath = match.worktree.path;
16915
+ targetWorktree = match.worktree;
16638
16916
  } else if (isHeadless()) {
16639
- targetPath = worktrees.find((w2) => w2.isCurrent)?.path ?? options.cwd;
16917
+ targetWorktree = worktrees.find((w2) => w2.isCurrent);
16918
+ targetPath = targetWorktree?.path ?? options.cwd;
16640
16919
  } else {
16641
- const chosen = await promptForWorktree2(sortByCurrentFirst(worktrees));
16920
+ const entries = await buildWorktreePromptEntries(
16921
+ worktrees.map((worktree) => ({
16922
+ repoName: repository.repoName,
16923
+ worktree
16924
+ }))
16925
+ );
16926
+ const chosen = await promptForWorktree2(entries);
16642
16927
  if (!chosen) {
16643
16928
  options.stderr("Aborted\n");
16644
16929
  return 1;
16645
16930
  }
16646
16931
  targetPath = chosen;
16932
+ targetWorktree = worktrees.find((w2) => w2.path === chosen);
16647
16933
  }
16648
16934
  const config = await loadEffectiveConfig(
16649
16935
  repository.repoRoot,
@@ -16708,6 +16994,7 @@ function createOpenCommand(dependencies = {}) {
16708
16994
  return 1;
16709
16995
  }
16710
16996
  const displayName = editorDef?.name ?? editorCli;
16997
+ await recordWorktreeUsage(targetPath, targetWorktree?.branch ?? null);
16711
16998
  options.stdout(`Opened ${targetPath} in ${displayName}
16712
16999
  `);
16713
17000
  return 0;
@@ -16732,16 +17019,7 @@ async function isCommandAvailable(command) {
16732
17019
  }
16733
17020
  }
16734
17021
  async function defaultPromptForWorktree(worktrees) {
16735
- const choice = await ve({
16736
- message: "Choose a worktree to open",
16737
- options: worktrees.map((w2) => ({
16738
- value: w2.path,
16739
- label: w2.branch ?? "(detached)",
16740
- hint: w2.isCurrent ? `${w2.path} (current)` : w2.path
16741
- }))
16742
- });
16743
- if (pD(choice)) return null;
16744
- return choice;
17022
+ return promptForSingleWorktree("Choose a worktree to open", worktrees);
16745
17023
  }
16746
17024
  async function defaultPromptForEditor(editors) {
16747
17025
  const choice = await ve({
@@ -16835,7 +17113,7 @@ function createPrCommand(dependencies = {}) {
16835
17113
  }
16836
17114
  const choice = await prompt(worktreePath);
16837
17115
  if (choice === "reuse") {
16838
- appendHistory(worktreePath, branchName).catch(() => void 0);
17116
+ await recordWorktreeUsage(worktreePath, branchName);
16839
17117
  await writeOutput2(worktreePath, options.stdout);
16840
17118
  return 0;
16841
17119
  }
@@ -16926,7 +17204,7 @@ function createPrCommand(dependencies = {}) {
16926
17204
  `
16927
17205
  );
16928
17206
  } else {
16929
- await appendHistory(worktreePath, branchName);
17207
+ await recordWorktreeUsage(worktreePath, branchName);
16930
17208
  await writeOutput2(worktreePath, options.stdout);
16931
17209
  }
16932
17210
  return 0;
@@ -17026,7 +17304,14 @@ function createRemoveCommand(dependencies = {}) {
17026
17304
  }
17027
17305
  return 1;
17028
17306
  }
17029
- const selection = options.branch ?? await promptForWorktree2(sortByCurrentFirst(linkedWorktrees));
17307
+ const selection = options.branch ?? await promptForWorktree2(
17308
+ await buildWorktreePromptEntries(
17309
+ linkedWorktrees.map((worktree2) => ({
17310
+ repoName: repository.repoName,
17311
+ worktree: worktree2
17312
+ }))
17313
+ )
17314
+ );
17030
17315
  if (!selection) {
17031
17316
  options.stderr("Aborted\n");
17032
17317
  return 1;
@@ -17140,15 +17425,7 @@ function createRemoveCommand(dependencies = {}) {
17140
17425
  }
17141
17426
  var runRemoveCommand = createRemoveCommand();
17142
17427
  async function defaultPromptForWorktree2(worktrees) {
17143
- const choice = await ve({
17144
- message: "Choose a worktree to finish",
17145
- options: worktrees.map((worktree) => ({
17146
- hint: worktree.isCurrent ? `${worktree.path} (current)` : worktree.path,
17147
- label: worktree.branch ?? "(detached)",
17148
- value: worktree.path
17149
- }))
17150
- });
17151
- return pD(choice) ? null : choice;
17428
+ return promptForSingleWorktree("Choose a worktree to finish", worktrees);
17152
17429
  }
17153
17430
  async function defaultConfirmRemoval2(worktree) {
17154
17431
  const choice = await ye({