@solaqua/gji 0.7.1 → 0.8.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.
@@ -3465,7 +3465,7 @@ var require_polyfills = __commonJS({
3465
3465
  var constants2 = __require("constants");
3466
3466
  var origCwd = process.cwd;
3467
3467
  var cwd = null;
3468
- var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
3468
+ var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
3469
3469
  process.cwd = function() {
3470
3470
  if (!cwd)
3471
3471
  cwd = origCwd.call(process);
@@ -3524,7 +3524,7 @@ var require_polyfills = __commonJS({
3524
3524
  fs5.lchownSync = function() {
3525
3525
  };
3526
3526
  }
3527
- if (platform === "win32") {
3527
+ if (platform2 === "win32") {
3528
3528
  fs5.rename = typeof fs5.rename !== "function" ? fs5.rename : (function(fs$rename) {
3529
3529
  function rename(from, to, cb) {
3530
3530
  var start = Date.now();
@@ -5036,11 +5036,11 @@ var require_utils = __commonJS({
5036
5036
  var content = file.apply(null, arguments);
5037
5037
  return content ? parse(content) : null;
5038
5038
  };
5039
- var env4 = exports.env = function(prefix, env5) {
5040
- env5 = env5 || process.env;
5039
+ var env5 = exports.env = function(prefix, env6) {
5040
+ env6 = env6 || process.env;
5041
5041
  var obj = {};
5042
5042
  var l2 = prefix.length;
5043
- for (var k3 in env5) {
5043
+ for (var k3 in env6) {
5044
5044
  if (k3.toLowerCase().indexOf(prefix.toLowerCase()) === 0) {
5045
5045
  var keypath = k3.substring(l2).split("__");
5046
5046
  var _emptyStringIndex;
@@ -5052,7 +5052,7 @@ var require_utils = __commonJS({
5052
5052
  if (!_subkey || typeof cursor !== "object")
5053
5053
  return;
5054
5054
  if (i === keypath.length - 1)
5055
- cursor[_subkey] = env5[k3];
5055
+ cursor[_subkey] = env6[k3];
5056
5056
  if (cursor[_subkey] === void 0)
5057
5057
  cursor[_subkey] = {};
5058
5058
  cursor = cursor[_subkey];
@@ -5398,7 +5398,7 @@ var require_rc = __commonJS({
5398
5398
  argv = require_minimist()(process.argv.slice(2));
5399
5399
  defaults = ("string" === typeof defaults ? cc.json(defaults) : defaults) || {};
5400
5400
  parse = parse || cc.parse;
5401
- var env4 = cc.env(name + "_");
5401
+ var env5 = cc.env(name + "_");
5402
5402
  var configs = [defaults];
5403
5403
  var configFiles = [];
5404
5404
  function addConfigFile(file) {
@@ -5422,10 +5422,10 @@ var require_rc = __commonJS({
5422
5422
  join10(home, "." + name + "rc")
5423
5423
  ].forEach(addConfigFile);
5424
5424
  addConfigFile(cc.find("." + name + "rc"));
5425
- if (env4.config) addConfigFile(env4.config);
5425
+ if (env5.config) addConfigFile(env5.config);
5426
5426
  if (argv.config) addConfigFile(argv.config);
5427
5427
  return deepExtend.apply(null, configs.concat([
5428
- env4,
5428
+ env5,
5429
5429
  argv,
5430
5430
  configFiles.length ? { configs: configFiles, config: configFiles[configFiles.length - 1] } : void 0
5431
5431
  ]));
@@ -5439,7 +5439,7 @@ var require_polyfills2 = __commonJS({
5439
5439
  var constants2 = __require("constants");
5440
5440
  var origCwd = process.cwd;
5441
5441
  var cwd = null;
5442
- var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
5442
+ var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
5443
5443
  process.cwd = function() {
5444
5444
  if (!cwd)
5445
5445
  cwd = origCwd.call(process);
@@ -5498,7 +5498,7 @@ var require_polyfills2 = __commonJS({
5498
5498
  fs5.lchownSync = function() {
5499
5499
  };
5500
5500
  }
5501
- if (platform === "win32") {
5501
+ if (platform2 === "win32") {
5502
5502
  fs5.rename = typeof fs5.rename !== "function" ? fs5.rename : (function(fs$rename) {
5503
5503
  function rename(from, to, cb) {
5504
5504
  var start = Date.now();
@@ -6423,13 +6423,13 @@ var require_config_chain = __commonJS({
6423
6423
  }
6424
6424
  return parse(content, file, "json");
6425
6425
  };
6426
- var env4 = exports.env = function(prefix, env5) {
6427
- env5 = env5 || process.env;
6426
+ var env5 = exports.env = function(prefix, env6) {
6427
+ env6 = env6 || process.env;
6428
6428
  var obj = {};
6429
6429
  var l2 = prefix.length;
6430
- for (var k3 in env5) {
6430
+ for (var k3 in env6) {
6431
6431
  if (k3.indexOf(prefix) === 0)
6432
- obj[k3.substring(l2)] = env5[k3];
6432
+ obj[k3.substring(l2)] = env6[k3];
6433
6433
  }
6434
6434
  return obj;
6435
6435
  };
@@ -6535,10 +6535,10 @@ var require_config_chain = __commonJS({
6535
6535
  }.bind(this));
6536
6536
  return this;
6537
6537
  };
6538
- ConfigChain.prototype.addEnv = function(prefix, env5, name) {
6538
+ ConfigChain.prototype.addEnv = function(prefix, env6, name) {
6539
6539
  name = name || "env";
6540
- var data = exports.env(prefix, env5);
6541
- this.sources[name] = { data, source: env5, prefix };
6540
+ var data = exports.env(prefix, env6);
6541
+ this.sources[name] = { data, source: env6, prefix };
6542
6542
  return this.add(data, name);
6543
6543
  };
6544
6544
  ConfigChain.prototype.addUrl = function(req, type, name) {
@@ -6630,28 +6630,28 @@ var require_env_replace = __commonJS({
6630
6630
  Object.defineProperty(exports, "__esModule", { value: true });
6631
6631
  exports.envReplace = void 0;
6632
6632
  var ENV_EXPR = /(?<!\\)(\\*)\$\{([^${}]+)\}/g;
6633
- function envReplace(settingValue, env4) {
6634
- return settingValue.replace(ENV_EXPR, replaceEnvMatch.bind(null, env4));
6633
+ function envReplace(settingValue, env5) {
6634
+ return settingValue.replace(ENV_EXPR, replaceEnvMatch.bind(null, env5));
6635
6635
  }
6636
6636
  exports.envReplace = envReplace;
6637
- function replaceEnvMatch(env4, orig, escape, name) {
6637
+ function replaceEnvMatch(env5, orig, escape, name) {
6638
6638
  if (escape.length % 2) {
6639
6639
  return orig.slice((escape.length + 1) / 2);
6640
6640
  }
6641
- const envValue = getEnvValue(env4, name);
6641
+ const envValue = getEnvValue(env5, name);
6642
6642
  if (envValue === void 0) {
6643
6643
  throw new Error(`Failed to replace env in config: ${orig}`);
6644
6644
  }
6645
6645
  return `${escape.slice(escape.length / 2)}${envValue}`;
6646
6646
  }
6647
6647
  var ENV_VALUE = /([^:-]+)(:?)-(.+)/;
6648
- function getEnvValue(env4, name) {
6648
+ function getEnvValue(env5, name) {
6649
6649
  const matched = name.match(ENV_VALUE);
6650
6650
  if (!matched)
6651
- return env4[name];
6651
+ return env5[name];
6652
6652
  const [, variableName, colon, fallback] = matched;
6653
- if (Object.prototype.hasOwnProperty.call(env4, variableName)) {
6654
- return !env4[variableName] && colon ? fallback : env4[variableName];
6653
+ if (Object.prototype.hasOwnProperty.call(env5, variableName)) {
6654
+ return !env5[variableName] && colon ? fallback : env5[variableName];
6655
6655
  }
6656
6656
  return fallback;
6657
6657
  }
@@ -6971,15 +6971,15 @@ var require_conf = __commonJS({
6971
6971
  }
6972
6972
  }
6973
6973
  // https://github.com/npm/cli/blob/latest/lib/config/core.js#L341-L357
6974
- addEnv(env4) {
6975
- env4 = env4 || process.env;
6974
+ addEnv(env5) {
6975
+ env5 = env5 || process.env;
6976
6976
  const conf = {};
6977
- Object.keys(env4).filter((x2) => /^npm_config_/i.test(x2)).forEach((x2) => {
6978
- if (!env4[x2]) {
6977
+ Object.keys(env5).filter((x2) => /^npm_config_/i.test(x2)).forEach((x2) => {
6978
+ if (!env5[x2]) {
6979
6979
  return;
6980
6980
  }
6981
6981
  const key = envKeyToSetting(x2.substr(11));
6982
- const rawVal = env4[x2];
6982
+ const rawVal = env5[x2];
6983
6983
  conf[key] = deserializeEnvVal(key, rawVal);
6984
6984
  });
6985
6985
  return super.addEnv("", conf, "env");
@@ -9354,8 +9354,8 @@ var require_picocolors = __commonJS({
9354
9354
  "node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports, module) {
9355
9355
  var p2 = process || {};
9356
9356
  var argv = p2.argv || [];
9357
- var env4 = p2.env || {};
9358
- var isColorSupported = !(!!env4.NO_COLOR || argv.includes("--no-color")) && (!!env4.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env4.TERM !== "dumb" || !!env4.CI);
9357
+ var env5 = p2.env || {};
9358
+ var isColorSupported = !(!!env5.NO_COLOR || argv.includes("--no-color")) && (!!env5.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env5.TERM !== "dumb" || !!env5.CI);
9359
9359
  var formatter = (open, close, replace = open) => (input) => {
9360
9360
  let string = "" + input, index = string.indexOf(close, open.length);
9361
9361
  return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
@@ -10178,7 +10178,7 @@ var Configstore = class {
10178
10178
  var ANSI_BACKGROUND_OFFSET = 10;
10179
10179
  var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
10180
10180
  var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
10181
- var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
10181
+ var wrapAnsi16m = (offset = 0) => (red2, green2, blue) => `\x1B[${38 + offset};2;${red2};${green2};${blue}m`;
10182
10182
  var styles = {
10183
10183
  modifier: {
10184
10184
  reset: [0, 0],
@@ -10273,17 +10273,17 @@ function assembleStyles() {
10273
10273
  styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
10274
10274
  Object.defineProperties(styles, {
10275
10275
  rgbToAnsi256: {
10276
- value(red, green, blue) {
10277
- if (red === green && green === blue) {
10278
- if (red < 8) {
10276
+ value(red2, green2, blue) {
10277
+ if (red2 === green2 && green2 === blue) {
10278
+ if (red2 < 8) {
10279
10279
  return 16;
10280
10280
  }
10281
- if (red > 248) {
10281
+ if (red2 > 248) {
10282
10282
  return 231;
10283
10283
  }
10284
- return Math.round((red - 8) / 247 * 24) + 232;
10284
+ return Math.round((red2 - 8) / 247 * 24) + 232;
10285
10285
  }
10286
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
10286
+ return 16 + 36 * Math.round(red2 / 255 * 5) + 6 * Math.round(green2 / 255 * 5) + Math.round(blue / 255 * 5);
10287
10287
  },
10288
10288
  enumerable: false
10289
10289
  },
@@ -10320,25 +10320,25 @@ function assembleStyles() {
10320
10320
  if (code < 16) {
10321
10321
  return 90 + (code - 8);
10322
10322
  }
10323
- let red;
10324
- let green;
10323
+ let red2;
10324
+ let green2;
10325
10325
  let blue;
10326
10326
  if (code >= 232) {
10327
- red = ((code - 232) * 10 + 8) / 255;
10328
- green = red;
10329
- blue = red;
10327
+ red2 = ((code - 232) * 10 + 8) / 255;
10328
+ green2 = red2;
10329
+ blue = red2;
10330
10330
  } else {
10331
10331
  code -= 16;
10332
10332
  const remainder = code % 36;
10333
- red = Math.floor(code / 36) / 5;
10334
- green = Math.floor(remainder / 6) / 5;
10333
+ red2 = Math.floor(code / 36) / 5;
10334
+ green2 = Math.floor(remainder / 6) / 5;
10335
10335
  blue = remainder % 6 / 5;
10336
10336
  }
10337
- const value = Math.max(red, green, blue) * 2;
10337
+ const value = Math.max(red2, green2, blue) * 2;
10338
10338
  if (value === 0) {
10339
10339
  return 30;
10340
10340
  }
10341
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
10341
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green2) << 1 | Math.round(red2));
10342
10342
  if (value === 2) {
10343
10343
  result += 60;
10344
10344
  }
@@ -10347,7 +10347,7 @@ function assembleStyles() {
10347
10347
  enumerable: false
10348
10348
  },
10349
10349
  rgbToAnsi: {
10350
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
10350
+ value: (red2, green2, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red2, green2, blue)),
10351
10351
  enumerable: false
10352
10352
  },
10353
10353
  hexToAnsi: {
@@ -11976,7 +11976,7 @@ var import_ansi_align = __toESM(require_ansi_align(), 1);
11976
11976
  var ANSI_BACKGROUND_OFFSET2 = 10;
11977
11977
  var wrapAnsi162 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
11978
11978
  var wrapAnsi2562 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
11979
- var wrapAnsi16m2 = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
11979
+ var wrapAnsi16m2 = (offset = 0) => (red2, green2, blue) => `\x1B[${38 + offset};2;${red2};${green2};${blue}m`;
11980
11980
  var styles3 = {
11981
11981
  modifier: {
11982
11982
  reset: [0, 0],
@@ -12071,17 +12071,17 @@ function assembleStyles2() {
12071
12071
  styles3.bgColor.ansi16m = wrapAnsi16m2(ANSI_BACKGROUND_OFFSET2);
12072
12072
  Object.defineProperties(styles3, {
12073
12073
  rgbToAnsi256: {
12074
- value(red, green, blue) {
12075
- if (red === green && green === blue) {
12076
- if (red < 8) {
12074
+ value(red2, green2, blue) {
12075
+ if (red2 === green2 && green2 === blue) {
12076
+ if (red2 < 8) {
12077
12077
  return 16;
12078
12078
  }
12079
- if (red > 248) {
12079
+ if (red2 > 248) {
12080
12080
  return 231;
12081
12081
  }
12082
- return Math.round((red - 8) / 247 * 24) + 232;
12082
+ return Math.round((red2 - 8) / 247 * 24) + 232;
12083
12083
  }
12084
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
12084
+ return 16 + 36 * Math.round(red2 / 255 * 5) + 6 * Math.round(green2 / 255 * 5) + Math.round(blue / 255 * 5);
12085
12085
  },
12086
12086
  enumerable: false
12087
12087
  },
@@ -12118,25 +12118,25 @@ function assembleStyles2() {
12118
12118
  if (code < 16) {
12119
12119
  return 90 + (code - 8);
12120
12120
  }
12121
- let red;
12122
- let green;
12121
+ let red2;
12122
+ let green2;
12123
12123
  let blue;
12124
12124
  if (code >= 232) {
12125
- red = ((code - 232) * 10 + 8) / 255;
12126
- green = red;
12127
- blue = red;
12125
+ red2 = ((code - 232) * 10 + 8) / 255;
12126
+ green2 = red2;
12127
+ blue = red2;
12128
12128
  } else {
12129
12129
  code -= 16;
12130
12130
  const remainder = code % 36;
12131
- red = Math.floor(code / 36) / 5;
12132
- green = Math.floor(remainder / 6) / 5;
12131
+ red2 = Math.floor(code / 36) / 5;
12132
+ green2 = Math.floor(remainder / 6) / 5;
12133
12133
  blue = remainder % 6 / 5;
12134
12134
  }
12135
- const value = Math.max(red, green, blue) * 2;
12135
+ const value = Math.max(red2, green2, blue) * 2;
12136
12136
  if (value === 0) {
12137
12137
  return 30;
12138
12138
  }
12139
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
12139
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green2) << 1 | Math.round(red2));
12140
12140
  if (value === 2) {
12141
12141
  result += 60;
12142
12142
  }
@@ -12145,7 +12145,7 @@ function assembleStyles2() {
12145
12145
  enumerable: false
12146
12146
  },
12147
12147
  rgbToAnsi: {
12148
- value: (red, green, blue) => styles3.ansi256ToAnsi(styles3.rgbToAnsi256(red, green, blue)),
12148
+ value: (red2, green2, blue) => styles3.ansi256ToAnsi(styles3.rgbToAnsi256(red2, green2, blue)),
12149
12149
  enumerable: false
12150
12150
  },
12151
12151
  hexToAnsi: {
@@ -12319,15 +12319,15 @@ var NEWLINE = "\n";
12319
12319
  var PAD = " ";
12320
12320
  var NONE = "none";
12321
12321
  var terminalColumns = () => {
12322
- const { env: env4, stdout, stderr } = process9;
12323
- if (stdout?.columns) {
12324
- return stdout.columns;
12322
+ const { env: env5, stdout: stdout2, stderr } = process9;
12323
+ if (stdout2?.columns) {
12324
+ return stdout2.columns;
12325
12325
  }
12326
12326
  if (stderr?.columns) {
12327
12327
  return stderr.columns;
12328
12328
  }
12329
- if (env4.COLUMNS) {
12330
- return Number.parseInt(env4.COLUMNS, 10);
12329
+ if (env5.COLUMNS) {
12330
+ return Number.parseInt(env5.COLUMNS, 10);
12331
12331
  }
12332
12332
  return 80;
12333
12333
  };
@@ -12554,10 +12554,10 @@ var determineDimensions = (text, options) => {
12554
12554
  }
12555
12555
  return options;
12556
12556
  };
12557
- var isHex = (color) => color.match(/^#(?:[0-f]{3}){1,2}$/i);
12558
- var isColorValid = (color) => typeof color === "string" && (source_default[color] ?? isHex(color));
12559
- var getColorFunction = (color) => isHex(color) ? source_default.hex(color) : source_default[color];
12560
- var getBGColorFunction = (color) => isHex(color) ? source_default.bgHex(color) : source_default[camelCase(["bg", color])];
12557
+ var isHex = (color2) => color2.match(/^#(?:[0-f]{3}){1,2}$/i);
12558
+ var isColorValid = (color2) => typeof color2 === "string" && (source_default[color2] ?? isHex(color2));
12559
+ var getColorFunction = (color2) => isHex(color2) ? source_default.hex(color2) : source_default[color2];
12560
+ var getBGColorFunction = (color2) => isHex(color2) ? source_default.bgHex(color2) : source_default[camelCase(["bg", color2])];
12561
12561
  function boxen(text, options) {
12562
12562
  options = {
12563
12563
  padding: 0,
@@ -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
  }
@@ -13203,20 +13209,20 @@ import { promisify as promisify2 } from "node:util";
13203
13209
  var execFileAsync = promisify2(execFile);
13204
13210
  async function runGit(cwd, args) {
13205
13211
  try {
13206
- const { stdout } = await execFileAsync("git", args, { cwd });
13207
- return stdout.trim();
13212
+ const { stdout: stdout2 } = await execFileAsync("git", args, { cwd });
13213
+ return stdout2.trim();
13208
13214
  } catch (error) {
13209
13215
  const message = error instanceof Error ? error.message : String(error);
13210
13216
  throw new Error(`Git command failed in '${cwd}': ${message}`);
13211
13217
  }
13212
13218
  }
13213
13219
  async function readWorktreeHealth(cwd) {
13214
- const { stdout } = await execFileAsync(
13220
+ const { stdout: stdout2 } = await execFileAsync(
13215
13221
  "git",
13216
13222
  ["status", "--porcelain=v2", "--branch"],
13217
13223
  { cwd }
13218
13224
  );
13219
- return parseWorktreeHealth(stdout);
13225
+ return parseWorktreeHealth(stdout2);
13220
13226
  }
13221
13227
  async function isDirtyWorktree(cwd) {
13222
13228
  const health = await readWorktreeHealth(cwd);
@@ -13236,12 +13242,12 @@ async function isBranchMergedInto(cwd, branch, base = "HEAD") {
13236
13242
  }
13237
13243
  }
13238
13244
  async function resolveRemoteDefaultBranch(cwd, remote) {
13239
- const { stdout } = await execFileAsync(
13245
+ const { stdout: stdout2 } = await execFileAsync(
13240
13246
  "git",
13241
13247
  ["ls-remote", "--symref", remote, "HEAD"],
13242
13248
  { cwd }
13243
13249
  );
13244
- const refLine = stdout.split("\n").find((line) => line.startsWith("ref: refs/heads/"));
13250
+ const refLine = stdout2.split("\n").find((line) => line.startsWith("ref: refs/heads/"));
13245
13251
  if (!refLine) {
13246
13252
  return null;
13247
13253
  }
@@ -13250,12 +13256,12 @@ async function resolveRemoteDefaultBranch(cwd, remote) {
13250
13256
  }
13251
13257
  async function readBranchLastCommitTimestamp(cwd, branch) {
13252
13258
  try {
13253
- const { stdout } = await execFileAsync(
13259
+ const { stdout: stdout2 } = await execFileAsync(
13254
13260
  "git",
13255
13261
  ["log", "-1", "--format=%ct", branch],
13256
13262
  { cwd }
13257
13263
  );
13258
- const timestamp = Number(stdout.trim());
13264
+ const timestamp = Number(stdout2.trim());
13259
13265
  return Number.isFinite(timestamp) ? timestamp : null;
13260
13266
  } catch {
13261
13267
  return null;
@@ -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) {
@@ -13405,14 +13404,14 @@ function findOptionalPorcelainValue(block, key) {
13405
13404
 
13406
13405
  // src/shell-handoff.ts
13407
13406
  import { writeFile as writeFile3 } from "node:fs/promises";
13408
- async function writeShellOutput(envVar, value, stdout) {
13407
+ async function writeShellOutput(envVar, value, stdout2) {
13409
13408
  const output = `${value}
13410
13409
  `;
13411
13410
  if (process.env[envVar]) {
13412
13411
  await writeFile3(process.env[envVar], output, "utf8");
13413
13412
  return;
13414
13413
  }
13415
- stdout(output);
13414
+ stdout2(output);
13416
13415
  }
13417
13416
 
13418
13417
  // src/back.ts
@@ -13823,41 +13822,6 @@ var dD = class extends x {
13823
13822
  };
13824
13823
  var A;
13825
13824
  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 {
13830
- 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) {
13835
- case "left":
13836
- case "up":
13837
- this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
13838
- break;
13839
- case "down":
13840
- case "right":
13841
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
13842
- break;
13843
- case "space":
13844
- this.toggleValue();
13845
- break;
13846
- }
13847
- });
13848
- }
13849
- get _value() {
13850
- return this.options[this.cursor].value;
13851
- }
13852
- toggleAll() {
13853
- const u2 = this.value.length === this.options.length;
13854
- this.value = u2 ? [] : this.options.map((t) => t.value);
13855
- }
13856
- toggleValue() {
13857
- const u2 = this.value.includes(this._value);
13858
- this.value = u2 ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
13859
- }
13860
- };
13861
13825
  var OD = Object.defineProperty;
13862
13826
  var PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
13863
13827
  var J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
@@ -14026,46 +13990,6 @@ ${import_picocolors2.default.cyan(d2)}
14026
13990
  }
14027
13991
  } }).prompt();
14028
13992
  };
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)}`;
14033
- };
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.
14036
- ${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
- }, render() {
14038
- const r2 = `${import_picocolors2.default.gray(o)}
14039
- ${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
- };
14044
- switch (this.state) {
14045
- 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")}`;
14047
- 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}
14050
- ${import_picocolors2.default.gray(o)}` : ""}`;
14051
- }
14052
- case "error": {
14053
- const s = this.error.split(`
14054
- `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
14055
- `);
14056
- return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
14057
- ${import_picocolors2.default.yellow(o)} `)}
14058
- ${s}
14059
- `;
14060
- }
14061
- default:
14062
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
14063
- ${import_picocolors2.default.cyan(o)} `)}
14064
- ${import_picocolors2.default.cyan(d2)}
14065
- `;
14066
- }
14067
- } }).prompt();
14068
- };
14069
13993
  var Ie = (t = "") => {
14070
13994
  process.stdout.write(`${import_picocolors2.default.gray(ue)} ${t}
14071
13995
  `);
@@ -14084,8 +14008,29 @@ function isHeadless() {
14084
14008
  }
14085
14009
 
14086
14010
  // src/worktree-info.ts
14011
+ var MAX_WORKTREE_INFO_READ_CONCURRENCY = 8;
14087
14012
  async function readWorktreeInfos(worktrees) {
14088
- return Promise.all(worktrees.map((worktree) => readWorktreeInfo(worktree)));
14013
+ return mapWithConcurrency(
14014
+ worktrees,
14015
+ MAX_WORKTREE_INFO_READ_CONCURRENCY,
14016
+ readWorktreeInfo
14017
+ );
14018
+ }
14019
+ async function mapWithConcurrency(items, limit, mapper) {
14020
+ const results = new Array(items.length);
14021
+ let nextIndex = 0;
14022
+ async function readNext() {
14023
+ for (; ; ) {
14024
+ const index = nextIndex;
14025
+ nextIndex += 1;
14026
+ if (index >= items.length) return;
14027
+ results[index] = await mapper(items[index]);
14028
+ }
14029
+ }
14030
+ await Promise.all(
14031
+ Array.from({ length: Math.min(limit, items.length) }, () => readNext())
14032
+ );
14033
+ return results;
14089
14034
  }
14090
14035
  async function readWorktreeInfo(worktree) {
14091
14036
  const [healthResult, lastCommitResult] = await Promise.allSettled([
@@ -14129,16 +14074,6 @@ function serializeWorktreeInfo(info) {
14129
14074
  upstream: info.upstream
14130
14075
  };
14131
14076
  }
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
14077
  function formatUpstreamState(upstream) {
14143
14078
  if (upstream.kind === "detached") {
14144
14079
  return "n/a";
@@ -14239,6 +14174,881 @@ function hasStderr(error) {
14239
14174
  return error instanceof Error && "stderr" in error && typeof error.stderr === "string";
14240
14175
  }
14241
14176
 
14177
+ // src/worktree-picker.ts
14178
+ import { env as env4, platform, stdin, stdout } from "node:process";
14179
+ async function buildWorktreePromptEntries(sources) {
14180
+ const [history, infos] = await Promise.all([
14181
+ loadHistory(),
14182
+ readWorktreeInfos(sources.map((source) => source.worktree))
14183
+ ]);
14184
+ const historyByPath = new Map(history.map((entry) => [entry.path, entry]));
14185
+ const entries = sources.map(
14186
+ (source, index) => buildWorktreePromptEntry(
14187
+ source,
14188
+ infos[index],
14189
+ historyByPath.get(source.worktree.path)?.timestamp ?? null,
14190
+ Date.now()
14191
+ )
14192
+ );
14193
+ return entries.sort(comparePromptEntries).map(
14194
+ ({ lastActivityTimestamp: _lastActivityTimestamp, ...entry }) => entry
14195
+ );
14196
+ }
14197
+ function resolveWorktreeQuery(sources, query) {
14198
+ const normalizedQuery = normalizeQuery(query);
14199
+ if (normalizedQuery === null) return null;
14200
+ const matches = findWorktreePromptSourceMatches(sources, normalizedQuery);
14201
+ if (isAmbiguousRepoOnlyQuery(matches, normalizedQuery)) return null;
14202
+ return matches[0]?.source ?? null;
14203
+ }
14204
+ function findWorktreePromptSourceMatches(sources, normalizedQuery) {
14205
+ return sources.flatMap((source) => {
14206
+ const matchScore = scoreWorktreeMatch(
14207
+ {
14208
+ ...source.worktree,
14209
+ repoName: source.repoName
14210
+ },
14211
+ normalizedQuery
14212
+ );
14213
+ return matchScore === null ? [] : [{ matchScore, source }];
14214
+ }).sort(compareQueryMatches);
14215
+ }
14216
+ function isAmbiguousRepoOnlyQuery(matches, query) {
14217
+ if (matches[0]?.matchScore === 1e3) return false;
14218
+ return matches.filter((match) => match.source.repoName.toLowerCase() === query).length > 1;
14219
+ }
14220
+ async function promptForSingleWorktree(message, worktrees, io = {}) {
14221
+ const choice = await runSearchablePrompt({
14222
+ entries: worktrees.map(buildSearchableWorktreeEntry),
14223
+ input: io.input,
14224
+ message,
14225
+ multiple: false,
14226
+ output: io.output
14227
+ });
14228
+ return typeof choice === "string" ? choice : null;
14229
+ }
14230
+ async function promptForMultipleWorktrees(message, worktrees, io = {}) {
14231
+ const choice = await runSearchablePrompt({
14232
+ entries: buildGroupedSearchableEntries(worktrees),
14233
+ input: io.input,
14234
+ message,
14235
+ multiple: true,
14236
+ output: io.output
14237
+ });
14238
+ return Array.isArray(choice) ? choice : null;
14239
+ }
14240
+ function compareQueryMatches(a, b3) {
14241
+ if (a.matchScore !== b3.matchScore) {
14242
+ return b3.matchScore - a.matchScore;
14243
+ }
14244
+ if (a.source.worktree.isCurrent && !b3.source.worktree.isCurrent) return -1;
14245
+ if (!a.source.worktree.isCurrent && b3.source.worktree.isCurrent) return 1;
14246
+ return a.source.repoName.localeCompare(b3.source.repoName) || (a.source.worktree.branch ?? "").localeCompare(
14247
+ b3.source.worktree.branch ?? ""
14248
+ ) || a.source.worktree.path.localeCompare(b3.source.worktree.path);
14249
+ }
14250
+ function groupPromptEntries(worktrees) {
14251
+ const groups = /* @__PURE__ */ new Map();
14252
+ for (const worktree of worktrees) {
14253
+ const group = worktree.group === "recent" ? "Recent worktrees" : "Other worktrees";
14254
+ const groupWorktrees = groups.get(group);
14255
+ if (groupWorktrees === void 0) {
14256
+ groups.set(group, [worktree]);
14257
+ } else {
14258
+ groupWorktrees.push(worktree);
14259
+ }
14260
+ }
14261
+ return [...groups.entries()];
14262
+ }
14263
+ function buildGroupedSearchableEntries(worktrees) {
14264
+ const entries = [];
14265
+ for (const [group, groupWorktrees] of groupPromptEntries(worktrees)) {
14266
+ entries.push({
14267
+ label: group,
14268
+ searchText: group.toLowerCase(),
14269
+ selectable: false
14270
+ });
14271
+ for (const worktree of groupWorktrees) {
14272
+ entries.push(buildSearchableWorktreeEntry(worktree));
14273
+ }
14274
+ }
14275
+ return entries;
14276
+ }
14277
+ function buildSearchableWorktreeEntry(worktree) {
14278
+ const metadata = worktree.metadata ?? null;
14279
+ const branch = worktree.branch ?? "(detached)";
14280
+ return {
14281
+ detail: [worktree.repoName, branch, metadata, worktree.path].filter((part) => part !== null && part.length > 0).join(" \xB7 "),
14282
+ label: worktree.label,
14283
+ searchText: buildPromptSearchText(worktree),
14284
+ value: worktree.path,
14285
+ worktree: {
14286
+ branch,
14287
+ metadata,
14288
+ path: worktree.path,
14289
+ repoName: worktree.repoName
14290
+ }
14291
+ };
14292
+ }
14293
+ async function runSearchablePrompt(options) {
14294
+ const input = options.input ?? stdin;
14295
+ const output = options.output ?? stdout;
14296
+ if (!input.isTTY) {
14297
+ return null;
14298
+ }
14299
+ const prompt = new SearchablePrompt(options, input, output);
14300
+ const value = await prompt.run();
14301
+ return pD(value) ? null : value;
14302
+ }
14303
+ var SearchablePrompt = class {
14304
+ constructor(options, input, output) {
14305
+ this.options = options;
14306
+ this.output = output;
14307
+ this.cursor = this.firstSelectableIndex();
14308
+ const owner = this;
14309
+ this.prompt = new WorktreeCorePrompt(this, {
14310
+ input,
14311
+ output,
14312
+ render: function renderWorktreePrompt() {
14313
+ return owner.render(this.state, this.error);
14314
+ }
14315
+ });
14316
+ this.syncValue();
14317
+ }
14318
+ cursor = 0;
14319
+ query = "";
14320
+ searchActive = false;
14321
+ selected = /* @__PURE__ */ new Set();
14322
+ prompt;
14323
+ run() {
14324
+ return this.prompt.prompt();
14325
+ }
14326
+ handleKeypress(prompt, character, key) {
14327
+ if (prompt.state === "error") {
14328
+ prompt.state = "active";
14329
+ prompt.error = "";
14330
+ }
14331
+ const action = resolvePromptAction(character, key);
14332
+ if (action === "cancel") {
14333
+ this.cancel(prompt);
14334
+ return;
14335
+ }
14336
+ if (action === "escape") {
14337
+ this.handleEscapeKey(prompt);
14338
+ return;
14339
+ }
14340
+ if (action === "enter") {
14341
+ this.submit(prompt);
14342
+ return;
14343
+ }
14344
+ if (this.searchActive && this.handleSearchKey(character, key?.name)) {
14345
+ this.syncValue();
14346
+ renderPrompt(prompt);
14347
+ return;
14348
+ }
14349
+ if (character === "/" && !this.searchActive) {
14350
+ this.searchActive = true;
14351
+ this.query = "";
14352
+ this.cursor = this.firstSelectableIndex();
14353
+ this.syncValue();
14354
+ renderPrompt(prompt);
14355
+ return;
14356
+ }
14357
+ this.handleNavigationKey(character, action);
14358
+ this.syncValue();
14359
+ renderPrompt(prompt);
14360
+ }
14361
+ handleEscapeKey(prompt) {
14362
+ if (this.searchActive) {
14363
+ this.searchActive = false;
14364
+ this.query = "";
14365
+ this.cursor = this.firstSelectableIndex();
14366
+ this.syncValue();
14367
+ renderPrompt(prompt);
14368
+ return;
14369
+ }
14370
+ this.cancel(prompt);
14371
+ }
14372
+ cancel(prompt) {
14373
+ prompt.state = "cancel";
14374
+ renderPrompt(prompt);
14375
+ closePrompt(prompt);
14376
+ }
14377
+ handleSearchKey(character, keyName) {
14378
+ if (keyName === "space" || character === " ") {
14379
+ return false;
14380
+ }
14381
+ if (keyName === "backspace" || keyName === "delete") {
14382
+ this.query = this.query.slice(0, -1);
14383
+ this.cursor = this.firstSelectableIndex();
14384
+ return true;
14385
+ }
14386
+ if (isPrintableSearchCharacter(character)) {
14387
+ this.query += character;
14388
+ this.cursor = this.firstSelectableIndex();
14389
+ return true;
14390
+ }
14391
+ return false;
14392
+ }
14393
+ handleNavigationKey(character, action) {
14394
+ switch (action) {
14395
+ case "up":
14396
+ this.moveCursor(-1);
14397
+ return;
14398
+ case "down":
14399
+ this.moveCursor(1);
14400
+ return;
14401
+ case "space":
14402
+ this.toggleSelection();
14403
+ return;
14404
+ }
14405
+ switch (character) {
14406
+ case "k":
14407
+ this.moveCursor(-1);
14408
+ return;
14409
+ case "j":
14410
+ this.moveCursor(1);
14411
+ return;
14412
+ case " ":
14413
+ this.toggleSelection();
14414
+ return;
14415
+ }
14416
+ }
14417
+ moveCursor(direction) {
14418
+ const entries = this.visibleEntries();
14419
+ if (entries.length === 0) return;
14420
+ let nextCursor = this.cursor;
14421
+ for (let index = 0; index < entries.length; index += 1) {
14422
+ nextCursor = wrapIndex(nextCursor + direction, entries.length);
14423
+ if (isSelectableEntry(entries[nextCursor])) {
14424
+ this.cursor = nextCursor;
14425
+ return;
14426
+ }
14427
+ }
14428
+ }
14429
+ submit(prompt) {
14430
+ const entry = this.visibleEntries()[this.cursor];
14431
+ if (!this.options.multiple) {
14432
+ prompt.value = isSelectableEntry(entry) ? entry.value : null;
14433
+ prompt.state = "submit";
14434
+ renderPrompt(prompt);
14435
+ closePrompt(prompt);
14436
+ return;
14437
+ }
14438
+ if (this.selected.size === 0) {
14439
+ prompt.error = "Please select at least one option.";
14440
+ prompt.state = "error";
14441
+ renderPrompt(prompt);
14442
+ return;
14443
+ }
14444
+ prompt.value = [...this.selected];
14445
+ prompt.state = "submit";
14446
+ renderPrompt(prompt);
14447
+ closePrompt(prompt);
14448
+ }
14449
+ toggleSelection() {
14450
+ if (!this.options.multiple) return;
14451
+ const entry = this.visibleEntries()[this.cursor];
14452
+ if (!isSelectableEntry(entry)) return;
14453
+ if (this.selected.has(entry.value)) {
14454
+ this.selected.delete(entry.value);
14455
+ return;
14456
+ }
14457
+ this.selected.add(entry.value);
14458
+ }
14459
+ render(state, error) {
14460
+ const entries = this.visibleEntries();
14461
+ const visibleEntries = windowPromptEntries(
14462
+ entries,
14463
+ this.cursor,
14464
+ this.maxItems()
14465
+ );
14466
+ const glyphs = promptGlyphs();
14467
+ const colors = promptColors();
14468
+ const frame = promptFrameColor(state, colors);
14469
+ const lines = [
14470
+ colors.bar(glyphs.bar),
14471
+ this.renderTitle(state, glyphs, colors),
14472
+ `${frame(glyphs.bar)} ${this.renderSearchHint(colors)}`,
14473
+ frame(glyphs.bar)
14474
+ ];
14475
+ if (state === "error" && error.length > 0) {
14476
+ lines.push(`${colors.errorBar(glyphs.bar)} ${colors.error(error)}`);
14477
+ lines.push(frame(glyphs.bar));
14478
+ }
14479
+ lines.push(
14480
+ ...this.renderEntries(entries, visibleEntries, glyphs, colors, frame)
14481
+ );
14482
+ lines.push(frame(glyphs.bar));
14483
+ const preview = this.activePreview(entries, colors);
14484
+ if (preview !== null) {
14485
+ lines.push(`${frame(glyphs.bar)} ${preview}`);
14486
+ lines.push(frame(glyphs.bar));
14487
+ }
14488
+ const visibleWorktreeCount = entries.filter(isSelectableEntry).length;
14489
+ const footer = this.footerText(visibleWorktreeCount, colors);
14490
+ if (footer.length > 0) {
14491
+ lines.push(`${frame(glyphs.corner)} ${footer}`);
14492
+ }
14493
+ return `${lines.join("\n")}
14494
+ `;
14495
+ }
14496
+ renderTitle(state, glyphs, colors) {
14497
+ const symbol = colors.symbol(promptSymbol(state, glyphs), state);
14498
+ const maxTitleLength = Math.max(8, this.columns() - 3);
14499
+ if (!this.searchActive) {
14500
+ return `${symbol} ${middleEllipsize(this.options.message, maxTitleLength)}`;
14501
+ }
14502
+ const search = ` /${this.query}`;
14503
+ const maxSearchLength = Math.min(
14504
+ terminalWidth(search),
14505
+ Math.floor(maxTitleLength / 2)
14506
+ );
14507
+ const message = middleEllipsize(
14508
+ this.options.message,
14509
+ Math.max(1, maxTitleLength - maxSearchLength)
14510
+ );
14511
+ const visibleSearch = middleEllipsize(
14512
+ search,
14513
+ Math.max(1, maxTitleLength - terminalWidth(message))
14514
+ );
14515
+ return `${symbol} ${message}${colors.search(visibleSearch)}`;
14516
+ }
14517
+ renderSearchHint(colors) {
14518
+ if (this.searchActive) {
14519
+ return `${colors.hint("type to filter")} ${colors.hint("\xB7")} ${colors.key("esc")} ${colors.hint("clears")}`;
14520
+ }
14521
+ return `${colors.hint("press ")}${colors.key("/")}${colors.hint(" to search")}`;
14522
+ }
14523
+ renderEntries(entries, visibleEntries, glyphs, colors, frame) {
14524
+ if (entries.length === 0) {
14525
+ return [`${frame(glyphs.bar)} ${colors.hint("No matching worktrees")}`];
14526
+ }
14527
+ return visibleEntries.map(
14528
+ (entry) => entry === "ellipsis" ? `${frame(glyphs.bar)} ${colors.hint("...")}` : this.renderEntry(entries, entry, glyphs, colors, frame)
14529
+ );
14530
+ }
14531
+ renderEntry(entries, entry, glyphs, colors, frame) {
14532
+ const active = entries[this.cursor] === entry;
14533
+ const selected = isSelectableEntry(entry) && this.selected.has(entry.value);
14534
+ const prefix = this.entryPrefix(entry, active, selected, glyphs);
14535
+ const label = this.entryLabel(entry, prefix);
14536
+ const line = isSelectableEntry(entry) ? active ? label : colors.hint(label) : colors.hint(label);
14537
+ const marker = selected ? colors.selected(prefix) : active ? this.options.multiple ? colors.option(prefix) : colors.selected(prefix) : colors.hint(prefix);
14538
+ return `${frame(glyphs.bar)} ${marker} ${line}`;
14539
+ }
14540
+ activePreview(entries, colors) {
14541
+ const entry = entries[this.cursor];
14542
+ if (!isSelectableEntry(entry) || entry.detail === void 0) return null;
14543
+ const label = "current";
14544
+ const separator = " ";
14545
+ const width = this.previewWidth();
14546
+ const prefixWidth = terminalWidth(label) + terminalWidth(separator);
14547
+ if (width <= prefixWidth) return colors.key(middleEllipsize(label, width));
14548
+ const detail = middleEllipsize(entry.detail, width - prefixWidth);
14549
+ return `${colors.key(label)}${colors.hint(separator)}${colors.hint(detail)}`;
14550
+ }
14551
+ entryLabel(entry, prefix) {
14552
+ const width = this.labelWidth(prefix);
14553
+ if (entry.worktree === void 0) {
14554
+ return middleEllipsize(entry.label, width);
14555
+ }
14556
+ return fitPromptPieces(
14557
+ [
14558
+ {
14559
+ ellipsize: middleEllipsize,
14560
+ max: 18,
14561
+ min: 6,
14562
+ value: entry.worktree.repoName
14563
+ },
14564
+ {
14565
+ ellipsize: middleEllipsize,
14566
+ max: 32,
14567
+ min: 8,
14568
+ value: entry.worktree.branch
14569
+ },
14570
+ ...entry.worktree.metadata === null ? [] : [
14571
+ {
14572
+ ellipsize: middleEllipsize,
14573
+ max: 30,
14574
+ min: 10,
14575
+ value: entry.worktree.metadata
14576
+ }
14577
+ ],
14578
+ {
14579
+ ellipsize: startEllipsize,
14580
+ max: 36,
14581
+ min: 12,
14582
+ value: entry.worktree.path
14583
+ }
14584
+ ],
14585
+ width
14586
+ );
14587
+ }
14588
+ labelWidth(prefix) {
14589
+ return Math.max(8, this.columns() - terminalWidth(prefix) - 4);
14590
+ }
14591
+ previewWidth() {
14592
+ return Math.max(8, this.columns() - 3);
14593
+ }
14594
+ entryPrefix(entry, active, selected, glyphs) {
14595
+ if (!isSelectableEntry(entry)) {
14596
+ return active ? glyphs.active : " ";
14597
+ }
14598
+ if (!this.options.multiple) {
14599
+ return active ? glyphs.active : glyphs.inactive;
14600
+ }
14601
+ if (active && selected) return glyphs.checked;
14602
+ if (active) return glyphs.uncheckedActive;
14603
+ return selected ? glyphs.checked : glyphs.unchecked;
14604
+ }
14605
+ footerText(visibleCount, colors) {
14606
+ if (!this.options.multiple) return "";
14607
+ return [
14608
+ `${colors.key("space")} ${colors.hint("select")}`,
14609
+ `${colors.selected(String(this.selected.size))} ${colors.hint("selected")}`,
14610
+ colors.hint(`${visibleCount} shown`)
14611
+ ].join(colors.hint(" \xB7 "));
14612
+ }
14613
+ firstSelectableIndex() {
14614
+ const index = this.visibleEntries().findIndex(isSelectableEntry);
14615
+ return index === -1 ? 0 : index;
14616
+ }
14617
+ visibleEntries() {
14618
+ const query = normalizeQuery(this.query);
14619
+ if (query === null) {
14620
+ return this.options.entries;
14621
+ }
14622
+ return filterSearchablePromptEntries(this.options.entries, query);
14623
+ }
14624
+ maxItems() {
14625
+ const rows = this.output.rows ?? 16;
14626
+ return Math.max(5, Math.min(12, rows - 8));
14627
+ }
14628
+ columns() {
14629
+ return Math.max(20, this.output.columns ?? 80);
14630
+ }
14631
+ syncValue() {
14632
+ if (this.options.multiple) {
14633
+ this.prompt.value = [...this.selected];
14634
+ return;
14635
+ }
14636
+ const entry = this.visibleEntries()[this.cursor];
14637
+ this.prompt.value = isSelectableEntry(entry) ? entry.value : null;
14638
+ }
14639
+ };
14640
+ var WorktreeCorePrompt = class extends x {
14641
+ constructor(worktreePrompt, options) {
14642
+ super(options, false);
14643
+ this.worktreePrompt = worktreePrompt;
14644
+ }
14645
+ };
14646
+ Object.defineProperty(WorktreeCorePrompt.prototype, "onKeypress", {
14647
+ value: onWorktreeKeypress,
14648
+ writable: true
14649
+ });
14650
+ function renderPrompt(prompt) {
14651
+ prompt.render();
14652
+ }
14653
+ function closePrompt(prompt) {
14654
+ prompt.close();
14655
+ }
14656
+ function onWorktreeKeypress(character, key) {
14657
+ this.worktreePrompt.handleKeypress(this, character, key);
14658
+ }
14659
+ function resolvePromptAction(character, key) {
14660
+ if (key?.ctrl && key.name === "c" || character === "") {
14661
+ return "cancel";
14662
+ }
14663
+ switch (key?.name) {
14664
+ case "escape":
14665
+ return "escape";
14666
+ case "return":
14667
+ return "enter";
14668
+ case "space":
14669
+ return "space";
14670
+ case "up":
14671
+ case "left":
14672
+ return "up";
14673
+ case "down":
14674
+ case "right":
14675
+ return "down";
14676
+ }
14677
+ switch (character) {
14678
+ case " ":
14679
+ return "space";
14680
+ case "k":
14681
+ case "h":
14682
+ return "up";
14683
+ case "j":
14684
+ case "l":
14685
+ return "down";
14686
+ }
14687
+ return void 0;
14688
+ }
14689
+ function promptGlyphs() {
14690
+ return supportsUnicode() ? {
14691
+ active: "\u25CF",
14692
+ bar: "\u2502",
14693
+ checked: "\u25FC",
14694
+ corner: "\u2514",
14695
+ inactive: "\u25CB",
14696
+ pending: "\u25C6",
14697
+ submitted: "\u25C7",
14698
+ unchecked: "\u25FB",
14699
+ uncheckedActive: "\u25FB"
14700
+ } : {
14701
+ active: ">",
14702
+ bar: "|",
14703
+ checked: "[x]",
14704
+ corner: "-",
14705
+ inactive: " ",
14706
+ pending: "*",
14707
+ submitted: "o",
14708
+ unchecked: "[ ]",
14709
+ uncheckedActive: "[ ]"
14710
+ };
14711
+ }
14712
+ function promptColors() {
14713
+ return {
14714
+ activeBar: cyan,
14715
+ bar: gray,
14716
+ error: yellow,
14717
+ errorBar: yellow,
14718
+ hint: dim,
14719
+ key: cyan,
14720
+ option: cyan,
14721
+ search: cyan,
14722
+ selected: green,
14723
+ symbol: (value, state) => {
14724
+ if (state === "submit") return green(value);
14725
+ if (state === "error") return yellow(value);
14726
+ if (state === "cancel") return red(value);
14727
+ return cyan(value);
14728
+ }
14729
+ };
14730
+ }
14731
+ function promptFrameColor(state, colors) {
14732
+ if (state === "error") return colors.errorBar;
14733
+ if (state === "active" || state === "initial") return colors.activeBar;
14734
+ return colors.bar;
14735
+ }
14736
+ function promptSymbol(state, glyphs) {
14737
+ return state === "submit" ? glyphs.submitted : glyphs.pending;
14738
+ }
14739
+ function cyan(value) {
14740
+ return color("\x1B[36m", "\x1B[39m", value);
14741
+ }
14742
+ function dim(value) {
14743
+ return color("\x1B[2m", "\x1B[22m", value);
14744
+ }
14745
+ function gray(value) {
14746
+ return color("\x1B[90m", "\x1B[39m", value);
14747
+ }
14748
+ function green(value) {
14749
+ return color("\x1B[32m", "\x1B[39m", value);
14750
+ }
14751
+ function red(value) {
14752
+ return color("\x1B[31m", "\x1B[39m", value);
14753
+ }
14754
+ function yellow(value) {
14755
+ return color("\x1B[33m", "\x1B[39m", value);
14756
+ }
14757
+ function color(open, close, value) {
14758
+ if (env4.NO_COLOR !== void 0) return value;
14759
+ return `${open}${value}${close}`;
14760
+ }
14761
+ function supportsUnicode() {
14762
+ if (platform !== "win32") {
14763
+ return env4.TERM !== "linux";
14764
+ }
14765
+ return Boolean(
14766
+ env4.CI || env4.WT_SESSION || env4.TERMINUS_SUBLIME || env4.ConEmuTask === "{cmd::Cmder}" || env4.TERM_PROGRAM === "Terminus-Sublime" || env4.TERM_PROGRAM === "vscode" || env4.TERM === "xterm-256color" || env4.TERM === "alacritty" || env4.TERMINAL_EMULATOR === "JetBrains-JediTerm"
14767
+ );
14768
+ }
14769
+ function isPrintableSearchCharacter(character) {
14770
+ return typeof character === "string" && character.length === 1 && character >= " " && character !== "\x7F";
14771
+ }
14772
+ function wrapIndex(index, length) {
14773
+ return (index + length) % length;
14774
+ }
14775
+ function windowPromptEntries(entries, cursor, maxItems) {
14776
+ if (entries.length <= maxItems) {
14777
+ return entries;
14778
+ }
14779
+ const activeIndex = Math.min(Math.max(cursor, 0), entries.length - 1);
14780
+ const start = Math.max(
14781
+ 0,
14782
+ Math.min(activeIndex - 2, entries.length - maxItems)
14783
+ );
14784
+ const window = entries.slice(start, start + maxItems);
14785
+ if (start > 0) {
14786
+ window[0] = "ellipsis";
14787
+ }
14788
+ if (start + maxItems < entries.length) {
14789
+ window[window.length - 1] = "ellipsis";
14790
+ }
14791
+ return window;
14792
+ }
14793
+ function fitPromptPieces(pieces, width) {
14794
+ const separator = " \xB7 ";
14795
+ let visiblePieces = pieces.filter((piece) => piece.value.length > 0);
14796
+ let available = width - terminalWidth(separator) * Math.max(0, visiblePieces.length - 1);
14797
+ while (visiblePieces.length > 1 && available < minimumPieceLength(visiblePieces)) {
14798
+ const removableIndex = visiblePieces.length === 4 ? 2 : visiblePieces.length - 2;
14799
+ visiblePieces = visiblePieces.filter(
14800
+ (_3, index) => index !== removableIndex
14801
+ );
14802
+ available = width - terminalWidth(separator) * Math.max(0, visiblePieces.length - 1);
14803
+ }
14804
+ if (available <= 0 || available < minimumPieceLength(visiblePieces)) {
14805
+ return middleEllipsize(
14806
+ visiblePieces.map((piece) => piece.value).join(separator),
14807
+ width
14808
+ );
14809
+ }
14810
+ const lengths = visiblePieces.map(
14811
+ (piece) => Math.min(terminalWidth(piece.value), piece.max)
14812
+ );
14813
+ while (sum(lengths) > available) {
14814
+ const index = largestShrinkablePieceIndex(visiblePieces, lengths);
14815
+ if (index === -1) break;
14816
+ lengths[index] -= 1;
14817
+ }
14818
+ return visiblePieces.map((piece, index) => piece.ellipsize(piece.value, lengths[index])).join(separator);
14819
+ }
14820
+ function minimumPieceLength(pieces) {
14821
+ return sum(
14822
+ pieces.map((piece) => Math.min(terminalWidth(piece.value), piece.min))
14823
+ );
14824
+ }
14825
+ function largestShrinkablePieceIndex(pieces, lengths) {
14826
+ let candidate = -1;
14827
+ for (let index = 0; index < pieces.length; index += 1) {
14828
+ if (lengths[index] <= Math.min(terminalWidth(pieces[index].value), pieces[index].min)) {
14829
+ continue;
14830
+ }
14831
+ if (candidate === -1 || lengths[index] > lengths[candidate]) {
14832
+ candidate = index;
14833
+ }
14834
+ }
14835
+ return candidate;
14836
+ }
14837
+ function sum(values) {
14838
+ return values.reduce((total, value) => total + value, 0);
14839
+ }
14840
+ function filterSearchablePromptEntries(entries, query) {
14841
+ const filtered = [];
14842
+ let pendingGroup = null;
14843
+ for (const entry of entries) {
14844
+ if (!isSelectableEntry(entry)) {
14845
+ pendingGroup = entry;
14846
+ continue;
14847
+ }
14848
+ if (!entry.searchText.includes(query)) {
14849
+ continue;
14850
+ }
14851
+ if (pendingGroup !== null) {
14852
+ filtered.push(pendingGroup);
14853
+ pendingGroup = null;
14854
+ }
14855
+ filtered.push(entry);
14856
+ }
14857
+ return filtered;
14858
+ }
14859
+ function isSelectableEntry(entry) {
14860
+ return entry !== void 0 && entry.selectable !== false && typeof entry.value === "string";
14861
+ }
14862
+ function buildPromptSearchText(worktree) {
14863
+ return [
14864
+ worktree.repoName,
14865
+ worktree.branch ?? "detached",
14866
+ worktree.path,
14867
+ worktree.label,
14868
+ `${worktree.repoName}/${worktree.branch ?? "detached"}`
14869
+ ].join(" ").toLowerCase();
14870
+ }
14871
+ function buildWorktreePromptEntry(source, info, lastUsedTimestamp, now) {
14872
+ const lastWorkedTimestamp = info.lastCommitTimestamp === null ? null : info.lastCommitTimestamp * 1e3;
14873
+ const lastActivityTimestamp = lastUsedTimestamp ?? lastWorkedTimestamp;
14874
+ const lastActivityType = lastUsedTimestamp !== null ? "used" : lastWorkedTimestamp !== null ? "worked" : null;
14875
+ const branch = source.worktree.branch ?? "(detached)";
14876
+ const badges = buildStatusBadges(info);
14877
+ const recency = formatPromptRecency(
14878
+ lastActivityTimestamp,
14879
+ lastActivityType,
14880
+ now
14881
+ );
14882
+ const status = badges.length > 0 ? badges.map((badge) => `[${badge}]`).join(" ") : null;
14883
+ const metadataParts = [status, recency].filter(
14884
+ (part) => part !== null && part.length > 0
14885
+ );
14886
+ const metadata = metadataParts.length === 0 ? null : metadataParts.join(" \xB7 ");
14887
+ const path9 = middleEllipsize(source.worktree.path, 76);
14888
+ const label = [
14889
+ middleEllipsize(source.repoName, 22),
14890
+ middleEllipsize(branch, 34),
14891
+ metadata,
14892
+ path9
14893
+ ].filter((part) => part !== null && part.length > 0).join(" \xB7 ");
14894
+ return {
14895
+ ...source.worktree,
14896
+ group: lastUsedTimestamp !== null ? "recent" : "other",
14897
+ label,
14898
+ lastActivityTimestamp,
14899
+ metadata,
14900
+ repoName: source.repoName
14901
+ };
14902
+ }
14903
+ function buildStatusBadges(info) {
14904
+ const badges = [];
14905
+ if (info.isCurrent) {
14906
+ badges.push("current");
14907
+ }
14908
+ if (info.branch === null) {
14909
+ badges.push("detached");
14910
+ }
14911
+ if (info.status === "dirty") {
14912
+ badges.push("dirty");
14913
+ }
14914
+ if (info.upstream.kind === "stale") {
14915
+ badges.push("stale", "gone");
14916
+ }
14917
+ if (isUpToDate(info.upstream)) {
14918
+ badges.push("up to date");
14919
+ }
14920
+ return badges;
14921
+ }
14922
+ function isUpToDate(upstream) {
14923
+ return upstream.kind === "tracked" && upstream.ahead === 0 && upstream.behind === 0;
14924
+ }
14925
+ function formatPromptRecency(timestamp, type, now) {
14926
+ if (timestamp === null || type === null) {
14927
+ return "last used: never";
14928
+ }
14929
+ const label = type === "used" ? "last used" : "last worked";
14930
+ return `${label}: ${formatPickerAge(timestamp, now)}`;
14931
+ }
14932
+ function formatPickerAge(timestamp, now) {
14933
+ const ageSeconds = Math.max(0, Math.floor((now - timestamp) / 1e3));
14934
+ if (ageSeconds < 60) {
14935
+ return "now";
14936
+ }
14937
+ if (ageSeconds < 60 * 60) {
14938
+ return `${Math.floor(ageSeconds / 60)}m ago`;
14939
+ }
14940
+ if (ageSeconds < 24 * 60 * 60) {
14941
+ return `${Math.floor(ageSeconds / (60 * 60))}h ago`;
14942
+ }
14943
+ if (isYesterday(timestamp, now)) {
14944
+ return "yesterday";
14945
+ }
14946
+ return new Intl.DateTimeFormat("en-US", {
14947
+ day: "numeric",
14948
+ month: "short"
14949
+ }).format(new Date(timestamp));
14950
+ }
14951
+ function isYesterday(timestamp, now) {
14952
+ const date = new Date(timestamp);
14953
+ const yesterday = new Date(now);
14954
+ yesterday.setDate(yesterday.getDate() - 1);
14955
+ return date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === yesterday.getDate();
14956
+ }
14957
+ function buildSearchText(repoName, worktree) {
14958
+ return [
14959
+ repoName,
14960
+ worktree.branch ?? "detached",
14961
+ worktree.path,
14962
+ `${repoName}/${worktree.branch ?? "detached"}`
14963
+ ].join(" ").toLowerCase();
14964
+ }
14965
+ function normalizeQuery(query) {
14966
+ const normalized = query?.trim().toLowerCase();
14967
+ return normalized && normalized.length > 0 ? normalized : null;
14968
+ }
14969
+ function comparePromptEntries(a, b3) {
14970
+ if (a.isCurrent && !b3.isCurrent) return -1;
14971
+ if (!a.isCurrent && b3.isCurrent) return 1;
14972
+ if (a.group !== b3.group) {
14973
+ return groupRank(a.group) - groupRank(b3.group);
14974
+ }
14975
+ const aRecent = a.lastActivityTimestamp ?? 0;
14976
+ const bRecent = b3.lastActivityTimestamp ?? 0;
14977
+ if (aRecent !== bRecent) {
14978
+ return bRecent - aRecent;
14979
+ }
14980
+ return a.repoName.localeCompare(b3.repoName) || (a.branch ?? "").localeCompare(b3.branch ?? "") || a.path.localeCompare(b3.path);
14981
+ }
14982
+ function groupRank(group) {
14983
+ return group === "recent" ? 0 : 1;
14984
+ }
14985
+ function scoreWorktreeMatch(entry, query) {
14986
+ const branch = entry.branch ?? "detached";
14987
+ const exactCandidates = [
14988
+ branch,
14989
+ entry.path,
14990
+ `${entry.repoName}/${branch}`
14991
+ ].map((candidate) => candidate.toLowerCase());
14992
+ if (exactCandidates.includes(query)) {
14993
+ return 1e3;
14994
+ }
14995
+ return buildSearchText(entry.repoName, entry).includes(query) ? 1 : null;
14996
+ }
14997
+ function middleEllipsize(value, maxLength) {
14998
+ if (terminalWidth(value) <= maxLength) {
14999
+ return value;
15000
+ }
15001
+ if (maxLength <= 1) {
15002
+ return "\u2026";
15003
+ }
15004
+ const keep = maxLength - 1;
15005
+ const start = takeTerminalColumns(value, Math.ceil(keep / 2), "start");
15006
+ const end = takeTerminalColumns(value, Math.floor(keep / 2), "end");
15007
+ return `${start}\u2026${end}`;
15008
+ }
15009
+ function startEllipsize(value, maxLength) {
15010
+ if (terminalWidth(value) <= maxLength) {
15011
+ return value;
15012
+ }
15013
+ if (maxLength <= 1) {
15014
+ return "\u2026";
15015
+ }
15016
+ return `\u2026${takeTerminalColumns(value, maxLength - 1, "end")}`;
15017
+ }
15018
+ function takeTerminalColumns(value, maxWidth, direction) {
15019
+ const characters = direction === "start" ? Array.from(value) : Array.from(value).reverse();
15020
+ const kept = [];
15021
+ let width = 0;
15022
+ for (const character of characters) {
15023
+ const characterWidth = terminalWidth(character);
15024
+ if (width + characterWidth > maxWidth) break;
15025
+ kept.push(character);
15026
+ width += characterWidth;
15027
+ }
15028
+ return direction === "start" ? kept.join("") : kept.reverse().join("");
15029
+ }
15030
+ function terminalWidth(value) {
15031
+ let width = 0;
15032
+ for (const character of Array.from(value)) {
15033
+ width += characterTerminalWidth(character);
15034
+ }
15035
+ return width;
15036
+ }
15037
+ function characterTerminalWidth(character) {
15038
+ const codePoint = character.codePointAt(0);
15039
+ if (codePoint === void 0) return 0;
15040
+ if (isZeroWidthCodePoint(codePoint) || new RegExp("\\p{Mark}", "u").test(character)) {
15041
+ return 0;
15042
+ }
15043
+ return isWideCodePoint(codePoint) ? 2 : 1;
15044
+ }
15045
+ function isZeroWidthCodePoint(codePoint) {
15046
+ return codePoint === 0 || codePoint === 8205 || codePoint >= 65024 && codePoint <= 65039;
15047
+ }
15048
+ function isWideCodePoint(codePoint) {
15049
+ return codePoint >= 4352 && codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 131072 && codePoint <= 262141;
15050
+ }
15051
+
14242
15052
  // src/worktree-prompts.ts
14243
15053
  async function defaultConfirmForceRemoveWorktree(worktreePath) {
14244
15054
  const choice = await ye({
@@ -14299,7 +15109,14 @@ function createCleanCommand(dependencies = {}) {
14299
15109
  return 1;
14300
15110
  }
14301
15111
  const shouldSelectAll = options.force || options.dryRun && (options.stale || options.json || isHeadless());
14302
- const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(cleanupCandidates);
15112
+ const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(
15113
+ await buildWorktreePromptEntries(
15114
+ cleanupCandidates.map((worktree) => ({
15115
+ repoName: repository.repoName,
15116
+ worktree
15117
+ }))
15118
+ )
15119
+ );
14303
15120
  if (!selections || selections.length === 0) {
14304
15121
  options.stderr("Aborted\n");
14305
15122
  return 1;
@@ -14534,19 +15351,7 @@ function toMessage(error) {
14534
15351
  return error instanceof Error ? error.message : String(error);
14535
15352
  }
14536
15353
  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;
15354
+ return promptForMultipleWorktrees("Choose worktrees to clean", worktrees);
14550
15355
  }
14551
15356
  async function defaultConfirmRemoval(worktrees) {
14552
15357
  const branchCount = worktrees.filter(
@@ -15007,8 +15812,8 @@ async function runConfigCommand(options) {
15007
15812
  `Invalid config arguments: ${[options.action, options.key, options.value].filter(Boolean).join(" ")}`
15008
15813
  );
15009
15814
  }
15010
- function writeJson(stdout, value) {
15011
- stdout(`${JSON.stringify(value, null, 2)}
15815
+ function writeJson(stdout2, value) {
15816
+ stdout2(`${JSON.stringify(value, null, 2)}
15012
15817
  `);
15013
15818
  }
15014
15819
 
@@ -15567,7 +16372,7 @@ function createNewCommand(dependencies = {}) {
15567
16372
  } else {
15568
16373
  const choice = await prompt(worktreePath);
15569
16374
  if (choice === "reuse") {
15570
- appendHistory(worktreePath, worktreeName).catch(() => void 0);
16375
+ await recordWorktreeUsage(worktreePath, worktreeName);
15571
16376
  await writeOutput(worktreePath, options.stdout);
15572
16377
  return 0;
15573
16378
  }
@@ -15635,7 +16440,7 @@ function createNewCommand(dependencies = {}) {
15635
16440
  `
15636
16441
  );
15637
16442
  } else {
15638
- await appendHistory(worktreePath, worktreeName);
16443
+ await recordWorktreeUsage(worktreePath, worktreeName);
15639
16444
  await writeOutput(worktreePath, options.stdout);
15640
16445
  }
15641
16446
  if (options.open) {
@@ -15672,7 +16477,32 @@ function generateBranchPlaceholder(random = Math.random) {
15672
16477
  "newton",
15673
16478
  "lovelace",
15674
16479
  "nietzsche",
15675
- "kafka"
16480
+ "kafka",
16481
+ "sappho",
16482
+ "aristotle",
16483
+ "pythagoras",
16484
+ "artemis",
16485
+ "apollo",
16486
+ "minerva",
16487
+ "persephone",
16488
+ "icarus",
16489
+ "odysseus",
16490
+ "murasaki",
16491
+ "shakespeare",
16492
+ "frida",
16493
+ "davinci",
16494
+ "kepler",
16495
+ "copernicus",
16496
+ "faraday",
16497
+ "noether",
16498
+ "hopper",
16499
+ "boole",
16500
+ "shannon",
16501
+ "gauss",
16502
+ "ramanujan",
16503
+ "austen",
16504
+ "borges",
16505
+ "zeno"
15676
16506
  ];
15677
16507
  const antics = [
15678
16508
  "borrowed-a-bike",
@@ -15689,9 +16519,50 @@ function generateBranchPlaceholder(random = Math.random) {
15689
16519
  "watered-the-plants",
15690
16520
  "washed-the-dishes",
15691
16521
  "folded-the-laundry",
15692
- "took-a-nap"
16522
+ "took-a-nap",
16523
+ "lost-a-sock",
16524
+ "patched-the-boat",
16525
+ "alphabetized-the-spoons",
16526
+ "argued-with-the-calendar",
16527
+ "misplaced-the-moon",
16528
+ "painted-the-fence",
16529
+ "overcooked-the-rice",
16530
+ "packed-the-snacks",
16531
+ "dropped-the-spoon",
16532
+ "hid-the-remote",
16533
+ "untangled-the-cables",
16534
+ "rebooted-the-kettle",
16535
+ "indexed-the-attic",
16536
+ "forgot-the-password",
16537
+ "sorted-the-buttons",
16538
+ "mopped-the-ceiling",
16539
+ "polished-the-doorknob",
16540
+ "misread-the-map",
16541
+ "reheated-the-tea",
16542
+ "fixed-the-squeak",
16543
+ "labeled-the-drawer",
16544
+ "stacked-the-chairs",
16545
+ "overslept-the-standup",
16546
+ "claimed-the-last-bagel",
16547
+ "debugged-the-toaster"
15693
16548
  ];
15694
- return `${pickRandom(roots, random)}-${pickRandom(antics, random)}`;
16549
+ const root = pickRandom(roots, random);
16550
+ const antic = pickRandom(antics, random);
16551
+ const suffix = generateBranchPlaceholderSuffix(random);
16552
+ return `${root}-${antic}-${suffix}`;
16553
+ }
16554
+ function pickRandom(values, random) {
16555
+ const index = Math.floor(random() * values.length);
16556
+ return values[Math.min(index, values.length - 1)];
16557
+ }
16558
+ function generateBranchPlaceholderSuffix(random) {
16559
+ const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
16560
+ let suffix = "";
16561
+ for (let index = 0; index < 3; index += 1) {
16562
+ const characterIndex = Math.floor(random() * characters.length);
16563
+ suffix += characters[Math.min(characterIndex, characters.length - 1)];
16564
+ }
16565
+ return suffix;
15695
16566
  }
15696
16567
  function applyConfiguredBranchPrefix(branch, branchPrefix) {
15697
16568
  if (typeof branchPrefix !== "string" || branchPrefix.length === 0) {
@@ -15732,10 +16603,6 @@ async function defaultPromptForBranch(placeholder) {
15732
16603
  }
15733
16604
  return choice.trim();
15734
16605
  }
15735
- function pickRandom(values, random) {
15736
- const index = Math.floor(random() * values.length);
15737
- return values[Math.min(index, values.length - 1)];
15738
- }
15739
16606
  async function localBranchExists(repoRoot, branchName) {
15740
16607
  try {
15741
16608
  await execFileAsync3(
@@ -15748,8 +16615,8 @@ async function localBranchExists(repoRoot, branchName) {
15748
16615
  return false;
15749
16616
  }
15750
16617
  }
15751
- async function writeOutput(worktreePath, stdout) {
15752
- await writeShellOutput(NEW_OUTPUT_FILE_ENV, worktreePath, stdout);
16618
+ async function writeOutput(worktreePath, stdout2) {
16619
+ await writeShellOutput(NEW_OUTPUT_FILE_ENV, worktreePath, stdout2);
15753
16620
  }
15754
16621
  function isNotRegisteredWorktreeError(error) {
15755
16622
  const stderr = hasExecStderr(error) ? error.stderr : String(error);
@@ -15892,7 +16759,7 @@ async function runWarpNavigate(options) {
15892
16759
  );
15893
16760
  return 0;
15894
16761
  }
15895
- appendHistory(target.path, target.branch).catch(() => void 0);
16762
+ await recordWorktreeUsage(target.path, target.branch);
15896
16763
  await writeShellOutput(WARP_OUTPUT_FILE_ENV, target.path, options.stdout);
15897
16764
  return 0;
15898
16765
  }
@@ -15975,18 +16842,6 @@ async function canonicalizeRepoPath2(repoPath) {
15975
16842
  return resolve5(repoPath);
15976
16843
  }
15977
16844
  }
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
16845
  async function resolveWarpTarget(options) {
15991
16846
  const cmd = options.commandName ?? "gji";
15992
16847
  const emitError4 = (message, hint) => {
@@ -16000,6 +16855,7 @@ async function resolveWarpTarget(options) {
16000
16855
  }
16001
16856
  };
16002
16857
  const registry = await loadRegistry();
16858
+ const currentRoot = await detectRepository(options.cwd).then((repository) => repository.currentRoot).catch(() => null);
16003
16859
  if (registry.length === 0) {
16004
16860
  emitError4(
16005
16861
  "not in a git repository and no repos registered yet.",
@@ -16018,57 +16874,42 @@ async function resolveWarpTarget(options) {
16018
16874
  if (result.status === "rejected") continue;
16019
16875
  const { repoName, worktrees } = result.value;
16020
16876
  for (const worktree of worktrees) {
16021
- allItems.push({ repoName, worktree });
16877
+ allItems.push({
16878
+ repoName,
16879
+ worktree: {
16880
+ ...worktree,
16881
+ isCurrent: currentRoot !== null && worktree.path === currentRoot
16882
+ }
16883
+ });
16022
16884
  }
16023
16885
  }
16024
16886
  if (allItems.length === 0) {
16025
16887
  emitError4("no accessible worktrees found in any registered repo.");
16026
16888
  return null;
16027
16889
  }
16890
+ const promptSources = allItems.map((item) => ({
16891
+ repoName: item.repoName,
16892
+ worktree: item.worktree
16893
+ }));
16028
16894
  if (options.branch) {
16029
- const match = findByQuery(allItems, options.branch);
16895
+ const match = resolveWorktreeQuery(promptSources, options.branch);
16030
16896
  if (!match) {
16031
16897
  emitError4(`no worktree found matching: ${options.branch}`);
16032
16898
  return null;
16033
16899
  }
16034
16900
  return { branch: match.worktree.branch, path: match.worktree.path };
16035
16901
  }
16036
- const path9 = await promptForWarpTarget(allItems);
16902
+ const promptEntries = await buildWorktreePromptEntries(promptSources);
16903
+ const path9 = await promptForWarpTarget(promptEntries);
16037
16904
  if (!path9) {
16038
16905
  options.stderr("Aborted\n");
16039
16906
  return null;
16040
16907
  }
16041
- const chosen = allItems.find((item) => item.worktree.path === path9);
16042
- return { branch: chosen?.worktree.branch ?? null, path: path9 };
16908
+ const chosen = promptEntries.find((item) => item.path === path9);
16909
+ return { branch: chosen?.branch ?? null, path: path9 };
16043
16910
  }
16044
16911
  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}`;
16912
+ return promptForSingleWorktree("Warp to a worktree", items);
16072
16913
  }
16073
16914
 
16074
16915
  // src/go.ts
@@ -16095,7 +16936,7 @@ function createGoCommand(dependencies = {}) {
16095
16936
  commandName: "gji go"
16096
16937
  });
16097
16938
  if (!target) return 1;
16098
- appendHistory(target.path, target.branch).catch(() => void 0);
16939
+ await recordWorktreeUsage(target.path, target.branch);
16099
16940
  await writeShellOutput(GO_OUTPUT_FILE_ENV, target.path, options.stdout);
16100
16941
  return 0;
16101
16942
  }
@@ -16105,8 +16946,14 @@ function createGoCommand(dependencies = {}) {
16105
16946
  );
16106
16947
  return 1;
16107
16948
  }
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;
16949
+ const promptSources = worktrees.map((worktree) => ({
16950
+ repoName: repository.repoName,
16951
+ worktree
16952
+ }));
16953
+ const promptEntries = options.branch ? [] : await buildWorktreePromptEntries(promptSources);
16954
+ const queried = options.branch ? resolveWorktreeQuery(promptSources, options.branch) : null;
16955
+ const prompted = options.branch ? null : await prompt(promptEntries);
16956
+ const resolvedPath = options.branch ? queried?.worktree.path : prompted ?? void 0;
16110
16957
  if (!resolvedPath) {
16111
16958
  if (options.branch) {
16112
16959
  options.stderr(`No worktree found for branch: ${options.branch}
@@ -16135,44 +16982,14 @@ function createGoCommand(dependencies = {}) {
16135
16982
  },
16136
16983
  options.stderr
16137
16984
  );
16138
- appendHistory(resolvedPath, chosenWorktree?.branch ?? null).catch(
16139
- () => void 0
16140
- );
16985
+ await recordWorktreeUsage(resolvedPath, chosenWorktree?.branch ?? null);
16141
16986
  await writeShellOutput(GO_OUTPUT_FILE_ENV, resolvedPath, options.stdout);
16142
16987
  return 0;
16143
16988
  };
16144
16989
  }
16145
16990
  var runGoCommand = createGoCommand();
16146
16991
  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}`;
16992
+ return promptForSingleWorktree("Choose a worktree", worktrees);
16176
16993
  }
16177
16994
 
16178
16995
  // src/history-command.ts
@@ -16623,27 +17440,43 @@ function createOpenCommand(dependencies = {}) {
16623
17440
  detectRepository(options.cwd)
16624
17441
  ]);
16625
17442
  let targetPath;
17443
+ let targetWorktree;
16626
17444
  if (options.branch) {
16627
- const entry = worktrees.find((w2) => w2.branch === options.branch);
16628
- if (!entry) {
17445
+ const match = resolveWorktreeQuery(
17446
+ worktrees.map((worktree) => ({
17447
+ repoName: repository.repoName,
17448
+ worktree
17449
+ })),
17450
+ options.branch
17451
+ );
17452
+ if (!match) {
16629
17453
  options.stderr(
16630
- `gji open: no worktree found for branch: ${options.branch}
17454
+ `gji open: no worktree found matching: ${options.branch}
16631
17455
  `
16632
17456
  );
16633
17457
  options.stderr(`Hint: Use 'gji ls' to see available worktrees
16634
17458
  `);
16635
17459
  return 1;
16636
17460
  }
16637
- targetPath = entry.path;
17461
+ targetPath = match.worktree.path;
17462
+ targetWorktree = match.worktree;
16638
17463
  } else if (isHeadless()) {
16639
- targetPath = worktrees.find((w2) => w2.isCurrent)?.path ?? options.cwd;
17464
+ targetWorktree = worktrees.find((w2) => w2.isCurrent);
17465
+ targetPath = targetWorktree?.path ?? options.cwd;
16640
17466
  } else {
16641
- const chosen = await promptForWorktree2(sortByCurrentFirst(worktrees));
17467
+ const entries = await buildWorktreePromptEntries(
17468
+ worktrees.map((worktree) => ({
17469
+ repoName: repository.repoName,
17470
+ worktree
17471
+ }))
17472
+ );
17473
+ const chosen = await promptForWorktree2(entries);
16642
17474
  if (!chosen) {
16643
17475
  options.stderr("Aborted\n");
16644
17476
  return 1;
16645
17477
  }
16646
17478
  targetPath = chosen;
17479
+ targetWorktree = worktrees.find((w2) => w2.path === chosen);
16647
17480
  }
16648
17481
  const config = await loadEffectiveConfig(
16649
17482
  repository.repoRoot,
@@ -16708,6 +17541,7 @@ function createOpenCommand(dependencies = {}) {
16708
17541
  return 1;
16709
17542
  }
16710
17543
  const displayName = editorDef?.name ?? editorCli;
17544
+ await recordWorktreeUsage(targetPath, targetWorktree?.branch ?? null);
16711
17545
  options.stdout(`Opened ${targetPath} in ${displayName}
16712
17546
  `);
16713
17547
  return 0;
@@ -16732,16 +17566,7 @@ async function isCommandAvailable(command) {
16732
17566
  }
16733
17567
  }
16734
17568
  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;
17569
+ return promptForSingleWorktree("Choose a worktree to open", worktrees);
16745
17570
  }
16746
17571
  async function defaultPromptForEditor(editors) {
16747
17572
  const choice = await ve({
@@ -16835,7 +17660,7 @@ function createPrCommand(dependencies = {}) {
16835
17660
  }
16836
17661
  const choice = await prompt(worktreePath);
16837
17662
  if (choice === "reuse") {
16838
- appendHistory(worktreePath, branchName).catch(() => void 0);
17663
+ await recordWorktreeUsage(worktreePath, branchName);
16839
17664
  await writeOutput2(worktreePath, options.stdout);
16840
17665
  return 0;
16841
17666
  }
@@ -16926,7 +17751,7 @@ function createPrCommand(dependencies = {}) {
16926
17751
  `
16927
17752
  );
16928
17753
  } else {
16929
- await appendHistory(worktreePath, branchName);
17754
+ await recordWorktreeUsage(worktreePath, branchName);
16930
17755
  await writeOutput2(worktreePath, options.stdout);
16931
17756
  }
16932
17757
  return 0;
@@ -16994,8 +17819,8 @@ function sourceRefForForge(forge, prNumber) {
16994
17819
  return `refs/merge-requests/${prNumber}/head`;
16995
17820
  }
16996
17821
  }
16997
- async function writeOutput2(worktreePath, stdout) {
16998
- await writeShellOutput(PR_OUTPUT_FILE_ENV, worktreePath, stdout);
17822
+ async function writeOutput2(worktreePath, stdout2) {
17823
+ await writeShellOutput(PR_OUTPUT_FILE_ENV, worktreePath, stdout2);
16999
17824
  }
17000
17825
 
17001
17826
  // src/remove.ts
@@ -17026,7 +17851,14 @@ function createRemoveCommand(dependencies = {}) {
17026
17851
  }
17027
17852
  return 1;
17028
17853
  }
17029
- const selection = options.branch ?? await promptForWorktree2(sortByCurrentFirst(linkedWorktrees));
17854
+ const selection = options.branch ?? await promptForWorktree2(
17855
+ await buildWorktreePromptEntries(
17856
+ linkedWorktrees.map((worktree2) => ({
17857
+ repoName: repository.repoName,
17858
+ worktree: worktree2
17859
+ }))
17860
+ )
17861
+ );
17030
17862
  if (!selection) {
17031
17863
  options.stderr("Aborted\n");
17032
17864
  return 1;
@@ -17140,15 +17972,7 @@ function createRemoveCommand(dependencies = {}) {
17140
17972
  }
17141
17973
  var runRemoveCommand = createRemoveCommand();
17142
17974
  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;
17975
+ return promptForSingleWorktree("Choose a worktree to finish", worktrees);
17152
17976
  }
17153
17977
  async function defaultConfirmRemoval2(worktree) {
17154
17978
  const choice = await ye({
@@ -17159,8 +17983,8 @@ async function defaultConfirmRemoval2(worktree) {
17159
17983
  });
17160
17984
  return !pD(choice) && choice;
17161
17985
  }
17162
- async function writeOutput3(repoRoot, stdout) {
17163
- await writeShellOutput(REMOVE_OUTPUT_FILE_ENV, repoRoot, stdout);
17986
+ async function writeOutput3(repoRoot, stdout2) {
17987
+ await writeShellOutput(REMOVE_OUTPUT_FILE_ENV, repoRoot, stdout2);
17164
17988
  }
17165
17989
  function emitError2(options, message) {
17166
17990
  if (options.json) {
@@ -17533,17 +18357,17 @@ function readSyncFiles(config) {
17533
18357
  if (!Array.isArray(syncFiles2)) return [];
17534
18358
  return syncFiles2.filter((item) => typeof item === "string");
17535
18359
  }
17536
- function writeSyncFiles(stdout, files, json) {
18360
+ function writeSyncFiles(stdout2, files, json) {
17537
18361
  if (json) {
17538
- stdout(`${JSON.stringify(files, null, 2)}
18362
+ stdout2(`${JSON.stringify(files, null, 2)}
17539
18363
  `);
17540
18364
  return;
17541
18365
  }
17542
18366
  if (files.length === 0) {
17543
- stdout("No sync files configured for this repo.\n");
18367
+ stdout2("No sync files configured for this repo.\n");
17544
18368
  return;
17545
18369
  }
17546
- stdout(`${files.join("\n")}
18370
+ stdout2(`${files.join("\n")}
17547
18371
  `);
17548
18372
  }
17549
18373
  function validatePaths(paths, options) {
@@ -17633,11 +18457,11 @@ async function runCli(argv, options = {}) {
17633
18457
  maybeRegisterCurrentRepo(options.cwd ?? process.cwd());
17634
18458
  const program2 = createProgram();
17635
18459
  const cwd = options.cwd ?? process.cwd();
17636
- const stdout = options.stdout ?? (() => void 0);
18460
+ const stdout2 = options.stdout ?? (() => void 0);
17637
18461
  const stderr = options.stderr ?? (() => void 0);
17638
18462
  program2.configureOutput({
17639
18463
  writeErr: stderr,
17640
- writeOut: stdout
18464
+ writeOut: stdout2
17641
18465
  });
17642
18466
  program2.exitOverride();
17643
18467
  if (argv.length === 0) {
@@ -17645,7 +18469,7 @@ async function runCli(argv, options = {}) {
17645
18469
  return { exitCode: 0 };
17646
18470
  }
17647
18471
  try {
17648
- attachCommandActions(program2, { cwd, stderr, stdout });
18472
+ attachCommandActions(program2, { cwd, stderr, stdout: stdout2 });
17649
18473
  await program2.parseAsync(["node", "gji", ...argv], { from: "node" });
17650
18474
  return { exitCode: 0 };
17651
18475
  } catch (error) {