@kaynooo/cli-doctor 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +26 -2
- package/dist/cli-bin.js +1203 -0
- package/dist/cli.js +1156 -0
- package/package.json +39 -4
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
import process8 from "process";
|
|
6
|
+
|
|
7
|
+
// ../kit/src/args.ts
|
|
8
|
+
import process2 from "process";
|
|
9
|
+
import { parseArgs } from "util";
|
|
10
|
+
|
|
11
|
+
// ../kit/src/style.ts
|
|
12
|
+
import process from "process";
|
|
13
|
+
|
|
14
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
15
|
+
function x(s) {
|
|
16
|
+
let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
|
|
17
|
+
if (!/^[a-f0-9]+$/.test(r))
|
|
18
|
+
return [0, 0, 0];
|
|
19
|
+
let t = r.length, n = (c) => Number.parseInt(c, 16);
|
|
20
|
+
if (t === 3 || t === 4) {
|
|
21
|
+
let c = n(r[0] + r[0]), u = n(r[1] + r[1]), h = n(r[2] + r[2]), g = t === 4 ? n(r[3] + r[3]) : undefined;
|
|
22
|
+
return g !== undefined ? [c, u, h, g] : [c, u, h];
|
|
23
|
+
}
|
|
24
|
+
if (t !== 6 && t !== 8)
|
|
25
|
+
throw Error(`Invalid hex color length: ${t}`);
|
|
26
|
+
let a = n(r.slice(0, 2)), b = n(r.slice(2, 4)), o = n(r.slice(4, 6)), y = t === 8 ? n(r.slice(6, 8)) : undefined;
|
|
27
|
+
if ([a, b, o].some(Number.isNaN) || y !== undefined && Number.isNaN(y))
|
|
28
|
+
throw Error(`Invalid hex color value: ${s}`);
|
|
29
|
+
return y !== undefined ? [a, b, o, y] : [a, b, o];
|
|
30
|
+
}
|
|
31
|
+
function A(s) {
|
|
32
|
+
let [r, t, n, a] = s;
|
|
33
|
+
if (a)
|
|
34
|
+
return `#${(r << 24 | t << 16 | n << 8 | Math.round((a ?? 1) * 255)).toString(16).padStart(8, "0").toUpperCase()}`;
|
|
35
|
+
return `#${(r << 16 | t << 8 | n).toString(16).padStart(6, "0").toUpperCase()}`;
|
|
36
|
+
}
|
|
37
|
+
function R(s, r, t) {
|
|
38
|
+
s = (s % 360 + 360) % 360, t /= 100;
|
|
39
|
+
let n = r * Math.min(t, 1 - t) / 100, a = (b) => {
|
|
40
|
+
let o = (b + s / 30) % 12, y = t - n * Math.max(Math.min(o - 3, 9 - o, 1), -1);
|
|
41
|
+
return Math.round(255 * y);
|
|
42
|
+
};
|
|
43
|
+
return [a(0), a(8), a(4)];
|
|
44
|
+
}
|
|
45
|
+
function k(s) {
|
|
46
|
+
let [r, t, n] = s;
|
|
47
|
+
r /= 255, t /= 255, n /= 255;
|
|
48
|
+
let a = Math.max(r, t, n), b = Math.min(r, t, n), o = 0, y = 0, c = (a + b) / 2;
|
|
49
|
+
if (a !== b) {
|
|
50
|
+
let u = a - b;
|
|
51
|
+
if (y = c > 0 && c < 1 ? u / (1 - Math.abs(2 * c - 1)) : 0, a === r)
|
|
52
|
+
o = (t - n) / u + (t < n ? 6 : 0);
|
|
53
|
+
else if (a === t)
|
|
54
|
+
o = (n - r) / u + 2;
|
|
55
|
+
else
|
|
56
|
+
o = (r - t) / u + 4;
|
|
57
|
+
o /= 6;
|
|
58
|
+
}
|
|
59
|
+
return [Math.round(o * 360), Math.round(y * 100), Math.round(c * 100)];
|
|
60
|
+
}
|
|
61
|
+
function H(s, r, t) {
|
|
62
|
+
s = (s % 360 + 360) % 360, r /= 100, t /= 100;
|
|
63
|
+
let n = (a) => {
|
|
64
|
+
let b = (a + s / 60) % 6, o = t - t * r * Math.max(Math.min(b, 4 - b, 1), 0);
|
|
65
|
+
return Math.round(255 * o);
|
|
66
|
+
};
|
|
67
|
+
return [n(5), n(3), n(1)];
|
|
68
|
+
}
|
|
69
|
+
function f(s) {
|
|
70
|
+
let [r, t, n] = s;
|
|
71
|
+
r /= 255, t /= 255, n /= 255;
|
|
72
|
+
let a = Math.max(r, t, n), b = Math.min(r, t, n), o = a - b, y = 0, c = a === 0 ? 0 : o / a, u = a;
|
|
73
|
+
if (o !== 0) {
|
|
74
|
+
if (a === r)
|
|
75
|
+
y = (t - n) / o + (t < n ? 6 : 0);
|
|
76
|
+
else if (a === t)
|
|
77
|
+
y = (n - r) / o + 2;
|
|
78
|
+
else
|
|
79
|
+
y = (r - t) / o + 4;
|
|
80
|
+
y /= 6;
|
|
81
|
+
}
|
|
82
|
+
return [Math.round(y * 360), Math.round(c * 100), Math.round(u * 100)];
|
|
83
|
+
}
|
|
84
|
+
function O(s) {
|
|
85
|
+
let r = s[0] / 255, t = s[1] / 255, n = s[2] / 255, a = 1 - Math.max(r, t, n);
|
|
86
|
+
if (a === 1)
|
|
87
|
+
return [0, 0, 0, 100];
|
|
88
|
+
let b = (1 - r - a) / (1 - a), o = (1 - t - a) / (1 - a), y = (1 - n - a) / (1 - a);
|
|
89
|
+
return [Math.round(b * 100), Math.round(o * 100), Math.round(y * 100), Math.round(a * 100)];
|
|
90
|
+
}
|
|
91
|
+
function v(s, r, t, n) {
|
|
92
|
+
return s /= 100, r /= 100, t /= 100, n /= 100, [Math.round(255 * (1 - s) * (1 - n)), Math.round(255 * (1 - r) * (1 - n)), Math.round(255 * (1 - t) * (1 - n))];
|
|
93
|
+
}
|
|
94
|
+
var m = (s) => s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
95
|
+
var p = (s) => s <= 0.0031308 ? 12.92 * s : 1.055 * s ** 0.4166666666666667 - 0.055;
|
|
96
|
+
var i = (s, r) => Math.round(s * 10 ** r) / 10 ** r;
|
|
97
|
+
function T(s) {
|
|
98
|
+
let r = m(s[0] / 255), t = m(s[1] / 255), n = m(s[2] / 255), a = Math.cbrt(0.4122214708 * r + 0.5363325363 * t + 0.0514459929 * n), b = Math.cbrt(0.2119034982 * r + 0.6806995451 * t + 0.1073969566 * n), o = Math.cbrt(0.0883024619 * r + 0.2817188376 * t + 0.6299787005 * n);
|
|
99
|
+
return [i(0.2104542553 * a + 0.793617785 * b - 0.0040720468 * o, 4), i(1.9779984951 * a - 2.428592205 * b + 0.4505937099 * o, 4), i(0.0259040371 * a + 0.7827717662 * b - 0.808675766 * o, 4)];
|
|
100
|
+
}
|
|
101
|
+
function M(s, r, t) {
|
|
102
|
+
let n = (s + 0.3963377774 * r + 0.2158037573 * t) ** 3, a = (s - 0.1055613458 * r - 0.0638541728 * t) ** 3, b = (s - 0.0894841775 * r - 1.291485548 * t) ** 3, o = p(4.0767416621 * n - 3.3077115913 * a + 0.2309699292 * b), y = p(-1.2684380046 * n + 2.6097574011 * a - 0.3413193965 * b), c = p(-0.0041960863 * n - 0.7034186147 * a + 1.707614701 * b), u = (h) => Math.min(255, Math.max(0, Math.round(h * 255)));
|
|
103
|
+
return [u(o), u(y), u(c)];
|
|
104
|
+
}
|
|
105
|
+
function d(s) {
|
|
106
|
+
let [r, t, n] = T(s), a = Math.sqrt(t * t + n * n), b = (Math.atan2(n, t) * 180 / Math.PI + 360) % 360;
|
|
107
|
+
return [i(r, 4), i(a, 4), i(b, 2)];
|
|
108
|
+
}
|
|
109
|
+
function C(s, r, t) {
|
|
110
|
+
let n = t * Math.PI / 180;
|
|
111
|
+
return M(s, r * Math.cos(n), r * Math.sin(n));
|
|
112
|
+
}
|
|
113
|
+
function l(s) {
|
|
114
|
+
let r = s / 255;
|
|
115
|
+
return r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055) ** 2.4;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class e {
|
|
119
|
+
r;
|
|
120
|
+
g;
|
|
121
|
+
b;
|
|
122
|
+
a;
|
|
123
|
+
constructor(s, r, t, n = 1) {
|
|
124
|
+
this.r = s, this.g = r, this.b = t, this.a = n;
|
|
125
|
+
}
|
|
126
|
+
static from(s, r) {
|
|
127
|
+
if (s === "hex") {
|
|
128
|
+
let [t, n, a, b] = x(r);
|
|
129
|
+
return new e(t, n, a, b);
|
|
130
|
+
}
|
|
131
|
+
if (s === "rgb") {
|
|
132
|
+
if (Array.isArray(r)) {
|
|
133
|
+
let [o, y, c, u] = r;
|
|
134
|
+
return new e(o, y, c, u);
|
|
135
|
+
}
|
|
136
|
+
let { r: t, g: n, b: a, a: b } = r;
|
|
137
|
+
return new e(t, n, a, b);
|
|
138
|
+
}
|
|
139
|
+
if (s === "hsl") {
|
|
140
|
+
let t, n, a, b;
|
|
141
|
+
if (Array.isArray(r))
|
|
142
|
+
[t, n, a, b] = r;
|
|
143
|
+
else
|
|
144
|
+
({ h: t, s: n, l: a, a: b } = r);
|
|
145
|
+
let [o, y, c] = R(t, n, a);
|
|
146
|
+
return new e(o, y, c, b);
|
|
147
|
+
}
|
|
148
|
+
if (s === "hsv") {
|
|
149
|
+
let t, n, a, b;
|
|
150
|
+
if (Array.isArray(r))
|
|
151
|
+
[t, n, a, b] = r;
|
|
152
|
+
else
|
|
153
|
+
({ h: t, s: n, v: a, a: b } = r);
|
|
154
|
+
let [o, y, c] = H(t, n, a);
|
|
155
|
+
return new e(o, y, c, b);
|
|
156
|
+
}
|
|
157
|
+
if (s === "cmyk") {
|
|
158
|
+
let t, n, a, b, o;
|
|
159
|
+
if (Array.isArray(r))
|
|
160
|
+
[t, n, a, b, o] = r;
|
|
161
|
+
else
|
|
162
|
+
({ c: t, m: n, y: a, k: b, a: o } = r);
|
|
163
|
+
let [y, c, u] = v(t, n, a, b);
|
|
164
|
+
return new e(y, c, u, o);
|
|
165
|
+
}
|
|
166
|
+
if (s === "oklch") {
|
|
167
|
+
let t, n, a, b;
|
|
168
|
+
if (Array.isArray(r))
|
|
169
|
+
[t, n, a, b] = r;
|
|
170
|
+
else
|
|
171
|
+
({ l: t, c: n, h: a, a: b } = r);
|
|
172
|
+
let [o, y, c] = C(t, n, a);
|
|
173
|
+
return new e(o, y, c, b);
|
|
174
|
+
}
|
|
175
|
+
if (s === "oklab") {
|
|
176
|
+
let t, n, a, b;
|
|
177
|
+
if (Array.isArray(r))
|
|
178
|
+
[t, n, a, b] = r;
|
|
179
|
+
else
|
|
180
|
+
({ l: t, a: n, b: a, alpha: b } = r);
|
|
181
|
+
let [o, y, c] = M(t, n, a);
|
|
182
|
+
return new e(o, y, c, b);
|
|
183
|
+
}
|
|
184
|
+
throw Error("Invalid input");
|
|
185
|
+
}
|
|
186
|
+
to(s) {
|
|
187
|
+
switch (s) {
|
|
188
|
+
case "rgb":
|
|
189
|
+
return [this.r, this.g, this.b];
|
|
190
|
+
case "rgba":
|
|
191
|
+
return [this.r, this.g, this.b, this.a];
|
|
192
|
+
case "hex":
|
|
193
|
+
return A([this.r, this.g, this.b]);
|
|
194
|
+
case "hexa":
|
|
195
|
+
return A([this.r, this.g, this.b, this.a]);
|
|
196
|
+
case "hsl":
|
|
197
|
+
return k([this.r, this.g, this.b]);
|
|
198
|
+
case "hsla":
|
|
199
|
+
return [...k([this.r, this.g, this.b]), this.a];
|
|
200
|
+
case "hsv":
|
|
201
|
+
return f([this.r, this.g, this.b]);
|
|
202
|
+
case "hsva":
|
|
203
|
+
return [...f([this.r, this.g, this.b]), this.a];
|
|
204
|
+
case "cmyk":
|
|
205
|
+
return O([this.r, this.g, this.b]);
|
|
206
|
+
case "cmyka":
|
|
207
|
+
return [...O([this.r, this.g, this.b]), this.a];
|
|
208
|
+
case "oklch":
|
|
209
|
+
return d([this.r, this.g, this.b]);
|
|
210
|
+
case "oklcha":
|
|
211
|
+
return [...d([this.r, this.g, this.b]), this.a];
|
|
212
|
+
case "oklab":
|
|
213
|
+
return T([this.r, this.g, this.b]);
|
|
214
|
+
case "oklaba":
|
|
215
|
+
return [...T([this.r, this.g, this.b]), this.a];
|
|
216
|
+
default:
|
|
217
|
+
return [this.r, this.g, this.b];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
getLuminance() {
|
|
221
|
+
let s = l(this.r), r = l(this.g), t = l(this.b);
|
|
222
|
+
return 0.2126 * s + 0.7152 * r + 0.0722 * t;
|
|
223
|
+
}
|
|
224
|
+
contrastRatio(s) {
|
|
225
|
+
let r = this.getLuminance(), t = s.getLuminance(), [n, a] = r >= t ? [r, t] : [t, r];
|
|
226
|
+
return (n + 0.05) / (a + 0.05);
|
|
227
|
+
}
|
|
228
|
+
bestContrastForeground() {
|
|
229
|
+
let s = new e(255, 255, 255, 1), r = new e(0, 0, 0, 1), t = this.contrastRatio(s), n = this.contrastRatio(r);
|
|
230
|
+
return t >= n ? s : r;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
|
|
235
|
+
var r = 39;
|
|
236
|
+
var o = 49;
|
|
237
|
+
var f2 = "\x1B[";
|
|
238
|
+
var C2 = { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29], black: [30, r], red: [31, r], green: [32, r], yellow: [33, r], blue: [34, r], magenta: [35, r], cyan: [36, r], white: [37, r], gray: [90, r], grey: [90, r], blackBright: [90, r], redBright: [91, r], greenBright: [92, r], yellowBright: [93, r], blueBright: [94, r], magentaBright: [95, r], cyanBright: [96, r], whiteBright: [97, r], bgBlack: [40, o], bgRed: [41, o], bgGreen: [42, o], bgYellow: [43, o], bgBlue: [44, o], bgMagenta: [45, o], bgCyan: [46, o], bgWhite: [47, o], bgBlackBright: [100, o], bgRedBright: [101, o], bgGreenBright: [102, o], bgYellowBright: [103, o], bgBlueBright: [104, o], bgMagentaBright: [105, o], bgCyanBright: [106, o], bgWhiteBright: [107, o], rgb: (i, n, a, l = "fg") => [`${l === "fg" ? 38 : 48};2;${i};${n};${a}`, l === "fg" ? r : o], hex: (i, n = "fg") => {
|
|
239
|
+
let [a, l, s] = e.from("hex", i).to("rgb");
|
|
240
|
+
return [`${n === "fg" ? 38 : 48};2;${a};${l};${s}`, n === "fg" ? r : o];
|
|
241
|
+
}, ansi256: (i, n = "fg") => [`${n === "fg" ? 38 : 48};5;${i}`, n === "fg" ? r : o] };
|
|
242
|
+
var b = new Set(["", "1", "2", "3", "true"]);
|
|
243
|
+
function h(i, n = globalThis.process?.env ?? {}) {
|
|
244
|
+
if (n.FORCE_COLOR !== undefined)
|
|
245
|
+
return b.has(n.FORCE_COLOR);
|
|
246
|
+
if (n.NO_COLOR !== undefined || n.NODE_DISABLE_COLORS !== undefined || n.TERM === "dumb")
|
|
247
|
+
return false;
|
|
248
|
+
return i?.isTTY === true;
|
|
249
|
+
}
|
|
250
|
+
function d2(i, n, a) {
|
|
251
|
+
if (!h(a.stream ?? globalThis.process?.stdout, a.env))
|
|
252
|
+
return n;
|
|
253
|
+
let l = "", s = "";
|
|
254
|
+
for (let g = 0, y = i.length;g < y; g++) {
|
|
255
|
+
let [c, p] = i[g];
|
|
256
|
+
l += `${f2}${c}m`, s = `${f2}${p}m${s}`;
|
|
257
|
+
}
|
|
258
|
+
return l + n + s;
|
|
259
|
+
}
|
|
260
|
+
function u(i, n = []) {
|
|
261
|
+
return new Proxy((l) => d2(n, l, i), { get(l, s) {
|
|
262
|
+
let g = C2[s];
|
|
263
|
+
if (Array.isArray(g))
|
|
264
|
+
return u(i, [...n, g]);
|
|
265
|
+
if (typeof g === "function")
|
|
266
|
+
return (...y) => u(i, [...n, g(...y)]);
|
|
267
|
+
throw Error(`Unknown style: ${String(s)}`);
|
|
268
|
+
} });
|
|
269
|
+
}
|
|
270
|
+
function le(i = {}) {
|
|
271
|
+
return u(i);
|
|
272
|
+
}
|
|
273
|
+
var t = le();
|
|
274
|
+
|
|
275
|
+
// ../kit/src/style.ts
|
|
276
|
+
function takesControlCodes(stream, env = process.env) {
|
|
277
|
+
return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
|
|
278
|
+
}
|
|
279
|
+
function paint(stream, env = process.env) {
|
|
280
|
+
return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
|
|
281
|
+
}
|
|
282
|
+
var STATUSES = {
|
|
283
|
+
passed: { symbol: "\u2713", words: ["passed", "success", "succeeded", "ok"], paint: (colors) => colors.green },
|
|
284
|
+
failed: { symbol: "\u2717", words: ["failed", "failure", "error", "errored"], paint: (colors) => colors.red },
|
|
285
|
+
running: { symbol: "\u25CF", words: ["running", "in_progress", "started"], paint: (colors) => colors.cyan },
|
|
286
|
+
queued: { symbol: "\u25CB", words: ["queued", "waiting", "pending", "created", "blocked", "scheduled"], paint: (colors) => colors.yellow },
|
|
287
|
+
skipped: { symbol: "-", words: ["skipped", "cancelled", "canceled", "manual"], paint: (colors) => colors.dim }
|
|
288
|
+
};
|
|
289
|
+
var STATUS_OF = new Map(Object.entries(STATUSES).flatMap(([status, { words }]) => words.map((word) => [word, status])));
|
|
290
|
+
|
|
291
|
+
// ../kit/src/format.ts
|
|
292
|
+
function upTo(text, separator) {
|
|
293
|
+
const end = text.indexOf(separator);
|
|
294
|
+
return end === -1 ? text : text.slice(0, end);
|
|
295
|
+
}
|
|
296
|
+
var ESCAPE = String.fromCharCode(27);
|
|
297
|
+
function printJson(value) {
|
|
298
|
+
console.log(JSON.stringify(value, undefined, 2));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ../kit/src/text.ts
|
|
302
|
+
var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
|
|
303
|
+
function distance(a, b) {
|
|
304
|
+
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
305
|
+
for (const [i, left] of [...a].entries()) {
|
|
306
|
+
const current = [i + 1];
|
|
307
|
+
for (const [j, right] of [...b].entries())
|
|
308
|
+
current.push(Math.min((previous[j + 1] ?? 0) + 1, (current[j] ?? 0) + 1, (previous[j] ?? 0) + (left === right ? 0 : 1)));
|
|
309
|
+
previous = current;
|
|
310
|
+
}
|
|
311
|
+
return previous[b.length] ?? 0;
|
|
312
|
+
}
|
|
313
|
+
function closest(name, names) {
|
|
314
|
+
const started = names.find((known) => known.startsWith(name));
|
|
315
|
+
if (started !== undefined)
|
|
316
|
+
return started;
|
|
317
|
+
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
318
|
+
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ../kit/src/args.ts
|
|
322
|
+
function jsonOption(holds) {
|
|
323
|
+
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
class Exit extends Error {
|
|
327
|
+
code;
|
|
328
|
+
hint;
|
|
329
|
+
constructor(message, code, hint) {
|
|
330
|
+
super(message);
|
|
331
|
+
this.code = code;
|
|
332
|
+
this.hint = hint;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
class UsageError extends Exit {
|
|
336
|
+
constructor(message, hint) {
|
|
337
|
+
super(message, 2, hint);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function badUsage(message) {
|
|
341
|
+
throw new UsageError(message);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
class HelpShown extends Exit {
|
|
345
|
+
constructor() {
|
|
346
|
+
super("help shown", 0);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
class Completing {
|
|
351
|
+
options;
|
|
352
|
+
positionals;
|
|
353
|
+
candidates;
|
|
354
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
355
|
+
this.options = options;
|
|
356
|
+
this.positionals = positionals;
|
|
357
|
+
this.candidates = candidates;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
var completion = { active: false };
|
|
361
|
+
var OPTIONAL = /\[[^\]]+\]/g;
|
|
362
|
+
var USAGE_PREFIX = /^usage:\s*/i;
|
|
363
|
+
var FULL_STOP = /\.$/;
|
|
364
|
+
var DASHES = /^-+/;
|
|
365
|
+
function usageLine(usage, colors = paint(process2.stdout)) {
|
|
366
|
+
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
367
|
+
}
|
|
368
|
+
function helpText(usage, options, colors = paint(process2.stdout), { examples = [], replaces } = {}) {
|
|
369
|
+
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
|
|
370
|
+
`);
|
|
371
|
+
}
|
|
372
|
+
function replacesRows(replaces, colors = paint(process2.stdout)) {
|
|
373
|
+
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
374
|
+
}
|
|
375
|
+
function wantsHelpJson(env = process2.env) {
|
|
376
|
+
return env.KIT_HELP === "json";
|
|
377
|
+
}
|
|
378
|
+
function description(usage, { examples = [], replaces } = {}) {
|
|
379
|
+
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
380
|
+
`);
|
|
381
|
+
const first = lines[0];
|
|
382
|
+
const colon = first.indexOf(": ");
|
|
383
|
+
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
384
|
+
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
385
|
+
return {
|
|
386
|
+
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
387
|
+
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
388
|
+
...replaces === undefined ? {} : { replaces },
|
|
389
|
+
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function exampleRows(examples, colors = paint(process2.stdout)) {
|
|
393
|
+
if (examples.length === 0)
|
|
394
|
+
return [];
|
|
395
|
+
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
396
|
+
}
|
|
397
|
+
function optionRows(options, colors = paint(process2.stdout)) {
|
|
398
|
+
const rows = Object.entries(options).map(([name, option]) => {
|
|
399
|
+
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
400
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
401
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
402
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
403
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
404
|
+
});
|
|
405
|
+
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
406
|
+
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
407
|
+
}
|
|
408
|
+
var OUTPUT_OPTIONS = {
|
|
409
|
+
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
410
|
+
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
411
|
+
};
|
|
412
|
+
function applyOutput(color, plain, env = process2.env) {
|
|
413
|
+
const when = plain === true ? "never" : color ?? "auto";
|
|
414
|
+
if (when === "auto")
|
|
415
|
+
return;
|
|
416
|
+
if (when !== "always" && when !== "never")
|
|
417
|
+
badUsage(`--color takes auto, always or never, not ${when}`);
|
|
418
|
+
if (when === "always") {
|
|
419
|
+
env.FORCE_COLOR = "1";
|
|
420
|
+
delete env.NO_COLOR;
|
|
421
|
+
} else {
|
|
422
|
+
env.NO_COLOR = "1";
|
|
423
|
+
delete env.FORCE_COLOR;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function parse(args, usage, options, settings = {}) {
|
|
427
|
+
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
428
|
+
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
429
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
430
|
+
if (completion.active)
|
|
431
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
432
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
433
|
+
let parsed;
|
|
434
|
+
try {
|
|
435
|
+
parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
436
|
+
} catch (error) {
|
|
437
|
+
const message = parseError(error);
|
|
438
|
+
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
439
|
+
}
|
|
440
|
+
const values = parsed.values;
|
|
441
|
+
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
442
|
+
if (values.help === true) {
|
|
443
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
|
|
444
|
+
throw new HelpShown;
|
|
445
|
+
}
|
|
446
|
+
checkChoices(parsed.values, all);
|
|
447
|
+
checkConflicts(parsed.values, all);
|
|
448
|
+
if (settings.positionals !== undefined)
|
|
449
|
+
checkPositionals(parsed.positionals, settings.positionals);
|
|
450
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
451
|
+
}
|
|
452
|
+
function splitRest(args, passes) {
|
|
453
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
454
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
455
|
+
}
|
|
456
|
+
function splitTrailing(args, options, count) {
|
|
457
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
458
|
+
let seen = 0;
|
|
459
|
+
if (count === 0)
|
|
460
|
+
return { own: args, rest: undefined };
|
|
461
|
+
for (let at = 0;at < args.length; at++) {
|
|
462
|
+
const word = args[at];
|
|
463
|
+
if (word === "--")
|
|
464
|
+
break;
|
|
465
|
+
if (word.startsWith("-")) {
|
|
466
|
+
if (takesValue(word))
|
|
467
|
+
at++;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
seen++;
|
|
471
|
+
if (seen === count)
|
|
472
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
473
|
+
}
|
|
474
|
+
return { own: args, rest: undefined };
|
|
475
|
+
}
|
|
476
|
+
function listed(options, allowNegative) {
|
|
477
|
+
if (!allowNegative)
|
|
478
|
+
return options;
|
|
479
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
480
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
481
|
+
return [[name, option]];
|
|
482
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
function withBare(args, options) {
|
|
486
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
487
|
+
if (bare.size === 0)
|
|
488
|
+
return args;
|
|
489
|
+
const separator = args.indexOf("--");
|
|
490
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
491
|
+
return [...own.flatMap((arg, index) => {
|
|
492
|
+
const optional = bare.get(arg);
|
|
493
|
+
const next = own[index + 1];
|
|
494
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
495
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
496
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
497
|
+
}
|
|
498
|
+
function checkConflicts(values, options) {
|
|
499
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
500
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
501
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
502
|
+
if (other !== undefined)
|
|
503
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function checkChoices(values, options) {
|
|
507
|
+
for (const [name, option] of Object.entries(options)) {
|
|
508
|
+
if (option.choices === undefined)
|
|
509
|
+
continue;
|
|
510
|
+
const given = values[name];
|
|
511
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
512
|
+
if (typeof value === "string")
|
|
513
|
+
oneOf(value, name, option.choices);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function oneOf(value, name, choices) {
|
|
518
|
+
if (!choices.includes(value))
|
|
519
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
520
|
+
return value;
|
|
521
|
+
}
|
|
522
|
+
function checkPositionals(given, names) {
|
|
523
|
+
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
524
|
+
const required = names.filter((name) => !name.startsWith("[")).length;
|
|
525
|
+
if (given.length < required)
|
|
526
|
+
badUsage(`missing ${names[given.length]}`);
|
|
527
|
+
if (!variadic && given.length > names.length)
|
|
528
|
+
badUsage(`unexpected argument ${given[names.length]}`);
|
|
529
|
+
}
|
|
530
|
+
function joinDashValues(args, options) {
|
|
531
|
+
const longOf = new Map(Object.entries(options).flatMap(([name, option]) => option.short === undefined ? [[`--${name}`, name]] : [[`--${name}`, name], [`-${option.short}`, name]]));
|
|
532
|
+
const isOption = (arg) => arg === "--" || longOf.has(arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg);
|
|
533
|
+
const joined = [];
|
|
534
|
+
for (let i = 0;i < args.length; i++) {
|
|
535
|
+
const arg = args[i] ?? "";
|
|
536
|
+
if (arg === "--")
|
|
537
|
+
return [...joined, ...args.slice(i)];
|
|
538
|
+
const name = longOf.get(arg);
|
|
539
|
+
const next = args[i + 1];
|
|
540
|
+
if (name !== undefined && options[name]?.type === "string" && next !== undefined && next.startsWith("-") && next !== "-" && !isOption(next)) {
|
|
541
|
+
joined.push(`--${name}=${next}`);
|
|
542
|
+
i++;
|
|
543
|
+
} else {
|
|
544
|
+
joined.push(arg);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return joined;
|
|
548
|
+
}
|
|
549
|
+
function parseError(error) {
|
|
550
|
+
const message = upTo(error instanceof Error ? error.message : String(error), ". ");
|
|
551
|
+
return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
|
|
552
|
+
}
|
|
553
|
+
var UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
|
|
554
|
+
function nearestOption(message, options, allowNegative) {
|
|
555
|
+
const typed = UNKNOWN_OPTION.exec(message)?.[1];
|
|
556
|
+
if (typed === undefined)
|
|
557
|
+
return;
|
|
558
|
+
const word = typed.replace(DASHES, "");
|
|
559
|
+
if (word.length < 2 && !typed.startsWith("--"))
|
|
560
|
+
return;
|
|
561
|
+
const known = Object.entries(options).flatMap(([name, option]) => [
|
|
562
|
+
...option.short === undefined ? [] : [`-${option.short}`],
|
|
563
|
+
`--${name}`,
|
|
564
|
+
...allowNegative && option.type === "boolean" ? [`--no-${name}`] : []
|
|
565
|
+
]);
|
|
566
|
+
const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
|
|
567
|
+
return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
|
|
568
|
+
}
|
|
569
|
+
// ../kit/src/process.ts
|
|
570
|
+
import { constants } from "os";
|
|
571
|
+
import process3 from "process";
|
|
572
|
+
var KILL_GRACE_MS = 2000;
|
|
573
|
+
function exitCodeOf(code, signal) {
|
|
574
|
+
if (code !== null)
|
|
575
|
+
return code;
|
|
576
|
+
if (typeof signal === "number")
|
|
577
|
+
return 128 + signal;
|
|
578
|
+
return signal === null ? 129 : 128 + constants.signals[signal];
|
|
579
|
+
}
|
|
580
|
+
async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
|
|
581
|
+
const chunks = [];
|
|
582
|
+
const grouped = timeoutMs !== undefined || signal !== undefined || onStart !== undefined || onChunk !== undefined;
|
|
583
|
+
const proc = Bun.spawn(argv, { ...cwd === undefined ? {} : { cwd }, ...env === undefined ? {} : { env }, stdin, stdout: "pipe", stderr: "pipe", ...grouped ? { detached: true } : {} });
|
|
584
|
+
const state = { kept: 0, truncated: false, timedOut: false, failure: undefined, done: false };
|
|
585
|
+
const timers = [];
|
|
586
|
+
const killGroup = (killing) => {
|
|
587
|
+
if (state.done)
|
|
588
|
+
return;
|
|
589
|
+
try {
|
|
590
|
+
if (grouped)
|
|
591
|
+
process3.kill(-proc.pid, killing);
|
|
592
|
+
else
|
|
593
|
+
proc.kill(killing);
|
|
594
|
+
} catch {}
|
|
595
|
+
};
|
|
596
|
+
const stop = () => {
|
|
597
|
+
killGroup("SIGTERM");
|
|
598
|
+
timers.push(setTimeout(killGroup, KILL_GRACE_MS, "SIGKILL"));
|
|
599
|
+
};
|
|
600
|
+
const onAbort = () => stop();
|
|
601
|
+
if (timeoutMs !== undefined) {
|
|
602
|
+
timers.push(setTimeout(() => {
|
|
603
|
+
state.timedOut = true;
|
|
604
|
+
stop();
|
|
605
|
+
}, timeoutMs));
|
|
606
|
+
}
|
|
607
|
+
if (signal?.aborted === true)
|
|
608
|
+
onAbort();
|
|
609
|
+
else
|
|
610
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
611
|
+
onStart?.({ kill: (killing) => killGroup(killing) });
|
|
612
|
+
const drain = async (stream, name) => {
|
|
613
|
+
for await (const data of stream) {
|
|
614
|
+
if (state.failure !== undefined)
|
|
615
|
+
continue;
|
|
616
|
+
try {
|
|
617
|
+
onChunk?.({ stream: name, data });
|
|
618
|
+
} catch (error) {
|
|
619
|
+
state.failure = error;
|
|
620
|
+
stop();
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (state.kept + data.length > maxBytes) {
|
|
624
|
+
state.truncated = true;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
state.kept += data.length;
|
|
628
|
+
chunks.push({ stream: name, data });
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
try {
|
|
632
|
+
await Promise.all([drain(proc.stdout, "stdout"), drain(proc.stderr, "stderr")]);
|
|
633
|
+
await proc.exited;
|
|
634
|
+
} finally {
|
|
635
|
+
state.done = true;
|
|
636
|
+
for (const timer of timers)
|
|
637
|
+
clearTimeout(timer);
|
|
638
|
+
signal?.removeEventListener("abort", onAbort);
|
|
639
|
+
}
|
|
640
|
+
if (state.failure !== undefined)
|
|
641
|
+
throw state.failure;
|
|
642
|
+
const joined = () => Buffer.concat(chunks.map(({ data }) => data));
|
|
643
|
+
let output;
|
|
644
|
+
let text;
|
|
645
|
+
return {
|
|
646
|
+
code: exitCodeOf(proc.exitCode, proc.signalCode),
|
|
647
|
+
chunks,
|
|
648
|
+
timedOut: state.timedOut,
|
|
649
|
+
truncated: state.truncated,
|
|
650
|
+
get output() {
|
|
651
|
+
return output ??= joined();
|
|
652
|
+
},
|
|
653
|
+
get text() {
|
|
654
|
+
return text ??= (output ??= joined()).toString();
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// ../kit/src/progress.ts
|
|
660
|
+
var ESC = String.fromCharCode(27);
|
|
661
|
+
var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
|
|
662
|
+
var RESET_STYLE = `${ESC}[0m`;
|
|
663
|
+
var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
664
|
+
// ../kit/src/cli.ts
|
|
665
|
+
import process4 from "process";
|
|
666
|
+
var LEVEL_OPTIONS = {
|
|
667
|
+
...OUTPUT_OPTIONS,
|
|
668
|
+
help: { type: "boolean", short: "h", description: "show this help" }
|
|
669
|
+
};
|
|
670
|
+
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
671
|
+
async function commandRows(all, colors) {
|
|
672
|
+
const commands = shown(all);
|
|
673
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
674
|
+
const groups = new Map([["Commands:", []]]);
|
|
675
|
+
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
676
|
+
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
677
|
+
const heading = group === undefined ? "Commands:" : `${group}:`;
|
|
678
|
+
const rows = groups.get(heading) ?? [];
|
|
679
|
+
rows.push(` ${colors.cyan(name.padEnd(width))} ${colors.dim(summaries[index])}`);
|
|
680
|
+
groups.set(heading, rows);
|
|
681
|
+
}
|
|
682
|
+
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
683
|
+
}
|
|
684
|
+
function shown(commands) {
|
|
685
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
686
|
+
}
|
|
687
|
+
function takeOutputFlags(argv) {
|
|
688
|
+
let index = 0;
|
|
689
|
+
for (;index < argv.length; index++) {
|
|
690
|
+
const word = argv[index];
|
|
691
|
+
if (word === "--plain")
|
|
692
|
+
applyOutput(undefined, true);
|
|
693
|
+
else if (word.startsWith("--color="))
|
|
694
|
+
applyOutput(word.slice("--color=".length), undefined);
|
|
695
|
+
else if (word === "--color" && argv[index + 1] !== undefined)
|
|
696
|
+
applyOutput(argv[++index], undefined);
|
|
697
|
+
else
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
return argv.slice(index);
|
|
701
|
+
}
|
|
702
|
+
async function resolveSummary(summary) {
|
|
703
|
+
return typeof summary === "string" ? summary : summary();
|
|
704
|
+
}
|
|
705
|
+
function scopeOf(commands, extra, aliases) {
|
|
706
|
+
return { commands: typeof commands === "function" ? {} : commands, lazy: [...typeof commands === "function" ? [commands] : [], ...extra === undefined ? [] : [extra]], aliases };
|
|
707
|
+
}
|
|
708
|
+
async function expand(scope, words) {
|
|
709
|
+
if (scope.lazy.length === 0)
|
|
710
|
+
return scope;
|
|
711
|
+
const commands = { ...scope.commands };
|
|
712
|
+
for (const added of await Promise.all(scope.lazy.map(async (lazy) => lazy(words)))) {
|
|
713
|
+
for (const [name, command] of Object.entries(added))
|
|
714
|
+
commands[name] ??= command;
|
|
715
|
+
}
|
|
716
|
+
return { ...scope, commands, lazy: [] };
|
|
717
|
+
}
|
|
718
|
+
async function help({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
|
|
719
|
+
const colors = paint(stream);
|
|
720
|
+
const prefix = path.join(" ");
|
|
721
|
+
const summary = await resolveSummary(lazySummary);
|
|
722
|
+
if (wantsHelpJson())
|
|
723
|
+
return JSON.stringify(description(`${prefix} <command> [options]: ${summary}`, { examples, replaces }));
|
|
724
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
725
|
+
const options = fallback !== undefined && "run" in fallback ? await optionsOf(fallback.run) : undefined;
|
|
726
|
+
const own = Object.entries(options ?? {}).filter(([name]) => !(name in TOP_OPTIONS));
|
|
727
|
+
return [
|
|
728
|
+
header(colors, summary),
|
|
729
|
+
"",
|
|
730
|
+
usageLine(`${prefix} ${defaultCommand === undefined ? colors.cyan("<command>") : `[${colors.cyan("<command>")}]`} [options]`, colors),
|
|
731
|
+
...replacesRows(replaces, colors),
|
|
732
|
+
"",
|
|
733
|
+
...await commandRows(commands, colors),
|
|
734
|
+
...own.length === 0 ? [] : ["", `${colors.bold(`Options of ${defaultCommand}:`)} ${colors.dim("the command run when none is named")}`, ...optionRows(Object.fromEntries(own), colors)],
|
|
735
|
+
"",
|
|
736
|
+
colors.bold("Options:"),
|
|
737
|
+
...optionRows(path.length === 1 ? TOP_OPTIONS : LEVEL_OPTIONS, colors),
|
|
738
|
+
...exampleRows(examples ?? [], colors),
|
|
739
|
+
"",
|
|
740
|
+
`${colors.dim("Run")} ${prefix} ${colors.cyan("<command>")} --help ${colors.dim("for its options.")}`
|
|
741
|
+
].join(`
|
|
742
|
+
`);
|
|
743
|
+
}
|
|
744
|
+
function printError(prefix, message, hint, cause) {
|
|
745
|
+
const colors = paint(process4.stderr);
|
|
746
|
+
const styled = takesControlCodes(process4.stderr);
|
|
747
|
+
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
748
|
+
const under = (label, text, tint) => text === undefined ? "" : `
|
|
749
|
+
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
750
|
+
console.error(`${head} ${message}${under("cause", cause, colors.dim)}${under("hint", hint, colors.yellow)}`);
|
|
751
|
+
}
|
|
752
|
+
function printJsonError(message, hint, cause) {
|
|
753
|
+
console.error(JSON.stringify({ error: message, ...hint === undefined ? {} : { hint }, ...cause === undefined ? {} : { cause } }));
|
|
754
|
+
}
|
|
755
|
+
function wantsJson(argv) {
|
|
756
|
+
const end = argv.indexOf("--");
|
|
757
|
+
return (end === -1 ? argv : argv.slice(0, end)).includes("--json");
|
|
758
|
+
}
|
|
759
|
+
function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
760
|
+
if (error instanceof HelpShown)
|
|
761
|
+
return 0;
|
|
762
|
+
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
763
|
+
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
764
|
+
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
765
|
+
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
766
|
+
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
767
|
+
return error instanceof Exit ? error.code : 1;
|
|
768
|
+
}
|
|
769
|
+
async function dispatch(level, typed, { version, redact }) {
|
|
770
|
+
const prefix = level.path.join(" ");
|
|
771
|
+
let argv;
|
|
772
|
+
try {
|
|
773
|
+
argv = takeOutputFlags(typed);
|
|
774
|
+
} catch (error) {
|
|
775
|
+
return reportThrown(prefix, error);
|
|
776
|
+
}
|
|
777
|
+
const [name, ...args] = argv;
|
|
778
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
779
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
780
|
+
try {
|
|
781
|
+
return await fallback.run([]);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (name === undefined && level.path.length > 1) {
|
|
787
|
+
console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stderr));
|
|
788
|
+
return 2;
|
|
789
|
+
}
|
|
790
|
+
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
791
|
+
console.log(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stdout));
|
|
792
|
+
return 0;
|
|
793
|
+
}
|
|
794
|
+
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
795
|
+
console.log(version);
|
|
796
|
+
return 0;
|
|
797
|
+
}
|
|
798
|
+
let command = level.commands[level.aliases?.[name] ?? name];
|
|
799
|
+
let rest = args;
|
|
800
|
+
let ran = name;
|
|
801
|
+
if (command === undefined && level.lazy.length > 0) {
|
|
802
|
+
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
803
|
+
command = level.commands[level.aliases?.[name] ?? name];
|
|
804
|
+
}
|
|
805
|
+
if (command === undefined && level.defaultCommand !== undefined) {
|
|
806
|
+
command = level.commands[level.defaultCommand];
|
|
807
|
+
rest = argv;
|
|
808
|
+
ran = level.defaultCommand;
|
|
809
|
+
}
|
|
810
|
+
if (command === undefined) {
|
|
811
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
812
|
+
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
813
|
+
return 2;
|
|
814
|
+
}
|
|
815
|
+
if ("commands" in command) {
|
|
816
|
+
const { summary } = command;
|
|
817
|
+
return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, runBare: false, examples: command.examples, replaces: undefined }, rest, { version, redact });
|
|
818
|
+
}
|
|
819
|
+
try {
|
|
820
|
+
return await command.run(rest);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
var COMPLETE = "__complete";
|
|
826
|
+
var FILES = "__files__";
|
|
827
|
+
async function completingOf(run) {
|
|
828
|
+
completion.active = true;
|
|
829
|
+
try {
|
|
830
|
+
await run([]);
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (error instanceof Completing)
|
|
833
|
+
return error;
|
|
834
|
+
} finally {
|
|
835
|
+
completion.active = false;
|
|
836
|
+
}
|
|
837
|
+
return new Completing({});
|
|
838
|
+
}
|
|
839
|
+
async function optionsOf(run) {
|
|
840
|
+
return (await completingOf(run)).options;
|
|
841
|
+
}
|
|
842
|
+
function candidateLine(candidate) {
|
|
843
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
844
|
+
}
|
|
845
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
846
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
847
|
+
let index = 0;
|
|
848
|
+
let at = 0;
|
|
849
|
+
while (at < before.length) {
|
|
850
|
+
const word = before[at];
|
|
851
|
+
if (word === "--") {
|
|
852
|
+
index += before.length - at - 1;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
if (!word.startsWith("-"))
|
|
856
|
+
index++;
|
|
857
|
+
else if (takesValue(word))
|
|
858
|
+
at++;
|
|
859
|
+
at++;
|
|
860
|
+
}
|
|
861
|
+
const last = positionals.at(-1);
|
|
862
|
+
if (index < positionals.length)
|
|
863
|
+
return positionals[index];
|
|
864
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
865
|
+
}
|
|
866
|
+
async function leafCandidates(run, words) {
|
|
867
|
+
const completing = await completingOf(run);
|
|
868
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
869
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
870
|
+
const flag = words.at(-2);
|
|
871
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
872
|
+
if (option?.type === "string") {
|
|
873
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
874
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
875
|
+
}
|
|
876
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
877
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
878
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
879
|
+
}
|
|
880
|
+
async function candidates(scope, defaultCommand, words, before = []) {
|
|
881
|
+
const [word = "", ...rest] = words;
|
|
882
|
+
const listing = rest.length === 0 && !word.startsWith("-");
|
|
883
|
+
let { commands, aliases } = scope;
|
|
884
|
+
let command = commands[aliases?.[word] ?? word];
|
|
885
|
+
if (listing || command === undefined) {
|
|
886
|
+
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
887
|
+
command = commands[aliases?.[word] ?? word];
|
|
888
|
+
}
|
|
889
|
+
if (listing) {
|
|
890
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
891
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
892
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
893
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
894
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
895
|
+
}
|
|
896
|
+
if (command === undefined) {
|
|
897
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
898
|
+
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
899
|
+
}
|
|
900
|
+
return "commands" in command ? candidates(scopeOf(command.commands, command.extraCommands, command.aliases), undefined, rest, [...before, word]) : leafCandidates(command.run, rest);
|
|
901
|
+
}
|
|
902
|
+
async function printCandidates(found) {
|
|
903
|
+
for (const line of await found)
|
|
904
|
+
console.log(line);
|
|
905
|
+
return 0;
|
|
906
|
+
}
|
|
907
|
+
async function runCli(cli, argv = process4.argv.slice(2)) {
|
|
908
|
+
if (argv[0] === COMPLETE)
|
|
909
|
+
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
910
|
+
return dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${about}`, summary: cli.about, ...scopeOf(cli.commands, cli.extraCommands, cli.aliases), defaultCommand: cli.defaultCommand, runBare: cli.runBare === true, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
|
|
911
|
+
}
|
|
912
|
+
async function runMain({ name, version, run, redact }, argv = process4.argv.slice(2)) {
|
|
913
|
+
if (argv[0] === COMPLETE)
|
|
914
|
+
return printCandidates(leafCandidates(run, argv.slice(1)));
|
|
915
|
+
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
|
|
916
|
+
console.log(version);
|
|
917
|
+
return 0;
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
return await run(argv);
|
|
921
|
+
} catch (error) {
|
|
922
|
+
return reportThrown(name, error, redact, wantsJson(argv));
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
// ../kit/src/logs.ts
|
|
926
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
927
|
+
// ../kit/src/secrets.ts
|
|
928
|
+
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
929
|
+
// ../kit/src/sessions.ts
|
|
930
|
+
var {Glob } = globalThis.Bun;
|
|
931
|
+
var SESSION_FILES = new Glob("*.json");
|
|
932
|
+
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
933
|
+
// package.json
|
|
934
|
+
var version = "0.3.0";
|
|
935
|
+
|
|
936
|
+
// src/checks.ts
|
|
937
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
938
|
+
import { basename, join as join2 } from "path";
|
|
939
|
+
|
|
940
|
+
// src/completions.ts
|
|
941
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "fs";
|
|
942
|
+
import { join } from "path";
|
|
943
|
+
import process6 from "process";
|
|
944
|
+
var MARKER = "# Generated by `cli completions fish`";
|
|
945
|
+
var FISH_FILE = /\.fish$/;
|
|
946
|
+
var FUNCTION = `functions -q __kaynooo_cli_complete; or function __kaynooo_cli_complete --argument-names bin --description 'what a @kaynooo/cli-* bin offers for the word being typed'
|
|
947
|
+
set -l tokens (commandline -xpc 2>/dev/null; or commandline -opc)
|
|
948
|
+
set -l current (commandline -ct)
|
|
949
|
+
for line in ($bin __complete $tokens[2..-1] $current 2>/dev/null)
|
|
950
|
+
if test "$line" = __files__
|
|
951
|
+
__fish_complete_path $current
|
|
952
|
+
else
|
|
953
|
+
echo $line
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
end`;
|
|
957
|
+
function completionDir(home, env = process6.env) {
|
|
958
|
+
return join(env.XDG_CACHE_HOME ?? join(home, ".cache"), "fish", "kaynooo-cli");
|
|
959
|
+
}
|
|
960
|
+
function installedBins(host) {
|
|
961
|
+
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
962
|
+
}
|
|
963
|
+
function completionFile(bin) {
|
|
964
|
+
return `${MARKER}
|
|
965
|
+
${FUNCTION}
|
|
966
|
+
complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
967
|
+
`;
|
|
968
|
+
}
|
|
969
|
+
async function installCompletions(host, dir) {
|
|
970
|
+
const bins = installedBins(host);
|
|
971
|
+
mkdirSync(dir, { recursive: true });
|
|
972
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync(join(dir, name), "utf8").startsWith(MARKER));
|
|
973
|
+
for (const name of removed)
|
|
974
|
+
rmSync(join(dir, name));
|
|
975
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join(dir, `${bin}.fish`), completionFile(bin))));
|
|
976
|
+
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
977
|
+
}
|
|
978
|
+
function missingCompletions(host, dir) {
|
|
979
|
+
return installedBins(host).filter((bin) => !existsSync(join(dir, `${bin}.fish`)));
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/checks.ts
|
|
983
|
+
var TOOLS = {
|
|
984
|
+
"@kaynooo/cli-base": ["base", "proof"],
|
|
985
|
+
"@kaynooo/cli-changelog": ["changelog"],
|
|
986
|
+
"@kaynooo/cli-check": ["check", "fix", "errs"],
|
|
987
|
+
"@kaynooo/cli-code": ["peek", "todo-scan", "hotspots"],
|
|
988
|
+
"@kaynooo/cli-db": ["db"],
|
|
989
|
+
"@kaynooo/cli-doctor": ["doctor", "cli"],
|
|
990
|
+
"@kaynooo/cli-edit": ["sub"],
|
|
991
|
+
"@kaynooo/cli-env": ["ev", "redact"],
|
|
992
|
+
"@kaynooo/cli-forgejo": ["ft"],
|
|
993
|
+
"@kaynooo/cli-git": ["gst", "gc"],
|
|
994
|
+
"@kaynooo/cli-gitlab": ["gl"],
|
|
995
|
+
"@kaynooo/cli-guard": ["guard"],
|
|
996
|
+
"@kaynooo/cli-out": ["out"],
|
|
997
|
+
"@kaynooo/cli-pkg": ["pkg"],
|
|
998
|
+
"@kaynooo/cli-stack": ["stack"],
|
|
999
|
+
"@kaynooo/cli-view": ["view"],
|
|
1000
|
+
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
1001
|
+
};
|
|
1002
|
+
var FORGE = "git.kaynooo.fr";
|
|
1003
|
+
var FORGE_HOSTS = new Set([FORGE, "git.kevyn.fr"]);
|
|
1004
|
+
var PLUGIN = "cli-hooks@kaynooo-cli";
|
|
1005
|
+
var MARKETPLACE = `https://${FORGE}/kaynooo/cli.git`;
|
|
1006
|
+
var LEADING_TILDE = /^~(?=\/|$)/;
|
|
1007
|
+
var VERSION = /^\d+\.\d+\.\d+(?:-[\w.]+)?$/;
|
|
1008
|
+
var INFO_AT_ONCE = 4;
|
|
1009
|
+
function readJsonText(text) {
|
|
1010
|
+
try {
|
|
1011
|
+
return JSON.parse(text);
|
|
1012
|
+
} catch {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
function readJson(path) {
|
|
1017
|
+
return existsSync2(path) ? readJsonText(readFileSync2(path, "utf8")) : undefined;
|
|
1018
|
+
}
|
|
1019
|
+
function firstLine(text) {
|
|
1020
|
+
const trimmed = text.trim();
|
|
1021
|
+
const end = trimmed.indexOf(`
|
|
1022
|
+
`);
|
|
1023
|
+
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
1024
|
+
}
|
|
1025
|
+
function installed(host, name) {
|
|
1026
|
+
return readJson(join2(host.globalModules, name, "package.json"));
|
|
1027
|
+
}
|
|
1028
|
+
function ownBin(host, name, bin) {
|
|
1029
|
+
const declared = installed(host, name)?.bin;
|
|
1030
|
+
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
1031
|
+
return file === undefined ? undefined : join2(host.globalModules, name, file);
|
|
1032
|
+
}
|
|
1033
|
+
async function checkTool(host, name, bins) {
|
|
1034
|
+
const check = name.slice(name.indexOf("/") + 1);
|
|
1035
|
+
const version = installed(host, name)?.version;
|
|
1036
|
+
if (version === undefined)
|
|
1037
|
+
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
1038
|
+
const off = bins.filter((bin) => host.which(bin) === null);
|
|
1039
|
+
if (off.length > 0)
|
|
1040
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join2(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1041
|
+
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
1042
|
+
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
1043
|
+
`).at(-1) ?? "") : "";
|
|
1044
|
+
const latest = VERSION.test(answered) ? answered : "";
|
|
1045
|
+
if (latest === "")
|
|
1046
|
+
return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
|
|
1047
|
+
if (Bun.semver.order(version, latest) < 0)
|
|
1048
|
+
return { check, ok: false, detail: `${version}, ${latest} released`, fix: `bun install -g ${name}@${latest}` };
|
|
1049
|
+
return { check, ok: true, detail: version };
|
|
1050
|
+
}
|
|
1051
|
+
async function checkTea(host) {
|
|
1052
|
+
if (host.which("tea") === null)
|
|
1053
|
+
return { check: "tea", ok: false, detail: "not installed, and ft goes through it", fix: "install tea from https://gitea.com/gitea/tea/releases" };
|
|
1054
|
+
const logins = await host.run(["tea", "login", "list", "-o", "json"]);
|
|
1055
|
+
const parsed = logins.code === 0 ? readJsonText(logins.stdout) : undefined;
|
|
1056
|
+
const login = parsed?.find((each) => URL.canParse(each.url) && FORGE_HOSTS.has(new URL(each.url).host));
|
|
1057
|
+
if (login === undefined)
|
|
1058
|
+
return { check: "tea", ok: false, detail: `no login to ${FORGE}`, fix: `tea login add --url https://${FORGE} --oauth` };
|
|
1059
|
+
const version = await host.run(["tea", "api", "--login", login.name, "version"]);
|
|
1060
|
+
const served = version.code === 0 ? readJsonText(version.stdout)?.version : undefined;
|
|
1061
|
+
const forge = new URL(login.url).host;
|
|
1062
|
+
if (served === undefined)
|
|
1063
|
+
return { check: "tea", ok: false, detail: `${forge} did not answer: ${firstLine(version.stderr || version.stdout)}`, fix: `tea login edit ${login.name} --token <token>, or check the network` };
|
|
1064
|
+
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
1065
|
+
}
|
|
1066
|
+
function checkHooks(host) {
|
|
1067
|
+
const settings = readJson(join2(host.home, ".claude", "settings.json")) ?? {};
|
|
1068
|
+
const plugin = settings.enabledPlugins?.[PLUGIN] === true;
|
|
1069
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync2(join2(dir, "hooks", "hooks.json")));
|
|
1070
|
+
if (plugin && mod !== undefined)
|
|
1071
|
+
return { check: "hooks", ok: false, detail: `both the ${PLUGIN} plugin and the mod in ${mod} run guard, so every call is checked twice`, fix: `claude plugin disable ${PLUGIN}` };
|
|
1072
|
+
if (plugin)
|
|
1073
|
+
return { check: "hooks", ok: true, detail: `the ${PLUGIN} plugin` };
|
|
1074
|
+
if (mod !== undefined)
|
|
1075
|
+
return { check: "hooks", ok: true, detail: `the cli mod in ${mod}` };
|
|
1076
|
+
return { check: "hooks", ok: false, detail: "neither the plugin nor the cli mod is wired into ~/.claude/settings.json", fix: `claude plugin install cli-hooks --marketplace ${MARKETPLACE}` };
|
|
1077
|
+
}
|
|
1078
|
+
function checkBun(host) {
|
|
1079
|
+
const floors = Object.keys(TOOLS).flatMap((name) => installed(host, name)?.engines?.bun ?? []);
|
|
1080
|
+
const unmet = floors.filter((range) => !Bun.semver.satisfies(host.bunVersion, range));
|
|
1081
|
+
if (unmet.length > 0)
|
|
1082
|
+
return { check: "bun", ok: false, detail: `${host.bunVersion}, the tools ask for ${[...new Set(unmet)].join(" and ")}`, fix: "bun upgrade" };
|
|
1083
|
+
return { check: "bun", ok: true, detail: floors.length === 0 ? host.bunVersion : `${host.bunVersion}, the tools ask for ${[...new Set(floors)].join(" and ")}` };
|
|
1084
|
+
}
|
|
1085
|
+
function checkCompletions(host) {
|
|
1086
|
+
if (host.which("fish") === null)
|
|
1087
|
+
return { check: "completions", ok: true, detail: "fish is not installed" };
|
|
1088
|
+
const dir = completionDir(host.home);
|
|
1089
|
+
const missing = missingCompletions(host, dir);
|
|
1090
|
+
if (missing.length > 0)
|
|
1091
|
+
return { check: "completions", ok: false, detail: `no fish completion in ${dir} for ${missing.join(", ")}`, fix: "cli completions fish --install" };
|
|
1092
|
+
return { check: "completions", ok: true, detail: dir };
|
|
1093
|
+
}
|
|
1094
|
+
async function runChecks(host) {
|
|
1095
|
+
const entries = Object.entries(TOOLS);
|
|
1096
|
+
const tools = [];
|
|
1097
|
+
for (let at = 0;at < entries.length; at += INFO_AT_ONCE)
|
|
1098
|
+
tools.push(...await Promise.all(entries.slice(at, at + INFO_AT_ONCE).map(async ([name, bins]) => checkTool(host, name, bins))));
|
|
1099
|
+
return [...tools, await checkTea(host), checkHooks(host), checkBun(host), checkCompletions(host)];
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/host.ts
|
|
1103
|
+
import { homedir } from "os";
|
|
1104
|
+
import { join as join3 } from "path";
|
|
1105
|
+
import process7 from "process";
|
|
1106
|
+
var RUN_TIMEOUT_MS = 20000;
|
|
1107
|
+
function hostHere() {
|
|
1108
|
+
const home = homedir();
|
|
1109
|
+
return {
|
|
1110
|
+
home,
|
|
1111
|
+
globalModules: join3(process7.env.BUN_INSTALL ?? join3(home, ".bun"), "install", "global", "node_modules"),
|
|
1112
|
+
bunVersion: Bun.version,
|
|
1113
|
+
which: (bin) => Bun.which(bin),
|
|
1114
|
+
run: async (argv, env) => {
|
|
1115
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process7.env, ...env } } });
|
|
1116
|
+
if (ran.timedOut)
|
|
1117
|
+
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
1118
|
+
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
1119
|
+
return { code: ran.code, stdout: of("stdout"), stderr: of("stderr") };
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/cli.ts
|
|
1125
|
+
var USAGE = `doctor [--json]: checks this host's @kaynooo/cli-* install, one line per check, the command that fixes a failing one beside it
|
|
1126
|
+
|
|
1127
|
+
Each @kaynooo/cli-* package installed globally with its bins on the PATH and no older than the registry's latest; tea logged in to git.kaynooo.fr and reaching it; the Claude Code hooks wired through the cli-hooks plugin or the cli mod, not both; Bun at least the version the tools ask for.
|
|
1128
|
+
Exits 0 when every check passes, 1 when one fails, 2 on a usage error.`;
|
|
1129
|
+
var INSTALL = "bun install -g ";
|
|
1130
|
+
var OPTIONS = {
|
|
1131
|
+
json: jsonOption("[{check, ok, detail, fix?}]")
|
|
1132
|
+
};
|
|
1133
|
+
function print(results) {
|
|
1134
|
+
const { green, red, dim } = paint(process8.stdout);
|
|
1135
|
+
const width = Math.max(...results.map((result) => result.check.length));
|
|
1136
|
+
for (const { check, ok, detail, fix } of results)
|
|
1137
|
+
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
1138
|
+
const installs = results.flatMap((result) => result.fix?.startsWith(INSTALL) === true ? [result.fix.slice(INSTALL.length)] : []);
|
|
1139
|
+
if (installs.length > 1)
|
|
1140
|
+
console.log(`
|
|
1141
|
+
${dim("fix them all:")} ${INSTALL}${installs.join(" ")}`);
|
|
1142
|
+
}
|
|
1143
|
+
async function main(args, host = hostHere()) {
|
|
1144
|
+
const { values } = parse(args, USAGE, OPTIONS, { positionals: [], replaces: "checking each bin's version, tea's login and the hooks by hand", examples: ["doctor", ["doctor --json", "the checks for a script"]] });
|
|
1145
|
+
const results = await runChecks(host);
|
|
1146
|
+
if (values.json === true)
|
|
1147
|
+
printJson(results);
|
|
1148
|
+
else
|
|
1149
|
+
print(results);
|
|
1150
|
+
return results.every((result) => result.ok) ? 0 : 1;
|
|
1151
|
+
}
|
|
1152
|
+
if (import.meta.main)
|
|
1153
|
+
process8.exitCode = await runMain({ name: "doctor", version, run: async (args) => main(args) });
|
|
1154
|
+
export {
|
|
1155
|
+
main
|
|
1156
|
+
};
|