@n-seiji/nuthatch 0.1.5 → 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.
- package/README.ja.md +21 -21
- package/README.md +21 -21
- package/dist/clean-dkrfmz85.js +207 -0
- package/dist/cli-26d2drt6.js +30 -0
- package/dist/cli-77yanq37.js +21 -0
- package/dist/cli-9y31xrsx.js +187 -0
- package/dist/cli-a56b2ar0.js +20 -0
- package/dist/cli-b0tk5wcx.js +86 -0
- package/dist/cli-btqjpf1q.js +35 -0
- package/dist/cli-dbzx9nzg.js +40 -0
- package/dist/cli-fhta3tr8.js +126 -0
- package/dist/cli-fqz7emfm.js +88 -0
- package/dist/cli-mfz1rx2x.js +87 -0
- package/dist/cli-ph0gp56k.js +54 -0
- package/dist/cli-pick-wdtja21m.js +1442 -0
- package/dist/cli-r39xsdym.js +103 -0
- package/dist/cli-s5qcjcwk.js +25 -0
- package/dist/cli-update-kepe7h0z.js +639 -0
- package/dist/cli-wcnyhk4w.js +124 -0
- package/dist/cli.js +241 -5864
- package/{shell/init.zsh → dist/init-2x8g9295.js} +14 -4
- package/dist/jump-tnpgxcsz.js +103 -0
- package/dist/ls-ky84e8ep.js +34 -0
- package/dist/render-ls-x4hpnsvy.js +149 -0
- package/dist/rm-0g8cxees.js +7 -0
- package/dist/root-czmzc98y.js +7 -0
- package/package.json +4 -6
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/domain/porcelain.ts
|
|
2
|
+
var REFS_HEADS_PREFIX = "refs/heads/";
|
|
3
|
+
var branchNameFromRef = (ref) => ref.startsWith(REFS_HEADS_PREFIX) ? ref.slice(REFS_HEADS_PREFIX.length) : ref;
|
|
4
|
+
var parseRecord = (lines) => {
|
|
5
|
+
let path = null;
|
|
6
|
+
let head = null;
|
|
7
|
+
let branch = null;
|
|
8
|
+
let detached = false;
|
|
9
|
+
let bare = false;
|
|
10
|
+
let locked = false;
|
|
11
|
+
let lockReason = null;
|
|
12
|
+
let prunable = false;
|
|
13
|
+
let prunableReason = null;
|
|
14
|
+
for (const line of lines.filter((candidate) => candidate.length > 0)) {
|
|
15
|
+
const spaceIndex = line.indexOf(" ");
|
|
16
|
+
const key = spaceIndex === -1 ? line : line.slice(0, spaceIndex);
|
|
17
|
+
const value = spaceIndex === -1 ? "" : line.slice(spaceIndex + 1);
|
|
18
|
+
switch (key) {
|
|
19
|
+
case "worktree": {
|
|
20
|
+
path = value;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case "HEAD": {
|
|
24
|
+
head = value;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
case "branch": {
|
|
28
|
+
branch = branchNameFromRef(value);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case "detached": {
|
|
32
|
+
detached = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case "bare": {
|
|
36
|
+
bare = true;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case "locked": {
|
|
40
|
+
locked = true;
|
|
41
|
+
lockReason = value.length > 0 ? value : null;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case "prunable": {
|
|
45
|
+
prunable = true;
|
|
46
|
+
prunableReason = value.length > 0 ? value : null;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
default: {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (path === null) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
path,
|
|
59
|
+
head,
|
|
60
|
+
branch,
|
|
61
|
+
detached,
|
|
62
|
+
bare,
|
|
63
|
+
locked,
|
|
64
|
+
lockReason,
|
|
65
|
+
prunable,
|
|
66
|
+
prunableReason
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
var parsePorcelain = (output) => {
|
|
70
|
+
if (output.length === 0) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const trimmed = output.endsWith("\x00") ? output.slice(0, -1) : output;
|
|
74
|
+
const records = trimmed.split("\x00\x00");
|
|
75
|
+
const result = [];
|
|
76
|
+
for (const record of records.filter((candidate) => candidate.length > 0)) {
|
|
77
|
+
const lines = record.split("\x00");
|
|
78
|
+
const parsed = parseRecord(lines);
|
|
79
|
+
if (parsed !== null) {
|
|
80
|
+
result.push(parsed);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { REFS_HEADS_PREFIX, parsePorcelain };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REFS_HEADS_PREFIX
|
|
3
|
+
} from "./cli-b0tk5wcx.js";
|
|
4
|
+
|
|
5
|
+
// src/domain/upstream.ts
|
|
6
|
+
var NONE = { kind: "none" };
|
|
7
|
+
var FIELD_SEPARATOR = "\x00";
|
|
8
|
+
var UPSTREAM_FORMAT = "%(refname)%00%(upstream)%00%(upstream:track,nobracket)";
|
|
9
|
+
var countOf = (track, word) => {
|
|
10
|
+
const match = new RegExp(`${word} (?<count>\\d+)`, "u").exec(track);
|
|
11
|
+
return match?.groups?.count === undefined ? 0 : Number(match.groups.count);
|
|
12
|
+
};
|
|
13
|
+
var parseTrack = (upstream, track) => {
|
|
14
|
+
if (upstream.length === 0) {
|
|
15
|
+
return NONE;
|
|
16
|
+
}
|
|
17
|
+
if (track === "gone") {
|
|
18
|
+
return { kind: "gone" };
|
|
19
|
+
}
|
|
20
|
+
return { kind: "tracking", ahead: countOf(track, "ahead"), behind: countOf(track, "behind") };
|
|
21
|
+
};
|
|
22
|
+
var parseUpstreamStates = (output) => {
|
|
23
|
+
const states = new Map;
|
|
24
|
+
for (const line of output.split(`
|
|
25
|
+
`)) {
|
|
26
|
+
const [refname = "", upstream = "", track = ""] = line.split(FIELD_SEPARATOR);
|
|
27
|
+
if (refname.startsWith(REFS_HEADS_PREFIX)) {
|
|
28
|
+
states.set(refname.slice(REFS_HEADS_PREFIX.length), parseTrack(upstream, track.trim()));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return states;
|
|
32
|
+
};
|
|
33
|
+
var upstreamStateOf = (states, branch) => states.get(branch) ?? NONE;
|
|
34
|
+
|
|
35
|
+
export { UPSTREAM_FORMAT, parseUpstreamStates, upstreamStateOf };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/render.ts
|
|
2
|
+
var PLAIN_JSON_INDENT = 2;
|
|
3
|
+
var render = (command, result, json) => {
|
|
4
|
+
if (json) {
|
|
5
|
+
const envelope = {
|
|
6
|
+
schemaVersion: 1,
|
|
7
|
+
command,
|
|
8
|
+
data: result.ok ? result.data : undefined,
|
|
9
|
+
warnings: result.warnings ?? []
|
|
10
|
+
};
|
|
11
|
+
process.stdout.write(`${JSON.stringify(envelope)}
|
|
12
|
+
`);
|
|
13
|
+
if (!result.ok && result.errorMessage !== undefined) {
|
|
14
|
+
process.stderr.write(`${result.errorMessage}
|
|
15
|
+
`);
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (result.path !== undefined) {
|
|
20
|
+
process.stdout.write(`${result.path}
|
|
21
|
+
`);
|
|
22
|
+
} else if (result.ok && result.data !== undefined) {
|
|
23
|
+
process.stdout.write(`${JSON.stringify(result.data, null, PLAIN_JSON_INDENT)}
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
for (const warning of result.warnings ?? []) {
|
|
27
|
+
process.stderr.write(`warning: ${warning}
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
if (!result.ok && result.errorMessage !== undefined) {
|
|
31
|
+
process.stderr.write(`${result.errorMessage}
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var reportResult = (command, result, json) => {
|
|
36
|
+
render(command, result, json);
|
|
37
|
+
process.exitCode = result.exitCode;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { render, reportResult };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/domain/display-width.ts
|
|
2
|
+
var WIDTH_ZERO = 0;
|
|
3
|
+
var WIDTH_NARROW = 1;
|
|
4
|
+
var WIDTH_WIDE = 2;
|
|
5
|
+
var segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
6
|
+
var ZERO_WIDTH_JOINER = 8205;
|
|
7
|
+
var VARIATION_SELECTOR_TEXT = 65038;
|
|
8
|
+
var VARIATION_SELECTOR_EMOJI = 65039;
|
|
9
|
+
var ZERO_WIDTH_CODE_POINTS = new Set([
|
|
10
|
+
ZERO_WIDTH_JOINER,
|
|
11
|
+
VARIATION_SELECTOR_TEXT,
|
|
12
|
+
VARIATION_SELECTOR_EMOJI
|
|
13
|
+
]);
|
|
14
|
+
var isCombiningMark = (codePoint) => /\p{M}/u.test(String.fromCodePoint(codePoint));
|
|
15
|
+
var HANGUL_JAMO_START = 4352;
|
|
16
|
+
var HANGUL_JAMO_END = 4447;
|
|
17
|
+
var CJK_RADICALS_START = 11904;
|
|
18
|
+
var CJK_SYMBOLS_END = 12350;
|
|
19
|
+
var HIRAGANA_START = 12353;
|
|
20
|
+
var CJK_COMPATIBILITY_END = 13311;
|
|
21
|
+
var CJK_EXT_A_START = 13312;
|
|
22
|
+
var CJK_EXT_A_END = 19903;
|
|
23
|
+
var CJK_UNIFIED_START = 19968;
|
|
24
|
+
var CJK_UNIFIED_END = 40959;
|
|
25
|
+
var YI_SYLLABLES_START = 40960;
|
|
26
|
+
var YI_SYLLABLES_END = 42191;
|
|
27
|
+
var HANGUL_SYLLABLES_START = 44032;
|
|
28
|
+
var HANGUL_SYLLABLES_END = 55203;
|
|
29
|
+
var CJK_COMPAT_IDEOGRAPHS_START = 63744;
|
|
30
|
+
var CJK_COMPAT_IDEOGRAPHS_END = 64255;
|
|
31
|
+
var CJK_COMPAT_FORMS_START = 65072;
|
|
32
|
+
var CJK_COMPAT_FORMS_END = 65103;
|
|
33
|
+
var FULLWIDTH_FORMS_START = 65280;
|
|
34
|
+
var FULLWIDTH_FORMS_END = 65376;
|
|
35
|
+
var FULLWIDTH_SIGNS_START = 65504;
|
|
36
|
+
var FULLWIDTH_SIGNS_END = 65510;
|
|
37
|
+
var EMOJI_MISC_START = 127744;
|
|
38
|
+
var EMOJI_MISC_END = 128591;
|
|
39
|
+
var EMOJI_TRANSPORT_START = 128640;
|
|
40
|
+
var EMOJI_TRANSPORT_END = 129535;
|
|
41
|
+
var CJK_EXT_B_START = 131072;
|
|
42
|
+
var CJK_EXT_B_END = 262141;
|
|
43
|
+
var WIDE_RANGES = [
|
|
44
|
+
[HANGUL_JAMO_START, HANGUL_JAMO_END],
|
|
45
|
+
[CJK_RADICALS_START, CJK_SYMBOLS_END],
|
|
46
|
+
[HIRAGANA_START, CJK_COMPATIBILITY_END],
|
|
47
|
+
[CJK_EXT_A_START, CJK_EXT_A_END],
|
|
48
|
+
[CJK_UNIFIED_START, CJK_UNIFIED_END],
|
|
49
|
+
[YI_SYLLABLES_START, YI_SYLLABLES_END],
|
|
50
|
+
[HANGUL_SYLLABLES_START, HANGUL_SYLLABLES_END],
|
|
51
|
+
[CJK_COMPAT_IDEOGRAPHS_START, CJK_COMPAT_IDEOGRAPHS_END],
|
|
52
|
+
[CJK_COMPAT_FORMS_START, CJK_COMPAT_FORMS_END],
|
|
53
|
+
[FULLWIDTH_FORMS_START, FULLWIDTH_FORMS_END],
|
|
54
|
+
[FULLWIDTH_SIGNS_START, FULLWIDTH_SIGNS_END],
|
|
55
|
+
[EMOJI_MISC_START, EMOJI_MISC_END],
|
|
56
|
+
[EMOJI_TRANSPORT_START, EMOJI_TRANSPORT_END],
|
|
57
|
+
[CJK_EXT_B_START, CJK_EXT_B_END]
|
|
58
|
+
];
|
|
59
|
+
var isWide = (codePoint) => WIDE_RANGES.some(([start, end]) => codePoint >= start && codePoint <= end);
|
|
60
|
+
var codePointWidth = (codePoint) => {
|
|
61
|
+
if (ZERO_WIDTH_CODE_POINTS.has(codePoint) || isCombiningMark(codePoint)) {
|
|
62
|
+
return WIDTH_ZERO;
|
|
63
|
+
}
|
|
64
|
+
return isWide(codePoint) ? WIDTH_WIDE : WIDTH_NARROW;
|
|
65
|
+
};
|
|
66
|
+
var graphemeWidth = (grapheme) => {
|
|
67
|
+
const codePoints = Array.from(grapheme, (char) => char.codePointAt(0)).filter((codePoint) => codePoint !== undefined);
|
|
68
|
+
return codePoints.reduce((max, codePoint) => Math.max(max, codePointWidth(codePoint)), WIDTH_ZERO);
|
|
69
|
+
};
|
|
70
|
+
var graphemes = (value) => Array.from(segmenter.segment(value), (entry) => entry.segment);
|
|
71
|
+
var displayWidth = (value) => graphemes(value).reduce((total, grapheme) => total + graphemeWidth(grapheme), WIDTH_ZERO);
|
|
72
|
+
var padToWidth = (value, width) => {
|
|
73
|
+
const current = displayWidth(value);
|
|
74
|
+
return current >= width ? value : value + " ".repeat(width - current);
|
|
75
|
+
};
|
|
76
|
+
var DEFAULT_ELLIPSIS = "…";
|
|
77
|
+
var takeWithinWidth = (clusters, budget) => {
|
|
78
|
+
const taken = [];
|
|
79
|
+
let usedWidth = WIDTH_ZERO;
|
|
80
|
+
for (const grapheme of clusters) {
|
|
81
|
+
const nextWidth = usedWidth + graphemeWidth(grapheme);
|
|
82
|
+
if (nextWidth > budget) {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
taken.push(grapheme);
|
|
86
|
+
usedWidth = nextWidth;
|
|
87
|
+
}
|
|
88
|
+
return taken;
|
|
89
|
+
};
|
|
90
|
+
var truncateToWidth = (value, width, ellipsis = DEFAULT_ELLIPSIS) => {
|
|
91
|
+
if (displayWidth(value) <= width) {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
const budget = width - displayWidth(ellipsis);
|
|
95
|
+
if (budget < WIDTH_ZERO) {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
return takeWithinWidth(graphemes(value), budget).join("") + ellipsis;
|
|
99
|
+
};
|
|
100
|
+
var truncateToWidthKeepingTail = (value, width, ellipsis = DEFAULT_ELLIPSIS) => {
|
|
101
|
+
if (displayWidth(value) <= width) {
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
const budget = width - displayWidth(ellipsis);
|
|
105
|
+
if (budget < WIDTH_ZERO) {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
const keptFromEnd = takeWithinWidth(graphemes(value).toReversed(), budget);
|
|
109
|
+
return ellipsis + keptFromEnd.toReversed().join("");
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/domain/ansi.ts
|
|
113
|
+
var ESC = "\x1B";
|
|
114
|
+
var SGR = {
|
|
115
|
+
bold: `${ESC}[1m`,
|
|
116
|
+
dim: `${ESC}[2m`,
|
|
117
|
+
inverse: `${ESC}[7m`,
|
|
118
|
+
red: `${ESC}[31m`,
|
|
119
|
+
green: `${ESC}[32m`,
|
|
120
|
+
yellow: `${ESC}[33m`,
|
|
121
|
+
magenta: `${ESC}[35m`,
|
|
122
|
+
cyan: `${ESC}[36m`
|
|
123
|
+
};
|
|
124
|
+
var SGR_RESET = `${ESC}[0m`;
|
|
125
|
+
|
|
126
|
+
export { graphemes, displayWidth, padToWidth, truncateToWidth, truncateToWidthKeepingTail, ESC, SGR, SGR_RESET };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parsePorcelain
|
|
3
|
+
} from "./cli-b0tk5wcx.js";
|
|
4
|
+
import {
|
|
5
|
+
EXIT_USAGE_ERROR,
|
|
6
|
+
fail,
|
|
7
|
+
pass,
|
|
8
|
+
reject
|
|
9
|
+
} from "./cli-77yanq37.js";
|
|
10
|
+
import {
|
|
11
|
+
isWithin,
|
|
12
|
+
classifyWorktreePath
|
|
13
|
+
} from "./cli-a56b2ar0.js";
|
|
14
|
+
|
|
15
|
+
// src/infra/repo.ts
|
|
16
|
+
import { basename, dirname, join } from "node:path";
|
|
17
|
+
|
|
18
|
+
// src/domain/tracking.ts
|
|
19
|
+
var resolveTrackingRef = (branch, remotes) => {
|
|
20
|
+
if (remotes.includes("origin")) {
|
|
21
|
+
return { kind: "track", ref: `origin/${branch}` };
|
|
22
|
+
}
|
|
23
|
+
const [first, second] = remotes;
|
|
24
|
+
if (first === undefined) {
|
|
25
|
+
return { kind: "none" };
|
|
26
|
+
}
|
|
27
|
+
if (second !== undefined) {
|
|
28
|
+
return { kind: "ambiguous" };
|
|
29
|
+
}
|
|
30
|
+
return { kind: "track", ref: `${first}/${branch}` };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/infra/repo.ts
|
|
34
|
+
var loadRepoContext = async (git, fs, cwd) => {
|
|
35
|
+
const [commonDir, porcelainOut] = await Promise.all([
|
|
36
|
+
git.commonDir(cwd),
|
|
37
|
+
git.listWorktreesPorcelain(cwd)
|
|
38
|
+
]);
|
|
39
|
+
const parsed = parsePorcelain(porcelainOut);
|
|
40
|
+
const [firstEntry] = parsed;
|
|
41
|
+
if (firstEntry === undefined) {
|
|
42
|
+
throw new Error("git worktree list returned no entries");
|
|
43
|
+
}
|
|
44
|
+
const rootPath = await realpathOrRaw(fs, firstEntry.path);
|
|
45
|
+
const managedRoot = join(dirname(rootPath), "_worktree", basename(rootPath));
|
|
46
|
+
const worktrees = await Promise.all(parsed.map(async (entry) => {
|
|
47
|
+
const resolvedPath = await realpathOrRaw(fs, entry.path);
|
|
48
|
+
return {
|
|
49
|
+
...entry,
|
|
50
|
+
path: resolvedPath,
|
|
51
|
+
kind: classifyWorktreePath(resolvedPath, rootPath, managedRoot)
|
|
52
|
+
};
|
|
53
|
+
}));
|
|
54
|
+
return { rootPath, managedRoot, commonDir, worktrees };
|
|
55
|
+
};
|
|
56
|
+
var nestedWorktrees = (worktrees, path) => worktrees.filter((wt) => wt.path !== path && !wt.prunable && isWithin(path, wt.path));
|
|
57
|
+
var isWorktreeDirty = (git, worktrees, path) => git.isDirty(path, nestedWorktrees(worktrees, path).map((wt) => wt.path));
|
|
58
|
+
var worktreeDirtyState = async (git, fs, worktrees, wt) => {
|
|
59
|
+
if (wt.bare || wt.prunable || !await fs.exists(wt.path)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return isWorktreeDirty(git, worktrees, wt.path);
|
|
63
|
+
};
|
|
64
|
+
var resolveBranchCheckout = async (git, rootPath, branch, explicitTrack) => {
|
|
65
|
+
const localBranches = await git.listBranches(rootPath);
|
|
66
|
+
const createBranch = !localBranches.includes(branch);
|
|
67
|
+
let track = explicitTrack;
|
|
68
|
+
if (createBranch && track === undefined) {
|
|
69
|
+
const remotes = await git.remotesWithBranch(rootPath, branch);
|
|
70
|
+
const resolution = resolveTrackingRef(branch, remotes);
|
|
71
|
+
if (resolution.kind === "ambiguous") {
|
|
72
|
+
return reject(fail(EXIT_USAGE_ERROR, `Branch "${branch}" exists on multiple remotes (${remotes.join(", ")}). Use --track to disambiguate.`));
|
|
73
|
+
}
|
|
74
|
+
if (resolution.kind === "track") {
|
|
75
|
+
track = resolution.ref;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return pass({ createBranch, ...track === undefined ? {} : { track } });
|
|
79
|
+
};
|
|
80
|
+
var realpathOrRaw = async (fs, path) => {
|
|
81
|
+
try {
|
|
82
|
+
return await fs.realpath(path);
|
|
83
|
+
} catch {
|
|
84
|
+
return path;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export { loadRepoContext, nestedWorktrees, isWorktreeDirty, worktreeDirtyState, resolveBranchCheckout };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeFatalError
|
|
3
|
+
} from "./cli-ph0gp56k.js";
|
|
4
|
+
import {
|
|
5
|
+
reportResult
|
|
6
|
+
} from "./cli-dbzx9nzg.js";
|
|
7
|
+
import {
|
|
8
|
+
fail
|
|
9
|
+
} from "./cli-77yanq37.js";
|
|
10
|
+
|
|
11
|
+
// src/cli-dispatch.ts
|
|
12
|
+
var normalizeCliArgs = (rawArgs, argv0, isCompiledBinary) => isCompiledBinary && rawArgs.length === 1 && rawArgs[0] === argv0 ? [] : rawArgs;
|
|
13
|
+
var isRunningAsCompiledBinary = () => typeof Bun !== "undefined" && Bun.main.startsWith("/$bunfs/");
|
|
14
|
+
var isUpdateRequest = (rawArgs) => rawArgs[0] === "--update";
|
|
15
|
+
var HELP_FLAGS = new Set(["--help", "-h", "help"]);
|
|
16
|
+
var UPDATE_HELP_FLAGS = new Set(["--help", "-h"]);
|
|
17
|
+
var isHelpRequest = (rawArgs) => rawArgs.length > 0 && (HELP_FLAGS.has(rawArgs[0] ?? "") || isUpdateRequest(rawArgs) && rawArgs.slice(1).some((token) => UPDATE_HELP_FLAGS.has(token)));
|
|
18
|
+
var isVersionRequest = (rawArgs) => rawArgs[0] === "--version";
|
|
19
|
+
var dispatchCliArgs = (rawArgs, reservedNames) => {
|
|
20
|
+
const [first, ...rest] = rawArgs;
|
|
21
|
+
if (first === "--") {
|
|
22
|
+
return { kind: "jump", args: rest };
|
|
23
|
+
}
|
|
24
|
+
if (first !== undefined && reservedNames.includes(first)) {
|
|
25
|
+
return { kind: "reserved", name: first, args: rest };
|
|
26
|
+
}
|
|
27
|
+
return { kind: "jump", args: rawArgs };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/cli-fatal.ts
|
|
31
|
+
var wantsJson = (args) => args.some((arg) => arg === "--json" || arg.startsWith("--json="));
|
|
32
|
+
var reportFatalError = (command, json, error) => {
|
|
33
|
+
const fatal = describeFatalError(error);
|
|
34
|
+
const result = fail(fatal.exitCode, `hop: ${fatal.message}`);
|
|
35
|
+
reportResult(command, result, json);
|
|
36
|
+
};
|
|
37
|
+
// package.json
|
|
38
|
+
var package_default = {
|
|
39
|
+
name: "@n-seiji/nuthatch",
|
|
40
|
+
version: "0.2.0",
|
|
41
|
+
description: "git worktree manager (hop)",
|
|
42
|
+
homepage: "https://github.com/n-seiji/nuthatch#readme",
|
|
43
|
+
bugs: {
|
|
44
|
+
url: "https://github.com/n-seiji/nuthatch/issues"
|
|
45
|
+
},
|
|
46
|
+
license: "GPL-3.0-only",
|
|
47
|
+
repository: {
|
|
48
|
+
type: "git",
|
|
49
|
+
url: "git+https://github.com/n-seiji/nuthatch.git"
|
|
50
|
+
},
|
|
51
|
+
bin: {
|
|
52
|
+
hop: "dist/cli.js"
|
|
53
|
+
},
|
|
54
|
+
files: [
|
|
55
|
+
"dist"
|
|
56
|
+
],
|
|
57
|
+
type: "module",
|
|
58
|
+
publishConfig: {
|
|
59
|
+
access: "public"
|
|
60
|
+
},
|
|
61
|
+
scripts: {
|
|
62
|
+
build: "bun build ./src/cli.ts --outdir dist --target node --format esm --splitting",
|
|
63
|
+
typecheck: "tsc --noEmit",
|
|
64
|
+
lint: "oxlint .",
|
|
65
|
+
"lint:fix": "oxlint . --fix",
|
|
66
|
+
format: "oxfmt --write .",
|
|
67
|
+
"format:check": "oxfmt --check .",
|
|
68
|
+
test: "bun test"
|
|
69
|
+
},
|
|
70
|
+
dependencies: {
|
|
71
|
+
valibot: "^1.4.2"
|
|
72
|
+
},
|
|
73
|
+
devDependencies: {
|
|
74
|
+
"@types/bun": "latest",
|
|
75
|
+
oxfmt: "^0.72.0",
|
|
76
|
+
oxlint: "^1.81.0",
|
|
77
|
+
typescript: "^5.6.0"
|
|
78
|
+
},
|
|
79
|
+
engines: {
|
|
80
|
+
node: ">=22"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/version.ts
|
|
85
|
+
var VERSION = package_default.version;
|
|
86
|
+
|
|
87
|
+
export { normalizeCliArgs, isRunningAsCompiledBinary, isUpdateRequest, isHelpRequest, isVersionRequest, dispatchCliArgs, wantsJson, reportFatalError, VERSION };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXIT_GENERAL_ERROR,
|
|
3
|
+
EXIT_SAFE_REJECTION
|
|
4
|
+
} from "./cli-77yanq37.js";
|
|
5
|
+
import {
|
|
6
|
+
absoluteCandidates,
|
|
7
|
+
splitPathEnv
|
|
8
|
+
} from "./cli-s5qcjcwk.js";
|
|
9
|
+
|
|
10
|
+
// src/domain/git-executable.ts
|
|
11
|
+
var GIT_EXECUTABLE_OVERRIDE_ENV = "HOP_GIT";
|
|
12
|
+
var GIT_NOT_FOUND_ERROR_CODE = "HOP_GIT_NOT_FOUND";
|
|
13
|
+
var GIT_NOT_FOUND_HINT = `Install git, or set ${GIT_EXECUTABLE_OVERRIDE_ENV} to its absolute path.`;
|
|
14
|
+
var GIT_FALLBACK_BIN_DIRS = [
|
|
15
|
+
"/opt/homebrew/bin",
|
|
16
|
+
"/usr/local/bin",
|
|
17
|
+
"/usr/bin",
|
|
18
|
+
"/bin"
|
|
19
|
+
];
|
|
20
|
+
var gitExecutableCandidates = (input) => {
|
|
21
|
+
const override = input.override?.trim() ?? "";
|
|
22
|
+
if (override.length > 0) {
|
|
23
|
+
return [override];
|
|
24
|
+
}
|
|
25
|
+
return absoluteCandidates([...splitPathEnv(input.path), ...GIT_FALLBACK_BIN_DIRS], "git");
|
|
26
|
+
};
|
|
27
|
+
var gitNotFoundMessage = (candidates) => `git executable not found. Looked in: ${candidates.join(", ")}. ${GIT_NOT_FOUND_HINT}`;
|
|
28
|
+
|
|
29
|
+
// src/domain/fatal-error.ts
|
|
30
|
+
var fieldsOf = (error) => typeof error === "object" && error !== null ? error : {};
|
|
31
|
+
var messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
32
|
+
var trimmedStringOf = (value) => typeof value === "string" ? value.trim() : "";
|
|
33
|
+
var isSpawnFailure = (fields) => fields.code === "ENOENT" && typeof fields.syscall === "string" && fields.syscall.startsWith("spawn");
|
|
34
|
+
var isGitFailure = (fields) => typeof fields.code === "number" || typeof fields.signal === "string" && fields.signal !== "";
|
|
35
|
+
var describeFatalError = (error) => {
|
|
36
|
+
const fields = fieldsOf(error);
|
|
37
|
+
if (isSpawnFailure(fields)) {
|
|
38
|
+
const binary = trimmedStringOf(fields.path) || "git";
|
|
39
|
+
return {
|
|
40
|
+
message: `failed to run ${binary}: no such executable. ${GIT_NOT_FOUND_HINT}`,
|
|
41
|
+
exitCode: EXIT_GENERAL_ERROR
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (isGitFailure(fields)) {
|
|
45
|
+
const stderr = trimmedStringOf(fields.stderr);
|
|
46
|
+
return {
|
|
47
|
+
message: stderr.length > 0 ? stderr : messageOf(error),
|
|
48
|
+
exitCode: EXIT_SAFE_REJECTION
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return { message: messageOf(error), exitCode: EXIT_GENERAL_ERROR };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { GIT_EXECUTABLE_OVERRIDE_ENV, GIT_NOT_FOUND_ERROR_CODE, gitExecutableCandidates, gitNotFoundMessage, messageOf, describeFatalError };
|