@n-seiji/nuthatch 0.1.4 → 0.1.7
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 +36 -6
- package/README.md +36 -9
- package/dist/clean-n2f9z8n3.js +208 -0
- package/dist/cli-23z98dw1.js +116 -0
- package/dist/cli-2bhb9p0x.js +19 -0
- package/dist/cli-3ya15je0.js +114 -0
- package/dist/cli-71jgs14h.js +112 -0
- package/dist/cli-7jg5qqs9.js +88 -0
- package/dist/cli-dbzx9nzg.js +40 -0
- package/dist/cli-g72ra4e5.js +104 -0
- package/dist/cli-pick-w9qk71fy.js +1533 -0
- package/dist/cli-pqd2qe7v.js +35 -0
- package/dist/cli-qrxgkskr.js +69 -0
- package/dist/cli-rcw5adqd.js +124 -0
- package/dist/cli-update-7exha1em.js +891 -0
- package/dist/cli-ygt2w4e1.js +195 -0
- package/dist/cli.js +248 -4915
- package/dist/init-s0dafa83.js +65 -0
- package/dist/jump-nvdkeega.js +103 -0
- package/dist/ls-kvz4wmc9.js +34 -0
- package/dist/render-ls-6r3vv7y0.js +160 -0
- package/dist/rm-231qjgd8.js +7 -0
- package/dist/root-mmyazwc3.js +7 -0
- package/package.json +2 -3
- package/shell/init.zsh +7 -0
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
export { graphemes, displayWidth, padToWidth, truncateToWidth, truncateToWidthKeepingTail };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isWithin,
|
|
3
|
+
classifyWorktreePath,
|
|
4
|
+
parsePorcelain
|
|
5
|
+
} from "./cli-g72ra4e5.js";
|
|
6
|
+
import {
|
|
7
|
+
EXIT_USAGE_ERROR,
|
|
8
|
+
fail
|
|
9
|
+
} from "./cli-2bhb9p0x.js";
|
|
10
|
+
|
|
11
|
+
// src/infra/repo.ts
|
|
12
|
+
import { basename, dirname, join } from "node:path";
|
|
13
|
+
|
|
14
|
+
// src/domain/tracking.ts
|
|
15
|
+
var resolveTrackingRef = (branch, remotes) => {
|
|
16
|
+
if (remotes.includes("origin")) {
|
|
17
|
+
return { kind: "track", ref: `origin/${branch}` };
|
|
18
|
+
}
|
|
19
|
+
const [first, second] = remotes;
|
|
20
|
+
if (first === undefined) {
|
|
21
|
+
return { kind: "none" };
|
|
22
|
+
}
|
|
23
|
+
if (second !== undefined) {
|
|
24
|
+
return { kind: "ambiguous" };
|
|
25
|
+
}
|
|
26
|
+
return { kind: "track", ref: `${first}/${branch}` };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/infra/repo.ts
|
|
30
|
+
var loadRepoContext = async (git, fs, cwd) => {
|
|
31
|
+
const [commonDir, porcelainOut] = await Promise.all([
|
|
32
|
+
git.commonDir(cwd),
|
|
33
|
+
git.listWorktreesPorcelain(cwd)
|
|
34
|
+
]);
|
|
35
|
+
const parsed = parsePorcelain(porcelainOut);
|
|
36
|
+
const [firstEntry] = parsed;
|
|
37
|
+
if (firstEntry === undefined) {
|
|
38
|
+
throw new Error("git worktree list returned no entries");
|
|
39
|
+
}
|
|
40
|
+
const rootPath = await realpathOrRaw(fs, firstEntry.path);
|
|
41
|
+
const managedRoot = join(dirname(rootPath), "_worktree", basename(rootPath));
|
|
42
|
+
const worktrees = await Promise.all(parsed.map(async (entry) => {
|
|
43
|
+
const resolvedPath = await realpathOrRaw(fs, entry.path);
|
|
44
|
+
return {
|
|
45
|
+
...entry,
|
|
46
|
+
path: resolvedPath,
|
|
47
|
+
kind: classifyWorktreePath(resolvedPath, rootPath, managedRoot)
|
|
48
|
+
};
|
|
49
|
+
}));
|
|
50
|
+
return { rootPath, managedRoot, commonDir, worktrees };
|
|
51
|
+
};
|
|
52
|
+
var nestedWorktreePaths = (worktrees, path) => nestedWorktrees(worktrees, path).map((wt) => wt.path);
|
|
53
|
+
var nestedWorktrees = (worktrees, path) => worktrees.filter((wt) => wt.path !== path && !wt.prunable && isWithin(path, wt.path));
|
|
54
|
+
var isWorktreeDirty = (git, worktrees, path) => git.isDirty(path, nestedWorktreePaths(worktrees, path));
|
|
55
|
+
var worktreeDirtyState = async (git, fs, worktrees, wt) => {
|
|
56
|
+
if (wt.bare || wt.prunable || !await fs.exists(wt.path)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return isWorktreeDirty(git, worktrees, wt.path);
|
|
60
|
+
};
|
|
61
|
+
var resolveBranchCheckout = async (git, rootPath, branch, explicitTrack) => {
|
|
62
|
+
const localBranches = await git.listBranches(rootPath);
|
|
63
|
+
const createBranch = !localBranches.includes(branch);
|
|
64
|
+
let track = explicitTrack;
|
|
65
|
+
if (createBranch && track === undefined) {
|
|
66
|
+
const remotes = await git.remotesWithBranch(rootPath, branch);
|
|
67
|
+
const resolution = resolveTrackingRef(branch, remotes);
|
|
68
|
+
if (resolution.kind === "ambiguous") {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
rejection: fail(EXIT_USAGE_ERROR, `Branch "${branch}" exists on multiple remotes (${remotes.join(", ")}). Use --track to disambiguate.`)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (resolution.kind === "track") {
|
|
75
|
+
track = resolution.ref;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, options: { 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,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,104 @@
|
|
|
1
|
+
// src/domain/classify.ts
|
|
2
|
+
var isWithin = (parent, child) => {
|
|
3
|
+
if (parent === child) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
const parentWithSlash = parent.endsWith("/") ? parent : `${parent}/`;
|
|
7
|
+
return child.startsWith(parentWithSlash);
|
|
8
|
+
};
|
|
9
|
+
var classifyWorktreePath = (path, rootPath, managedRoot) => {
|
|
10
|
+
if (path === rootPath) {
|
|
11
|
+
return "root";
|
|
12
|
+
}
|
|
13
|
+
if (path === managedRoot || isWithin(managedRoot, path)) {
|
|
14
|
+
return "managed";
|
|
15
|
+
}
|
|
16
|
+
return "external";
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/domain/porcelain.ts
|
|
20
|
+
var REFS_HEADS_PREFIX = "refs/heads/";
|
|
21
|
+
var branchNameFromRef = (ref) => ref.startsWith(REFS_HEADS_PREFIX) ? ref.slice(REFS_HEADS_PREFIX.length) : ref;
|
|
22
|
+
var parseRecord = (lines) => {
|
|
23
|
+
let path = null;
|
|
24
|
+
let head = null;
|
|
25
|
+
let branch = null;
|
|
26
|
+
let detached = false;
|
|
27
|
+
let bare = false;
|
|
28
|
+
let locked = false;
|
|
29
|
+
let lockReason = null;
|
|
30
|
+
let prunable = false;
|
|
31
|
+
let prunableReason = null;
|
|
32
|
+
for (const line of lines.filter((candidate) => candidate.length > 0)) {
|
|
33
|
+
const spaceIndex = line.indexOf(" ");
|
|
34
|
+
const key = spaceIndex === -1 ? line : line.slice(0, spaceIndex);
|
|
35
|
+
const value = spaceIndex === -1 ? "" : line.slice(spaceIndex + 1);
|
|
36
|
+
switch (key) {
|
|
37
|
+
case "worktree": {
|
|
38
|
+
path = value;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "HEAD": {
|
|
42
|
+
head = value;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case "branch": {
|
|
46
|
+
branch = branchNameFromRef(value);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "detached": {
|
|
50
|
+
detached = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "bare": {
|
|
54
|
+
bare = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "locked": {
|
|
58
|
+
locked = true;
|
|
59
|
+
lockReason = value.length > 0 ? value : null;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case "prunable": {
|
|
63
|
+
prunable = true;
|
|
64
|
+
prunableReason = value.length > 0 ? value : null;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default: {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (path === null) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
path,
|
|
77
|
+
head,
|
|
78
|
+
branch,
|
|
79
|
+
detached,
|
|
80
|
+
bare,
|
|
81
|
+
locked,
|
|
82
|
+
lockReason,
|
|
83
|
+
prunable,
|
|
84
|
+
prunableReason
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
var parsePorcelain = (output) => {
|
|
88
|
+
if (output.length === 0) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const trimmed = output.endsWith("\x00") ? output.slice(0, -1) : output;
|
|
92
|
+
const records = trimmed.split("\x00\x00");
|
|
93
|
+
const result = [];
|
|
94
|
+
for (const record of records.filter((candidate) => candidate.length > 0)) {
|
|
95
|
+
const lines = record.split("\x00");
|
|
96
|
+
const parsed = parseRecord(lines);
|
|
97
|
+
if (parsed !== null) {
|
|
98
|
+
result.push(parsed);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export { isWithin, classifyWorktreePath, REFS_HEADS_PREFIX, parsePorcelain };
|