mergestorm 0.3.8 → 0.3.13
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.md +59 -14
- package/dist/api.d.ts +27 -38
- package/dist/api.js +81 -4
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +5 -0
- package/dist/commands/credits.js +33 -12
- package/dist/commands/jobs.js +1 -1
- package/dist/commands/login.js +29 -8
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +26 -19
- package/dist/commands/review-client.d.ts +93 -0
- package/dist/commands/review-client.js +289 -0
- package/dist/commands/review.d.ts +30 -6
- package/dist/commands/review.js +385 -146
- package/dist/commands/stack.d.ts +34 -2
- package/dist/commands/stack.js +256 -62
- package/dist/commands/status.d.ts +16 -2
- package/dist/commands/status.js +221 -21
- package/dist/errors.d.ts +18 -2
- package/dist/errors.js +22 -1
- package/dist/git-stack.d.ts +2 -0
- package/dist/git-stack.js +4 -0
- package/dist/git.d.ts +12 -1
- package/dist/git.js +52 -7
- package/dist/shell.d.ts +1 -1
- package/dist/shell.js +7 -4
- package/dist/stack-dto.d.ts +185 -0
- package/dist/stack-dto.js +7 -0
- package/dist/submit-pr-base.d.ts +31 -0
- package/dist/submit-pr-base.js +69 -0
- package/dist/ui/banner.js +12 -0
- package/dist/ui/box.d.ts +13 -9
- package/dist/ui/box.js +28 -44
- package/dist/ui/findings.d.ts +2 -0
- package/dist/ui/findings.js +2 -1
- package/dist/ui/job-envelope.d.ts +68 -0
- package/dist/ui/job-envelope.js +35 -0
- package/dist/ui/prompt.d.ts +91 -0
- package/dist/ui/prompt.js +241 -62
- package/dist/ui/usage.d.ts +25 -1
- package/dist/ui/usage.js +50 -1
- package/dist/ui/width.d.ts +26 -0
- package/dist/ui/width.js +262 -0
- package/package.json +6 -2
package/dist/ui/width.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal cell arithmetic. Everything that frames, pads, or parks a cursor
|
|
3
|
+
* must count *cells*, not UTF-16 units: CJK is two cells, combining marks
|
|
4
|
+
* and ZWJ are zero, an astral emoji is one grapheme. Getting this wrong is
|
|
5
|
+
* why the right border of the prompt box drifted on non-Latin input.
|
|
6
|
+
*/
|
|
7
|
+
const SGR_PATTERN = /\u001b\[[0-9;]*m/g;
|
|
8
|
+
const SGR_SPLIT = /(\u001b\[[0-9;]*m)/;
|
|
9
|
+
export function stripSgr(str) {
|
|
10
|
+
return str.replace(SGR_PATTERN, "");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* East Asian Wide / Fullwidth ranges (plus emoji presentation blocks).
|
|
14
|
+
* Deliberately excludes Ambiguous-width ranges such as box drawing
|
|
15
|
+
* (U+2500..257F) and block elements (U+2580..259F, the TORNADO_LOGO
|
|
16
|
+
* glyphs), which every terminal we target renders in one cell.
|
|
17
|
+
*/
|
|
18
|
+
const WIDE_RANGES = [
|
|
19
|
+
[0x1100, 0x115f], // Hangul Jamo
|
|
20
|
+
[0x231a, 0x231b],
|
|
21
|
+
[0x2329, 0x232a],
|
|
22
|
+
[0x23e9, 0x23ec],
|
|
23
|
+
[0x23f0, 0x23f0],
|
|
24
|
+
[0x23f3, 0x23f3],
|
|
25
|
+
[0x25fd, 0x25fe],
|
|
26
|
+
[0x2614, 0x2615],
|
|
27
|
+
[0x2648, 0x2653],
|
|
28
|
+
[0x267f, 0x267f],
|
|
29
|
+
[0x2693, 0x2693],
|
|
30
|
+
[0x26a1, 0x26a1],
|
|
31
|
+
[0x26aa, 0x26ab],
|
|
32
|
+
[0x26bd, 0x26be],
|
|
33
|
+
[0x26c4, 0x26c5],
|
|
34
|
+
[0x26ce, 0x26ce],
|
|
35
|
+
[0x26d4, 0x26d4],
|
|
36
|
+
[0x26ea, 0x26ea],
|
|
37
|
+
[0x26f2, 0x26f3],
|
|
38
|
+
[0x26f5, 0x26f5],
|
|
39
|
+
[0x26fa, 0x26fa],
|
|
40
|
+
[0x26fd, 0x26fd],
|
|
41
|
+
[0x2705, 0x2705],
|
|
42
|
+
[0x270a, 0x270b],
|
|
43
|
+
[0x2728, 0x2728],
|
|
44
|
+
[0x274c, 0x274c],
|
|
45
|
+
[0x274e, 0x274e],
|
|
46
|
+
[0x2753, 0x2755],
|
|
47
|
+
[0x2757, 0x2757],
|
|
48
|
+
[0x2795, 0x2797],
|
|
49
|
+
[0x27b0, 0x27b0],
|
|
50
|
+
[0x27bf, 0x27bf],
|
|
51
|
+
[0x2b1b, 0x2b1c],
|
|
52
|
+
[0x2b50, 0x2b50],
|
|
53
|
+
[0x2b55, 0x2b55],
|
|
54
|
+
[0x2e80, 0x303e], // CJK radicals, Kangxi, ideographic description, CJK symbols
|
|
55
|
+
[0x3041, 0x33ff], // Hiragana, Katakana, Bopomofo, Hangul compat, enclosed CJK
|
|
56
|
+
[0x3400, 0x4dbf], // CJK ext A
|
|
57
|
+
[0x4e00, 0x9fff], // CJK unified
|
|
58
|
+
[0xa000, 0xa4cf], // Yi
|
|
59
|
+
[0xa960, 0xa97f], // Hangul Jamo ext A
|
|
60
|
+
[0xac00, 0xd7a3], // Hangul syllables
|
|
61
|
+
[0xf900, 0xfaff], // CJK compat ideographs
|
|
62
|
+
[0xfe10, 0xfe19], // vertical forms
|
|
63
|
+
[0xfe30, 0xfe6f], // CJK compat forms, small forms
|
|
64
|
+
[0xff00, 0xff60], // fullwidth forms
|
|
65
|
+
[0xffe0, 0xffe6],
|
|
66
|
+
[0x16fe0, 0x16fe4],
|
|
67
|
+
[0x17000, 0x18aff], // Tangut, Khitan
|
|
68
|
+
[0x1b000, 0x1b2ff], // Kana supplement / ext
|
|
69
|
+
[0x1f004, 0x1f004],
|
|
70
|
+
[0x1f0cf, 0x1f0cf],
|
|
71
|
+
[0x1f18e, 0x1f18e],
|
|
72
|
+
[0x1f191, 0x1f19a],
|
|
73
|
+
[0x1f1e6, 0x1f1ff], // regional indicators (flags pair to one 2-cell cluster)
|
|
74
|
+
[0x1f200, 0x1f202],
|
|
75
|
+
[0x1f210, 0x1f23b],
|
|
76
|
+
[0x1f240, 0x1f248],
|
|
77
|
+
[0x1f250, 0x1f251],
|
|
78
|
+
[0x1f260, 0x1f265],
|
|
79
|
+
[0x1f300, 0x1f320],
|
|
80
|
+
[0x1f32d, 0x1f335],
|
|
81
|
+
[0x1f337, 0x1f37c],
|
|
82
|
+
[0x1f37e, 0x1f393],
|
|
83
|
+
[0x1f3a0, 0x1f3ca],
|
|
84
|
+
[0x1f3cf, 0x1f3d3],
|
|
85
|
+
[0x1f3e0, 0x1f3f0],
|
|
86
|
+
[0x1f3f4, 0x1f3f4],
|
|
87
|
+
[0x1f3f8, 0x1f43e],
|
|
88
|
+
[0x1f440, 0x1f440],
|
|
89
|
+
[0x1f442, 0x1f4fc],
|
|
90
|
+
[0x1f4ff, 0x1f53d],
|
|
91
|
+
[0x1f54b, 0x1f54e],
|
|
92
|
+
[0x1f550, 0x1f567],
|
|
93
|
+
[0x1f57a, 0x1f57a],
|
|
94
|
+
[0x1f595, 0x1f596],
|
|
95
|
+
[0x1f5a4, 0x1f5a4],
|
|
96
|
+
[0x1f5fb, 0x1f64f],
|
|
97
|
+
[0x1f680, 0x1f6c5],
|
|
98
|
+
[0x1f6cc, 0x1f6cc],
|
|
99
|
+
[0x1f6d0, 0x1f6d2],
|
|
100
|
+
[0x1f6d5, 0x1f6d7],
|
|
101
|
+
[0x1f6dc, 0x1f6df],
|
|
102
|
+
[0x1f6eb, 0x1f6ec],
|
|
103
|
+
[0x1f6f4, 0x1f6fc],
|
|
104
|
+
[0x1f7e0, 0x1f7eb],
|
|
105
|
+
[0x1f7f0, 0x1f7f0],
|
|
106
|
+
[0x1f90c, 0x1f93a],
|
|
107
|
+
[0x1f93c, 0x1f945],
|
|
108
|
+
[0x1f947, 0x1f9ff],
|
|
109
|
+
[0x1fa70, 0x1faff],
|
|
110
|
+
[0x20000, 0x2fffd], // CJK ext B..F
|
|
111
|
+
[0x30000, 0x3fffd], // CJK ext G..
|
|
112
|
+
];
|
|
113
|
+
const ZERO_WIDTH_RE = /^[\p{M}\p{Cc}\p{Cf}\u200b-\u200f\u2028-\u202e\u2060-\u2064\ufeff]$/u;
|
|
114
|
+
function isWide(cp) {
|
|
115
|
+
// Binary search over the sorted range table.
|
|
116
|
+
let lo = 0;
|
|
117
|
+
let hi = WIDE_RANGES.length - 1;
|
|
118
|
+
while (lo <= hi) {
|
|
119
|
+
const mid = (lo + hi) >> 1;
|
|
120
|
+
const [start, end] = WIDE_RANGES[mid];
|
|
121
|
+
if (cp < start)
|
|
122
|
+
hi = mid - 1;
|
|
123
|
+
else if (cp > end)
|
|
124
|
+
lo = mid + 1;
|
|
125
|
+
else
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/** Cells for one code point on its own (no cluster context). */
|
|
131
|
+
export function codePointWidth(cp) {
|
|
132
|
+
if (cp === 0xfe0f)
|
|
133
|
+
return 0; // VS16 handled at cluster level
|
|
134
|
+
const ch = String.fromCodePoint(cp);
|
|
135
|
+
if (ZERO_WIDTH_RE.test(ch))
|
|
136
|
+
return 0;
|
|
137
|
+
if (cp >= 0xfe00 && cp <= 0xfe0f)
|
|
138
|
+
return 0; // variation selectors
|
|
139
|
+
if (cp >= 0xe0100 && cp <= 0xe01ef)
|
|
140
|
+
return 0; // VS supplement
|
|
141
|
+
if (cp >= 0xe0020 && cp <= 0xe007f)
|
|
142
|
+
return 0; // emoji tag sequence
|
|
143
|
+
if (cp >= 0xd800 && cp <= 0xdfff)
|
|
144
|
+
return 1; // lone surrogate (broken slice)
|
|
145
|
+
return isWide(cp) ? 2 : 1;
|
|
146
|
+
}
|
|
147
|
+
let segmenter;
|
|
148
|
+
/**
|
|
149
|
+
* Grapheme clusters of `text`, yielded lazily so an early exit (e.g.
|
|
150
|
+
* `sliceVisible` cutting a cell window) never segments the whole tail of a
|
|
151
|
+
* long buffer. Falls back to code-point iteration without Intl.Segmenter.
|
|
152
|
+
*/
|
|
153
|
+
function* graphemes(text) {
|
|
154
|
+
if (segmenter === undefined) {
|
|
155
|
+
try {
|
|
156
|
+
segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
segmenter = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (segmenter) {
|
|
163
|
+
for (const seg of segmenter.segment(text))
|
|
164
|
+
yield seg.segment;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
for (const ch of text)
|
|
168
|
+
yield ch;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Cells one grapheme cluster occupies. A cluster is as wide as its widest
|
|
172
|
+
* base (a ZWJ family emoji is 2, not 6); VS16 promotes a text-style symbol
|
|
173
|
+
* to emoji presentation (2 cells); a cluster of nothing but marks is 0.
|
|
174
|
+
*/
|
|
175
|
+
export function graphemeWidth(cluster) {
|
|
176
|
+
let width = 0;
|
|
177
|
+
let vs16 = false;
|
|
178
|
+
for (const ch of cluster) {
|
|
179
|
+
const cp = ch.codePointAt(0);
|
|
180
|
+
if (cp === 0xfe0f) {
|
|
181
|
+
vs16 = true;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
width = Math.max(width, codePointWidth(cp));
|
|
185
|
+
}
|
|
186
|
+
if (vs16 && width === 1)
|
|
187
|
+
return 2;
|
|
188
|
+
return width;
|
|
189
|
+
}
|
|
190
|
+
/** Terminal cells a string occupies, ignoring SGR colour codes. */
|
|
191
|
+
export function visibleWidth(str) {
|
|
192
|
+
const plain = stripSgr(str);
|
|
193
|
+
if (!plain)
|
|
194
|
+
return 0;
|
|
195
|
+
let total = 0;
|
|
196
|
+
for (const cluster of graphemes(plain))
|
|
197
|
+
total += graphemeWidth(cluster);
|
|
198
|
+
return total;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Take the cells `[start, start + cells)` of `str`, keeping every SGR code
|
|
202
|
+
* (so colours survive) and substituting spaces for a wide glyph cut by
|
|
203
|
+
* either edge, so the result's geometry is exactly what the caller asked
|
|
204
|
+
* for even when a CJK character straddles the window.
|
|
205
|
+
*/
|
|
206
|
+
export function sliceVisible(str, start, cells) {
|
|
207
|
+
if (cells <= 0)
|
|
208
|
+
return "";
|
|
209
|
+
const end = start + cells;
|
|
210
|
+
let out = "";
|
|
211
|
+
let offset = 0;
|
|
212
|
+
const parts = str.split(SGR_SPLIT);
|
|
213
|
+
for (let i = 0; i < parts.length; i++) {
|
|
214
|
+
const part = parts[i];
|
|
215
|
+
if (!part)
|
|
216
|
+
continue;
|
|
217
|
+
if (i % 2 === 1) {
|
|
218
|
+
out += part;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// Offsets only grow, so once we're past the window no later content can
|
|
222
|
+
// land in it; stop segmenting the tail of a long buffer.
|
|
223
|
+
if (offset > end)
|
|
224
|
+
continue;
|
|
225
|
+
for (const cluster of graphemes(part)) {
|
|
226
|
+
if (offset > end)
|
|
227
|
+
break;
|
|
228
|
+
const w = graphemeWidth(cluster);
|
|
229
|
+
const from = offset;
|
|
230
|
+
const to = offset + w;
|
|
231
|
+
offset = to;
|
|
232
|
+
if (w === 0) {
|
|
233
|
+
// Marks attach to the previous glyph; keep them only when that glyph
|
|
234
|
+
// was emitted.
|
|
235
|
+
if (from > start && from <= end)
|
|
236
|
+
out += cluster;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (to <= start || from >= end)
|
|
240
|
+
continue;
|
|
241
|
+
if (from >= start && to <= end) {
|
|
242
|
+
out += cluster;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
out += " ".repeat(Math.min(to, end) - Math.max(from, start));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
/** Pad or truncate to an exact cell width (ANSI-safe). */
|
|
252
|
+
export function padVisible(line, width) {
|
|
253
|
+
const target = Math.max(0, width);
|
|
254
|
+
const w = visibleWidth(line);
|
|
255
|
+
if (w === target)
|
|
256
|
+
return line;
|
|
257
|
+
if (w < target)
|
|
258
|
+
return `${line}${" ".repeat(target - w)}`;
|
|
259
|
+
const cut = sliceVisible(line, 0, target);
|
|
260
|
+
const cutWidth = visibleWidth(cut);
|
|
261
|
+
return cutWidth < target ? `${cut}${" ".repeat(target - cutWidth)}` : cut;
|
|
262
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mergestorm",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Mergestorm CLI —
|
|
3
|
+
"version": "0.3.13",
|
|
4
|
+
"description": "Mergestorm CLI (mg) — local reviews & stacked PRs. MIT; build from source: https://github.com/marginsystems/mergestorm-cli",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Margin Systems <hello@mergestorm.ai>",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"mergestorm": "./dist/cli.js",
|
|
19
19
|
"mg": "./dist/cli.js"
|
|
20
20
|
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./dist/cli.js",
|
|
23
|
+
"./client": "./dist/client/index.js"
|
|
24
|
+
},
|
|
21
25
|
"homepage": "https://mergestorm.ai/docs#review-jobs-api",
|
|
22
26
|
"repository": {
|
|
23
27
|
"type": "git",
|