@n-seiji/nuthatch 0.1.7 → 0.2.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.
@@ -1,17 +1,23 @@
1
- import"./cli-qrxgkskr.js";
1
+ import"./cli-ph0gp56k.js";
2
2
  import {
3
3
  rm2
4
- } from "./cli-23z98dw1.js";
4
+ } from "./cli-r39xsdym.js";
5
5
  import {
6
6
  root2
7
- } from "./cli-ygt2w4e1.js";
7
+ } from "./cli-9y31xrsx.js";
8
8
  import {
9
9
  graphemes,
10
10
  displayWidth,
11
11
  padToWidth,
12
12
  truncateToWidth,
13
- truncateToWidthKeepingTail
14
- } from "./cli-71jgs14h.js";
13
+ truncateToWidthKeepingTail,
14
+ ESC,
15
+ SGR,
16
+ SGR_RESET
17
+ } from "./cli-fhta3tr8.js";
18
+ import {
19
+ colorEnabled
20
+ } from "./cli-26d2drt6.js";
15
21
  import {
16
22
  render,
17
23
  reportResult
@@ -19,11 +25,14 @@ import {
19
25
  import {
20
26
  loadRepoContext,
21
27
  worktreeDirtyState
22
- } from "./cli-7jg5qqs9.js";
28
+ } from "./cli-fqz7emfm.js";
23
29
  import {
24
30
  EXIT_CANCELLED,
25
31
  ok
26
- } from "./cli-2bhb9p0x.js";
32
+ } from "./cli-77yanq37.js";
33
+ import {
34
+ shortenHome
35
+ } from "./cli-s5qcjcwk.js";
27
36
 
28
37
  // src/domain/candidates.ts
29
38
  var buildPickCandidates = (worktrees, dirtyByPath, localBranches, remoteBranches) => {
@@ -72,30 +81,16 @@ var pick = async (git, fs, options) => {
72
81
  // src/ui/picker-render.ts
73
82
  import { homedir } from "node:os";
74
83
 
75
- // src/ui/ansi.ts
76
- var ESC = "\x1B";
77
-
78
84
  // src/ui/picker-frame.ts
79
85
  var CURSOR_HOME = `${ESC}[H`;
80
86
  var CLEAR_TO_END = `${ESC}[J`;
81
- var SGR_RESET = `${ESC}[0m`;
82
- var SGR_CODES = {
83
- plain: "",
84
- cyan: `${ESC}[36m`,
85
- dim: `${ESC}[2m`,
86
- "bold-dim": `${ESC}[1m${ESC}[2m`,
87
- inverse: `${ESC}[7m`,
88
- red: `${ESC}[31m`,
89
- green: `${ESC}[32m`,
90
- yellow: `${ESC}[33m`,
91
- magenta: `${ESC}[35m`
92
- };
87
+ var sgrCode = (style) => style === "bold-dim" ? `${SGR.bold}${SGR.dim}` : SGR[style];
93
88
  var renderSpan = (span, colorEnabled) => {
94
89
  const style = span.style ?? "plain";
95
90
  if (!colorEnabled || style === "plain") {
96
91
  return span.text;
97
92
  }
98
- return `${SGR_CODES[style]}${span.text}${SGR_RESET}`;
93
+ return `${sgrCode(style)}${span.text}${SGR_RESET}`;
99
94
  };
100
95
  var lineWidth = (line) => line.reduce((total, span) => total + displayWidth(span.text), 0);
101
96
  var renderLine = (line, colorEnabled) => line.map((span) => renderSpan(span, colorEnabled)).join("");
@@ -268,20 +263,16 @@ var sortCandidatesForDisplay = (candidates) => [
268
263
  ...candidates.filter((candidate) => isCreatableCandidate(candidate)).toSorted((a, b) => compareCreatableCandidates(a, b))
269
264
  ];
270
265
  var KIND_COLUMN_WIDTH = Math.max(...Object.values(WORKTREE_KIND_LABELS).map((label) => label.length), ...Object.values(CREATABLE_SOURCE_LABELS).map((label) => label.length));
271
- var shortenPath = (path, homeDir, maxLength = MAX_PATH_LENGTH) => {
272
- const withTilde = homeDir.length > 0 && (path === homeDir || path.startsWith(`${homeDir}/`)) ? `~${path.slice(homeDir.length)}` : path;
273
- return truncateToWidthKeepingTail(withTilde, maxLength);
274
- };
266
+ var shortenPath = (path, homeDir, maxLength = MAX_PATH_LENGTH) => truncateToWidthKeepingTail(shortenHome(path, homeDir), maxLength);
275
267
  var candidatePathLabel = (candidate, homeDir, maxLength) => candidate.kind === "worktree" ? shortenPath(candidate.worktree.path, homeDir, maxLength) : "";
276
268
  var rawBranchColumnWidth = (candidates) => candidates.reduce((max, candidate) => Math.max(max, displayWidth(candidateBranchLabel(candidate))), 0);
277
269
  var branchColumnWidth = (candidates) => Math.min(MAX_BRANCH_COLUMN_WIDTH, rawBranchColumnWidth(candidates));
278
- var padBranchLabel = (label, width) => padToWidth(label, width);
279
270
  var toCandidateRow = (candidate, options) => ({
280
271
  kind: "candidate",
281
272
  index: options.index,
282
273
  section: options.section,
283
274
  statusMarker: statusMarker(candidate),
284
- branchLabel: padBranchLabel(truncateToWidth(candidateBranchLabel(candidate), options.branchWidth), options.branchWidth),
275
+ branchLabel: padToWidth(truncateToWidth(candidateBranchLabel(candidate), options.branchWidth), options.branchWidth),
285
276
  kindLabel: candidateKindLabel(candidate).padEnd(KIND_COLUMN_WIDTH, " "),
286
277
  pathLabel: candidatePathLabel(candidate, options.homeDir, options.pathMaxLength)
287
278
  });
@@ -575,8 +566,6 @@ var renderPickerFrame = ({
575
566
  };
576
567
 
577
568
  // src/ui/picker-keys.ts
578
- var isCtrlJByte = (input) => input === `
579
- `;
580
569
  var resolvePickerKeyAction = (input, key) => {
581
570
  if (key.escape) {
582
571
  return { type: "cancel", reason: "esc" };
@@ -599,7 +588,7 @@ var resolvePickerKeyAction = (input, key) => {
599
588
  if (key.upArrow || key.ctrl && (input === "p" || input === "k")) {
600
589
  return { type: "up" };
601
590
  }
602
- if (key.downArrow || key.ctrl && (input === "n" || input === "j") || isCtrlJByte(input)) {
591
+ if (key.downArrow || key.ctrl && input === "n") {
603
592
  return { type: "down" };
604
593
  }
605
594
  if (key.ctrl && input === "u") {
@@ -608,7 +597,7 @@ var resolvePickerKeyAction = (input, key) => {
608
597
  if (key.backspace || key.delete) {
609
598
  return { type: "backspace" };
610
599
  }
611
- if (input.length > 0 && !key.ctrl && !key.meta) {
600
+ if (input.length > 0 && !key.ctrl) {
612
601
  return { type: "char", char: input };
613
602
  }
614
603
  return { type: "ignore" };
@@ -624,17 +613,17 @@ var resolvePanelKeyAction = (input, key) => {
624
613
  if (key.upArrow || key.ctrl && (input === "p" || input === "k")) {
625
614
  return { type: "up" };
626
615
  }
627
- if (key.downArrow || key.ctrl && (input === "n" || input === "j") || isCtrlJByte(input)) {
616
+ if (key.downArrow || key.ctrl && input === "n") {
628
617
  return { type: "down" };
629
618
  }
630
- if (!key.ctrl && !key.meta && isPanelLetterShortcut(input)) {
619
+ if (!key.ctrl && isPanelLetterShortcut(input)) {
631
620
  return { type: "letter", char: input };
632
621
  }
633
622
  return { type: "ignore" };
634
623
  };
635
624
  var isPanelLetterShortcut = (input) => PANEL_LETTER_SHORTCUTS.includes(input);
636
625
  var resolveConfirmKeyAction = (input, key) => {
637
- if (!key.ctrl && !key.meta && (input === "y" || input === "Y")) {
626
+ if (!key.ctrl && (input === "y" || input === "Y")) {
638
627
  return { type: "yes" };
639
628
  }
640
629
  return { type: "no" };
@@ -789,20 +778,14 @@ var createFilteredSelector = () => {
789
778
  var runMutation = async (callbacks, candidate, action) => {
790
779
  if (action === "switchRoot") {
791
780
  const result = await callbacks.switchRootHere(candidate);
792
- if (!result.ok || result.path === undefined) {
793
- return {
794
- type: "patch",
795
- patch: panelErrorTransition(candidate, result.message ?? "Failed to switch root.")
796
- };
781
+ if (!result.ok) {
782
+ return { type: "patch", patch: panelErrorTransition(candidate, result.message) };
797
783
  }
798
784
  return { type: "exit", outcome: { type: "path", path: result.path } };
799
785
  }
800
786
  const result = await callbacks.deleteWorktree(candidate);
801
787
  if (!result.ok) {
802
- return {
803
- type: "patch",
804
- patch: panelErrorTransition(candidate, result.message ?? "Failed to delete worktree.")
805
- };
788
+ return { type: "patch", patch: panelErrorTransition(candidate, result.message) };
806
789
  }
807
790
  const fresh = await callbacks.reloadCandidates();
808
791
  return {
@@ -953,7 +936,6 @@ var leaveAltScreen = (target) => {
953
936
  // src/ui/picker-key-event.ts
954
937
  var NO_MODIFIERS = {
955
938
  ctrl: false,
956
- meta: false,
957
939
  escape: false,
958
940
  return: false,
959
941
  tab: false,
@@ -968,89 +950,40 @@ var event = (input, key = {}) => ({
968
950
  input,
969
951
  key: { ...NO_MODIFIERS, ...key }
970
952
  });
971
- var withMeta = (keyEvent) => ({
972
- input: keyEvent.input,
973
- key: { ...keyEvent.key, meta: true }
974
- });
975
953
 
976
954
  // src/ui/picker-key-csi.ts
977
- var ARROW_FINAL_BYTE_A = "A".codePointAt(0);
978
- var ARROW_FINAL_BYTE_B = "B".codePointAt(0);
979
- var ARROW_FINAL_BYTE_C = "C".codePointAt(0);
980
- var ARROW_FINAL_BYTE_D = "D".codePointAt(0);
981
- var arrowModifierKey = (finalByte) => {
982
- if (finalByte === ARROW_FINAL_BYTE_A) {
983
- return "upArrow";
984
- }
985
- if (finalByte === ARROW_FINAL_BYTE_B) {
986
- return "downArrow";
987
- }
988
- if (finalByte === ARROW_FINAL_BYTE_C) {
989
- return "rightArrow";
990
- }
991
- if (finalByte === ARROW_FINAL_BYTE_D) {
992
- return "leftArrow";
993
- }
994
- return null;
995
- };
996
- var modifierBitsFromParam = (param) => {
997
- const bits = param - 1;
998
- const CTRL_BIT = 4;
999
- const ALT_BIT = 2;
1000
- return { ctrl: (bits & CTRL_BIT) !== 0, meta: (bits & ALT_BIT) !== 0 };
1001
- };
955
+ var ARROW_KEYS = new Map([
956
+ ["A", "upArrow"],
957
+ ["B", "downArrow"],
958
+ ["C", "rightArrow"],
959
+ ["D", "leftArrow"]
960
+ ]);
1002
961
  var CSI_FINAL_BYTE_MIN = 64;
1003
962
  var CSI_FINAL_BYTE_MAX = 126;
1004
963
  var isCsiFinalByte = (byte) => byte >= CSI_FINAL_BYTE_MIN && byte <= CSI_FINAL_BYTE_MAX;
1005
- var CTRL_C_BYTE = 3;
1006
- var BRACKETED_PASTE_START = "200~";
1007
- var BRACKETED_PASTE_END = "201~";
1008
- var modifiedArrowOrDelete = (paramText, finalByte) => {
1009
- const MODIFIED_PATTERN = /^\d+;\d+$/u;
1010
- const DELETE_PARAM = "3";
1011
- if (finalByte === "~" && (paramText === DELETE_PARAM || paramText.startsWith(`${DELETE_PARAM};`))) {
1012
- return event("", { delete: true });
1013
- }
1014
- const arrowKey = arrowModifierKey(finalByte.codePointAt(0));
1015
- if (arrowKey === null || !(paramText === "" || MODIFIED_PATTERN.test(paramText))) {
1016
- return null;
964
+ var DELETE_PARAM = "3";
965
+ var csiEvent = (paramText, finalByte) => {
966
+ if (finalByte === "~") {
967
+ const [first] = paramText.split(";");
968
+ return first === DELETE_PARAM ? event("", { delete: true }) : null;
1017
969
  }
1018
- const [, modifierParam] = paramText.split(";");
1019
- const mods = modifierParam === undefined ? {} : modifierBitsFromParam(Number(modifierParam));
1020
- return event("", { [arrowKey]: true, ...mods });
970
+ const arrowKey = ARROW_KEYS.get(finalByte);
971
+ return arrowKey === undefined ? null : event("", { [arrowKey]: true });
1021
972
  };
1022
973
  var parseCsi = (buf) => {
1023
974
  const CSI_PREFIX_LENGTH = 2;
1024
975
  let index = CSI_PREFIX_LENGTH;
1025
- let paramText = "";
1026
976
  while (index < buf.length && !isCsiFinalByte(buf[index])) {
1027
- const paramByte = buf[index];
1028
- if (paramByte === CTRL_C_BYTE) {
1029
- return {
1030
- kind: "interrupted",
1031
- consumed: index + 1,
1032
- event: event("c", { ctrl: true })
1033
- };
1034
- }
1035
- paramText += String.fromCodePoint(paramByte);
1036
977
  index += 1;
1037
978
  }
1038
979
  if (index >= buf.length) {
1039
980
  return { kind: "incomplete" };
1040
981
  }
1041
- const finalByte = String.fromCodePoint(buf[index]);
1042
- const consumed = index + 1;
1043
- const full = paramText + finalByte;
1044
- if (full === BRACKETED_PASTE_START) {
1045
- return { kind: "resolved", consumed, event: null, pasteStarted: true };
1046
- }
1047
- if (full === BRACKETED_PASTE_END) {
1048
- return { kind: "resolved", consumed, event: null, pasteEnded: true };
1049
- }
982
+ const paramText = buf.subarray(CSI_PREFIX_LENGTH, index).toString("latin1");
1050
983
  return {
1051
984
  kind: "resolved",
1052
- consumed,
1053
- event: modifiedArrowOrDelete(paramText, finalByte)
985
+ consumed: index + 1,
986
+ event: csiEvent(paramText, String.fromCodePoint(buf[index]))
1054
987
  };
1055
988
  };
1056
989
  var SS3_SEQUENCE_LENGTH = 3;
@@ -1058,19 +991,11 @@ var parseSs3 = (buf) => {
1058
991
  if (buf.length < SS3_SEQUENCE_LENGTH) {
1059
992
  return { kind: "incomplete" };
1060
993
  }
1061
- const finalByte = buf[2];
1062
- if (finalByte === CTRL_C_BYTE) {
1063
- return {
1064
- kind: "interrupted",
1065
- consumed: SS3_SEQUENCE_LENGTH,
1066
- event: event("c", { ctrl: true })
1067
- };
1068
- }
1069
- const arrowKey = arrowModifierKey(finalByte);
994
+ const arrowKey = ARROW_KEYS.get(String.fromCodePoint(buf[2]));
1070
995
  return {
1071
996
  kind: "resolved",
1072
997
  consumed: SS3_SEQUENCE_LENGTH,
1073
- event: arrowKey === null ? null : event("", { [arrowKey]: true })
998
+ event: arrowKey === undefined ? null : event("", { [arrowKey]: true })
1074
999
  };
1075
1000
  };
1076
1001
 
@@ -1124,13 +1049,11 @@ var isPrintableChar = (char) => {
1124
1049
  return codePoint < C1_CONTROL_MIN || codePoint > C1_CONTROL_MAX;
1125
1050
  };
1126
1051
  var pastedTextToEvents = (text) => [...text].filter((char) => isPrintableChar(char)).map((char) => event(char));
1052
+ var PASTE_START_MARKER = Buffer.from("\x1B[200~", "latin1");
1053
+ var PASTE_END_MARKER = Buffer.from("\x1B[201~", "latin1");
1127
1054
  var CSI_INTRODUCER_CODE = "[".codePointAt(0);
1128
1055
  var SS3_INTRODUCER_CODE = "O".codePointAt(0);
1129
1056
  var MAX_PENDING_SEQUENCE_BYTES = 32;
1130
- var PASTE_END_MARKER = Buffer.concat([
1131
- Buffer.from([ESC2]),
1132
- Buffer.from(`[${BRACKETED_PASTE_END}`)
1133
- ]);
1134
1057
 
1135
1058
  class PickerKeyParser {
1136
1059
  buffer = Buffer.alloc(0);
@@ -1166,11 +1089,9 @@ class PickerKeyParser {
1166
1089
  this.inPaste = false;
1167
1090
  return pastedTextToEvents(text);
1168
1091
  }
1169
- const maxOverlap = Math.min(PASTE_END_MARKER.length - 1, this.buffer.length);
1170
1092
  let overlap = 0;
1171
- for (let candidate = maxOverlap;candidate > 0; candidate -= 1) {
1172
- const tail = this.buffer.subarray(this.buffer.length - candidate);
1173
- if (tail.equals(PASTE_END_MARKER.subarray(0, candidate))) {
1093
+ for (let candidate = Math.min(PASTE_END_MARKER.length - 1, this.buffer.length);candidate > 0; candidate -= 1) {
1094
+ if (this.buffer.subarray(this.buffer.length - candidate).equals(PASTE_END_MARKER.subarray(0, candidate))) {
1174
1095
  overlap = candidate;
1175
1096
  break;
1176
1097
  }
@@ -1197,8 +1118,7 @@ class PickerKeyParser {
1197
1118
  }
1198
1119
  if (first === LF) {
1199
1120
  this.buffer = this.buffer.subarray(1);
1200
- return [event(`
1201
- `)];
1121
+ return [];
1202
1122
  }
1203
1123
  if (first === TAB) {
1204
1124
  this.buffer = this.buffer.subarray(1);
@@ -1224,14 +1144,18 @@ class PickerKeyParser {
1224
1144
  }
1225
1145
  const second = this.buffer[1];
1226
1146
  if (second === CSI_INTRODUCER_CODE) {
1147
+ if (this.buffer.subarray(0, PASTE_START_MARKER.length).equals(PASTE_START_MARKER)) {
1148
+ this.buffer = this.buffer.subarray(PASTE_START_MARKER.length);
1149
+ this.inPaste = true;
1150
+ return [];
1151
+ }
1227
1152
  return this.applySequence(parseCsi(this.buffer));
1228
1153
  }
1229
1154
  if (second === SS3_INTRODUCER_CODE) {
1230
1155
  return this.applySequence(parseSs3(this.buffer));
1231
1156
  }
1232
1157
  this.buffer = this.buffer.subarray(1);
1233
- const inner = this.stepUtf8Char();
1234
- return inner === null ? null : inner.map((keyEvent) => withMeta(keyEvent));
1158
+ return [event("", { escape: true })];
1235
1159
  }
1236
1160
  applySequence(result) {
1237
1161
  if (result.kind === "incomplete") {
@@ -1240,17 +1164,7 @@ class PickerKeyParser {
1240
1164
  }
1241
1165
  return null;
1242
1166
  }
1243
- if (result.kind === "interrupted") {
1244
- this.buffer = this.buffer.subarray(result.consumed);
1245
- return [result.event];
1246
- }
1247
1167
  this.buffer = this.buffer.subarray(result.consumed);
1248
- if (result.pasteStarted === true) {
1249
- this.inPaste = true;
1250
- }
1251
- if (result.pasteEnded === true) {
1252
- this.inPaste = false;
1253
- }
1254
1168
  return result.event === null ? [] : [result.event];
1255
1169
  }
1256
1170
  stepUtf8Char() {
@@ -1432,7 +1346,6 @@ var runTerminalSession = (createHandlers, streams = defaultStreams()) => new Pro
1432
1346
  // src/ui/picker.ts
1433
1347
  var DEFAULT_TERMINAL_WIDTH = 80;
1434
1348
  var terminalDimension = (value, fallback) => value !== undefined && value > 0 ? value : fallback;
1435
- var colorEnabled = (stderr) => stderr.isTTY === true && (process.env["NO_COLOR"] ?? "") === "";
1436
1349
  var isCtrlC = (event) => event.key.ctrl && event.input === "c";
1437
1350
  var runPicker = (candidates, callbacks) => runTerminalSession(({ requestRender, finish }) => {
1438
1351
  const store = createPickerStore(candidates, callbacks, (outcome) => finish(outcome), (reason) => finish({ type: "cancelled", reason }));
@@ -1473,8 +1386,7 @@ var deleteWorktree = async (git, fs, candidate) => {
1473
1386
  branch,
1474
1387
  ...candidate.kind === "worktree" ? { expectedPath: candidate.worktree.path } : {},
1475
1388
  allowPrunable: candidate.kind === "worktree" && candidate.worktree.prunable,
1476
- force: false,
1477
- ext: false
1389
+ force: false
1478
1390
  });
1479
1391
  if (!result.ok) {
1480
1392
  return {
@@ -1500,7 +1412,7 @@ var createPickerCallbacks = (git, fs, json, onSwitchedBranch) => ({
1500
1412
  allowExternalHolderSwap: candidate.kind === "worktree" && candidate.worktree.kind === "external",
1501
1413
  ...candidate.kind === "worktree" ? { expectedHolderPath: candidate.worktree.path } : {}
1502
1414
  });
1503
- if (!result.ok) {
1415
+ if (!result.ok || result.path === undefined) {
1504
1416
  return {
1505
1417
  ok: false,
1506
1418
  message: result.errorMessage ?? "Failed to switch root."
@@ -1511,10 +1423,7 @@ var createPickerCallbacks = (git, fs, json, onSwitchedBranch) => ({
1511
1423
  detachedHolder: result.data?.detachedHolder ?? null,
1512
1424
  warnings: result.warnings ?? []
1513
1425
  });
1514
- return {
1515
- ok: true,
1516
- ...result.path === undefined ? {} : { path: result.path }
1517
- };
1426
+ return { ok: true, path: result.path };
1518
1427
  },
1519
1428
  reloadCandidates: async () => await loadPickCandidates(git, fs, json) ?? []
1520
1429
  });
@@ -1,24 +1,25 @@
1
1
  import {
2
2
  messageOf
3
- } from "./cli-qrxgkskr.js";
3
+ } from "./cli-ph0gp56k.js";
4
4
  import {
5
5
  withRepoLock
6
- } from "./cli-rcw5adqd.js";
6
+ } from "./cli-wcnyhk4w.js";
7
7
  import {
8
8
  loadRepoContext,
9
9
  nestedWorktrees,
10
10
  worktreeDirtyState
11
- } from "./cli-7jg5qqs9.js";
11
+ } from "./cli-fqz7emfm.js";
12
12
  import {
13
13
  EXIT_GENERAL_ERROR,
14
14
  EXIT_USAGE_ERROR,
15
15
  EXIT_SAFE_REJECTION,
16
16
  ok,
17
- fail
18
- } from "./cli-2bhb9p0x.js";
17
+ fail,
18
+ pass,
19
+ reject
20
+ } from "./cli-77yanq37.js";
19
21
 
20
22
  // src/commands/rm.ts
21
- var EXT_DEPRECATION_WARNING = "--ext is deprecated and no longer required: hop rm can now remove external worktrees without it.";
22
23
  var lockedRejection = (branch, lockReason) => fail(EXIT_SAFE_REJECTION, `Worktree for "${branch}" is locked by git${lockReason === null ? "" : ` (${lockReason})`}. hop never unlocks worktrees automatically — run "git worktree unlock" yourself first if you're sure.`);
23
24
  var turnedPrunableRejection = (branch) => fail(EXIT_SAFE_REJECTION, `The selected worktree for "${branch}" changed before removal (git now reports it prunable). Refresh the picker and retry.`);
24
25
  var stalePrunableWarning = ({ path, prunableReason }) => `Worktree at ${path} no longer exists${prunableReason === null ? "" : ` (${prunableReason})`}; removed its stale registration — if it had been moved by hand, the moved copy is no longer linked to this repository.`;
@@ -26,17 +27,13 @@ var resolveTarget = (worktrees, branch, expectedPath) => {
26
27
  const matches = worktrees.filter((wt) => wt.branch === branch);
27
28
  if (expectedPath !== undefined) {
28
29
  const target = matches.find((wt) => wt.path === expectedPath);
29
- return target === undefined ? {
30
- rejection: fail(EXIT_SAFE_REJECTION, `The selected worktree for "${branch}" changed before removal. Refresh the picker and retry.`)
31
- } : { target };
30
+ return target === undefined ? reject(fail(EXIT_SAFE_REJECTION, `The selected worktree for "${branch}" changed before removal. Refresh the picker and retry.`)) : pass(target);
32
31
  }
33
32
  if (matches.length > 1) {
34
- return {
35
- rejection: fail(EXIT_SAFE_REJECTION, `Branch "${branch}" is checked out at ${matches.length} worktrees (${matches.map((wt) => wt.path).join(", ")}). Refusing to guess which one to remove.`)
36
- };
33
+ return reject(fail(EXIT_SAFE_REJECTION, `Branch "${branch}" is checked out at ${matches.length} worktrees (${matches.map((wt) => wt.path).join(", ")}). Refusing to guess which one to remove.`));
37
34
  }
38
35
  const [target] = matches;
39
- return target === undefined ? {} : { target };
36
+ return target === undefined ? reject(fail(EXIT_GENERAL_ERROR, `No worktree found for branch "${branch}".`)) : pass(target);
40
37
  };
41
38
  var nestedWorktreeRejection = (branch, targetPath, worktrees) => {
42
39
  const nested = nestedWorktrees(worktrees, targetPath);
@@ -67,26 +64,16 @@ var targetSafetyRejection = async ({ git, fs, context, options }, target) => {
67
64
  var validateRemoval = async (check) => {
68
65
  const { context, options } = check;
69
66
  const resolved = resolveTarget(context.worktrees, options.branch, options.expectedPath);
70
- if (resolved.rejection !== undefined) {
71
- return { ok: false, rejection: resolved.rejection };
72
- }
73
- const { target } = resolved;
74
- if (target === undefined) {
75
- return {
76
- ok: false,
77
- rejection: fail(EXIT_GENERAL_ERROR, `No worktree found for branch "${options.branch}".`)
78
- };
67
+ if (!resolved.ok) {
68
+ return resolved;
79
69
  }
70
+ const target = resolved.value;
80
71
  if (target.kind === "root") {
81
- return { ok: false, rejection: fail(EXIT_USAGE_ERROR, "Cannot remove the root clone.") };
72
+ return reject(fail(EXIT_USAGE_ERROR, "Cannot remove the root clone."));
82
73
  }
83
74
  const rejection = await targetSafetyRejection(check, target);
84
- return rejection === null ? { ok: true, target } : { ok: false, rejection };
75
+ return rejection === null ? resolved : reject(rejection);
85
76
  };
86
- var withExtWarning = (ext, result) => ext ? {
87
- ...result,
88
- warnings: [...result.warnings ?? [], EXT_DEPRECATION_WARNING]
89
- } : result;
90
77
  var removeWorktree = async (git, fs, options) => {
91
78
  const context = await loadRepoContext(git, fs, options.cwd);
92
79
  const validation = await validateRemoval({ git, fs, context, options });
@@ -100,7 +87,7 @@ var removeWorktree = async (git, fs, options) => {
100
87
  if (!freshValidation.ok) {
101
88
  return freshValidation.rejection;
102
89
  }
103
- const { target } = freshValidation;
90
+ const target = freshValidation.value;
104
91
  await git.removeWorktree(context.rootPath, target.path, options.force);
105
92
  return ok({
106
93
  data: { branch: options.branch, path: target.path },
@@ -111,6 +98,6 @@ var removeWorktree = async (git, fs, options) => {
111
98
  }
112
99
  });
113
100
  };
114
- var rm2 = async (git, fs, options) => withExtWarning(options.ext, await removeWorktree(git, fs, options));
101
+ var rm2 = (git, fs, options) => removeWorktree(git, fs, options);
115
102
 
116
103
  export { rm2 };
@@ -0,0 +1,25 @@
1
+ // src/domain/posix-path.ts
2
+ var withoutTrailingSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
3
+ var shortenHome = (path, home) => {
4
+ if (home === "" || home === "/") {
5
+ return path;
6
+ }
7
+ if (path === home) {
8
+ return "~";
9
+ }
10
+ return path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
11
+ };
12
+ var parentDir = (path) => {
13
+ const trimmed = withoutTrailingSlash(path);
14
+ const index = trimmed.lastIndexOf("/");
15
+ if (index === -1) {
16
+ return ".";
17
+ }
18
+ return index === 0 ? "/" : trimmed.slice(0, index);
19
+ };
20
+ var absoluteCandidates = (dirs, name) => [
21
+ ...new Set(dirs.map((entry) => entry.trim()).filter((entry) => entry.startsWith("/")).map((dir) => `${dir.endsWith("/") ? dir.slice(0, -1) : dir}/${name}`))
22
+ ];
23
+ var splitPathEnv = (pathEnv) => (pathEnv ?? "").split(":");
24
+
25
+ export { shortenHome, parentDir, absoluteCandidates, splitPathEnv };